diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000000..4edbaa62765 --- /dev/null +++ b/.clang-format @@ -0,0 +1,111 @@ +--- +Language: Cpp +# BasedOnStyle: Google +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 120 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^' + Priority: 2 + - Regex: '^<.*\.h>' + Priority: 1 + - Regex: '^<.*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: false +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Right +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: true +SpaceAfterCStyleCast: true +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +TabWidth: 8 +UseTab: Never \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..9f00b755a6e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,31 @@ +# https://editorconfig.org/ + +root = true + +[*] +trim_trailing_whitespace = true +insert_final_newline = true +end_of_line = lf +charset = utf-8 +indent_style = space +tab_width = 4 + +[{*.{awk,sh},Makefile*}] +indent_size = 4 +indent_style = table +max_line_length = 80 + +[*.{c,cc,cpp,h,html,inc,php,phpt}] +indent_size = 4 +indent_style = space +max_line_length = 120 + +[*.md] +indent_style = space +max_line_length = 120 + +[COMMIT_EDITMSG] +indent_size = 4 +indent_style = space +max_line_length = 80 + 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 6edc26ee1ce..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://www.paypal.me/swooleinc','https://gitee.com/swoole/swoole?donate=true'] diff --git a/.github/ISSUE-CN.md b/.github/ISSUE-CN.md index d2f5cf39b9f..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的任何要求与建议. 在你提交错误报告之前, 请先阅读以下的**如何提交错误报告**. @@ -21,7 +21,7 @@ Swoole的内核开发者们或许还不知道问题的存在, Please answer these questions before submitting your issue. Thanks! > 在提交Issue前请回答以下问题: - + 1. What did you do? If possible, provide a simple script for reproducing the error. > 请详细描述问题的产生过程,贴出相关的代码,最好能提供一份可稳定重现的简单脚本代码。 @@ -32,16 +32,26 @@ Please answer these questions before submitting your issue. Thanks! > 实际运行的结果是什么? 4. What version of Swoole are you using (`php --ri swoole`)? -> 你的版本? 贴出 `php --ri swoole` 所打印的内容 +> 你的版本? 贴出 `php --ri swoole` 所打印的内容 5. What is your machine environment used (including the version of kernel & php & gcc)? -> 你使用的机器系统环境是什么(包括内核、PHP、gcc编译器版本信息)? +> 你使用的机器系统环境是什么(包括内核、PHP、gcc编译器版本信息)? > 可以使用`uname -a`, `php -v`, `gcc -v` 命令打印 ``` 其中, 最为关键的是提供**可稳定重现的简单脚本代码**, 否则你必须提供尽可能多的其它信息来帮助开发者判断错误原因 +## 内存分析 (强烈推荐) + +更多时候, Valgrind比gdb更能发现内存问题, 通过以下指令运行你的程序, 直到触发BUG + +```shell +USE_ZEND_ALLOC=0 valgrind --log-file=/tmp/valgrind.log php your_file.php +``` + +* 当程序发生错误时, 可以通过键入 `ctrl+c` 退出, 然后上传 `/tmp/valgrind.log` 文件以便于开发组定位BUG. + ## 关于段错误(核心转储) 此外, 在一种特殊情况下你可以使用调试工具来帮助开发者定位问题 @@ -59,8 +69,7 @@ WARNING swManager_check_exit_status: worker#1 abnormal exit, status=0, signal=11 ulimit -c unlimited ``` -触发BUG, 核心转储文件会生成在 程序目录 或 系统根目录 或 /cores目录下 (取决于你的系统配置) -键入以下命令进入gdb调试程序 +触发BUG, 核心转储文件会生成在 程序目录 或 系统根目录 或 `/cores` 目录下 (取决于你的系统配置), 键入以下命令进入gdb调试程序 ``` gdb php core gdb php /tmp/core.4596 @@ -78,13 +87,3 @@ gdb php /tmp/core.4596 ``` 将以上信息都贴在issue中 - -#### 内存分析 (强烈推荐) - -更多时候, Valgrind比gdb更能发现内存问题, 通过以下指令运行你的程序, 直到触发BUG - -```shell -USE_ZEND_ALLOC=0 valgrind --log-file= php xxx.php -``` - -* 当程序发生错误时, 可以通过键入 `ctrl+c` 退出, 然后上传 `/tmp/valgrind.log` 文件以便于开发组定位BUG. diff --git a/.github/ISSUE.md b/.github/ISSUE.md index 58dc8a20e7d..3cbe4908678 100644 --- a/.github/ISSUE.md +++ b/.github/ISSUE.md @@ -4,7 +4,7 @@ English | [中文](./ISSUE-CN.md) ## Instruction -If you think you have found a bug in Swoole, please report it. +If you think you have found a bug in Swoole, please report it. The Swoole developers probably don't know about it, and unless you report it, chances are it won't be fixed. You can report bugs at https://github.com/swoole/swoole-src/issues. @@ -16,7 +16,6 @@ Please read the **How to report a bug document** before submitting any bug repor ## New issue First, while creating an issue, the system will give the following template: -> 首先在创建issue的同时, 系统将会给出如下模板, 请你认真填写它, 否则issue由于缺乏信息可能会被忽略: ```markdown Please answer these questions before submitting your issue. Thanks! @@ -28,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 @@ -36,7 +45,7 @@ Besides, In a special case, you can use debugging tools to help developers locat WARNING swManager_check_exit_status: worker#1 abnormal exit, status=0, signal=11 ``` -When a segmentation error occurs with Swoole, You can use the `gdb` tool and use `bt` command. +When a segmentation error occurs with Swoole, You can use the `gdb` tool and use `bt` command. > Using `gdb` to track the core file need to add the `--enable-debug` parameter when compiling `swoole`. Enable core dump @@ -61,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= 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 1dca2b04559..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. @@ -16,6 +16,6 @@ Please answer these questions before submitting your issue. Thanks! -5. What is your machine environment used (including version of kernel & php & gcc) ? +5. What is your machine environment used (show your `uname -a` & `php -v` & `gcc -v`) ? 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 new file mode 100644 index 00000000000..494bb252deb --- /dev/null +++ b/.github/workflows/alpine.Dockerfile @@ -0,0 +1,23 @@ +ARG PHP_VERSION +ARG ALPINE_VERSION + +FROM phpswoole/php:${PHP_VERSION}-alpine + +LABEL maintainer="Swoole Team " version="1.0" license="Apache2" + +ARG PHP_VERSION + +COPY . /swoole + +WORKDIR /swoole + +RUN set -ex \ + && phpize \ + && ./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 89b09c94393..b9232561366 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -1,15 +1,85 @@ -name: ext-swoole +name: Compile Tests -on: [push, pull_request] +on: [ push, pull_request ] -jobs: - build: +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@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 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 - - name: configure - run: phpize && ./configure --enable-sockets --enable-mysqlnd --enable-http2 --enable-debug-log - - name: make - run: make + - uses: actions/checkout@v4 + - name: build + run: | + cp .github/workflows/alpine.Dockerfile alpine.Dockerfile + docker build -t swoole . -f alpine.Dockerfile --build-arg PHP_VERSION=${{ matrix.php-version }} diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml new file mode 100644 index 00000000000..12c6523bea5 --- /dev/null +++ b/.github/workflows/framework.yml @@ -0,0 +1,122 @@ +name: Framework Tests + +on: + push: + pull_request: + +env: + CPPFLAGS: "-I/opt/homebrew/opt/pcre2/include/" + +jobs: + linux: + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[framework]')" + strategy: + fail-fast: false + matrix: + php-version: [ '8.1', '8.2', '8.3' ] + framework: [ 'Laravel Octane', 'Hyperf', 'Simps' ] + name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: dom, curl, libxml, mbstring, zip, redis, pdo, pdo_mysql, bcmath + tools: phpize, composer:v2 + ini-values: extension=swoole + coverage: none + + - name: Build Swoole + run: | + sudo apt update -y && sudo apt install -y libcurl4-openssl-dev php-curl libc-ares-dev libpq-dev valgrind + phpize + ./configure --enable-openssl --enable-mysqlnd --enable-swoole-curl --enable-cares --enable-swoole-pgsql + make -j$(nproc) + sudo make install + php -v + php -m + php --ini + php --ri swoole + + - name: Laravel Octane Tests + if: matrix.framework == 'Laravel Octane' && matrix.php-version != '8.3' + run: | + git clone https://github.com/laravel/octane.git --depth=1 + cd octane/ + composer update --prefer-dist --no-interaction --no-progress + vendor/bin/phpunit + + - name: Hyperf Tests + if: matrix.framework == 'Hyperf' && matrix.php-version != '8.3' + env: + SW_VERSION: 'master' + MYSQL_VERSION: '5.7' + PGSQL_VERSION: '14' + run: | + git clone https://github.com/hyperf/hyperf.git --depth=1 + cd hyperf/ + composer update -o + ./.travis/requirement.install.sh + ./.travis/setup.services.sh + export TRAVIS_BUILD_DIR=$(pwd) && bash ./.travis/setup.mysql.sh + export TRAVIS_BUILD_DIR=$(pwd) && bash ./.travis/setup.pgsql.sh + cp .travis/.env.example .env + export SWOOLE_BRANCH=${GITHUB_REF##*/} + if [ "${SWOOLE_BRANCH}" = "valgrind" ]; then + USE_ZEND_ALLOC=0 valgrind php -dswoole.use_shortname='Off' bin/co-phpunit --exclude-group NonCoroutine + USE_ZEND_ALLOC=0 valgrind php -dswoole.use_shortname='Off' vendor/bin/phpunit --group NonCoroutine + USE_ZEND_ALLOC=0 valgrind php -dswoole.use_shortname='Off' vendor/bin/phpunit src/filesystem --group NonCoroutine + else + .travis/run.test.sh + fi + + - name: Simps Tests + if: matrix.framework == 'Simps' + run: | + git clone https://github.com/simps/mqtt.git --depth=1 + cd mqtt/ + composer install -o + composer test + + macos: + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[framework]')" + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + php-version: [ '8.1', '8.2', '8.3' ] + framework: [ 'Simps' ] + name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} - macOS + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: dom, curl, libxml, mbstring, zip, redis, pdo, pdo_mysql, bcmath + tools: phpize, composer:v2 + ini-values: extension=swoole + coverage: none + + - name: Build Swoole + run: | + phpize + ./configure CPPFLAGS="${CPPFLAGS}" --enable-openssl --enable-mysqlnd --enable-swoole-curl --enable-cares + make -j$(sysctl -n hw.ncpu) + sudo make install + php --ri swoole + + - name: Simps Tests + if: matrix.framework == 'Simps' + run: | + git clone https://github.com/simps/mqtt.git --depth=1 + cd mqtt/ + composer install -o + composer test + diff --git a/.github/workflows/iouring.yml b/.github/workflows/iouring.yml new file mode 100644 index 00000000000..047bfdfca05 --- /dev/null +++ b/.github/workflows/iouring.yml @@ -0,0 +1,40 @@ +name: Linux io_uring Tests + +on: [push, pull_request] + +jobs: + test-linux: + if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[iouring]')" + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + php: ['8.1', '8.2', '8.3', '8.1-zts', '8.2-zts', '8.3-zts'] + name: ${{ matrix.php }} + steps: + - uses: actions/checkout@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php }}" + coverage: none + - name: Show machine information + run: | + date + env + uname -a + ulimit -a + php -v + php --ini + ls -al + pwd + echo "`git log -20 --pretty --oneline`" + echo "`git log -10 --stat --pretty --oneline`" + - name: Run Swoole test + run: | + export SWOOLE_BRANCH=${GITHUB_REF##*/} + export SWOOLE_USE_IOURING=1 + export SWOOLE_BUILD_DIR=$(realpath .) + export PHP_VERSION=${{ matrix.php }} + ${SWOOLE_BUILD_DIR}/scripts/route.sh + diff --git a/.github/workflows/lib.yml b/.github/workflows/lib.yml deleted file mode 100644 index 188fd4fd67a..00000000000 --- a/.github/workflows/lib.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: lib-swoole - -on: [push, pull_request] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - name: gtest_install - run: sudo apt install -y googletest libgtest-dev && - cd /usr/src/googletest/googletest && - sudo mkdir build && - cd build && - sudo cmake .. && - sudo make && - 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 . && make && sudo make install - - name: make test - run: cd core-tests && ./run.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 0609263f2aa..9068deb313d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,15 +7,14 @@ *.so *.loT *.pid -*.gcno -*.gcov -/Debug/* +*.dep modules/* /.deps /.libs/ /core /examples/core /Debug +/Release /CMakeFiles /cmake_install.cmake /CMakeCache.txt @@ -81,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 @@ -93,9 +106,15 @@ 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 + +# coverage +*.gcno +*.gcov +*.gcda +*.info +/html +/tests/include/lib/vendor/ +/tests/include/lib/composer.lock +/scripts/data diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index b9a6d68584e..00000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "thirdparty/jemalloc"] - path = thirdparty/jemalloc - url = https://github.com/jemalloc/jemalloc.git diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index a2b60704add..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,67 +0,0 @@ -language: php - -compiler: - - gcc - - clang - -os: - - linux - -php: - - 7.1 - - 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 a70882400ef..008a4901608 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,16 +1,58 @@ PROJECT(libswoole) +cmake_minimum_required(VERSION 2.8.12) ENABLE_LANGUAGE(ASM) -SET(SWOOLE_VERSION 4.5.0) -SET(SWOOLE_CLFLAGS pthread rt dl ssl crypt crypto) +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() SET(CMAKE_BUILD_TYPE Debug) -file(GLOB_RECURSE SRC_LIST FOLLOW_SYMLINKS src/*.c src/*.cc thirdparty/boost/asm/combined.S) +# Code Coverage Configuration +add_library(coverage_config INTERFACE) + +option(CODE_COVERAGE "Enable coverage reporting" OFF) +if(CODE_COVERAGE) + message(STATUS "Open coverage") + # --coverage => -fprofile-arcs -ftest-coverage + target_compile_options(coverage_config INTERFACE + -O0 + -g + --coverage + ) + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) + target_link_options(coverage_config INTERFACE --coverage) + else() + target_link_libraries(coverage_config INTERFACE --coverage) + endif() +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) @@ -24,76 +66,94 @@ add_definitions(-DHAVE_CONFIG_H) # test #add_definitions(-DSW_USE_THREAD_CONTEXT) -INCLUDE_DIRECTORIES(BEFORE ./include ./include/wrapper ./) +include_directories(BEFORE ./include ./include/wrapper ext-src/ thirdparty/ ./) SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) +# find OpenSSL +if (DEFINED openssl_dir) + include_directories(BEFORE ${openssl_dir}/include) + link_directories(${openssl_dir}/lib) +else() + find_package(OpenSSL) + if (${OPENSSL_FOUND}) + message(STATUS "Found OpenSSL, ${OPENSSL_LIBRARIES}") + include_directories(BEFORE ${OPENSSL_INCLUDE_DIR}) + list(APPEND SWOOLE_LINK_LIBRARIES ssl crypto) + else() + message(STATUS "Not found OpenSSL") + endif() +endif() + +if (DEFINED brotli_dir) + include_directories(BEFORE ${brotli_dir}/include) + 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}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${PHP_INCLUDES}") +if (CMAKE_SYSTEM_NAME MATCHES "Linux") + execute_process(COMMAND ldconfig -p OUTPUT_VARIABLE LDCONFIG_LIST OUTPUT_STRIP_TRAILING_WHITESPACE) + #message(STATUS LDCONFIG_LIST) + if (LDCONFIG_LIST MATCHES brotlienc) + list(APPEND SWOOLE_LINK_LIBRARIES brotlienc) + endif() + + if (LDCONFIG_LIST MATCHES brotlidec) + list(APPEND SWOOLE_LINK_LIBRARIES brotlidec) + endif() +endif() + # lib-swoole -LINK_DIRECTORIES(${LIBRARY_OUTPUT_PATH}) +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_CLFLAGS}) +target_link_libraries(lib-swoole ${SWOOLE_LINK_LIBRARIES}) + +if (CODE_COVERAGE) + target_link_libraries(lib-swoole coverage_config gcov) +endif(CODE_COVERAGE) # test_server set(TEST_SRC_LIST examples/cpp/test_server.cc) add_executable(test_server ${TEST_SRC_LIST}) add_dependencies(test_server lib-swoole) -target_link_libraries(test_server ${SWOOLE_CLFLAGS} swoole) +target_link_libraries(test_server swoole pthread) # co set(TEST_SRC_LIST examples/cpp/co.cc) add_executable(co ${TEST_SRC_LIST}) add_dependencies(co lib-swoole) -target_link_libraries(co ${SWOOLE_CLFLAGS} swoole) +target_link_libraries(co swoole) # ext-swoole -set(ext_src_list php_swoole_cxx.cc - swoole.cc - swoole_async_coro.cc - swoole_atomic.cc - swoole_channel_coro.cc - swoole_client.cc - swoole_client_coro.cc - swoole_coroutine.cc - swoole_coroutine_scheduler.cc - swoole_coroutine_system.cc - swoole_event.cc - swoole_http2_client_coro.cc - swoole_http2_server.cc - swoole_http_client_coro.cc - swoole_http_request.cc - swoole_http_response.cc - swoole_http_server.cc - swoole_http_server_coro.cc - swoole_lock.cc - swoole_mysql_coro.cc - swoole_mysql_proto.cc - swoole_process.cc - swoole_process_pool.cc - swoole_redis_coro.cc - swoole_redis_server.cc - swoole_runtime.cc - swoole_server.cc - swoole_server_port.cc - swoole_socket_coro.cc - swoole_table.cc - swoole_timer.cc - swoole_websocket_server.cc +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/hiredis/async.c - thirdparty/hiredis/hiredis.c - thirdparty/hiredis/net.c - thirdparty/hiredis/read.c - thirdparty/hiredis/sds.c thirdparty/nghttp2/nghttp2_hd.c thirdparty/nghttp2/nghttp2_rcbuf.c thirdparty/nghttp2/nghttp2_helper.c @@ -106,10 +166,24 @@ add_library(ext-swoole SHARED ${ext_src_list}) 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_CLFLAGS} swoole) -#install -INSTALL(CODE "MESSAGE(\"Are you run command using root user?\")") +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(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) INSTALL(FILES ${HEAD_WAPPER_FILES} DESTINATION include/swoole/wrapper) 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/Makefile.frag b/Makefile.frag new file mode 100644 index 00000000000..ddb3a5d6e86 --- /dev/null +++ b/Makefile.frag @@ -0,0 +1,11 @@ +swoole-build-coverage: + CCACHE_DISABLE=1 EXTRA_CFLAGS="-fprofile-arcs -ftest-coverage" EXTRA_CXXFLAGS="-fprofile-arcs -ftest-coverage" $(MAKE) + +swoole-test-coverage: + CCACHE_DISABLE=1 EXTRA_CFLAGS="-fprofile-arcs -ftest-coverage" EXTRA_CXXFLAGS="-fprofile-arcs -ftest-coverage" $(MAKE) && $(MAKE) install && $(top_srcdir)/tests/start.sh $(top_srcdir)/tests + +swoole-test-coverage-lcov: swoole-test-coverage + lcov -c --directory $(top_srcdir)/.libs --output-file $(top_srcdir)/coverage.info + +swoole-test-coverage-html: swoole-test-coverage-lcov + genhtml $(top_srcdir)/coverage.info --output-directory=$(top_srcdir)/html diff --git a/README-CN.md b/README-CN.md deleted file mode 100644 index 2559f692dcc..00000000000 --- a/README-CN.md +++ /dev/null @@ -1,533 +0,0 @@ -[English](./README.md) | 中文 - -# Swoole - -[![Latest Version](https://img.shields.io/github/release/swoole/swoole-src.svg?style=flat-square)](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) -[![Join the chat at https://gitter.im/swoole/swoole-src](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/swoole/swoole-src?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Coverity Scan Build Status](https://scan.coverity.com/projects/11654/badge.svg)](https://scan.coverity.com/projects/swoole-swoole-src) -[![Backers on Open Collective](https://opencollective.com/swoole-src/backers/badge.svg)](#backers) -[![Sponsors on Open Collective](https://opencollective.com/swoole-src/sponsors/badge.svg)](#sponsors) - -![](./mascot.png) - -**Swoole是一个为PHP用C和C++编写的基于事件的高性能异步&协程并行网络通信引擎** - -## ✨事件驱动 - -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](./benchmark/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 ea3adbd71dd..991e85f5468 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,142 @@ -English | [中文](./README-CN.md) +

+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) + +[![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) -# Swoole +## ⚙️ Quick Start -[![Latest Version](https://img.shields.io/github/release/swoole/swoole-src.svg?style=flat-square)](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) -[![Join the chat at https://gitter.im/swoole/swoole-src](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/swoole/swoole-src?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Coverity Scan Build Status](https://scan.coverity.com/projects/11654/badge.svg)](https://scan.coverity.com/projects/swoole-swoole-src) -[![Backers on Open Collective](https://opencollective.com/swoole-src/backers/badge.svg)](#backers) -[![Sponsors on Open Collective](https://opencollective.com/swoole-src/sponsors/badge.svg)](#sponsors) +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.** -![](./mascot.png) +### Supported extension/functions -**Swoole is an event-driven asynchronous & coroutine-based concurrency networking communication engine with high performance written in C and 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. @@ -305,7 +421,7 @@ Co\run(function() { } }); } - + // 10K file read and write for ($c = 100; $c--;) { go(function () use ($c) { @@ -318,7 +434,7 @@ Co\run(function() { unlink($tmp_filename); }); } - + // 10K pdo and mysqli read for ($c = 50; $c--;) { go(function () { @@ -342,18 +458,18 @@ Co\run(function() { } }); } - + // 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( @@ -394,7 +510,7 @@ Co\run(function() { } }); } - + // udp server & client with 12.8K requests in single process go(function () { $socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_DGRAM, 0); @@ -439,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) @@ -450,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: @@ -476,25 +592,24 @@ 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 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 @@ -503,28 +618,10 @@ 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. -+ You can just run [Benchmark Script](./benchmark/benchmark.php) to quickly test the maximum QPS of Swoole-HTTP-Server on your machine. ++ You can just run [Benchmark Script](https://github.com/swoole/benchmark/blob/master/benchmark.php) to quickly test the maximum QPS of Swoole-HTTP-Server on your machine. ## 🔰️ Security issues diff --git a/SUPPORTED.md b/SUPPORTED.md deleted file mode 100644 index 95269d312b0..00000000000 --- a/SUPPORTED.md +++ /dev/null @@ -1,20 +0,0 @@ -Supported Versions ---- -| Branch | Initialization | Active Support Until | Security Support Until | -| ---------------------------------------------------------- | -------------- | -------------------- | ---------------------- | -| [v4.3.x](https://github.com/swoole/swoole-src/tree/v4.3.x) | 2019-2-7 | 2019-9-30 | 2019-12-31 | -| [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) 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 c5386b2f892..82a8645d512 100644 --- a/config.m4 +++ b/config.m4 @@ -12,42 +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 >= 0.9.6)], 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(gcov, enable gcov, -[ --enable-gcov Enable gcov], 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 @@ -85,6 +183,7 @@ AC_DEFUN([AC_SWOOLE_CPU_AFFINITY], #include typedef cpuset_t cpu_set_t; #else + #define _GNU_SOURCE 1 #include #endif ]], [[ @@ -137,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 @@ -154,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 ]], [[ @@ -165,40 +265,67 @@ AC_DEFUN([AC_SWOOLE_HAVE_VALGRIND], ],[ 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_BOOST_STACKTRACE], +[ + AC_MSG_CHECKING([for valgrind]) + AC_LANG_PUSH([C++]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include + ]], [[ + + ]])],[ + AC_DEFINE([HAVE_BOOST_STACKTRACE], 1, [have boost-stacktrace?]) + AC_MSG_RESULT([yes]) + ],[ + AC_MSG_RESULT([no]) ]) + AC_LANG_POP([C++]) +]) - if test "$ac_cv_cmsghdr" = yes; then - AC_DEFINE(HAVE_CMSGHDR,1,[Whether you have struct cmsghdr]) - fi +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]) + ]) +]) + +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], [ @@ -250,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 @@ -273,32 +399,534 @@ if test "$PHP_SWOOLE" != "no"; then AC_CHECK_LIB(c, inotify_init1, AC_DEFINE(HAVE_INOTIFY_INIT1, 1, [have inotify_init1])) AC_CHECK_LIB(c, gethostbyname2_r, AC_DEFINE(HAVE_GETHOSTBYNAME2_R, 1, [have gethostbyname2_r])) AC_CHECK_LIB(c, ptrace, AC_DEFINE(HAVE_PTRACE, 1, [have ptrace])) + AC_CHECK_LIB(c, getrandom, AC_DEFINE(HAVE_GETRANDOM, 1, [have getrandom])) + AC_CHECK_LIB(c, arc4random, AC_DEFINE(HAVE_ARC4RANDOM, 1, [have arc4random])) + AC_CHECK_LIB(c, CCRandomGenerateBytes, AC_DEFINE(HAVE_CCRANDOMGENERATEBYTES, 1, [have_ccrandomgeneratebytes])) AC_CHECK_LIB(pthread, pthread_rwlock_init, AC_DEFINE(HAVE_RWLOCK, 1, [have pthread_rwlock_init])) AC_CHECK_LIB(pthread, pthread_spin_lock, AC_DEFINE(HAVE_SPINLOCK, 1, [have pthread_spin_lock])) AC_CHECK_LIB(pthread, pthread_mutex_timedlock, AC_DEFINE(HAVE_MUTEX_TIMEDLOCK, 1, [have pthread_mutex_timedlock])) AC_CHECK_LIB(pthread, pthread_barrier_init, AC_DEFINE(HAVE_PTHREAD_BARRIER, 1, [have pthread_barrier_init])) - AC_CHECK_LIB(pcre, pcre_compile, AC_DEFINE(HAVE_PCRE, 1, [have pcre])) + 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])) + + if test "$PHP_SWOOLE_DEV" = "yes"; then + AX_CHECK_COMPILE_FLAG(-Wbool-conversion, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wbool-conversion") + AX_CHECK_COMPILE_FLAG(-Wignored-qualifiers, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wignored-qualifiers") + AX_CHECK_COMPILE_FLAG(-Wduplicate-enum, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wduplicate-enum") + AX_CHECK_COMPILE_FLAG(-Wempty-body, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wempty-body") + AX_CHECK_COMPILE_FLAG(-Wenum-compare, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wenum-compare") + AX_CHECK_COMPILE_FLAG(-Wextra, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wextra") + AX_CHECK_COMPILE_FLAG(-Wformat-security, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wformat-security") + AX_CHECK_COMPILE_FLAG(-Wheader-guard, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wheader-guard") + AX_CHECK_COMPILE_FLAG(-Wincompatible-pointer-types-discards-qualifiers, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wincompatible-pointer-types-discards-qualifiers") + AX_CHECK_COMPILE_FLAG(-Winit-self, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Winit-self") + AX_CHECK_COMPILE_FLAG(-Wlogical-not-parentheses, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wlogical-not-parentheses") + AX_CHECK_COMPILE_FLAG(-Wlogical-op-parentheses, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wlogical-op-parentheses") + AX_CHECK_COMPILE_FLAG(-Wloop-analysis, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wloop-analysis") + AX_CHECK_COMPILE_FLAG(-Wuninitialized, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wuninitialized") + AX_CHECK_COMPILE_FLAG(-Wno-missing-field-initializers, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-missing-field-initializers") + AX_CHECK_COMPILE_FLAG(-Wno-sign-compare, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-sign-compare") + AX_CHECK_COMPILE_FLAG(-Wno-unused-const-variable, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-unused-const-variable") + AX_CHECK_COMPILE_FLAG(-Wno-unused-parameter, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-unused-parameter") + AX_CHECK_COMPILE_FLAG(-Wno-variadic-macros, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wno-variadic-macros") + AX_CHECK_COMPILE_FLAG(-Wparentheses, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wparentheses") + AX_CHECK_COMPILE_FLAG(-Wpointer-bool-conversion, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wpointer-bool-conversion") + AX_CHECK_COMPILE_FLAG(-Wsizeof-array-argument, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wsizeof-array-argument") + AX_CHECK_COMPILE_FLAG(-Wwrite-strings, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wwrite-strings") + AX_CHECK_COMPILE_FLAG(-fdiagnostics-show-option, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -fdiagnostics-show-option") + AX_CHECK_COMPILE_FLAG(-fno-omit-frame-pointer, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -fno-omit-frame-pointer") + AX_CHECK_COMPILE_FLAG(-fno-optimize-sibling-calls, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -fno-optimize-sibling-calls") + AX_CHECK_COMPILE_FLAG(-fsanitize-address, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -fsanitize-address") + AX_CHECK_COMPILE_FLAG(-fstack-protector, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -fstack-protector") + + EXTRA_CFLAGS="$_MAINTAINER_CFLAGS" + CFLAGS="-g -O0 -Wall $CFLAGS" + CXXFLAGS="-g -O0 -Wall $CXXFLAGS" + 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] ) @@ -313,17 +941,15 @@ if test "$PHP_SWOOLE" != "no"; then CFLAGS="$CFLAGS -fsanitize=address -fno-omit-frame-pointer" CXXFLAGS="$CXXFLAGS -fsanitize=address -fno-omit-frame-pointer" fi - - if test "$PHP_GCOV" != "no"; then - PHP_DEBUG=1 - CFLAGS="$CFLAGS -fprofile-arcs -ftest-coverage" - CXXFLAGS="$CXXFLAGS -fprofile-arcs -ftest-coverage" - fi if test "$PHP_TRACE_LOG" != "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]) @@ -345,249 +971,257 @@ 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 AC_SWOOLE_HAVE_UCONTEXT AC_SWOOLE_HAVE_VALGRIND AC_SWOOLE_CHECK_SOCKETS + AC_SWOOLE_HAVE_BOOST_STACKTRACE AS_CASE([$host_os], [darwin*], [SW_OS="MAC"], [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=" \ - php_swoole_cxx.cc \ - src/core/base.cc \ - src/core/channel.cc \ - src/core/error.cc \ - src/core/hashmap.cc \ - src/core/heap.cc \ - src/core/log.cc \ - src/core/ring_queue.cc \ - src/core/socket.cc \ - src/core/string.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/coroutine/ucontext.cc \ - src/lock/atomic.cc \ - src/lock/cond.cc \ - src/lock/file_lock.cc \ - src/lock/mutex.cc \ - src/lock/rw_lock.cc \ - src/lock/semaphore.cc \ - src/lock/spin_lock.cc \ - src/memory/buffer.cc \ - src/memory/fixed_pool.cc \ - src/memory/global_memory.cc \ - src/memory/malloc.cc \ - src/memory/ring_buffer.cc \ - src/memory/shared_memory.cc \ - src/memory/table.cc \ - src/network/client.cc \ - src/network/dns.cc \ - src/network/process_pool.cc \ - src/network/stream.cc \ - src/network/thread_pool.cc \ - src/network/timer.cc \ - src/os/async_thread.cc \ - src/os/base.cc \ - src/os/msg_queue.cc \ - src/os/sendfile.cc \ - src/os/signal.cc \ - src/os/timer.cc \ - src/os/wait.cc \ - src/pipe/base.cc \ - src/pipe/eventfd.cc \ - src/pipe/unix_socket.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/sha1.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/server.cc \ - src/wrapper/timer.cc \ - swoole.cc \ - swoole_async_coro.cc \ - swoole_atomic.cc \ - swoole_channel_coro.cc \ - swoole_client.cc \ - swoole_client_coro.cc \ - swoole_coroutine.cc \ - swoole_coroutine_scheduler.cc \ - swoole_coroutine_system.cc \ - swoole_event.cc \ - swoole_http2_client_coro.cc \ - swoole_http2_server.cc \ - swoole_http_client_coro.cc \ - swoole_http_request.cc \ - swoole_http_response.cc \ - swoole_http_server.cc \ - swoole_http_server_coro.cc \ - swoole_lock.cc \ - swoole_mysql_coro.cc \ - swoole_mysql_proto.cc \ - swoole_process.cc \ - swoole_process_pool.cc \ - swoole_redis_coro.cc \ - swoole_redis_server.cc \ - swoole_runtime.cc \ - swoole_server.cc \ - swoole_server_port.cc \ - swoole_socket_coro.cc \ - swoole_table.cc \ - swoole_timer.cc \ - swoole_websocket_server.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 \ thirdparty/php/sockets/sockaddr_conv.cc \ + thirdparty/php/standard/var_decoder.cc \ thirdparty/php/standard/proc_open.cc" swoole_source_file="$swoole_source_file \ thirdparty/swoole_http_parser.c \ - thirdparty/multipart_parser.c" + thirdparty/multipart_parser.c" - swoole_source_file="$swoole_source_file \ - thirdparty/hiredis/async.c \ - 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" @@ -599,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} \ @@ -621,13 +1278,29 @@ 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,,, 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/thirdparty/hiredis]) + PHP_ADD_INCLUDE([$ext_srcdir/ext-src]) + PHP_ADD_INCLUDE([$ext_srcdir/thirdparty]) + + AC_MSG_CHECKING([swoole coverage]) + if test "$PHP_SWOOLE_COVERAGE" != "no"; then + AC_MSG_RESULT([enabled]) + + PHP_ADD_MAKEFILE_FRAGMENT + else + AC_MSG_RESULT([disabled]) + fi - PHP_INSTALL_HEADERS([ext/swoole], [*.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() @@ -639,10 +1312,14 @@ 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) PHP_ADD_BUILD_DIR($ext_builddir/src/reactor) - PHP_ADD_BUILD_DIR($ext_builddir/src/pipe) PHP_ADD_BUILD_DIR($ext_builddir/src/lock) PHP_ADD_BUILD_DIR($ext_builddir/src/os) PHP_ADD_BUILD_DIR($ext_builddir/src/network) @@ -652,8 +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 994fc849dc3..807f5337cf6 100755 --- a/core-tests/CMakeLists.txt +++ b/core-tests/CMakeLists.txt @@ -1,17 +1,74 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 2.8.12) project(core_tests) #set(CMAKE_BUILD_TYPE Released) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") -set(ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..") -file(GLOB_RECURSE SOURCE_FILES FOLLOW_SYMLINKS src/*.cpp) +file(GLOB_RECURSE SOURCE_FILES FOLLOW_SYMLINKS src/*.cpp deps/llhttp/src/*.c) add_definitions(-DHAVE_CONFIG_H) -link_directories(${ROOT_DIR}/lib) -include_directories(./include/ /usr/local/include/ /usr/include/ /usr/local/include/swoole /usr/include/swoole BEFORE) + +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 gtest gtest_main) + +# find GTest +find_package(GTest REQUIRED) +if (!${GTEST_FOUND}) + message(FATAL_ERROR "Not found GTest") +endif() +message(STATUS "Found GTest") + +list(APPEND core_tests_libraries ${GTEST_BOTH_LIBRARIES}) +list(APPEND core_tests_includes ${GTEST_INCLUDE_DIRS}) + +if (DEFINED swoole_dir) + add_definitions(-DHAVE_SWOOLE_DIR) + list(APPEND core_tests_includes ${swoole_dir} ${swoole_dir}/include) + list(APPEND core_tests_link_directories ${swoole_dir}/lib) +else() + list(APPEND core_tests_includes /usr/local/include/swoole /usr/include/swoole) +endif() + +list(APPEND core_tests_libraries swoole) + +# find OpenSSL +if (DEFINED openssl_dir) + list(APPEND core_tests_includes ${openssl_dir}/include) + list(APPEND core_tests_link_directories ${openssl_dir}/lib) + list(APPEND core_tests_libraries ssl crypto) +else() + find_package(OpenSSL) + if (${OPENSSL_FOUND}) + message(STATUS "Found OpenSSL") + list(APPEND core_tests_includes ${OPENSSL_INCLUDE_DIR}) + list(APPEND core_tests_libraries ${OPENSSL_LIBRARIES}) + else() + message(STATUS "Not found OpenSSL") + endif() +endif() + +if (DEFINED brotli_dir) + list(APPEND core_tests_includes ${brotli_dir}/include) + list(APPEND core_tests_link_directories ${brotli_dir}/lib) +endif() + +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}) + set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) add_executable(core_tests ${SOURCE_FILES}) -target_link_libraries(core_tests gtest gtest_main pthread swoole) +target_include_directories(core_tests PRIVATE ${core_tests_includes}) +target_link_libraries(core_tests ${core_tests_libraries}) 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/deps/llhttp/include/llhttp.h b/core-tests/deps/llhttp/include/llhttp.h new file mode 100644 index 00000000000..024392ace37 --- /dev/null +++ b/core-tests/deps/llhttp/include/llhttp.h @@ -0,0 +1,379 @@ +#ifndef INCLUDE_LLHTTP_H_ +#define INCLUDE_LLHTTP_H_ + +#define LLHTTP_VERSION_MAJOR 2 +#define LLHTTP_VERSION_MINOR 0 +#define LLHTTP_VERSION_PATCH 5 + +#ifndef INCLUDE_LLHTTP_ITSELF_H_ +#define INCLUDE_LLHTTP_ITSELF_H_ +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct llhttp__internal_s llhttp__internal_t; +struct llhttp__internal_s { + int32_t _index; + void* _span_pos0; + void* _span_cb0; + int32_t error; + const char* reason; + const char* error_pos; + void* data; + void* _current; + uint64_t content_length; + uint8_t type; + uint8_t method; + uint8_t http_major; + uint8_t http_minor; + uint8_t header_state; + uint16_t flags; + uint8_t upgrade; + uint16_t status_code; + uint8_t finish; + void* settings; +}; + +int llhttp__internal_init(llhttp__internal_t* s); +int llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* INCLUDE_LLHTTP_ITSELF_H_ */ + +#ifndef LLLLHTTP_C_HEADERS_ +#define LLLLHTTP_C_HEADERS_ +#ifdef __cplusplus +extern "C" { +#endif + +enum llhttp_errno { + HPE_OK = 0, + HPE_INTERNAL = 1, + HPE_STRICT = 2, + HPE_LF_EXPECTED = 3, + HPE_UNEXPECTED_CONTENT_LENGTH = 4, + HPE_CLOSED_CONNECTION = 5, + HPE_INVALID_METHOD = 6, + HPE_INVALID_URL = 7, + HPE_INVALID_CONSTANT = 8, + HPE_INVALID_VERSION = 9, + HPE_INVALID_HEADER_TOKEN = 10, + HPE_INVALID_CONTENT_LENGTH = 11, + HPE_INVALID_CHUNK_SIZE = 12, + HPE_INVALID_STATUS = 13, + HPE_INVALID_EOF_STATE = 14, + HPE_INVALID_TRANSFER_ENCODING = 15, + HPE_CB_MESSAGE_BEGIN = 16, + HPE_CB_HEADERS_COMPLETE = 17, + HPE_CB_MESSAGE_COMPLETE = 18, + HPE_CB_CHUNK_HEADER = 19, + HPE_CB_CHUNK_COMPLETE = 20, + HPE_PAUSED = 21, + HPE_PAUSED_UPGRADE = 22, + HPE_USER = 23 +}; +typedef enum llhttp_errno llhttp_errno_t; + +enum llhttp_flags { + F_CONNECTION_KEEP_ALIVE = 0x1, + F_CONNECTION_CLOSE = 0x2, + F_CONNECTION_UPGRADE = 0x4, + F_CHUNKED = 0x8, + F_UPGRADE = 0x10, + F_CONTENT_LENGTH = 0x20, + F_SKIPBODY = 0x40, + F_TRAILING = 0x80, + F_LENIENT = 0x100, + F_TRANSFER_ENCODING = 0x200 +}; +typedef enum llhttp_flags llhttp_flags_t; + +enum llhttp_type { + HTTP_BOTH = 0, + HTTP_REQUEST = 1, + HTTP_RESPONSE = 2 +}; +typedef enum llhttp_type llhttp_type_t; + +enum llhttp_finish { + HTTP_FINISH_SAFE = 0, + HTTP_FINISH_SAFE_WITH_CB = 1, + HTTP_FINISH_UNSAFE = 2 +}; +typedef enum llhttp_finish llhttp_finish_t; + +enum llhttp_method { + HTTP_DELETE = 0, + HTTP_GET = 1, + HTTP_HEAD = 2, + HTTP_POST = 3, + HTTP_PUT = 4, + HTTP_CONNECT = 5, + HTTP_OPTIONS = 6, + HTTP_TRACE = 7, + HTTP_COPY = 8, + HTTP_LOCK = 9, + HTTP_MKCOL = 10, + HTTP_MOVE = 11, + HTTP_PROPFIND = 12, + HTTP_PROPPATCH = 13, + HTTP_SEARCH = 14, + HTTP_UNLOCK = 15, + HTTP_BIND = 16, + HTTP_REBIND = 17, + HTTP_UNBIND = 18, + HTTP_ACL = 19, + HTTP_REPORT = 20, + HTTP_MKACTIVITY = 21, + HTTP_CHECKOUT = 22, + HTTP_MERGE = 23, + HTTP_MSEARCH = 24, + HTTP_NOTIFY = 25, + HTTP_SUBSCRIBE = 26, + HTTP_UNSUBSCRIBE = 27, + HTTP_PATCH = 28, + HTTP_PURGE = 29, + HTTP_MKCALENDAR = 30, + HTTP_LINK = 31, + HTTP_UNLINK = 32, + HTTP_SOURCE = 33, + HTTP_PRI = 34 +}; +typedef enum llhttp_method llhttp_method_t; + +#define HTTP_ERRNO_MAP(XX) \ + XX(0, OK, OK) \ + XX(1, INTERNAL, INTERNAL) \ + XX(2, STRICT, STRICT) \ + XX(3, LF_EXPECTED, LF_EXPECTED) \ + XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \ + XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \ + XX(6, INVALID_METHOD, INVALID_METHOD) \ + XX(7, INVALID_URL, INVALID_URL) \ + XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \ + XX(9, INVALID_VERSION, INVALID_VERSION) \ + XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \ + XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \ + XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \ + XX(13, INVALID_STATUS, INVALID_STATUS) \ + XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \ + XX(15, INVALID_TRANSFER_ENCODING, INVALID_TRANSFER_ENCODING) \ + XX(16, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \ + XX(17, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \ + XX(18, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \ + XX(19, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \ + XX(20, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \ + XX(21, PAUSED, PAUSED) \ + XX(22, PAUSED_UPGRADE, PAUSED_UPGRADE) \ + XX(23, USER, USER) \ + + +#define HTTP_METHOD_MAP(XX) \ + XX(0, DELETE, DELETE) \ + XX(1, GET, GET) \ + XX(2, HEAD, HEAD) \ + XX(3, POST, POST) \ + XX(4, PUT, PUT) \ + XX(5, CONNECT, CONNECT) \ + XX(6, OPTIONS, OPTIONS) \ + XX(7, TRACE, TRACE) \ + XX(8, COPY, COPY) \ + XX(9, LOCK, LOCK) \ + XX(10, MKCOL, MKCOL) \ + XX(11, MOVE, MOVE) \ + XX(12, PROPFIND, PROPFIND) \ + XX(13, PROPPATCH, PROPPATCH) \ + XX(14, SEARCH, SEARCH) \ + XX(15, UNLOCK, UNLOCK) \ + XX(16, BIND, BIND) \ + XX(17, REBIND, REBIND) \ + XX(18, UNBIND, UNBIND) \ + XX(19, ACL, ACL) \ + XX(20, REPORT, REPORT) \ + XX(21, MKACTIVITY, MKACTIVITY) \ + XX(22, CHECKOUT, CHECKOUT) \ + XX(23, MERGE, MERGE) \ + XX(24, MSEARCH, M-SEARCH) \ + XX(25, NOTIFY, NOTIFY) \ + XX(26, SUBSCRIBE, SUBSCRIBE) \ + XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \ + XX(28, PATCH, PATCH) \ + XX(29, PURGE, PURGE) \ + XX(30, MKCALENDAR, MKCALENDAR) \ + XX(31, LINK, LINK) \ + XX(32, UNLINK, UNLINK) \ + XX(33, SOURCE, SOURCE) \ + XX(34, PRI, PRI) \ + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* LLLLHTTP_C_HEADERS_ */ + +#ifndef INCLUDE_LLHTTP_API_H_ +#define INCLUDE_LLHTTP_API_H_ +#ifdef __cplusplus +extern "C" { +#endif +#include + +typedef llhttp__internal_t llhttp_t; +typedef struct llhttp_settings_s llhttp_settings_t; + +typedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length); +typedef int (*llhttp_cb)(llhttp_t*); + +struct llhttp_settings_s { + /* Possible return values 0, -1, `HPE_PAUSED` */ + llhttp_cb on_message_begin; + + llhttp_data_cb on_url; + llhttp_data_cb on_status; + llhttp_data_cb on_header_field; + llhttp_data_cb on_header_value; + + /* Possible return values: + * 0 - Proceed normally + * 1 - Assume that request/response has no body, and proceed to parsing the + * next message + * 2 - Assume absence of body (as above) and make `llhttp_execute()` return + * `HPE_PAUSED_UPGRADE` + * -1 - Error + * `HPE_PAUSED` + */ + llhttp_cb on_headers_complete; + + llhttp_data_cb on_body; + + /* Possible return values 0, -1, `HPE_PAUSED` */ + llhttp_cb on_message_complete; + + /* When on_chunk_header is called, the current chunk length is stored + * in parser->content_length. + * Possible return values 0, -1, `HPE_PAUSED` + */ + llhttp_cb on_chunk_header; + llhttp_cb on_chunk_complete; +}; + +/* Initialize the parser with specific type and user settings */ +void llhttp_init(llhttp_t* parser, llhttp_type_t type, + const llhttp_settings_t* settings); + +/* Initialize the settings object */ +void llhttp_settings_init(llhttp_settings_t* settings); + +/* Parse full or partial request/response, invoking user callbacks along the + * way. + * + * If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing + * interrupts, and such errno is returned from `llhttp_execute()`. If + * `HPE_PAUSED` was used as a errno, the execution can be resumed with + * `llhttp_resume()` call. + * + * In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE` + * is returned after fully parsing the request/response. If the user wishes to + * continue parsing, they need to invoke `llhttp_resume_after_upgrade()`. + * + * NOTE: if this function ever returns a non-pause type error, it will continue + * to return the same error upon each successive call up until `llhttp_init()` + * is called. + */ +llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len); + +/* This method should be called when the other side has no further bytes to + * send (e.g. shutdown of readable side of the TCP connection.) + * + * Requests without `Content-Length` and other messages might require treating + * all incoming bytes as the part of the body, up to the last byte of the + * connection. This method will invoke `on_message_complete()` callback if the + * request was terminated safely. Otherwise a error code would be returned. + */ +llhttp_errno_t llhttp_finish(llhttp_t* parser); + +/* Returns `1` if the incoming message is parsed until the last byte, and has + * to be completed by calling `llhttp_finish()` on EOF + */ +int llhttp_message_needs_eof(const llhttp_t* parser); + +/* Returns `1` if there might be any other messages following the last that was + * successfully parsed. + */ +int llhttp_should_keep_alive(const llhttp_t* parser); + +/* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set + * appropriate error reason. + * + * Important: do not call this from user callbacks! User callbacks must return + * `HPE_PAUSED` if pausing is required. + */ +void llhttp_pause(llhttp_t* parser); + +/* Might be called to resume the execution after the pause in user's callback. + * See `llhttp_execute()` above for details. + * + * Call this only if `llhttp_execute()` returns `HPE_PAUSED`. + */ +void llhttp_resume(llhttp_t* parser); + +/* Might be called to resume the execution after the pause in user's callback. + * See `llhttp_execute()` above for details. + * + * Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE` + */ +void llhttp_resume_after_upgrade(llhttp_t* parser); + +/* Returns the latest return error */ +llhttp_errno_t llhttp_get_errno(const llhttp_t* parser); + +/* Returns the verbal explanation of the latest returned error. + * + * Note: User callback should set error reason when returning the error. See + * `llhttp_set_error_reason()` for details. + */ +const char* llhttp_get_error_reason(const llhttp_t* parser); + +/* Assign verbal description to the returned error. Must be called in user + * callbacks right before returning the errno. + * + * Note: `HPE_USER` error code might be useful in user callbacks. + */ +void llhttp_set_error_reason(llhttp_t* parser, const char* reason); + +/* Returns the pointer to the last parsed byte before the returned error. The + * pointer is relative to the `data` argument of `llhttp_execute()`. + * + * Note: this method might be useful for counting the number of parsed bytes. + */ +const char* llhttp_get_error_pos(const llhttp_t* parser); + +/* Returns textual name of error code */ +const char* llhttp_errno_name(llhttp_errno_t err); + +/* Returns textual name of HTTP method */ +const char* llhttp_method_name(llhttp_method_t method); + + +/* Enables/disables lenient header value parsing (disabled by default). + * + * Lenient parsing disables header value token checks, extending llhttp's + * protocol support to highly non-compliant clients/server. No + * `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when + * lenient parsing is "on". + * + * **(USE AT YOUR OWN RISK)** + */ +void llhttp_set_lenient(llhttp_t* parser, int enabled); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* INCLUDE_LLHTTP_API_H_ */ + +#endif /* INCLUDE_LLHTTP_H_ */ diff --git a/core-tests/deps/llhttp/src/api.c b/core-tests/deps/llhttp/src/api.c new file mode 100644 index 00000000000..6f7246546df --- /dev/null +++ b/core-tests/deps/llhttp/src/api.c @@ -0,0 +1,224 @@ +#include +#include +#include + +#include "llhttp.h" + +#define CALLBACK_MAYBE(PARSER, NAME, ...) \ + do { \ + llhttp_settings_t* settings; \ + settings = (llhttp_settings_t*) (PARSER)->settings; \ + if (settings == NULL || settings->NAME == NULL) { \ + err = 0; \ + break; \ + } \ + err = settings->NAME(__VA_ARGS__); \ + } while (0) + +void llhttp_init(llhttp_t* parser, llhttp_type_t type, + const llhttp_settings_t* settings) { + llhttp__internal_init(parser); + + parser->type = type; + parser->settings = (void*) settings; +} + + +llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) { + return llhttp__internal_execute(parser, data, data + len); +} + + +void llhttp_settings_init(llhttp_settings_t* settings) { + memset(settings, 0, sizeof(*settings)); +} + + +llhttp_errno_t llhttp_finish(llhttp_t* parser) { + int err; + + /* We're in an error state. Don't bother doing anything. */ + if (parser->error != 0) { + return 0; + } + + switch (parser->finish) { + case HTTP_FINISH_SAFE_WITH_CB: + CALLBACK_MAYBE(parser, on_message_complete, parser); + if (err != HPE_OK) return err; + + /* FALLTHROUGH */ + case HTTP_FINISH_SAFE: + return HPE_OK; + case HTTP_FINISH_UNSAFE: + parser->reason = "Invalid EOF state"; + return HPE_INVALID_EOF_STATE; + default: + abort(); + } +} + + +void llhttp_pause(llhttp_t* parser) { + if (parser->error != HPE_OK) { + return; + } + + parser->error = HPE_PAUSED; + parser->reason = "Paused"; +} + + +void llhttp_resume(llhttp_t* parser) { + if (parser->error != HPE_PAUSED) { + return; + } + + parser->error = 0; +} + + +void llhttp_resume_after_upgrade(llhttp_t* parser) { + if (parser->error != HPE_PAUSED_UPGRADE) { + return; + } + + parser->error = 0; +} + + +llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) { + return parser->error; +} + + +const char* llhttp_get_error_reason(const llhttp_t* parser) { + return parser->reason; +} + + +void llhttp_set_error_reason(llhttp_t* parser, const char* reason) { + parser->reason = reason; +} + + +const char* llhttp_get_error_pos(const llhttp_t* parser) { + return parser->error_pos; +} + + +const char* llhttp_errno_name(llhttp_errno_t err) { +#define HTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return "HPE_" #NAME; + switch (err) { + HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) + default: abort(); + } +#undef HTTP_ERRNO_GEN +} + + +const char* llhttp_method_name(llhttp_method_t method) { +#define HTTP_METHOD_GEN(NUM, NAME, STRING) case HTTP_##NAME: return #STRING; + switch (method) { + HTTP_METHOD_MAP(HTTP_METHOD_GEN) + default: abort(); + } +#undef HTTP_METHOD_GEN +} + + +void llhttp_set_lenient(llhttp_t* parser, int enabled) { + if (enabled) { + parser->flags |= F_LENIENT; + } else { + parser->flags &= ~F_LENIENT; + } +} + + +/* Callbacks */ + + +int llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_message_begin, s); + return err; +} + + +int llhttp__on_url(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_url, s, p, endp - p); + return err; +} + + +int llhttp__on_status(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_status, s, p, endp - p); + return err; +} + + +int llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_header_field, s, p, endp - p); + return err; +} + + +int llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_header_value, s, p, endp - p); + return err; +} + + +int llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_headers_complete, s); + return err; +} + + +int llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_message_complete, s); + return err; +} + + +int llhttp__on_body(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_body, s, p, endp - p); + return err; +} + + +int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_chunk_header, s); + return err; +} + + +int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) { + int err; + CALLBACK_MAYBE(s, on_chunk_complete, s); + return err; +} + + +/* Private */ + + +void llhttp__debug(llhttp_t* s, const char* p, const char* endp, + const char* msg) { + if (p == endp) { + fprintf(stderr, "p=%p type=%d flags=%02x next=null debug=%s\n", s, s->type, + s->flags, msg); + } else { + fprintf(stderr, "p=%p type=%d flags=%02x next=%02x debug=%s\n", s, + s->type, s->flags, *p, msg); + } +} diff --git a/core-tests/deps/llhttp/src/http.c b/core-tests/deps/llhttp/src/http.c new file mode 100644 index 00000000000..6e4906d840b --- /dev/null +++ b/core-tests/deps/llhttp/src/http.c @@ -0,0 +1,150 @@ +#include +#ifndef LLHTTP__TEST +# include "llhttp.h" +#else +# define llhttp_t llparse_t +#endif /* */ + +int llhttp_message_needs_eof(const llhttp_t* parser); +int llhttp_should_keep_alive(const llhttp_t* parser); + +int llhttp__before_headers_complete(llhttp_t* parser, const char* p, + const char* endp) { + /* Set this here so that on_headers_complete() callbacks can see it */ + if ((parser->flags & F_UPGRADE) && + (parser->flags & F_CONNECTION_UPGRADE)) { + /* For responses, "Upgrade: foo" and "Connection: upgrade" are + * mandatory only when it is a 101 Switching Protocols response, + * otherwise it is purely informational, to announce support. + */ + parser->upgrade = + (parser->type == HTTP_REQUEST || parser->status_code == 101); + } else { + parser->upgrade = (parser->method == HTTP_CONNECT); + } + return 0; +} + + +/* Return values: + * 0 - No body, `restart`, message_complete + * 1 - CONNECT request, `restart`, message_complete, and pause + * 2 - chunk_size_start + * 3 - body_identity + * 4 - body_identity_eof + * 5 - invalid transfer-encoding for request + */ +int llhttp__after_headers_complete(llhttp_t* parser, const char* p, + const char* endp) { + int hasBody; + + hasBody = parser->flags & F_CHUNKED || parser->content_length > 0; + if (parser->upgrade && (parser->method == HTTP_CONNECT || + (parser->flags & F_SKIPBODY) || !hasBody)) { + /* Exit, the rest of the message is in a different protocol. */ + return 1; + } + + if (parser->flags & F_SKIPBODY) { + return 0; + } else if (parser->flags & F_CHUNKED) { + /* chunked encoding - ignore Content-Length header, prepare for a chunk */ + return 2; + } else if (parser->flags & F_TRANSFER_ENCODING) { + if (parser->type == HTTP_REQUEST && (parser->flags & F_LENIENT) == 0) { + /* RFC 7230 3.3.3 */ + + /* If a Transfer-Encoding header field + * is present in a request and the chunked transfer coding is not + * the final encoding, the message body length cannot be determined + * reliably; the server MUST respond with the 400 (Bad Request) + * status code and then close the connection. + */ + return 5; + } else { + /* RFC 7230 3.3.3 */ + + /* If a Transfer-Encoding header field is present in a response and + * the chunked transfer coding is not the final encoding, the + * message body length is determined by reading the connection until + * it is closed by the server. + */ + return 4; + } + } else { + if (!(parser->flags & F_CONTENT_LENGTH)) { + if (!llhttp_message_needs_eof(parser)) { + /* Assume content-length 0 - read the next */ + return 0; + } else { + /* Read body until EOF */ + return 4; + } + } else if (parser->content_length == 0) { + /* Content-Length header given but zero: Content-Length: 0\r\n */ + return 0; + } else { + /* Content-Length header given and non-zero */ + return 3; + } + } +} + + +int llhttp__after_message_complete(llhttp_t* parser, const char* p, + const char* endp) { + int should_keep_alive; + + should_keep_alive = llhttp_should_keep_alive(parser); + parser->finish = HTTP_FINISH_SAFE; + + /* Keep `F_LENIENT` flag between messages, but reset every other flag */ + parser->flags &= F_LENIENT; + + /* NOTE: this is ignored in loose parsing mode */ + return should_keep_alive; +} + + +int llhttp_message_needs_eof(const llhttp_t* parser) { + if (parser->type == HTTP_REQUEST) { + return 0; + } + + /* See RFC 2616 section 4.4 */ + if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ + parser->status_code == 204 || /* No Content */ + parser->status_code == 304 || /* Not Modified */ + (parser->flags & F_SKIPBODY)) { /* response to a HEAD request */ + return 0; + } + + /* RFC 7230 3.3.3, see `llhttp__after_headers_complete` */ + if ((parser->flags & F_TRANSFER_ENCODING) && + (parser->flags & F_CHUNKED) == 0) { + return 1; + } + + if (parser->flags & (F_CHUNKED | F_CONTENT_LENGTH)) { + return 0; + } + + return 1; +} + + +int llhttp_should_keep_alive(const llhttp_t* parser) { + if (parser->http_major > 0 && parser->http_minor > 0) { + /* HTTP/1.1 */ + if (parser->flags & F_CONNECTION_CLOSE) { + return 0; + } + } else { + /* HTTP/1.0 or earlier */ + if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { + return 0; + } + } + + return !llhttp_message_needs_eof(parser); +} diff --git a/core-tests/deps/llhttp/src/llhttp.c b/core-tests/deps/llhttp/src/llhttp.c new file mode 100644 index 00000000000..e146d2591ba --- /dev/null +++ b/core-tests/deps/llhttp/src/llhttp.c @@ -0,0 +1,6528 @@ +#include +#include +#include + +#ifdef __SSE4_2__ + #ifdef _MSC_VER + #include + #else /* !_MSC_VER */ + #include + #endif /* _MSC_VER */ +#endif /* __SSE4_2__ */ + +#ifdef _MSC_VER + #define ALIGN(n) _declspec(align(n)) +#else /* !_MSC_VER */ + #define ALIGN(n) __attribute__((aligned(n))) +#endif /* _MSC_VER */ + +#include "llhttp.h" + +typedef int (*llhttp__internal__span_cb)( + llhttp__internal_t*, const char*, const char*); + +static const unsigned char llparse_blob0[] = { + 'C', 'L' +}; +#ifdef __SSE4_2__ +static const unsigned char ALIGN(16) llparse_blob1[] = { + 0x9, 0x9, 0xc, 0xc, '!', '"', '$', '>', '@', '~', 0x80, + 0xff, 0x0, 0x0, 0x0, 0x0 +}; +#endif /* __SSE4_2__ */ +static const unsigned char llparse_blob2[] = { + 'o', 'n' +}; +static const unsigned char llparse_blob3[] = { + 'e', 'c', 't', 'i', 'o', 'n' +}; +static const unsigned char llparse_blob4[] = { + 'l', 'o', 's', 'e' +}; +static const unsigned char llparse_blob5[] = { + 'e', 'e', 'p', '-', 'a', 'l', 'i', 'v', 'e' +}; +static const unsigned char llparse_blob6[] = { + 'p', 'g', 'r', 'a', 'd', 'e' +}; +#ifdef __SSE4_2__ +static const unsigned char ALIGN(16) llparse_blob7[] = { + 0x9, 0x9, ' ', '~', 0x80, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0 +}; +#endif /* __SSE4_2__ */ +static const unsigned char llparse_blob8[] = { + 'c', 'h', 'u', 'n', 'k', 'e', 'd' +}; +#ifdef __SSE4_2__ +static const unsigned char ALIGN(16) llparse_blob9[] = { + ' ', '!', '#', '\'', '*', '+', '-', '.', '0', '9', 'A', + 'Z', '^', 'z', '|', '|' +}; +#endif /* __SSE4_2__ */ +#ifdef __SSE4_2__ +static const unsigned char ALIGN(16) llparse_blob10[] = { + '~', '~', 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0 +}; +#endif /* __SSE4_2__ */ +static const unsigned char llparse_blob11[] = { + 'e', 'n', 't', '-', 'l', 'e', 'n', 'g', 't', 'h' +}; +static const unsigned char llparse_blob12[] = { + 'r', 'o', 'x', 'y', '-', 'c', 'o', 'n', 'n', 'e', 'c', + 't', 'i', 'o', 'n' +}; +static const unsigned char llparse_blob13[] = { + 'r', 'a', 'n', 's', 'f', 'e', 'r', '-', 'e', 'n', 'c', + 'o', 'd', 'i', 'n', 'g' +}; +static const unsigned char llparse_blob14[] = { + 'p', 'g', 'r', 'a', 'd', 'e' +}; +static const unsigned char llparse_blob15[] = { + 0xd, 0xa +}; +static const unsigned char llparse_blob16[] = { + 'T', 'T', 'P', '/' +}; +static const unsigned char llparse_blob17[] = { + 'C', 'E', '/' +}; +static const unsigned char llparse_blob18[] = { + 'I', 'N', 'D' +}; +static const unsigned char llparse_blob19[] = { + 'E', 'C', 'K', 'O', 'U', 'T' +}; +static const unsigned char llparse_blob20[] = { + 'N', 'E', 'C', 'T' +}; +static const unsigned char llparse_blob21[] = { + 'E', 'L', 'E', 'T', 'E' +}; +static const unsigned char llparse_blob22[] = { + 'E', 'T' +}; +static const unsigned char llparse_blob23[] = { + 'E', 'A', 'D' +}; +static const unsigned char llparse_blob24[] = { + 'N', 'K' +}; +static const unsigned char llparse_blob25[] = { + 'C', 'K' +}; +static const unsigned char llparse_blob26[] = { + 'S', 'E', 'A', 'R', 'C', 'H' +}; +static const unsigned char llparse_blob27[] = { + 'R', 'G', 'E' +}; +static const unsigned char llparse_blob28[] = { + 'C', 'T', 'I', 'V', 'I', 'T', 'Y' +}; +static const unsigned char llparse_blob29[] = { + 'L', 'E', 'N', 'D', 'A', 'R' +}; +static const unsigned char llparse_blob30[] = { + 'V', 'E' +}; +static const unsigned char llparse_blob31[] = { + 'O', 'T', 'I', 'F', 'Y' +}; +static const unsigned char llparse_blob32[] = { + 'P', 'T', 'I', 'O', 'N', 'S' +}; +static const unsigned char llparse_blob33[] = { + 'T', 'C', 'H' +}; +static const unsigned char llparse_blob34[] = { + 'S', 'T' +}; +static const unsigned char llparse_blob35[] = { + 'I', 'N', 'D' +}; +static const unsigned char llparse_blob36[] = { + 'A', 'T', 'C', 'H' +}; +static const unsigned char llparse_blob37[] = { + 'G', 'E' +}; +static const unsigned char llparse_blob38[] = { + 'I', 'N', 'D' +}; +static const unsigned char llparse_blob39[] = { + 'O', 'R', 'T' +}; +static const unsigned char llparse_blob40[] = { + 'A', 'R', 'C', 'H' +}; +static const unsigned char llparse_blob41[] = { + 'U', 'R', 'C', 'E' +}; +static const unsigned char llparse_blob42[] = { + 'B', 'S', 'C', 'R', 'I', 'B', 'E' +}; +static const unsigned char llparse_blob43[] = { + 'R', 'A', 'C', 'E' +}; +static const unsigned char llparse_blob44[] = { + 'I', 'N', 'D' +}; +static const unsigned char llparse_blob45[] = { + 'N', 'K' +}; +static const unsigned char llparse_blob46[] = { + 'C', 'K' +}; +static const unsigned char llparse_blob47[] = { + 'U', 'B', 'S', 'C', 'R', 'I', 'B', 'E' +}; +static const unsigned char llparse_blob48[] = { + 'H', 'T', 'T', 'P', '/' +}; +static const unsigned char llparse_blob49[] = { + 'A', 'D' +}; +static const unsigned char llparse_blob50[] = { + 'T', 'P', '/' +}; + +enum llparse_match_status_e { + kMatchComplete, + kMatchPause, + kMatchMismatch +}; +typedef enum llparse_match_status_e llparse_match_status_t; + +struct llparse_match_s { + llparse_match_status_t status; + const unsigned char* current; +}; +typedef struct llparse_match_s llparse_match_t; + +static llparse_match_t llparse__match_sequence_id( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp, + const unsigned char* seq, uint32_t seq_len) { + uint32_t index; + llparse_match_t res; + + index = s->_index; + for (; p != endp; p++) { + unsigned char current; + + current = *p; + if (current == seq[index]) { + if (++index == seq_len) { + res.status = kMatchComplete; + goto reset; + } + } else { + res.status = kMatchMismatch; + goto reset; + } + } + s->_index = index; + res.status = kMatchPause; + res.current = p; + return res; +reset: + s->_index = 0; + res.current = p; + return res; +} + +static llparse_match_t llparse__match_sequence_to_lower_unsafe( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp, + const unsigned char* seq, uint32_t seq_len) { + uint32_t index; + llparse_match_t res; + + index = s->_index; + for (; p != endp; p++) { + unsigned char current; + + current = ((*p) | 0x20); + if (current == seq[index]) { + if (++index == seq_len) { + res.status = kMatchComplete; + goto reset; + } + } else { + res.status = kMatchMismatch; + goto reset; + } + } + s->_index = index; + res.status = kMatchPause; + res.current = p; + return res; +reset: + s->_index = 0; + res.current = p; + return res; +} + +enum llparse_state_e { + s_error, + s_n_llhttp__internal__n_invoke_llhttp__after_message_complete, + s_n_llhttp__internal__n_pause_1, + s_n_llhttp__internal__n_invoke_is_equal_upgrade, + s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2, + s_n_llhttp__internal__n_chunk_data_almost_done_skip, + s_n_llhttp__internal__n_chunk_data_almost_done, + s_n_llhttp__internal__n_consume_content_length, + s_n_llhttp__internal__n_span_start_llhttp__on_body, + s_n_llhttp__internal__n_invoke_is_equal_content_length, + s_n_llhttp__internal__n_chunk_size_almost_done, + s_n_llhttp__internal__n_chunk_parameters, + s_n_llhttp__internal__n_chunk_size_otherwise, + s_n_llhttp__internal__n_chunk_size, + s_n_llhttp__internal__n_chunk_size_digit, + s_n_llhttp__internal__n_invoke_update_content_length, + s_n_llhttp__internal__n_consume_content_length_1, + s_n_llhttp__internal__n_span_start_llhttp__on_body_1, + s_n_llhttp__internal__n_eof, + s_n_llhttp__internal__n_span_start_llhttp__on_body_2, + s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete, + s_n_llhttp__internal__n_headers_almost_done, + s_n_llhttp__internal__n_span_start_llhttp__on_header_value, + s_n_llhttp__internal__n_header_value_discard_lws, + s_n_llhttp__internal__n_header_value_discard_ws_almost_done, + s_n_llhttp__internal__n_header_value_lws, + s_n_llhttp__internal__n_header_value_almost_done, + s_n_llhttp__internal__n_header_value_lenient, + s_n_llhttp__internal__n_header_value_otherwise, + s_n_llhttp__internal__n_header_value_connection_token, + s_n_llhttp__internal__n_header_value_connection_ws, + s_n_llhttp__internal__n_header_value_connection_1, + s_n_llhttp__internal__n_header_value_connection_2, + s_n_llhttp__internal__n_header_value_connection_3, + s_n_llhttp__internal__n_header_value_connection, + s_n_llhttp__internal__n_error_18, + s_n_llhttp__internal__n_header_value, + s_n_llhttp__internal__n_header_value_discard_rws, + s_n_llhttp__internal__n_error_19, + s_n_llhttp__internal__n_header_value_content_length_ws, + s_n_llhttp__internal__n_header_value_content_length, + s_n_llhttp__internal__n_header_value_te_chunked_last, + s_n_llhttp__internal__n_header_value_te_token_ows, + s_n_llhttp__internal__n_header_value_te_token, + s_n_llhttp__internal__n_header_value_te_chunked, + s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1, + s_n_llhttp__internal__n_header_value_discard_ws, + s_n_llhttp__internal__n_header_field_general_otherwise, + s_n_llhttp__internal__n_header_field_general, + s_n_llhttp__internal__n_header_field_colon, + s_n_llhttp__internal__n_header_field_3, + s_n_llhttp__internal__n_header_field_4, + s_n_llhttp__internal__n_header_field_2, + s_n_llhttp__internal__n_header_field_1, + s_n_llhttp__internal__n_header_field_5, + s_n_llhttp__internal__n_header_field_6, + s_n_llhttp__internal__n_header_field_7, + s_n_llhttp__internal__n_header_field, + s_n_llhttp__internal__n_span_start_llhttp__on_header_field, + s_n_llhttp__internal__n_header_field_start, + s_n_llhttp__internal__n_url_skip_to_http09, + s_n_llhttp__internal__n_url_skip_lf_to_http09, + s_n_llhttp__internal__n_req_http_end_1, + s_n_llhttp__internal__n_req_http_end, + s_n_llhttp__internal__n_req_http_minor, + s_n_llhttp__internal__n_req_http_dot, + s_n_llhttp__internal__n_req_http_major, + s_n_llhttp__internal__n_req_http_start_1, + s_n_llhttp__internal__n_req_http_start_2, + s_n_llhttp__internal__n_req_http_start, + s_n_llhttp__internal__n_url_skip_to_http, + s_n_llhttp__internal__n_url_fragment, + s_n_llhttp__internal__n_span_end_stub_query_3, + s_n_llhttp__internal__n_url_query, + s_n_llhttp__internal__n_url_query_or_fragment, + s_n_llhttp__internal__n_url_path, + s_n_llhttp__internal__n_span_start_stub_path_2, + s_n_llhttp__internal__n_span_start_stub_path, + s_n_llhttp__internal__n_span_start_stub_path_1, + s_n_llhttp__internal__n_url_server_with_at, + s_n_llhttp__internal__n_url_server, + s_n_llhttp__internal__n_url_schema_delim_1, + s_n_llhttp__internal__n_url_schema_delim, + s_n_llhttp__internal__n_span_end_stub_schema, + s_n_llhttp__internal__n_url_schema, + s_n_llhttp__internal__n_url_start, + s_n_llhttp__internal__n_span_start_llhttp__on_url_1, + s_n_llhttp__internal__n_span_start_llhttp__on_url, + s_n_llhttp__internal__n_req_spaces_before_url, + s_n_llhttp__internal__n_req_first_space_before_url, + s_n_llhttp__internal__n_start_req_1, + s_n_llhttp__internal__n_start_req_2, + s_n_llhttp__internal__n_start_req_4, + s_n_llhttp__internal__n_start_req_6, + s_n_llhttp__internal__n_start_req_7, + s_n_llhttp__internal__n_start_req_5, + s_n_llhttp__internal__n_start_req_3, + s_n_llhttp__internal__n_start_req_8, + s_n_llhttp__internal__n_start_req_9, + s_n_llhttp__internal__n_start_req_10, + s_n_llhttp__internal__n_start_req_12, + s_n_llhttp__internal__n_start_req_13, + s_n_llhttp__internal__n_start_req_11, + s_n_llhttp__internal__n_start_req_15, + s_n_llhttp__internal__n_start_req_16, + s_n_llhttp__internal__n_start_req_18, + s_n_llhttp__internal__n_start_req_20, + s_n_llhttp__internal__n_start_req_21, + s_n_llhttp__internal__n_start_req_19, + s_n_llhttp__internal__n_start_req_17, + s_n_llhttp__internal__n_start_req_22, + s_n_llhttp__internal__n_start_req_14, + s_n_llhttp__internal__n_start_req_23, + s_n_llhttp__internal__n_start_req_24, + s_n_llhttp__internal__n_start_req_26, + s_n_llhttp__internal__n_start_req_27, + s_n_llhttp__internal__n_start_req_31, + s_n_llhttp__internal__n_start_req_32, + s_n_llhttp__internal__n_start_req_30, + s_n_llhttp__internal__n_start_req_29, + s_n_llhttp__internal__n_start_req_28, + s_n_llhttp__internal__n_start_req_34, + s_n_llhttp__internal__n_start_req_33, + s_n_llhttp__internal__n_start_req_25, + s_n_llhttp__internal__n_start_req_37, + s_n_llhttp__internal__n_start_req_38, + s_n_llhttp__internal__n_start_req_36, + s_n_llhttp__internal__n_start_req_35, + s_n_llhttp__internal__n_start_req_40, + s_n_llhttp__internal__n_start_req_41, + s_n_llhttp__internal__n_start_req_42, + s_n_llhttp__internal__n_start_req_39, + s_n_llhttp__internal__n_start_req_43, + s_n_llhttp__internal__n_start_req_46, + s_n_llhttp__internal__n_start_req_48, + s_n_llhttp__internal__n_start_req_49, + s_n_llhttp__internal__n_start_req_47, + s_n_llhttp__internal__n_start_req_50, + s_n_llhttp__internal__n_start_req_45, + s_n_llhttp__internal__n_start_req_44, + s_n_llhttp__internal__n_start_req, + s_n_llhttp__internal__n_res_line_almost_done, + s_n_llhttp__internal__n_res_status, + s_n_llhttp__internal__n_span_start_llhttp__on_status, + s_n_llhttp__internal__n_res_status_start, + s_n_llhttp__internal__n_res_status_code_otherwise, + s_n_llhttp__internal__n_res_status_code, + s_n_llhttp__internal__n_res_http_end, + s_n_llhttp__internal__n_res_http_minor, + s_n_llhttp__internal__n_res_http_dot, + s_n_llhttp__internal__n_res_http_major, + s_n_llhttp__internal__n_start_res, + s_n_llhttp__internal__n_req_or_res_method_2, + s_n_llhttp__internal__n_req_or_res_method_3, + s_n_llhttp__internal__n_req_or_res_method_1, + s_n_llhttp__internal__n_req_or_res_method, + s_n_llhttp__internal__n_start_req_or_res, + s_n_llhttp__internal__n_invoke_load_type, + s_n_llhttp__internal__n_start, +}; +typedef enum llparse_state_e llparse_state_t; + +int llhttp__on_url( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_header_field( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_header_value( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_body( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_status( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_update_finish( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->finish = 2; + return 0; +} + +int llhttp__on_message_begin( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_load_type( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->type; +} + +int llhttp__internal__c_store_method( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + state->method = match; + return 0; +} + +int llhttp__internal__c_is_equal_method( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->method == 5; +} + +int llhttp__internal__c_update_http_major( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->http_major = 0; + return 0; +} + +int llhttp__internal__c_update_http_minor( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->http_minor = 9; + return 0; +} + +int llhttp__internal__c_test_flags( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 128) == 128; +} + +int llhttp__on_chunk_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_message_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_is_equal_upgrade( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->upgrade == 1; +} + +int llhttp__after_message_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_update_finish_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->finish = 0; + return 0; +} + +int llhttp__internal__c_test_flags_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 544) == 544; +} + +int llhttp__internal__c_test_flags_2( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 256) == 256; +} + +int llhttp__internal__c_test_flags_3( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 40) == 40; +} + +int llhttp__before_headers_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__on_headers_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__after_headers_complete( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_update_content_length( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->content_length = 0; + return 0; +} + +int llhttp__internal__c_mul_add_content_length( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + /* Multiplication overflow */ + if (state->content_length > 0xffffffffffffffffULL / 16) { + return 1; + } + + state->content_length *= 16; + + /* Addition overflow */ + if (match >= 0) { + if (state->content_length > 0xffffffffffffffffULL - match) { + return 1; + } + } else { + if (state->content_length < 0ULL - match) { + return 1; + } + } + state->content_length += match; + return 0; +} + +int llhttp__on_chunk_header( + llhttp__internal_t* s, const unsigned char* p, + const unsigned char* endp); + +int llhttp__internal__c_is_equal_content_length( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->content_length == 0; +} + +int llhttp__internal__c_or_flags( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 128; + return 0; +} + +int llhttp__internal__c_update_finish_2( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->finish = 1; + return 0; +} + +int llhttp__internal__c_or_flags_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 64; + return 0; +} + +int llhttp__internal__c_update_upgrade( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->upgrade = 1; + return 0; +} + +int llhttp__internal__c_store_header_state( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + state->header_state = match; + return 0; +} + +int llhttp__internal__c_load_header_state( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->header_state; +} + +int llhttp__internal__c_or_flags_3( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 1; + return 0; +} + +int llhttp__internal__c_update_header_state( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 1; + return 0; +} + +int llhttp__internal__c_or_flags_4( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 2; + return 0; +} + +int llhttp__internal__c_or_flags_5( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 4; + return 0; +} + +int llhttp__internal__c_or_flags_6( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 8; + return 0; +} + +int llhttp__internal__c_update_header_state_2( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 6; + return 0; +} + +int llhttp__internal__c_update_header_state_4( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 0; + return 0; +} + +int llhttp__internal__c_update_header_state_5( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 5; + return 0; +} + +int llhttp__internal__c_update_header_state_6( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 7; + return 0; +} + +int llhttp__internal__c_test_flags_5( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 32) == 32; +} + +int llhttp__internal__c_mul_add_content_length_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + /* Multiplication overflow */ + if (state->content_length > 0xffffffffffffffffULL / 10) { + return 1; + } + + state->content_length *= 10; + + /* Addition overflow */ + if (match >= 0) { + if (state->content_length > 0xffffffffffffffffULL - match) { + return 1; + } + } else { + if (state->content_length < 0ULL - match) { + return 1; + } + } + state->content_length += match; + return 0; +} + +int llhttp__internal__c_or_flags_15( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 32; + return 0; +} + +int llhttp__internal__c_or_flags_16( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 512; + return 0; +} + +int llhttp__internal__c_update_header_state_8( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->header_state = 8; + return 0; +} + +int llhttp__internal__c_or_flags_17( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->flags |= 16; + return 0; +} + +int llhttp__internal__c_store_http_major( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + state->http_major = match; + return 0; +} + +int llhttp__internal__c_store_http_minor( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + state->http_minor = match; + return 0; +} + +int llhttp__internal__c_is_equal_method_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return state->method == 33; +} + +int llhttp__internal__c_update_status_code( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->status_code = 0; + return 0; +} + +int llhttp__internal__c_mul_add_status_code( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp, + int match) { + /* Multiplication overflow */ + if (state->status_code > 0xffff / 10) { + return 1; + } + + state->status_code *= 10; + + /* Addition overflow */ + if (match >= 0) { + if (state->status_code > 0xffff - match) { + return 1; + } + } else { + if (state->status_code < 0 - match) { + return 1; + } + } + state->status_code += match; + + /* Enforce maximum */ + if (state->status_code > 999) { + return 1; + } + return 0; +} + +int llhttp__internal__c_update_type( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->type = 1; + return 0; +} + +int llhttp__internal__c_update_type_1( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + state->type = 2; + return 0; +} + +int llhttp__internal_init(llhttp__internal_t* state) { + memset(state, 0, sizeof(*state)); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_start; + return 0; +} + +static llparse_state_t llhttp__internal__run( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + int match; + switch ((llparse_state_t) (intptr_t) state->_current) { + case s_n_llhttp__internal__n_invoke_llhttp__after_message_complete: + s_n_llhttp__internal__n_invoke_llhttp__after_message_complete: { + switch (llhttp__after_message_complete(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_finish_1; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_pause_1: + s_n_llhttp__internal__n_pause_1: { + state->error = 0x16; + state->reason = "Pause on CONNECT/Upgrade"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_is_equal_upgrade: + s_n_llhttp__internal__n_invoke_is_equal_upgrade: { + switch (llhttp__internal__c_is_equal_upgrade(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; + default: + goto s_n_llhttp__internal__n_pause_1; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2: + s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2: { + switch (llhttp__on_message_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_is_equal_upgrade; + case 21: + goto s_n_llhttp__internal__n_pause_5; + default: + goto s_n_llhttp__internal__n_error_9; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_data_almost_done_skip: + s_n_llhttp__internal__n_chunk_data_almost_done_skip: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_data_almost_done_skip; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_data_almost_done: + s_n_llhttp__internal__n_chunk_data_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_data_almost_done; + } + p++; + goto s_n_llhttp__internal__n_chunk_data_almost_done_skip; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_consume_content_length: + s_n_llhttp__internal__n_consume_content_length: { + size_t avail; + size_t need; + + avail = endp - p; + need = state->content_length; + if (avail >= need) { + p += need; + state->content_length = 0; + goto s_n_llhttp__internal__n_span_end_llhttp__on_body; + } + + state->content_length -= avail; + return s_n_llhttp__internal__n_consume_content_length; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_body: + s_n_llhttp__internal__n_span_start_llhttp__on_body: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_body; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_body; + goto s_n_llhttp__internal__n_consume_content_length; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_is_equal_content_length: + s_n_llhttp__internal__n_invoke_is_equal_content_length: { + switch (llhttp__internal__c_is_equal_content_length(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_start_llhttp__on_body; + default: + goto s_n_llhttp__internal__n_invoke_or_flags; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_size_almost_done: + s_n_llhttp__internal__n_chunk_size_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_size_almost_done; + } + p++; + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_parameters: + s_n_llhttp__internal__n_chunk_parameters: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_parameters; + } + switch (*p) { + case 13: { + p++; + goto s_n_llhttp__internal__n_chunk_size_almost_done; + } + default: { + p++; + goto s_n_llhttp__internal__n_chunk_parameters; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_size_otherwise: + s_n_llhttp__internal__n_chunk_size_otherwise: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_size_otherwise; + } + switch (*p) { + case 13: { + p++; + goto s_n_llhttp__internal__n_chunk_size_almost_done; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_chunk_parameters; + } + case ';': { + p++; + goto s_n_llhttp__internal__n_chunk_parameters; + } + default: { + goto s_n_llhttp__internal__n_error_6; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_size: + s_n_llhttp__internal__n_chunk_size: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_size; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'A': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'B': { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'C': { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'D': { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'E': { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'F': { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'a': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'b': { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'c': { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'd': { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'e': { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'f': { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + default: { + goto s_n_llhttp__internal__n_chunk_size_otherwise; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_chunk_size_digit: + s_n_llhttp__internal__n_chunk_size_digit: { + if (p == endp) { + return s_n_llhttp__internal__n_chunk_size_digit; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'A': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'B': { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'C': { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'D': { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'E': { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'F': { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'a': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'b': { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'c': { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'd': { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'e': { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + case 'f': { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length; + } + default: { + goto s_n_llhttp__internal__n_error_8; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_update_content_length: + s_n_llhttp__internal__n_invoke_update_content_length: { + switch (llhttp__internal__c_update_content_length(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_chunk_size_digit; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_consume_content_length_1: + s_n_llhttp__internal__n_consume_content_length_1: { + size_t avail; + size_t need; + + avail = endp - p; + need = state->content_length; + if (avail >= need) { + p += need; + state->content_length = 0; + goto s_n_llhttp__internal__n_span_end_llhttp__on_body_1; + } + + state->content_length -= avail; + return s_n_llhttp__internal__n_consume_content_length_1; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_body_1: + s_n_llhttp__internal__n_span_start_llhttp__on_body_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_body_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_body; + goto s_n_llhttp__internal__n_consume_content_length_1; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_eof: + s_n_llhttp__internal__n_eof: { + if (p == endp) { + return s_n_llhttp__internal__n_eof; + } + p++; + goto s_n_llhttp__internal__n_eof; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_body_2: + s_n_llhttp__internal__n_span_start_llhttp__on_body_2: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_body_2; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_body; + goto s_n_llhttp__internal__n_eof; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete: + s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete: { + switch (llhttp__after_headers_complete(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1; + case 2: + goto s_n_llhttp__internal__n_invoke_update_content_length; + case 3: + goto s_n_llhttp__internal__n_span_start_llhttp__on_body_1; + case 4: + goto s_n_llhttp__internal__n_invoke_update_finish_2; + case 5: + goto s_n_llhttp__internal__n_error_10; + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_headers_almost_done: + s_n_llhttp__internal__n_headers_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_headers_almost_done; + } + p++; + goto s_n_llhttp__internal__n_invoke_test_flags; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_header_value: + s_n_llhttp__internal__n_span_start_llhttp__on_header_value: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_header_value; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_header_value; + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_discard_lws: + s_n_llhttp__internal__n_header_value_discard_lws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_discard_lws; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + default: { + goto s_n_llhttp__internal__n_invoke_load_header_state; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_discard_ws_almost_done: + s_n_llhttp__internal__n_header_value_discard_ws_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_discard_ws_almost_done; + } + p++; + goto s_n_llhttp__internal__n_header_value_discard_lws; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_lws: + s_n_llhttp__internal__n_header_value_lws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_lws; + } + switch (*p) { + case 9: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; + } + case ' ': { + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; + } + default: { + goto s_n_llhttp__internal__n_invoke_load_header_state_3; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_almost_done: + s_n_llhttp__internal__n_header_value_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_almost_done; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_header_value_lws; + } + default: { + goto s_n_llhttp__internal__n_error_15; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_lenient: + s_n_llhttp__internal__n_header_value_lenient: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_lenient; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3; + } + default: { + p++; + goto s_n_llhttp__internal__n_header_value_lenient; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_otherwise: + s_n_llhttp__internal__n_header_value_otherwise: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_otherwise; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2; + } + default: { + goto s_n_llhttp__internal__n_invoke_test_flags_4; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection_token: + s_n_llhttp__internal__n_header_value_connection_token: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_token; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_header_value_connection_token; + } + case 2: { + p++; + goto s_n_llhttp__internal__n_header_value_connection; + } + default: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection_ws: + s_n_llhttp__internal__n_header_value_connection_ws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_ws; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + case 13: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_connection_ws; + } + case ',': { + p++; + goto s_n_llhttp__internal__n_invoke_load_header_state_4; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_4; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection_1: + s_n_llhttp__internal__n_header_value_connection_1: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_1; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob4, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_update_header_state_2; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_value_connection_1; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_header_value_connection_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection_2: + s_n_llhttp__internal__n_header_value_connection_2: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_2; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob5, 9); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_update_header_state_5; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_value_connection_2; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_header_value_connection_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection_3: + s_n_llhttp__internal__n_header_value_connection_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection_3; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob6, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_update_header_state_6; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_value_connection_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_header_value_connection_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_connection: + s_n_llhttp__internal__n_header_value_connection: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_connection; + } + switch (((*p) | 0x20)) { + case 9: { + p++; + goto s_n_llhttp__internal__n_header_value_connection; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_connection; + } + case 'c': { + p++; + goto s_n_llhttp__internal__n_header_value_connection_1; + } + case 'k': { + p++; + goto s_n_llhttp__internal__n_header_value_connection_2; + } + case 'u': { + p++; + goto s_n_llhttp__internal__n_header_value_connection_3; + } + default: { + goto s_n_llhttp__internal__n_header_value_connection_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_18: + s_n_llhttp__internal__n_error_18: { + state->error = 0xb; + state->reason = "Content-Length overflow"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value: + s_n_llhttp__internal__n_header_value: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_header_value; + } + #ifdef __SSE4_2__ + if (endp - p >= 16) { + __m128i ranges; + __m128i input; + int avail; + int match_len; + + /* Load input */ + input = _mm_loadu_si128((__m128i const*) p); + ranges = _mm_loadu_si128((__m128i const*) llparse_blob7); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 6, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_header_value; + } + goto s_n_llhttp__internal__n_header_value_otherwise; + } + #endif /* __SSE4_2__ */ + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_header_value; + } + default: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_discard_rws: + s_n_llhttp__internal__n_header_value_discard_rws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_discard_rws; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + case 13: { + goto s_n_llhttp__internal__n_header_value_otherwise; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_discard_rws; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_7; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_error_19: + s_n_llhttp__internal__n_error_19: { + state->error = 0xb; + state->reason = "Invalid character in Content-Length"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_content_length_ws: + s_n_llhttp__internal__n_header_value_content_length_ws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_content_length_ws; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_invoke_or_flags_15; + } + case 13: { + goto s_n_llhttp__internal__n_invoke_or_flags_15; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_content_length_ws; + } + default: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_content_length: + s_n_llhttp__internal__n_header_value_content_length: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_content_length; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_content_length_1; + } + default: { + goto s_n_llhttp__internal__n_header_value_content_length_ws; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_te_chunked_last: + s_n_llhttp__internal__n_header_value_te_chunked_last: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_te_chunked_last; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_invoke_update_header_state_8; + } + case 13: { + goto s_n_llhttp__internal__n_invoke_update_header_state_8; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_te_chunked_last; + } + default: { + goto s_n_llhttp__internal__n_header_value_te_chunked; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_te_token_ows: + s_n_llhttp__internal__n_header_value_te_token_ows: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_te_token_ows; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_header_value_te_token_ows; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_te_token_ows; + } + default: { + goto s_n_llhttp__internal__n_header_value_te_chunked; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_te_token: + s_n_llhttp__internal__n_header_value_te_token: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_header_value_te_token; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_header_value_te_token; + } + case 2: { + p++; + goto s_n_llhttp__internal__n_header_value_te_token_ows; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_7; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_te_chunked: + s_n_llhttp__internal__n_header_value_te_chunked: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_value_te_chunked; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob8, 7); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_header_value_te_chunked_last; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_value_te_chunked; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_header_value_te_token; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1: + s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_header_value; + goto s_n_llhttp__internal__n_invoke_load_header_state_2; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_value_discard_ws: + s_n_llhttp__internal__n_header_value_discard_ws: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_discard_ws; + } + switch (*p) { + case 9: { + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + case 10: { + p++; + goto s_n_llhttp__internal__n_header_value_discard_lws; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws_almost_done; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value_1; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_general_otherwise: + s_n_llhttp__internal__n_header_field_general_otherwise: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_general_otherwise; + } + switch (*p) { + case ':': { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1; + } + default: { + goto s_n_llhttp__internal__n_error_20; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_general: + s_n_llhttp__internal__n_header_field_general: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_header_field_general; + } + #ifdef __SSE4_2__ + if (endp - p >= 16) { + __m128i ranges; + __m128i input; + int avail; + int match_len; + + /* Load input */ + input = _mm_loadu_si128((__m128i const*) p); + ranges = _mm_loadu_si128((__m128i const*) llparse_blob9); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 16, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_header_field_general; + } + ranges = _mm_loadu_si128((__m128i const*) llparse_blob10); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 2, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_header_field_general; + } + goto s_n_llhttp__internal__n_header_field_general_otherwise; + } + #endif /* __SSE4_2__ */ + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_header_field_general; + } + default: { + goto s_n_llhttp__internal__n_header_field_general_otherwise; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_colon: + s_n_llhttp__internal__n_header_field_colon: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_colon; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_header_field_colon; + } + case ':': { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_field; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_9; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_3: + s_n_llhttp__internal__n_header_field_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_3; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob3, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_10; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_4: + s_n_llhttp__internal__n_header_field_4: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_4; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob11, 10); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_4; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_10; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_2: + s_n_llhttp__internal__n_header_field_2: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_2; + } + switch (((*p) | 0x20)) { + case 'n': { + p++; + goto s_n_llhttp__internal__n_header_field_3; + } + case 't': { + p++; + goto s_n_llhttp__internal__n_header_field_4; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_10; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_1: + s_n_llhttp__internal__n_header_field_1: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_1; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob2, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_header_field_2; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_1; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_10; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_5: + s_n_llhttp__internal__n_header_field_5: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_5; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob12, 15); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_5; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_10; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_6: + s_n_llhttp__internal__n_header_field_6: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_6; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob13, 16); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_6; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_10; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_7: + s_n_llhttp__internal__n_header_field_7: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_header_field_7; + } + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob14, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_header_state; + } + case kMatchPause: { + return s_n_llhttp__internal__n_header_field_7; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_invoke_update_header_state_10; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field: + s_n_llhttp__internal__n_header_field: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field; + } + switch (((*p) | 0x20)) { + case 'c': { + p++; + goto s_n_llhttp__internal__n_header_field_1; + } + case 'p': { + p++; + goto s_n_llhttp__internal__n_header_field_5; + } + case 't': { + p++; + goto s_n_llhttp__internal__n_header_field_6; + } + case 'u': { + p++; + goto s_n_llhttp__internal__n_header_field_7; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_header_state_10; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_header_field: + s_n_llhttp__internal__n_span_start_llhttp__on_header_field: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_header_field; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_header_field; + goto s_n_llhttp__internal__n_header_field; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_header_field_start: + s_n_llhttp__internal__n_header_field_start: { + if (p == endp) { + return s_n_llhttp__internal__n_header_field_start; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_headers_almost_done; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_headers_almost_done; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_field; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_skip_to_http09: + s_n_llhttp__internal__n_url_skip_to_http09: { + if (p == endp) { + return s_n_llhttp__internal__n_url_skip_to_http09; + } + p++; + goto s_n_llhttp__internal__n_invoke_update_http_major; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_skip_lf_to_http09: + s_n_llhttp__internal__n_url_skip_lf_to_http09: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_url_skip_lf_to_http09; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob15, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_update_http_major; + } + case kMatchPause: { + return s_n_llhttp__internal__n_url_skip_lf_to_http09; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_21; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_end_1: + s_n_llhttp__internal__n_req_http_end_1: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_end_1; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_header_field_start; + } + default: { + goto s_n_llhttp__internal__n_error_22; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_end: + s_n_llhttp__internal__n_req_http_end: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_end; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_header_field_start; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_req_http_end_1; + } + default: { + goto s_n_llhttp__internal__n_error_22; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_minor: + s_n_llhttp__internal__n_req_http_minor: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_minor; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_http_minor; + } + default: { + goto s_n_llhttp__internal__n_error_23; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_dot: + s_n_llhttp__internal__n_req_http_dot: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_dot; + } + switch (*p) { + case '.': { + p++; + goto s_n_llhttp__internal__n_req_http_minor; + } + default: { + goto s_n_llhttp__internal__n_error_24; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_major: + s_n_llhttp__internal__n_req_http_major: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_major; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_http_major; + } + default: { + goto s_n_llhttp__internal__n_error_25; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_start_1: + s_n_llhttp__internal__n_req_http_start_1: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_http_start_1; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob16, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_req_http_major; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_http_start_1; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_27; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_start_2: + s_n_llhttp__internal__n_req_http_start_2: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_http_start_2; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob17, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_is_equal_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_http_start_2; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_27; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_http_start: + s_n_llhttp__internal__n_req_http_start: { + if (p == endp) { + return s_n_llhttp__internal__n_req_http_start; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_req_http_start; + } + case 'H': { + p++; + goto s_n_llhttp__internal__n_req_http_start_1; + } + case 'I': { + p++; + goto s_n_llhttp__internal__n_req_http_start_2; + } + default: { + goto s_n_llhttp__internal__n_error_27; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_skip_to_http: + s_n_llhttp__internal__n_url_skip_to_http: { + if (p == endp) { + return s_n_llhttp__internal__n_url_skip_to_http; + } + p++; + goto s_n_llhttp__internal__n_req_http_start; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_fragment: + s_n_llhttp__internal__n_url_fragment: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_fragment; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_url_fragment; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_6; + } + case 3: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_7; + } + case 4: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_8; + } + default: { + goto s_n_llhttp__internal__n_error_28; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_end_stub_query_3: + s_n_llhttp__internal__n_span_end_stub_query_3: { + if (p == endp) { + return s_n_llhttp__internal__n_span_end_stub_query_3; + } + p++; + goto s_n_llhttp__internal__n_url_fragment; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_query: + s_n_llhttp__internal__n_url_query: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_query; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_url_query; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_9; + } + case 3: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_10; + } + case 4: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_11; + } + case 5: { + goto s_n_llhttp__internal__n_span_end_stub_query_3; + } + default: { + goto s_n_llhttp__internal__n_error_29; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_query_or_fragment: + s_n_llhttp__internal__n_url_query_or_fragment: { + if (p == endp) { + return s_n_llhttp__internal__n_url_query_or_fragment; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_3; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_4; + } + case ' ': { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_5; + } + case '#': { + p++; + goto s_n_llhttp__internal__n_url_fragment; + } + case '?': { + p++; + goto s_n_llhttp__internal__n_url_query; + } + default: { + goto s_n_llhttp__internal__n_error_30; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_path: + s_n_llhttp__internal__n_url_path: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_path; + } + #ifdef __SSE4_2__ + if (endp - p >= 16) { + __m128i ranges; + __m128i input; + int avail; + int match_len; + + /* Load input */ + input = _mm_loadu_si128((__m128i const*) p); + ranges = _mm_loadu_si128((__m128i const*) llparse_blob1); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 12, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_url_path; + } + goto s_n_llhttp__internal__n_url_query_or_fragment; + } + #endif /* __SSE4_2__ */ + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_url_path; + } + default: { + goto s_n_llhttp__internal__n_url_query_or_fragment; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_stub_path_2: + s_n_llhttp__internal__n_span_start_stub_path_2: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_stub_path_2; + } + p++; + goto s_n_llhttp__internal__n_url_path; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_stub_path: + s_n_llhttp__internal__n_span_start_stub_path: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_stub_path; + } + p++; + goto s_n_llhttp__internal__n_url_path; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_stub_path_1: + s_n_llhttp__internal__n_span_start_stub_path_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_stub_path_1; + } + p++; + goto s_n_llhttp__internal__n_url_path; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_server_with_at: + s_n_llhttp__internal__n_url_server_with_at: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 4, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 0, 6, + 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 0, 4, + 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 4, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_server_with_at; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_12; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_13; + } + case 3: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_14; + } + case 4: { + p++; + goto s_n_llhttp__internal__n_url_server; + } + case 5: { + goto s_n_llhttp__internal__n_span_start_stub_path_1; + } + case 6: { + p++; + goto s_n_llhttp__internal__n_url_query; + } + case 7: { + p++; + goto s_n_llhttp__internal__n_error_31; + } + default: { + goto s_n_llhttp__internal__n_error_32; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_server: + s_n_llhttp__internal__n_url_server: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 4, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 0, 6, + 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 0, 4, + 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 4, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_server; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_1; + } + case 3: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_url_2; + } + case 4: { + p++; + goto s_n_llhttp__internal__n_url_server; + } + case 5: { + goto s_n_llhttp__internal__n_span_start_stub_path; + } + case 6: { + p++; + goto s_n_llhttp__internal__n_url_query; + } + case 7: { + p++; + goto s_n_llhttp__internal__n_url_server_with_at; + } + default: { + goto s_n_llhttp__internal__n_error_33; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_schema_delim_1: + s_n_llhttp__internal__n_url_schema_delim_1: { + if (p == endp) { + return s_n_llhttp__internal__n_url_schema_delim_1; + } + switch (*p) { + case '/': { + p++; + goto s_n_llhttp__internal__n_url_server; + } + default: { + goto s_n_llhttp__internal__n_error_35; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_schema_delim: + s_n_llhttp__internal__n_url_schema_delim: { + if (p == endp) { + return s_n_llhttp__internal__n_url_schema_delim; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_error_34; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_error_34; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_error_34; + } + case '/': { + p++; + goto s_n_llhttp__internal__n_url_schema_delim_1; + } + default: { + goto s_n_llhttp__internal__n_error_35; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_end_stub_schema: + s_n_llhttp__internal__n_span_end_stub_schema: { + if (p == endp) { + return s_n_llhttp__internal__n_span_end_stub_schema; + } + p++; + goto s_n_llhttp__internal__n_url_schema_delim; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_schema: + s_n_llhttp__internal__n_url_schema: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_schema; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_34; + } + case 2: { + goto s_n_llhttp__internal__n_span_end_stub_schema; + } + case 3: { + p++; + goto s_n_llhttp__internal__n_url_schema; + } + default: { + goto s_n_llhttp__internal__n_error_36; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_url_start: + s_n_llhttp__internal__n_url_start: { + static uint8_t lookup_table[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + if (p == endp) { + return s_n_llhttp__internal__n_url_start; + } + switch (lookup_table[(uint8_t) *p]) { + case 1: { + p++; + goto s_n_llhttp__internal__n_error_34; + } + case 2: { + goto s_n_llhttp__internal__n_span_start_stub_path_2; + } + case 3: { + goto s_n_llhttp__internal__n_url_schema; + } + default: { + goto s_n_llhttp__internal__n_error_37; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_url_1: + s_n_llhttp__internal__n_span_start_llhttp__on_url_1: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_url_1; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_url; + goto s_n_llhttp__internal__n_url_start; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_url: + s_n_llhttp__internal__n_span_start_llhttp__on_url: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_url; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_url; + goto s_n_llhttp__internal__n_url_server; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_spaces_before_url: + s_n_llhttp__internal__n_req_spaces_before_url: { + if (p == endp) { + return s_n_llhttp__internal__n_req_spaces_before_url; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_req_spaces_before_url; + } + default: { + goto s_n_llhttp__internal__n_invoke_is_equal_method; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_first_space_before_url: + s_n_llhttp__internal__n_req_first_space_before_url: { + if (p == endp) { + return s_n_llhttp__internal__n_req_first_space_before_url; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_req_spaces_before_url; + } + default: { + goto s_n_llhttp__internal__n_error_38; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_1: + s_n_llhttp__internal__n_start_req_1: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_1; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob0, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 19; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_1; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_2: + s_n_llhttp__internal__n_start_req_2: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_2; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob18, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 16; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_2; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_4: + s_n_llhttp__internal__n_start_req_4: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_4; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob19, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 22; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_4; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_6: + s_n_llhttp__internal__n_start_req_6: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_6; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob20, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_6; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_7: + s_n_llhttp__internal__n_start_req_7: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_7; + } + switch (*p) { + case 'Y': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_5: + s_n_llhttp__internal__n_start_req_5: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_5; + } + switch (*p) { + case 'N': { + p++; + goto s_n_llhttp__internal__n_start_req_6; + } + case 'P': { + p++; + goto s_n_llhttp__internal__n_start_req_7; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_3: + s_n_llhttp__internal__n_start_req_3: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_3; + } + switch (*p) { + case 'H': { + p++; + goto s_n_llhttp__internal__n_start_req_4; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_start_req_5; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_8: + s_n_llhttp__internal__n_start_req_8: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_8; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob21, 5); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_8; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_9: + s_n_llhttp__internal__n_start_req_9: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_9; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob22, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_9; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_10: + s_n_llhttp__internal__n_start_req_10: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_10; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob23, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_10; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_12: + s_n_llhttp__internal__n_start_req_12: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_12; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob24, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 31; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_12; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_13: + s_n_llhttp__internal__n_start_req_13: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_13; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob25, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_13; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_11: + s_n_llhttp__internal__n_start_req_11: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_11; + } + switch (*p) { + case 'I': { + p++; + goto s_n_llhttp__internal__n_start_req_12; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_start_req_13; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_15: + s_n_llhttp__internal__n_start_req_15: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_15; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob26, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 24; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_15; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_16: + s_n_llhttp__internal__n_start_req_16: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_16; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob27, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 23; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_16; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_18: + s_n_llhttp__internal__n_start_req_18: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_18; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob28, 7); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 21; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_18; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_20: + s_n_llhttp__internal__n_start_req_20: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_20; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob29, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 30; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_20; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_21: + s_n_llhttp__internal__n_start_req_21: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_21; + } + switch (*p) { + case 'L': { + p++; + match = 10; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_19: + s_n_llhttp__internal__n_start_req_19: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_19; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_start_req_20; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_start_req_21; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_17: + s_n_llhttp__internal__n_start_req_17: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_17; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_start_req_18; + } + case 'C': { + p++; + goto s_n_llhttp__internal__n_start_req_19; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_22: + s_n_llhttp__internal__n_start_req_22: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_22; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob30, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 11; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_22; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_14: + s_n_llhttp__internal__n_start_req_14: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_14; + } + switch (*p) { + case '-': { + p++; + goto s_n_llhttp__internal__n_start_req_15; + } + case 'E': { + p++; + goto s_n_llhttp__internal__n_start_req_16; + } + case 'K': { + p++; + goto s_n_llhttp__internal__n_start_req_17; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_start_req_22; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_23: + s_n_llhttp__internal__n_start_req_23: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_23; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob31, 5); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 25; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_23; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_24: + s_n_llhttp__internal__n_start_req_24: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_24; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob32, 6); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_24; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_26: + s_n_llhttp__internal__n_start_req_26: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_26; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob33, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 28; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_26; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_27: + s_n_llhttp__internal__n_start_req_27: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_27; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob34, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_27; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_31: + s_n_llhttp__internal__n_start_req_31: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_31; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob35, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 12; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_31; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_32: + s_n_llhttp__internal__n_start_req_32: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_32; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob36, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 13; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_32; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_30: + s_n_llhttp__internal__n_start_req_30: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_30; + } + switch (*p) { + case 'F': { + p++; + goto s_n_llhttp__internal__n_start_req_31; + } + case 'P': { + p++; + goto s_n_llhttp__internal__n_start_req_32; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_29: + s_n_llhttp__internal__n_start_req_29: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_29; + } + switch (*p) { + case 'P': { + p++; + goto s_n_llhttp__internal__n_start_req_30; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_28: + s_n_llhttp__internal__n_start_req_28: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_28; + } + switch (*p) { + case 'I': { + p++; + match = 34; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_start_req_29; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_34: + s_n_llhttp__internal__n_start_req_34: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_34; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob37, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 29; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_34; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_33: + s_n_llhttp__internal__n_start_req_33: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_33; + } + switch (*p) { + case 'R': { + p++; + goto s_n_llhttp__internal__n_start_req_34; + } + case 'T': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_25: + s_n_llhttp__internal__n_start_req_25: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_25; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_start_req_26; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_start_req_27; + } + case 'R': { + p++; + goto s_n_llhttp__internal__n_start_req_28; + } + case 'U': { + p++; + goto s_n_llhttp__internal__n_start_req_33; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_37: + s_n_llhttp__internal__n_start_req_37: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_37; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob38, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 17; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_37; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_38: + s_n_llhttp__internal__n_start_req_38: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_38; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob39, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 20; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_38; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_36: + s_n_llhttp__internal__n_start_req_36: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_36; + } + switch (*p) { + case 'B': { + p++; + goto s_n_llhttp__internal__n_start_req_37; + } + case 'P': { + p++; + goto s_n_llhttp__internal__n_start_req_38; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_35: + s_n_llhttp__internal__n_start_req_35: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_35; + } + switch (*p) { + case 'E': { + p++; + goto s_n_llhttp__internal__n_start_req_36; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_40: + s_n_llhttp__internal__n_start_req_40: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_40; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob40, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 14; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_40; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_41: + s_n_llhttp__internal__n_start_req_41: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_41; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob41, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 33; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_41; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_42: + s_n_llhttp__internal__n_start_req_42: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_42; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob42, 7); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 26; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_42; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_39: + s_n_llhttp__internal__n_start_req_39: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_39; + } + switch (*p) { + case 'E': { + p++; + goto s_n_llhttp__internal__n_start_req_40; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_start_req_41; + } + case 'U': { + p++; + goto s_n_llhttp__internal__n_start_req_42; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_43: + s_n_llhttp__internal__n_start_req_43: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_43; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob43, 4); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_43; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_46: + s_n_llhttp__internal__n_start_req_46: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_46; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob44, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 18; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_46; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_48: + s_n_llhttp__internal__n_start_req_48: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_48; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob45, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 32; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_48; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_49: + s_n_llhttp__internal__n_start_req_49: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_49; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob46, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 15; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_49; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_47: + s_n_llhttp__internal__n_start_req_47: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_47; + } + switch (*p) { + case 'I': { + p++; + goto s_n_llhttp__internal__n_start_req_48; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_start_req_49; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_50: + s_n_llhttp__internal__n_start_req_50: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_req_50; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob47, 8); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 27; + goto s_n_llhttp__internal__n_invoke_store_method_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_req_50; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_45: + s_n_llhttp__internal__n_start_req_45: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_45; + } + switch (*p) { + case 'B': { + p++; + goto s_n_llhttp__internal__n_start_req_46; + } + case 'L': { + p++; + goto s_n_llhttp__internal__n_start_req_47; + } + case 'S': { + p++; + goto s_n_llhttp__internal__n_start_req_50; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_44: + s_n_llhttp__internal__n_start_req_44: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_44; + } + switch (*p) { + case 'N': { + p++; + goto s_n_llhttp__internal__n_start_req_45; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req: + s_n_llhttp__internal__n_start_req: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req; + } + switch (*p) { + case 'A': { + p++; + goto s_n_llhttp__internal__n_start_req_1; + } + case 'B': { + p++; + goto s_n_llhttp__internal__n_start_req_2; + } + case 'C': { + p++; + goto s_n_llhttp__internal__n_start_req_3; + } + case 'D': { + p++; + goto s_n_llhttp__internal__n_start_req_8; + } + case 'G': { + p++; + goto s_n_llhttp__internal__n_start_req_9; + } + case 'H': { + p++; + goto s_n_llhttp__internal__n_start_req_10; + } + case 'L': { + p++; + goto s_n_llhttp__internal__n_start_req_11; + } + case 'M': { + p++; + goto s_n_llhttp__internal__n_start_req_14; + } + case 'N': { + p++; + goto s_n_llhttp__internal__n_start_req_23; + } + case 'O': { + p++; + goto s_n_llhttp__internal__n_start_req_24; + } + case 'P': { + p++; + goto s_n_llhttp__internal__n_start_req_25; + } + case 'R': { + p++; + goto s_n_llhttp__internal__n_start_req_35; + } + case 'S': { + p++; + goto s_n_llhttp__internal__n_start_req_39; + } + case 'T': { + p++; + goto s_n_llhttp__internal__n_start_req_43; + } + case 'U': { + p++; + goto s_n_llhttp__internal__n_start_req_44; + } + default: { + goto s_n_llhttp__internal__n_error_46; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_line_almost_done: + s_n_llhttp__internal__n_res_line_almost_done: { + if (p == endp) { + return s_n_llhttp__internal__n_res_line_almost_done; + } + p++; + goto s_n_llhttp__internal__n_header_field_start; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_status: + s_n_llhttp__internal__n_res_status: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_status; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_status_1; + } + default: { + p++; + goto s_n_llhttp__internal__n_res_status; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_span_start_llhttp__on_status: + s_n_llhttp__internal__n_span_start_llhttp__on_status: { + if (p == endp) { + return s_n_llhttp__internal__n_span_start_llhttp__on_status; + } + state->_span_pos0 = (void*) p; + state->_span_cb0 = llhttp__on_status; + goto s_n_llhttp__internal__n_res_status; + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_status_start: + s_n_llhttp__internal__n_res_status_start: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status_start; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_header_field_start; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_res_line_almost_done; + } + default: { + goto s_n_llhttp__internal__n_span_start_llhttp__on_status; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_status_code_otherwise: + s_n_llhttp__internal__n_res_status_code_otherwise: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status_code_otherwise; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_res_status_start; + } + case 13: { + goto s_n_llhttp__internal__n_res_status_start; + } + case ' ': { + p++; + goto s_n_llhttp__internal__n_res_status_start; + } + default: { + goto s_n_llhttp__internal__n_error_40; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_status_code: + s_n_llhttp__internal__n_res_status_code: { + if (p == endp) { + return s_n_llhttp__internal__n_res_status_code; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_mul_add_status_code; + } + default: { + goto s_n_llhttp__internal__n_res_status_code_otherwise; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_http_end: + s_n_llhttp__internal__n_res_http_end: { + if (p == endp) { + return s_n_llhttp__internal__n_res_http_end; + } + switch (*p) { + case ' ': { + p++; + goto s_n_llhttp__internal__n_invoke_update_status_code; + } + default: { + goto s_n_llhttp__internal__n_error_41; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_http_minor: + s_n_llhttp__internal__n_res_http_minor: { + if (p == endp) { + return s_n_llhttp__internal__n_res_http_minor; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_http_minor_1; + } + default: { + goto s_n_llhttp__internal__n_error_42; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_http_dot: + s_n_llhttp__internal__n_res_http_dot: { + if (p == endp) { + return s_n_llhttp__internal__n_res_http_dot; + } + switch (*p) { + case '.': { + p++; + goto s_n_llhttp__internal__n_res_http_minor; + } + default: { + goto s_n_llhttp__internal__n_error_43; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_res_http_major: + s_n_llhttp__internal__n_res_http_major: { + if (p == endp) { + return s_n_llhttp__internal__n_res_http_major; + } + switch (*p) { + case '0': { + p++; + match = 0; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '1': { + p++; + match = 1; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '2': { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '3': { + p++; + match = 3; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '4': { + p++; + match = 4; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '5': { + p++; + match = 5; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '6': { + p++; + match = 6; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '7': { + p++; + match = 7; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '8': { + p++; + match = 8; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + case '9': { + p++; + match = 9; + goto s_n_llhttp__internal__n_invoke_store_http_major_1; + } + default: { + goto s_n_llhttp__internal__n_error_44; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_res: + s_n_llhttp__internal__n_start_res: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_start_res; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob48, 5); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_res_http_major; + } + case kMatchPause: { + return s_n_llhttp__internal__n_start_res; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_47; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_or_res_method_2: + s_n_llhttp__internal__n_req_or_res_method_2: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_or_res_method_2; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob49, 2); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + match = 2; + goto s_n_llhttp__internal__n_invoke_store_method; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_or_res_method_2; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_45; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_or_res_method_3: + s_n_llhttp__internal__n_req_or_res_method_3: { + llparse_match_t match_seq; + + if (p == endp) { + return s_n_llhttp__internal__n_req_or_res_method_3; + } + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob50, 3); + p = match_seq.current; + switch (match_seq.status) { + case kMatchComplete: { + p++; + goto s_n_llhttp__internal__n_invoke_update_type_1; + } + case kMatchPause: { + return s_n_llhttp__internal__n_req_or_res_method_3; + } + case kMatchMismatch: { + goto s_n_llhttp__internal__n_error_45; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_or_res_method_1: + s_n_llhttp__internal__n_req_or_res_method_1: { + if (p == endp) { + return s_n_llhttp__internal__n_req_or_res_method_1; + } + switch (*p) { + case 'E': { + p++; + goto s_n_llhttp__internal__n_req_or_res_method_2; + } + case 'T': { + p++; + goto s_n_llhttp__internal__n_req_or_res_method_3; + } + default: { + goto s_n_llhttp__internal__n_error_45; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_req_or_res_method: + s_n_llhttp__internal__n_req_or_res_method: { + if (p == endp) { + return s_n_llhttp__internal__n_req_or_res_method; + } + switch (*p) { + case 'H': { + p++; + goto s_n_llhttp__internal__n_req_or_res_method_1; + } + default: { + goto s_n_llhttp__internal__n_error_45; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start_req_or_res: + s_n_llhttp__internal__n_start_req_or_res: { + if (p == endp) { + return s_n_llhttp__internal__n_start_req_or_res; + } + switch (*p) { + case 'H': { + goto s_n_llhttp__internal__n_req_or_res_method; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_type_2; + } + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_invoke_load_type: + s_n_llhttp__internal__n_invoke_load_type: { + switch (llhttp__internal__c_load_type(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_start_req; + case 2: + goto s_n_llhttp__internal__n_start_res; + default: + goto s_n_llhttp__internal__n_start_req_or_res; + } + /* UNREACHABLE */; + abort(); + } + case s_n_llhttp__internal__n_start: + s_n_llhttp__internal__n_start: { + if (p == endp) { + return s_n_llhttp__internal__n_start; + } + switch (*p) { + case 10: { + p++; + goto s_n_llhttp__internal__n_start; + } + case 13: { + p++; + goto s_n_llhttp__internal__n_start; + } + default: { + goto s_n_llhttp__internal__n_invoke_update_finish; + } + } + /* UNREACHABLE */; + abort(); + } + default: + /* UNREACHABLE */ + abort(); + } + s_n_llhttp__internal__n_error_34: { + state->error = 0x7; + state->reason = "Invalid characters in url"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_finish_1: { + switch (llhttp__internal__c_update_finish_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_start; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_5: { + state->error = 0x15; + state->reason = "on_message_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_upgrade; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_9: { + state->error = 0x12; + state->reason = "`on_message_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_7: { + state->error = 0x15; + state->reason = "on_chunk_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_13: { + state->error = 0x14; + state->reason = "`on_chunk_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1: { + switch (llhttp__on_chunk_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + case 21: + goto s_n_llhttp__internal__n_pause_7; + default: + goto s_n_llhttp__internal__n_error_13; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_11: { + state->error = 0x4; + state->reason = "Content-Length can't be present with Transfer-Encoding"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_12: { + state->error = 0x4; + state->reason = "Content-Length can't be present with chunked encoding"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_2: { + state->error = 0x15; + state->reason = "on_message_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_pause_1; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_3: { + state->error = 0x12; + state->reason = "`on_message_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_1: { + switch (llhttp__on_message_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_pause_1; + case 21: + goto s_n_llhttp__internal__n_pause_2; + default: + goto s_n_llhttp__internal__n_error_3; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_7: { + state->error = 0xc; + state->reason = "Chunk size overflow"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_3: { + state->error = 0x15; + state->reason = "on_chunk_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_update_content_length; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_5: { + state->error = 0x14; + state->reason = "`on_chunk_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete: { + switch (llhttp__on_chunk_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_update_content_length; + case 21: + goto s_n_llhttp__internal__n_pause_3; + default: + goto s_n_llhttp__internal__n_error_5; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_body: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_body(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_chunk_data_almost_done; + return s_error; + } + goto s_n_llhttp__internal__n_chunk_data_almost_done; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags: { + switch (llhttp__internal__c_or_flags(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_field_start; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_4: { + state->error = 0x15; + state->reason = "on_chunk_header pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_is_equal_content_length; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_4: { + state->error = 0x13; + state->reason = "`on_chunk_header` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_chunk_header: { + switch (llhttp__on_chunk_header(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_is_equal_content_length; + case 21: + goto s_n_llhttp__internal__n_pause_4; + default: + goto s_n_llhttp__internal__n_error_4; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_6: { + state->error = 0xc; + state->reason = "Invalid character in chunk size"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_mul_add_content_length: { + switch (llhttp__internal__c_mul_add_content_length(state, p, endp, match)) { + case 1: + goto s_n_llhttp__internal__n_error_7; + default: + goto s_n_llhttp__internal__n_chunk_size; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_8: { + state->error = 0xc; + state->reason = "Invalid character in chunk size"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_body_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_body(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + return s_error; + } + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_complete_2; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_finish_2: { + switch (llhttp__internal__c_update_finish_2(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_body_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_10: { + state->error = 0xf; + state->reason = "Request has invalid `Transfer-Encoding`"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause: { + state->error = 0x15; + state->reason = "on_message_complete pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_2: { + state->error = 0x12; + state->reason = "`on_message_complete` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_message_complete: { + switch (llhttp__on_message_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__after_message_complete; + case 21: + goto s_n_llhttp__internal__n_pause; + default: + goto s_n_llhttp__internal__n_error_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_1: { + switch (llhttp__internal__c_or_flags_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_2: { + switch (llhttp__internal__c_or_flags_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_upgrade: { + switch (llhttp__internal__c_update_upgrade(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_or_flags_2; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_6: { + state->error = 0x15; + state->reason = "Paused by on_headers_complete"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_1: { + state->error = 0x11; + state->reason = "User callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete: { + switch (llhttp__on_headers_complete(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_llhttp__after_headers_complete; + case 1: + goto s_n_llhttp__internal__n_invoke_or_flags_1; + case 2: + goto s_n_llhttp__internal__n_invoke_update_upgrade; + case 21: + goto s_n_llhttp__internal__n_pause_6; + default: + goto s_n_llhttp__internal__n_error_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete: { + switch (llhttp__before_headers_complete(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags_3: { + switch (llhttp__internal__c_test_flags_3(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_error_12; + default: + goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags_2: { + switch (llhttp__internal__c_test_flags_2(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_error_11; + case 1: + goto s_n_llhttp__internal__n_invoke_test_flags_3; + default: + goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags_1: { + switch (llhttp__internal__c_test_flags_1(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_test_flags_2; + default: + goto s_n_llhttp__internal__n_invoke_llhttp__before_headers_complete; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags: { + switch (llhttp__internal__c_test_flags(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_invoke_llhttp__on_chunk_complete_1; + default: + goto s_n_llhttp__internal__n_invoke_test_flags_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_14: { + state->error = 0xb; + state->reason = "Empty Content-Length"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_field_start; + return s_error; + } + goto s_n_llhttp__internal__n_header_field_start; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state: { + switch (llhttp__internal__c_update_header_state(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_3: { + switch (llhttp__internal__c_or_flags_3(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_4: { + switch (llhttp__internal__c_or_flags_4(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_5: { + switch (llhttp__internal__c_or_flags_5(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_6: { + switch (llhttp__internal__c_or_flags_6(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state_1: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 5: + goto s_n_llhttp__internal__n_invoke_or_flags_3; + case 6: + goto s_n_llhttp__internal__n_invoke_or_flags_4; + case 7: + goto s_n_llhttp__internal__n_invoke_or_flags_5; + case 8: + goto s_n_llhttp__internal__n_invoke_or_flags_6; + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_header_value; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 2: + goto s_n_llhttp__internal__n_error_14; + default: + goto s_n_llhttp__internal__n_invoke_load_header_state_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_1: { + switch (llhttp__internal__c_update_header_state(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_field_start; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_7: { + switch (llhttp__internal__c_or_flags_3(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_8: { + switch (llhttp__internal__c_or_flags_4(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_9: { + switch (llhttp__internal__c_or_flags_5(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_1; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_10: { + switch (llhttp__internal__c_or_flags_6(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_field_start; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state_3: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 5: + goto s_n_llhttp__internal__n_invoke_or_flags_7; + case 6: + goto s_n_llhttp__internal__n_invoke_or_flags_8; + case 7: + goto s_n_llhttp__internal__n_invoke_or_flags_9; + case 8: + goto s_n_llhttp__internal__n_invoke_or_flags_10; + default: + goto s_n_llhttp__internal__n_header_field_start; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_15: { + state->error = 0x3; + state->reason = "Missing expected LF after header value"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; + return s_error; + } + goto s_n_llhttp__internal__n_header_value_almost_done; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_header_value_almost_done; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_header_value_almost_done; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_16: { + state->error = 0xa; + state->reason = "Invalid header value char"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags_4: { + switch (llhttp__internal__c_test_flags_2(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_lenient; + default: + goto s_n_llhttp__internal__n_error_16; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_3: { + switch (llhttp__internal__c_update_header_state(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_11: { + switch (llhttp__internal__c_or_flags_3(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_3; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_12: { + switch (llhttp__internal__c_or_flags_4(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_3; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_13: { + switch (llhttp__internal__c_or_flags_5(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_3; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_14: { + switch (llhttp__internal__c_or_flags_6(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state_4: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 5: + goto s_n_llhttp__internal__n_invoke_or_flags_11; + case 6: + goto s_n_llhttp__internal__n_invoke_or_flags_12; + case 7: + goto s_n_llhttp__internal__n_invoke_or_flags_13; + case 8: + goto s_n_llhttp__internal__n_invoke_or_flags_14; + default: + goto s_n_llhttp__internal__n_header_value_connection; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_4: { + switch (llhttp__internal__c_update_header_state_4(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection_token; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_2: { + switch (llhttp__internal__c_update_header_state_2(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection_ws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_5: { + switch (llhttp__internal__c_update_header_state_5(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection_ws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_6: { + switch (llhttp__internal__c_update_header_state_6(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_connection_ws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_18; + return s_error; + } + goto s_n_llhttp__internal__n_error_18; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_mul_add_content_length_1: { + switch (llhttp__internal__c_mul_add_content_length_1(state, p, endp, match)) { + case 1: + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4; + default: + goto s_n_llhttp__internal__n_header_value_content_length; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_7: { + switch (llhttp__internal__c_update_header_state_4(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_15: { + switch (llhttp__internal__c_or_flags_15(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_discard_rws; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_error_19; + return s_error; + } + goto s_n_llhttp__internal__n_error_19; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_17: { + state->error = 0x4; + state->reason = "Duplicate Content-Length"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_test_flags_5: { + switch (llhttp__internal__c_test_flags_5(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_header_value_content_length; + default: + goto s_n_llhttp__internal__n_error_17; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_8: { + switch (llhttp__internal__c_update_header_state_8(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_otherwise; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_16: { + switch (llhttp__internal__c_or_flags_16(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_value_te_chunked; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_or_flags_17: { + switch (llhttp__internal__c_or_flags_17(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_header_state_7; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_load_header_state_2: { + switch (llhttp__internal__c_load_header_state(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_connection; + case 2: + goto s_n_llhttp__internal__n_invoke_test_flags_5; + case 3: + goto s_n_llhttp__internal__n_invoke_or_flags_16; + case 4: + goto s_n_llhttp__internal__n_invoke_or_flags_17; + default: + goto s_n_llhttp__internal__n_header_value; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_field: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_field(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_discard_ws; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_header_field_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_field(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_discard_ws; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_header_value_discard_ws; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_20: { + state->error = 0xa; + state->reason = "Invalid header token"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_9: { + switch (llhttp__internal__c_update_header_state_4(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_field_general; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_header_state: { + switch (llhttp__internal__c_store_header_state(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_header_field_colon; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_header_state_10: { + switch (llhttp__internal__c_update_header_state_4(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_field_general; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_http_minor: { + switch (llhttp__internal__c_update_http_minor(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_header_field_start; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_http_major: { + switch (llhttp__internal__c_update_http_major(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_update_http_minor; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_21: { + state->error = 0x7; + state->reason = "Expected CRLF"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_4: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_22: { + state->error = 0x9; + state->reason = "Expected CRLF after version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_http_minor: { + switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_req_http_end; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_23: { + state->error = 0x9; + state->reason = "Invalid minor version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_24: { + state->error = 0x9; + state->reason = "Expected dot"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_http_major: { + switch (llhttp__internal__c_store_http_major(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_req_http_dot; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_25: { + state->error = 0x9; + state->reason = "Invalid major version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_27: { + state->error = 0x8; + state->reason = "Expected HTTP/"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_26: { + state->error = 0x8; + state->reason = "Expected SOURCE method for ICE/x.x request"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_is_equal_method_1: { + switch (llhttp__internal__c_is_equal_method_1(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_error_26; + default: + goto s_n_llhttp__internal__n_req_http_major; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_5: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_6: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_7: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_8: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_28: { + state->error = 0x7; + state->reason = "Invalid char in url fragment start"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_9: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_10: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_11: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_29: { + state->error = 0x7; + state->reason = "Invalid char in url query"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_30: { + state->error = 0x7; + state->reason = "Invalid char in url path"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_2: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_12: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_13: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_lf_to_http09; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_lf_to_http09; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_url_14: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_url(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_url_skip_to_http; + return s_error; + } + goto s_n_llhttp__internal__n_url_skip_to_http; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_31: { + state->error = 0x7; + state->reason = "Double @ in url"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_32: { + state->error = 0x7; + state->reason = "Unexpected char in url server"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_33: { + state->error = 0x7; + state->reason = "Unexpected char in url server"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_35: { + state->error = 0x7; + state->reason = "Unexpected char in url schema"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_36: { + state->error = 0x7; + state->reason = "Unexpected char in url schema"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_37: { + state->error = 0x7; + state->reason = "Unexpected start char in url"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_is_equal_method: { + switch (llhttp__internal__c_is_equal_method(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_span_start_llhttp__on_url_1; + default: + goto s_n_llhttp__internal__n_span_start_llhttp__on_url; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_38: { + state->error = 0x6; + state->reason = "Expected space after method"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_method_1: { + switch (llhttp__internal__c_store_method(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_req_first_space_before_url; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_46: { + state->error = 0x6; + state->reason = "Invalid method encountered"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_39: { + state->error = 0xd; + state->reason = "Response overflow"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_mul_add_status_code: { + switch (llhttp__internal__c_mul_add_status_code(state, p, endp, match)) { + case 1: + goto s_n_llhttp__internal__n_error_39; + default: + goto s_n_llhttp__internal__n_res_status_code; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_status: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_status(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_field_start; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_header_field_start; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_span_end_llhttp__on_status_1: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_status(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_res_line_almost_done; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_res_line_almost_done; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_40: { + state->error = 0xd; + state->reason = "Invalid response status"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_status_code: { + switch (llhttp__internal__c_update_status_code(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_res_status_code; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_41: { + state->error = 0x9; + state->reason = "Expected space after version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_http_minor_1: { + switch (llhttp__internal__c_store_http_minor(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_res_http_end; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_42: { + state->error = 0x9; + state->reason = "Invalid minor version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_43: { + state->error = 0x9; + state->reason = "Expected dot"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_http_major_1: { + switch (llhttp__internal__c_store_http_major(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_res_http_dot; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_44: { + state->error = 0x9; + state->reason = "Invalid major version"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_47: { + state->error = 0x8; + state->reason = "Expected HTTP/"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_type: { + switch (llhttp__internal__c_update_type(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_req_first_space_before_url; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_store_method: { + switch (llhttp__internal__c_store_method(state, p, endp, match)) { + default: + goto s_n_llhttp__internal__n_invoke_update_type; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error_45: { + state->error = 0x8; + state->reason = "Invalid word encountered"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_type_1: { + switch (llhttp__internal__c_update_type_1(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_res_http_major; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_type_2: { + switch (llhttp__internal__c_update_type(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_start_req; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_pause_8: { + state->error = 0x15; + state->reason = "on_message_begin pause"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_invoke_load_type; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_error: { + state->error = 0x10; + state->reason = "`on_message_begin` callback error"; + state->error_pos = (const char*) p; + state->_current = (void*) (intptr_t) s_error; + return s_error; + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_llhttp__on_message_begin: { + switch (llhttp__on_message_begin(state, p, endp)) { + case 0: + goto s_n_llhttp__internal__n_invoke_load_type; + case 21: + goto s_n_llhttp__internal__n_pause_8; + default: + goto s_n_llhttp__internal__n_error; + } + /* UNREACHABLE */; + abort(); + } + s_n_llhttp__internal__n_invoke_update_finish: { + switch (llhttp__internal__c_update_finish(state, p, endp)) { + default: + goto s_n_llhttp__internal__n_invoke_llhttp__on_message_begin; + } + /* UNREACHABLE */; + abort(); + } +} + +int llhttp__internal_execute(llhttp__internal_t* state, const char* p, const char* endp) { + llparse_state_t next; + + /* check lingering errors */ + if (state->error != 0) { + return state->error; + } + + /* restart spans */ + if (state->_span_pos0 != NULL) { + state->_span_pos0 = (void*) p; + } + + next = llhttp__internal__run(state, (const unsigned char*) p, (const unsigned char*) endp); + if (next == s_error) { + return state->error; + } + state->_current = (void*) (intptr_t) next; + + /* execute spans */ + if (state->_span_pos0 != NULL) { + int error; + + error = ((llhttp__internal__span_cb) state->_span_cb0)(state, state->_span_pos0, (const char*) endp); + if (error != 0) { + state->error = error; + state->error_pos = endp; + return error; + } + } + + return 0; +} \ No newline at end of file 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 new file mode 100644 index 00000000000..aa22c03a87a --- /dev/null +++ b/core-tests/include/httplib_client.h @@ -0,0 +1,4039 @@ +/** + * httplib.h + * + * Copyright (c) 2020 Yuji Hirose. All rights reserved. + * MIT License + * GitHub: https://github.com/yhirose/cpp-httplib + */ + +#pragma once + +#include "swoole_websocket.h" + +/* + * Configuration + */ + +#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND +#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND +#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT +#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5 +#endif + +#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND +#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300 +#endif + +#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND +#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND +#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND +#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_WRITE_TIMEOUT_SECOND +#define CPPHTTPLIB_WRITE_TIMEOUT_SECOND 5 +#endif + +#ifndef CPPHTTPLIB_WRITE_TIMEOUT_USECOND +#define CPPHTTPLIB_WRITE_TIMEOUT_USECOND 0 +#endif + +#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND +#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0 +#endif + +#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND +#ifdef _WIN32 +#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000 +#else +#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0 +#endif +#endif + +#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH +#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192 +#endif + +#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT +#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20 +#endif + +#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH +#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits::max)()) +#endif + +#ifndef CPPHTTPLIB_TCP_NODELAY +#define CPPHTTPLIB_TCP_NODELAY true +#endif + +#ifndef CPPHTTPLIB_RECV_BUFSIZ +#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u) +#endif + +#ifndef CPPHTTPLIB_THREAD_POOL_COUNT +#define CPPHTTPLIB_THREAD_POOL_COUNT \ + ((std::max)(8u, std::thread::hardware_concurrency() > 0 ? std::thread::hardware_concurrency() - 1 : 0)) +#endif + +// Prefer gnu::deprecated, otherwise gcc complains if we use +// [[deprecated]] together with pedantic. +#ifndef CPPHTTPLIB_DEPRECATED +#if defined(__has_cpp_attribute) +#if __has_cpp_attribute(gnu::deprecated) +#define CPPHTTPLIB_DEPRECATED [[gnu::deprecated]] +#else +#if __has_cpp_attribute(deprecated) +#define CPPHTTPLIB_DEPRECATED [[deprecated]] +#else +#define CPPHTTPLIB_DEPRECATED +#endif +#endif +#else +#define CPPHTTPLIB_DEPRECATED +#endif +#endif + +/* + * Headers + */ + +#ifdef _WIN32 +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif //_CRT_SECURE_NO_WARNINGS + +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif //_CRT_NONSTDC_NO_DEPRECATE + +#if defined(_MSC_VER) +#ifdef _WIN64 +using ssize_t = __int64; +#else +using ssize_t = int; +#endif + +#if _MSC_VER < 1900 +#define snprintf _snprintf_s +#endif +#endif // _MSC_VER + +#ifndef S_ISREG +#define S_ISREG(m) (((m) &S_IFREG) == S_IFREG) +#endif // S_ISREG + +#ifndef S_ISDIR +#define S_ISDIR(m) (((m) &S_IFDIR) == S_IFDIR) +#endif // S_ISDIR + +#ifndef NOMINMAX +#define NOMINMAX +#endif // NOMINMAX + +#include +#include +#include + +#ifndef WSA_FLAG_NO_HANDLE_INHERIT +#define WSA_FLAG_NO_HANDLE_INHERIT 0x80 +#endif + +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") +#endif + +#ifndef strcasecmp +#define strcasecmp _stricmp +#endif // strcasecmp + +using socket_t = SOCKET; +#ifdef CPPHTTPLIB_USE_POLL +#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout) +#endif + +#else // not _WIN32 + +#include +#include +#include +#include +#include +#include +#ifdef CPPHTTPLIB_USE_POLL +#include +#endif +#include +#include +#include +#include +#include + +using socket_t = int; +#define INVALID_SOCKET (-1) +#endif //_WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +#include +#include +#include +#include + +#include +#include +#include + +// #if OPENSSL_VERSION_NUMBER < 0x1010100fL +// #error Sorry, OpenSSL versions prior to 1.1.1 are not supported +// #endif + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +#include +inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) { + return M_ASN1_STRING_data(asn1); +} +#endif +#endif + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +#include +#endif +/* + * Declaration + */ +namespace httplib { + +const std::string USER_AGENT = "cpp-httplib/0.7"; + +namespace detail { + +struct ci { + bool operator()(const std::string &s1, const std::string &s2) const { + return std::lexicographical_compare( + s1.begin(), s1.end(), s2.begin(), s2.end(), [](char c1, char c2) { return ::tolower(c1) < ::tolower(c2); }); + } +}; + +} // namespace detail + +using Headers = std::multimap; + +using Params = std::multimap; +using Match = std::smatch; + +using Progress = std::function; + +struct Response; +struct WebSocketFrame; + +using ResponseHandler = std::function; + +struct MultipartFormData { + std::string name; + std::string content; + std::string filename; + std::string content_type; +}; +using MultipartFormDataItems = std::vector; +using MultipartFormDataMap = std::multimap; + +class DataSink { + public: + DataSink() : os(&sb_), sb_(*this) {} + + DataSink(const DataSink &) = delete; + DataSink &operator=(const DataSink &) = delete; + DataSink(DataSink &&) = delete; + DataSink &operator=(DataSink &&) = delete; + + std::function write; + std::function done; + std::function is_writable; + std::ostream os; + + private: + class data_sink_streambuf : public std::streambuf { + public: + data_sink_streambuf(DataSink &sink) : sink_(sink) {} + + protected: + std::streamsize xsputn(const char *s, std::streamsize n) { + sink_.write(s, static_cast(n)); + return n; + } + + private: + DataSink &sink_; + }; + + data_sink_streambuf sb_; +}; + +using ContentProvider = std::function; + +using ChunkedContentProvider = std::function; + +using ContentReceiver = std::function; + +using MultipartContentHeader = std::function; + +using Range = std::pair; +using Ranges = std::vector; + +struct Request { + std::string method; + std::string path; + Headers headers; + std::string body; + + std::string remote_addr; + int remote_port = -1; + + // for server + std::string version; + std::string target; + Params params; + MultipartFormDataMap files; + Ranges ranges; + Match matches; + + // for client + size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT; + ResponseHandler response_handler; + ContentReceiver content_receiver; + size_t content_length = 0; + ContentProvider content_provider; + Progress progress; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + const SSL *ssl; +#endif + + bool has_header(const char *key) const; + std::string get_header_value(const char *key, size_t id = 0) const; + size_t get_header_value_count(const char *key) const; + void set_header(const char *key, const char *val); + void set_header(const char *key, const std::string &val); + + bool has_param(const char *key) const; + std::string get_param_value(const char *key, size_t id = 0) const; + size_t get_param_value_count(const char *key) const; + + bool is_multipart_form_data() const; + + bool has_file(const char *key) const; + MultipartFormData get_file_value(const char *key) const; + + // private members... + size_t authorization_count_ = 0; +}; + +struct Response { + std::string version; + int status = -1; + Headers headers; + std::string body; + + bool has_header(const char *key) const; + std::string get_header_value(const char *key, size_t id = 0) const; + size_t get_header_value_count(const char *key) const; + void set_header(const char *key, const char *val); + void set_header(const char *key, const std::string &val); + + void set_redirect(const char *url, int status = 302); + void set_content(const char *s, size_t n, const char *content_type); + void set_content(std::string s, const char *content_type); + + void set_content_provider(size_t length, ContentProvider provider, std::function resource_releaser = [] {}); + + void set_chunked_content_provider(ChunkedContentProvider provider, std::function resource_releaser = [] {}); + + Response() = default; + Response(const Response &) = default; + Response &operator=(const Response &) = default; + Response(Response &&) = default; + Response &operator=(Response &&) = default; + ~Response() { + if (content_provider_resource_releaser_) { + content_provider_resource_releaser_(); + } + } + + // private members... + size_t content_length_ = 0; + ContentProvider content_provider_; + std::function content_provider_resource_releaser_; +}; + +struct WebSocketFrame : public swoole::websocket::Frame { + WebSocketFrame() = default; + ~WebSocketFrame() { + if (payload) { + sw_free(payload - header_length); + } + } +}; + +class Stream { + public: + virtual ~Stream() = default; + + virtual bool is_readable() const = 0; + virtual bool is_writable() const = 0; + + virtual ssize_t read(char *ptr, size_t size) = 0; + virtual ssize_t write(const char *ptr, size_t size) = 0; + virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0; + + template + ssize_t write_format(const char *fmt, const Args &... args); + ssize_t write(const char *ptr); + ssize_t write(const std::string &s); +}; + +using Logger = std::function; + +using SocketOptions = std::function; + +inline void default_socket_options(socket_t sock) { + int yes = 1; +#ifdef _WIN32 + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&yes), sizeof(yes)); + setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, reinterpret_cast(&yes), sizeof(yes)); +#else +#ifdef SO_REUSEPORT + setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast(&yes), sizeof(yes)); +#else + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&yes), sizeof(yes)); +#endif +#endif +} + +class Client { + public: + explicit Client(const std::string &host); + + explicit Client(const std::string &host, int port); + + explicit Client(const std::string &host, + int port, + const std::string &client_cert_path, + const std::string &client_key_path); + + virtual ~Client(); + + virtual bool is_valid() const; + + std::shared_ptr Get(const char *path); + + std::shared_ptr Get(const char *path, const Headers &headers); + + std::shared_ptr Get(const char *path, Progress progress); + + std::shared_ptr Get(const char *path, const Headers &headers, Progress progress); + + std::shared_ptr Get(const char *path, ContentReceiver content_receiver); + + std::shared_ptr Get(const char *path, const Headers &headers, ContentReceiver content_receiver); + + std::shared_ptr Get(const char *path, ContentReceiver content_receiver, Progress progress); + + std::shared_ptr Get(const char *path, + const Headers &headers, + ContentReceiver content_receiver, + Progress progress); + + std::shared_ptr Get(const char *path, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver); + + std::shared_ptr Get(const char *path, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress); + + bool Upgrade(const char *path, Headers &headers); + + std::shared_ptr Head(const char *path); + + std::shared_ptr Head(const char *path, const Headers &headers); + + std::shared_ptr Post(const char *path); + + std::shared_ptr Post(const char *path, const std::string &body, const char *content_type); + + std::shared_ptr Post(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type); + + std::shared_ptr Post(const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type); + + std::shared_ptr Post(const char *path, + const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type); + + std::shared_ptr Post(const char *path, const Params ¶ms); + + std::shared_ptr Post(const char *path, const Headers &headers, const Params ¶ms); + + std::shared_ptr Post(const char *path, const MultipartFormDataItems &items); + + std::shared_ptr Post(const char *path, const Headers &headers, const MultipartFormDataItems &items); + + std::shared_ptr Put(const char *path); + + std::shared_ptr Put(const char *path, const std::string &body, const char *content_type); + + std::shared_ptr Put(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type); + + std::shared_ptr Put(const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type); + + std::shared_ptr Put(const char *path, + const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type); + + std::shared_ptr Put(const char *path, const Params ¶ms); + + std::shared_ptr Put(const char *path, const Headers &headers, const Params ¶ms); + + // websocket + 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 = swoole::websocket::OPCODE_TEXT); + std::shared_ptr Recv(); + + std::shared_ptr Patch(const char *path, const std::string &body, const char *content_type); + + std::shared_ptr Patch(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type); + + std::shared_ptr Patch(const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type); + + std::shared_ptr Patch(const char *path, + const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type); + + std::shared_ptr Delete(const char *path); + + std::shared_ptr Delete(const char *path, const std::string &body, const char *content_type); + + std::shared_ptr Delete(const char *path, const Headers &headers); + + std::shared_ptr Delete(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type); + + std::shared_ptr Options(const char *path); + + std::shared_ptr Options(const char *path, const Headers &headers); + + bool send(const Request &req, Response &res); + + size_t is_socket_open() const; + + void stop(); + + void set_tcp_nodelay(bool on); + void set_socket_options(SocketOptions socket_options); + + CPPHTTPLIB_DEPRECATED void set_timeout_sec(time_t timeout_sec); + void set_connection_timeout(time_t sec, time_t usec = 0); + void set_read_timeout(time_t sec, time_t usec = 0); + void set_write_timeout(time_t sec, time_t usec = 0); + + void set_basic_auth(const char *username, const char *password); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_digest_auth(const char *username, const char *password); +#endif + + void set_keep_alive(bool on); + void set_follow_location(bool on); + + void set_compress(bool on); + + void set_decompress(bool on); + + void set_interface(const char *intf); + + void set_proxy(const char *host, int port); + void set_proxy_basic_auth(const char *username, const char *password); +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + void set_proxy_digest_auth(const char *username, const char *password); +#endif + + void set_logger(Logger logger); + + protected: + struct Socket { + socket_t sock = INVALID_SOCKET; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSL *ssl = nullptr; +#endif + + bool is_open() const { + return sock != INVALID_SOCKET; + } + }; + + virtual bool create_and_connect_socket(Socket &socket); + virtual void close_socket(Socket &socket, bool process_socket_ret); + + bool process_request(Stream &strm, const Request &req, Response &res, bool close_connection); + + // Socket endoint information + const std::string host_; + const int port_; + const std::string host_and_port_; + + // Current open socket + Socket socket_; + mutable std::mutex socket_mutex_; + std::recursive_mutex request_mutex_; + + // Settings + std::string client_cert_path_; + std::string client_key_path_; + + time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND; + time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND; + time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; + time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; + time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; + time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; + + std::string basic_auth_username_; + std::string basic_auth_password_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + std::string digest_auth_username_; + std::string digest_auth_password_; +#endif + + bool keep_alive_ = false; + bool follow_location_ = false; + + bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; + SocketOptions socket_options_ = nullptr; + + bool compress_ = false; + bool decompress_ = true; + + std::string interface_; + + std::string proxy_host_; + int proxy_port_ = 80; + + std::string proxy_basic_auth_username_; + std::string proxy_basic_auth_password_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + std::string proxy_digest_auth_username_; + std::string proxy_digest_auth_password_; +#endif + + Logger logger_; + + void copy_settings(const Client &rhs) { + client_cert_path_ = rhs.client_cert_path_; + client_key_path_ = rhs.client_key_path_; + connection_timeout_sec_ = rhs.connection_timeout_sec_; + read_timeout_sec_ = rhs.read_timeout_sec_; + read_timeout_usec_ = rhs.read_timeout_usec_; + write_timeout_sec_ = rhs.write_timeout_sec_; + write_timeout_usec_ = rhs.write_timeout_usec_; + basic_auth_username_ = rhs.basic_auth_username_; + basic_auth_password_ = rhs.basic_auth_password_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + digest_auth_username_ = rhs.digest_auth_username_; + digest_auth_password_ = rhs.digest_auth_password_; +#endif + keep_alive_ = rhs.keep_alive_; + follow_location_ = rhs.follow_location_; + tcp_nodelay_ = rhs.tcp_nodelay_; + socket_options_ = rhs.socket_options_; + compress_ = rhs.compress_; + decompress_ = rhs.decompress_; + interface_ = rhs.interface_; + proxy_host_ = rhs.proxy_host_; + proxy_port_ = rhs.proxy_port_; + proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_; + proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_; + proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_; +#endif + logger_ = rhs.logger_; + } + + private: + socket_t create_client_socket() const; + bool read_response_line(Stream &strm, Response &res); + bool write_request(Stream &strm, const Request &req, bool close_connection); + bool redirect(const Request &req, Response &res); + bool handle_request(Stream &strm, const Request &req, Response &res, bool close_connection); + + std::shared_ptr send_with_content_provider(const char *method, + const char *path, + const Headers &headers, + const std::string &body, + size_t content_length, + ContentProvider content_provider, + const char *content_type); + + virtual bool process_socket(Socket &socket, std::function callback); + virtual bool is_ssl() const; +}; + +inline void Get(std::vector &requests, const char *path, const Headers &headers) { + Request req; + req.method = "GET"; + req.path = path; + req.headers = headers; + requests.emplace_back(std::move(req)); +} + +inline void Get(std::vector &requests, const char *path) { + Get(requests, path, Headers()); +} + +inline void Post(std::vector &requests, + const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + Request req; + req.method = "POST"; + req.path = path; + req.headers = headers; + if (content_type) { + req.headers.emplace("Content-Type", content_type); + } + req.body = body; + requests.emplace_back(std::move(req)); +} + +inline void Post(std::vector &requests, const char *path, const std::string &body, const char *content_type) { + Post(requests, path, Headers(), body, content_type); +} + +inline void Post(std::vector &requests, + const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + Request req; + req.method = "POST"; + req.headers = Headers(); + req.path = path; + req.content_length = content_length; + req.content_provider = content_provider; + + if (content_type) { + req.headers.emplace("Content-Type", content_type); + } + + requests.emplace_back(std::move(req)); +} + +class Client2 { + public: + explicit Client2(const char *scheme_host_port) : Client2(scheme_host_port, std::string(), std::string()) {} + + explicit Client2(const char *scheme_host_port, + const std::string &client_cert_path, + const std::string &client_key_path) { + const static std::regex re(R"(^(https?)://([^:/?#]+)(?::(\d+))?)"); + + std::cmatch m; + if (std::regex_match(scheme_host_port, m, re)) { + auto scheme = m[1].str(); + auto host = m[2].str(); + auto port_str = m[3].str(); + + auto port = !port_str.empty() ? std::stoi(port_str) : (scheme == "https" ? 443 : 80); + + if (scheme == "https") { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + is_ssl_ = true; + cli_ = std::make_shared(host.c_str(), port, client_cert_path, client_key_path); +#endif + } else { + cli_ = std::make_shared(host.c_str(), port, client_cert_path, client_key_path); + } + } + } + + ~Client2() {} + + bool is_valid() const { + return cli_ != nullptr; + } + + std::shared_ptr Get(const char *path) { + return cli_->Get(path); + } + + std::shared_ptr Get(const char *path, const Headers &headers) { + return cli_->Get(path, headers); + } + + std::shared_ptr Get(const char *path, Progress progress) { + return cli_->Get(path, progress); + } + + std::shared_ptr Get(const char *path, const Headers &headers, Progress progress) { + return cli_->Get(path, headers, progress); + } + + std::shared_ptr Get(const char *path, ContentReceiver content_receiver) { + return cli_->Get(path, content_receiver); + } + + std::shared_ptr Get(const char *path, const Headers &headers, ContentReceiver content_receiver) { + return cli_->Get(path, headers, content_receiver); + } + + std::shared_ptr Get(const char *path, ContentReceiver content_receiver, Progress progress) { + return cli_->Get(path, content_receiver, progress); + } + + std::shared_ptr Get(const char *path, + const Headers &headers, + ContentReceiver content_receiver, + Progress progress) { + return cli_->Get(path, headers, content_receiver, progress); + } + + std::shared_ptr Get(const char *path, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver) { + return cli_->Get(path, headers, response_handler, content_receiver); + } + + std::shared_ptr Get(const char *path, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) { + return cli_->Get(path, headers, response_handler, content_receiver, progress); + } + + std::shared_ptr Head(const char *path) { + return cli_->Head(path); + } + + std::shared_ptr Head(const char *path, const Headers &headers) { + return cli_->Head(path, headers); + } + + std::shared_ptr Post(const char *path) { + return cli_->Post(path); + } + + std::shared_ptr Post(const char *path, const std::string &body, const char *content_type) { + return cli_->Post(path, body, content_type); + } + + std::shared_ptr Post(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + return cli_->Post(path, headers, body, content_type); + } + + std::shared_ptr Post(const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Post(path, content_length, content_provider, content_type); + } + + std::shared_ptr Post(const char *path, + const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Post(path, headers, content_length, content_provider, content_type); + } + + std::shared_ptr Post(const char *path, const Params ¶ms) { + return cli_->Post(path, params); + } + + std::shared_ptr Post(const char *path, const Headers &headers, const Params ¶ms) { + return cli_->Post(path, headers, params); + } + + std::shared_ptr Post(const char *path, const MultipartFormDataItems &items) { + return cli_->Post(path, items); + } + + std::shared_ptr Post(const char *path, const Headers &headers, const MultipartFormDataItems &items) { + return cli_->Post(path, headers, items); + } + + std::shared_ptr Put(const char *path) { + return cli_->Put(path); + } + + std::shared_ptr Put(const char *path, const std::string &body, const char *content_type) { + return cli_->Put(path, body, content_type); + } + + std::shared_ptr Put(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + return cli_->Put(path, headers, body, content_type); + } + + std::shared_ptr Put(const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Put(path, content_length, content_provider, content_type); + } + + std::shared_ptr Put(const char *path, + const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Put(path, headers, content_length, content_provider, content_type); + } + + std::shared_ptr Put(const char *path, const Params ¶ms) { + return cli_->Put(path, params); + } + + std::shared_ptr Put(const char *path, const Headers &headers, const Params ¶ms) { + return cli_->Put(path, headers, params); + } + + std::shared_ptr Patch(const char *path, const std::string &body, const char *content_type) { + return cli_->Patch(path, body, content_type); + } + + std::shared_ptr Patch(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + return cli_->Patch(path, headers, body, content_type); + } + + std::shared_ptr Patch(const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Patch(path, content_length, content_provider, content_type); + } + + std::shared_ptr Patch(const char *path, + const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return cli_->Patch(path, headers, content_length, content_provider, content_type); + } + + std::shared_ptr Delete(const char *path) { + return cli_->Delete(path); + } + + std::shared_ptr Delete(const char *path, const std::string &body, const char *content_type) { + return cli_->Delete(path, body, content_type); + } + + std::shared_ptr Delete(const char *path, const Headers &headers) { + return cli_->Delete(path, headers); + } + + std::shared_ptr Delete(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + return cli_->Delete(path, headers, body, content_type); + } + + std::shared_ptr Options(const char *path) { + return cli_->Options(path); + } + + std::shared_ptr Options(const char *path, const Headers &headers) { + return cli_->Options(path, headers); + } + + bool send(const Request &req, Response &res) { + return cli_->send(req, res); + } + + bool is_socket_open() { + return cli_->is_socket_open(); + } + + void stop() { + cli_->stop(); + } + + void set_tcp_nodelay(bool on) { + cli_->set_tcp_nodelay(on); + } + + void set_socket_options(SocketOptions socket_options) { + cli_->set_socket_options(socket_options); + } + + Client2 &set_connection_timeout(time_t sec, time_t usec) { + cli_->set_connection_timeout(sec, usec); + return *this; + } + + Client2 &set_read_timeout(time_t sec, time_t usec) { + cli_->set_read_timeout(sec, usec); + return *this; + } + + Client2 &set_basic_auth(const char *username, const char *password) { + cli_->set_basic_auth(username, password); + return *this; + } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + Client2 &set_digest_auth(const char *username, const char *password) { + cli_->set_digest_auth(username, password); + return *this; + } +#endif + + Client2 &set_keep_alive(bool on) { + cli_->set_keep_alive(on); + return *this; + } + + Client2 &set_follow_location(bool on) { + cli_->set_follow_location(on); + return *this; + } + + Client2 &set_compress(bool on) { + cli_->set_compress(on); + return *this; + } + + Client2 &set_decompress(bool on) { + cli_->set_decompress(on); + return *this; + } + + Client2 &set_interface(const char *intf) { + cli_->set_interface(intf); + return *this; + } + + Client2 &set_proxy(const char *host, int port) { + cli_->set_proxy(host, port); + return *this; + } + + Client2 &set_proxy_basic_auth(const char *username, const char *password) { + cli_->set_proxy_basic_auth(username, password); + return *this; + } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + Client2 &set_proxy_digest_auth(const char *username, const char *password) { + cli_->set_proxy_digest_auth(username, password); + return *this; + } +#endif + + Client2 &set_logger(Logger logger) { + cli_->set_logger(logger); + return *this; + } + + // SSL +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + Client2 &set_ca_cert_path(const char *ca_cert_file_path, const char *ca_cert_dir_path = nullptr) { + if (is_ssl_) { + static_cast(*cli_).set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path); + } + return *this; + } + + Client2 &set_ca_cert_store(X509_STORE *ca_cert_store) { + if (is_ssl_) { + static_cast(*cli_).set_ca_cert_store(ca_cert_store); + } + return *this; + } + + Client2 &enable_server_certificate_verification(bool enabled) { + if (is_ssl_) { + static_cast(*cli_).enable_server_certificate_verification(enabled); + } + return *this; + } + + long get_openssl_verify_result() const { + if (is_ssl_) { + return static_cast(*cli_).get_openssl_verify_result(); + } + return -1; // NOTE: -1 doesn't match any of X509_V_ERR_??? + } + + SSL_CTX *ssl_context() const { + if (is_ssl_) { + return static_cast(*cli_).ssl_context(); + } + return nullptr; + } +#endif + + private: +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + bool is_ssl_ = false; +#endif + std::shared_ptr cli_; +}; + +// ---------------------------------------------------------------------------- + +/* + * Implementation + */ + +namespace detail { + +inline bool is_hex(char c, int &v) { + if (0x20 <= c && isdigit(c)) { + v = c - '0'; + return true; + } else if ('A' <= c && c <= 'F') { + v = c - 'A' + 10; + return true; + } else if ('a' <= c && c <= 'f') { + v = c - 'a' + 10; + return true; + } + return false; +} + +inline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt, int &val) { + if (i >= s.size()) { + return false; + } + + val = 0; + for (; cnt; i++, cnt--) { + if (!s[i]) { + return false; + } + int v = 0; + if (is_hex(s[i], v)) { + val = val * 16 + v; + } else { + return false; + } + } + return true; +} + +inline std::string from_i_to_hex(size_t n) { + const char *charset = "0123456789abcdef"; + std::string ret; + do { + ret = charset[n & 15] + ret; + n >>= 4; + } while (n > 0); + return ret; +} + +inline size_t to_utf8(int code, char *buff) { + if (code < 0x0080) { + buff[0] = (code & 0x7F); + return 1; + } else if (code < 0x0800) { + buff[0] = static_cast(0xC0 | ((code >> 6) & 0x1F)); + buff[1] = static_cast(0x80 | (code & 0x3F)); + return 2; + } else if (code < 0xD800) { + buff[0] = static_cast(0xE0 | ((code >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (code & 0x3F)); + return 3; + } else if (code < 0xE000) { // D800 - DFFF is invalid... + return 0; + } else if (code < 0x10000) { + buff[0] = static_cast(0xE0 | ((code >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (code & 0x3F)); + return 3; + } else if (code < 0x110000) { + buff[0] = static_cast(0xF0 | ((code >> 18) & 0x7)); + buff[1] = static_cast(0x80 | ((code >> 12) & 0x3F)); + buff[2] = static_cast(0x80 | ((code >> 6) & 0x3F)); + buff[3] = static_cast(0x80 | (code & 0x3F)); + return 4; + } + + // NOTREACHED + return 0; +} + +// NOTE: This code came up with the following stackoverflow post: +// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c +inline std::string base64_encode(const std::string &in) { + static const auto lookup = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + std::string out; + out.reserve(in.size()); + + int val = 0; + int valb = -6; + + for (auto c : in) { + val = (val << 8) + static_cast(c); + valb += 8; + while (valb >= 0) { + out.push_back(lookup[(val >> valb) & 0x3F]); + valb -= 6; + } + } + + if (valb > -6) { + out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); + } + + while (out.size() % 4) { + out.push_back('='); + } + + return out; +} + +inline bool is_file(const std::string &path) { + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode); +} + +inline bool is_dir(const std::string &path) { + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode); +} + +inline bool is_valid_path(const std::string &path) { + size_t level = 0; + size_t i = 0; + + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } + + while (i < path.size()) { + // Read component + auto beg = i; + while (i < path.size() && path[i] != '/') { + i++; + } + + auto len = i - beg; + assert(len > 0); + + if (!path.compare(beg, len, ".")) { + ; + } else if (!path.compare(beg, len, "..")) { + if (level == 0) { + return false; + } + level--; + } else { + level++; + } + + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } + } + + return true; +} + +inline void read_file(const std::string &path, std::string &out) { + std::ifstream fs(path, std::ios_base::binary); + fs.seekg(0, std::ios_base::end); + auto size = fs.tellg(); + fs.seekg(0); + out.resize(static_cast(size)); + fs.read(&out[0], static_cast(size)); +} + +inline std::string file_extension(const std::string &path) { + std::smatch m; + static auto re = std::regex("\\.([a-zA-Z0-9]+)$"); + if (std::regex_search(path, m, re)) { + return m[1].str(); + } + return std::string(); +} + +template +void split(const char *b, const char *e, char d, Fn fn) { + int i = 0; + int beg = 0; + + while (e ? (b + i != e) : (b[i] != '\0')) { + if (b[i] == d) { + fn(&b[beg], &b[i]); + beg = i + 1; + } + i++; + } + + if (i) { + fn(&b[beg], &b[i]); + } +} + +// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer` +// to store data. The call can set memory on stack for performance. +class stream_line_reader { + public: + stream_line_reader(Stream &strm, char *fixed_buffer, size_t fixed_buffer_size) + : strm_(strm), fixed_buffer_(fixed_buffer), fixed_buffer_size_(fixed_buffer_size) {} + + const char *ptr() const { + if (glowable_buffer_.empty()) { + return fixed_buffer_; + } else { + return glowable_buffer_.data(); + } + } + + size_t size() const { + if (glowable_buffer_.empty()) { + return fixed_buffer_used_size_; + } else { + return glowable_buffer_.size(); + } + } + + bool end_with_crlf() const { + auto end = ptr() + size(); + return size() >= 2 && end[-2] == '\r' && end[-1] == '\n'; + } + + bool getline() { + fixed_buffer_used_size_ = 0; + glowable_buffer_.clear(); + + for (size_t i = 0;; i++) { + char byte; + auto n = strm_.read(&byte, 1); + + if (n < 0) { + return false; + } else if (n == 0) { + if (i == 0) { + return false; + } else { + break; + } + } + + append(byte); + + if (byte == '\n') { + break; + } + } + + return true; + } + + private: + void append(char c) { + if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) { + fixed_buffer_[fixed_buffer_used_size_++] = c; + fixed_buffer_[fixed_buffer_used_size_] = '\0'; + } else { + if (glowable_buffer_.empty()) { + assert(fixed_buffer_[fixed_buffer_used_size_] == '\0'); + glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_); + } + glowable_buffer_ += c; + } + } + + Stream &strm_; + char *fixed_buffer_; + const size_t fixed_buffer_size_; + size_t fixed_buffer_used_size_ = 0; + std::string glowable_buffer_; +}; + +inline int close_socket(socket_t sock) { +#ifdef _WIN32 + return closesocket(sock); +#else + return close(sock); +#endif +} + +template +inline ssize_t handle_EINTR(T fn) { + ssize_t res = false; + while (true) { + res = fn(); + if (res < 0 && errno == EINTR) { + continue; + } + break; + } + return res; +} + +inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) { +#ifdef CPPHTTPLIB_USE_POLL + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLIN; + + auto timeout = static_cast(sec * 1000 + usec); + + return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); +#else + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + return handle_EINTR([&]() { return select(static_cast(sock + 1), &fds, nullptr, nullptr, &tv); }); +#endif +} + +inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) { +#ifdef CPPHTTPLIB_USE_POLL + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLOUT; + + auto timeout = static_cast(sec * 1000 + usec / 1000); + + return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); +#else + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + return handle_EINTR([&]() { return select(static_cast(sock + 1), nullptr, &fds, nullptr, &tv); }); +#endif +} + +inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) { +#ifdef CPPHTTPLIB_USE_POLL + struct pollfd pfd_read; + pfd_read.fd = sock; + pfd_read.events = POLLIN | POLLOUT; + + auto timeout = static_cast(sec * 1000 + usec); + + auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); }); + + if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) { + int error = 0; + socklen_t len = sizeof(error); + auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR, reinterpret_cast(&error), &len); + return res >= 0 && !error; + } + return false; +#else + fd_set fdsr; + FD_ZERO(&fdsr); + FD_SET(sock, &fdsr); + + auto fdsw = fdsr; + auto fdse = fdsr; + + timeval tv; + tv.tv_sec = static_cast(sec); + tv.tv_usec = static_cast(usec); + + auto ret = handle_EINTR([&]() { return select(static_cast(sock + 1), &fdsr, &fdsw, &fdse, &tv); }); + + if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) { + int error = 0; + socklen_t len = sizeof(error); + return getsockopt(sock, SOL_SOCKET, SO_ERROR, reinterpret_cast(&error), &len) >= 0 && !error; + } + return false; +#endif +} + +class SocketStream : public Stream { + public: + SocketStream(socket_t sock, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec); + ~SocketStream() override; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + + private: + socket_t sock_; + time_t read_timeout_sec_; + time_t read_timeout_usec_; + time_t write_timeout_sec_; + time_t write_timeout_usec_; +}; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +class SSLSocketStream : public Stream { + public: + SSLSocketStream(socket_t sock, + SSL *ssl, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec); + ~SSLSocketStream() override; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + + private: + socket_t sock_; + SSL *ssl_; + time_t read_timeout_sec_; + time_t read_timeout_usec_; + time_t write_timeout_sec_; + time_t write_timeout_usec_; +}; +#endif + +class BufferStream : public Stream { + public: + BufferStream() = default; + ~BufferStream() override = default; + + bool is_readable() const override; + bool is_writable() const override; + ssize_t read(char *ptr, size_t size) override; + ssize_t write(const char *ptr, size_t size) override; + void get_remote_ip_and_port(std::string &ip, int &port) const override; + + const std::string &get_buffer() const; + + private: + std::string buffer; + size_t position = 0; +}; + +inline bool keep_alive(socket_t sock) { + using namespace std::chrono; + auto start = steady_clock::now(); + while (true) { + auto val = select_read(sock, 0, 10000); + if (val < 0) { + return false; + } else if (val == 0) { + auto current = steady_clock::now(); + auto duration = duration_cast(current - start); + auto timeout = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND * 100 + CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND; + if (duration.count() > timeout) { + return false; + } + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } else { + return true; + } + } +} + +template +inline bool process_client_socket(socket_t sock, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec, + T callback) { + SocketStream strm(sock, read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec); + return callback(strm); +} + +inline int shutdown_socket(socket_t sock) { +#ifdef _WIN32 + return shutdown(sock, SD_BOTH); +#else + return shutdown(sock, SHUT_RDWR); +#endif +} + +template +socket_t create_socket(const char *host, + int port, + int socket_flags, + bool tcp_nodelay, + SocketOptions socket_options, + BindOrConnect bind_or_connect) { + // Get address info + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = socket_flags; + hints.ai_protocol = 0; + + auto service = std::to_string(port); + + if (getaddrinfo(host, service.c_str(), &hints, &result)) { + return INVALID_SOCKET; + } + + for (auto rp = result; rp; rp = rp->ai_next) { + // Create a socket +#ifdef _WIN32 + auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT); + /** + * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1 + * and above the socket creation fails on older Windows Systems. + * + * Let's try to create a socket the old way in this case. + * + * Reference: + * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa + * + * WSA_FLAG_NO_HANDLE_INHERIT: + * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with + * SP1, and later + * + */ + if (sock == INVALID_SOCKET) { + sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + } +#else + auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); +#endif + if (sock == INVALID_SOCKET) { + continue; + } + +#ifndef _WIN32 + if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { + continue; + } +#endif + + if (tcp_nodelay) { + int yes = 1; + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&yes), sizeof(yes)); + } + + if (socket_options) { + socket_options(sock); + } + + if (rp->ai_family == AF_INET6) { + int no = 0; + setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&no), sizeof(no)); + } + + // bind or connect + if (bind_or_connect(sock, *rp)) { + freeaddrinfo(result); + return sock; + } + + close_socket(sock); + } + + freeaddrinfo(result); + return INVALID_SOCKET; +} + +inline void set_nonblocking(socket_t sock, bool nonblocking) { +#ifdef _WIN32 + auto flags = nonblocking ? 1UL : 0UL; + ioctlsocket(sock, FIONBIO, &flags); +#else + auto flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK))); +#endif +} + +inline bool is_connection_error() { +#ifdef _WIN32 + return WSAGetLastError() != WSAEWOULDBLOCK; +#else + return errno != EINPROGRESS; +#endif +} + +inline bool bind_ip_address(socket_t sock, const char *host) { + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + if (getaddrinfo(host, "0", &hints, &result)) { + return false; + } + + auto ret = false; + for (auto rp = result; rp; rp = rp->ai_next) { + const auto &ai = *rp; + if (!::bind(sock, ai.ai_addr, static_cast(ai.ai_addrlen))) { + ret = true; + break; + } + } + + freeaddrinfo(result); + return ret; +} + +#ifndef _WIN32 +inline std::string if2ip(const std::string &ifn) { + struct ifaddrs *ifap; + getifaddrs(&ifap); + for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr && ifn == ifa->ifa_name) { + if (ifa->ifa_addr->sa_family == AF_INET) { + auto sa = reinterpret_cast(ifa->ifa_addr); + char buf[INET_ADDRSTRLEN]; + if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) { + freeifaddrs(ifap); + return std::string(buf, INET_ADDRSTRLEN); + } + } + } + } + freeifaddrs(ifap); + return std::string(); +} +#endif + +inline socket_t create_client_socket(const char *host, + int port, + bool tcp_nodelay, + SocketOptions socket_options, + time_t timeout_sec, + time_t timeout_usec, + const std::string &intf) { + return create_socket(host, port, 0, tcp_nodelay, socket_options, [&](socket_t sock, struct addrinfo &ai) -> bool { + if (!intf.empty()) { +#ifndef _WIN32 + auto ip = if2ip(intf); + if (ip.empty()) { + ip = intf; + } + if (!bind_ip_address(sock, ip.c_str())) { + return false; + } +#endif + } + + set_nonblocking(sock, true); + + auto ret = ::connect(sock, ai.ai_addr, static_cast(ai.ai_addrlen)); + if (ret < 0) { + if (is_connection_error() || !wait_until_socket_is_ready(sock, timeout_sec, timeout_usec)) { + close_socket(sock); + return false; + } + } + + set_nonblocking(sock, false); + return true; + }); +} + +inline void get_remote_ip_and_port(const struct sockaddr_storage &addr, + socklen_t addr_len, + std::string &ip, + int &port) { + if (addr.ss_family == AF_INET) { + port = ntohs(reinterpret_cast(&addr)->sin_port); + } else if (addr.ss_family == AF_INET6) { + port = ntohs(reinterpret_cast(&addr)->sin6_port); + } + + std::array ipstr{}; + if (!getnameinfo(reinterpret_cast(&addr), + addr_len, + ipstr.data(), + static_cast(ipstr.size()), + nullptr, + 0, + NI_NUMERICHOST)) { + ip = ipstr.data(); + } +} + +inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) { + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + + if (!getpeername(sock, reinterpret_cast(&addr), &addr_len)) { + get_remote_ip_and_port(addr, addr_len, ip, port); + } +} + +inline const char *find_content_type(const std::string &path, const std::map &user_data) { + auto ext = file_extension(path); + + auto it = user_data.find(ext); + if (it != user_data.end()) { + return it->second.c_str(); + } + + if (ext == "txt") { + return "text/plain"; + } else if (ext == "html" || ext == "htm") { + return "text/html"; + } else if (ext == "css") { + return "text/css"; + } else if (ext == "jpeg" || ext == "jpg") { + return "image/jpg"; + } else if (ext == "png") { + return "image/png"; + } else if (ext == "gif") { + return "image/gif"; + } else if (ext == "svg") { + return "image/svg+xml"; + } else if (ext == "ico") { + return "image/x-icon"; + } else if (ext == "json") { + return "application/json"; + } else if (ext == "pdf") { + return "application/pdf"; + } else if (ext == "js") { + return "application/javascript"; + } else if (ext == "wasm") { + return "application/wasm"; + } else if (ext == "xml") { + return "application/xml"; + } else if (ext == "xhtml") { + return "application/xhtml+xml"; + } + return nullptr; +} + +inline const char *status_message(int status) { + switch (status) { + case 100: + return "Continue"; + case 101: + return "Switching Protocol"; + case 102: + return "Processing"; + case 103: + return "Early Hints"; + case 200: + return "OK"; + case 201: + return "Created"; + case 202: + return "Accepted"; + case 203: + return "Non-Authoritative Information"; + case 204: + return "No Content"; + case 205: + return "Reset Content"; + case 206: + return "Partial Content"; + case 207: + return "Multi-Status"; + case 208: + return "Already Reported"; + case 226: + return "IM Used"; + case 300: + return "Multiple Choice"; + case 301: + return "Moved Permanently"; + case 302: + return "Found"; + case 303: + return "See Other"; + case 304: + return "Not Modified"; + case 305: + return "Use Proxy"; + case 306: + return "unused"; + case 307: + return "Temporary Redirect"; + case 308: + return "Permanent Redirect"; + case 400: + return "Bad Request"; + case 401: + return "Unauthorized"; + case 402: + return "Payment Required"; + case 403: + return "Forbidden"; + case 404: + return "Not Found"; + case 405: + return "Method Not Allowed"; + case 406: + return "Not Acceptable"; + case 407: + return "Proxy Authentication Required"; + case 408: + return "Request Timeout"; + case 409: + return "Conflict"; + case 410: + return "Gone"; + case 411: + return "Length Required"; + case 412: + return "Precondition Failed"; + case 413: + return "Payload Too Large"; + case 414: + return "URI Too Long"; + case 415: + return "Unsupported Media Type"; + case 416: + return "Range Not Satisfiable"; + case 417: + return "Expectation Failed"; + case 418: + return "I'm a teapot"; + case 421: + return "Misdirected Request"; + case 422: + return "Unprocessable Entity"; + case 423: + return "Locked"; + case 424: + return "Failed Dependency"; + case 425: + return "Too Early"; + case 426: + return "Upgrade Required"; + case 428: + return "Precondition Required"; + case 429: + return "Too Many Requests"; + case 431: + return "Request Header Fields Too Large"; + case 451: + return "Unavailable For Legal Reasons"; + case 501: + return "Not Implemented"; + case 502: + return "Bad Gateway"; + case 503: + return "Service Unavailable"; + case 504: + return "Gateway Timeout"; + case 505: + return "HTTP Version Not Supported"; + case 506: + return "Variant Also Negotiates"; + case 507: + return "Insufficient Storage"; + case 508: + return "Loop Detected"; + case 510: + return "Not Extended"; + case 511: + return "Network Authentication Required"; + + default: + case 500: + return "Internal Server Error"; + } +} + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +inline bool can_compress(const std::string &content_type) { + return !content_type.find("text/") || content_type == "image/svg+xml" || content_type == "application/javascript" || + content_type == "application/json" || content_type == "application/xml" || + content_type == "application/xhtml+xml"; +} + +inline bool compress(std::string &content) { + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + auto ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY); + if (ret != Z_OK) { + return false; + } + + strm.avail_in = static_cast(content.size()); + strm.next_in = const_cast(reinterpret_cast(content.data())); + + std::string compressed; + + std::array buff{}; + do { + strm.avail_out = buff.size(); + strm.next_out = reinterpret_cast(buff.data()); + ret = deflate(&strm, Z_FINISH); + assert(ret != Z_STREAM_ERROR); + compressed.append(buff.data(), buff.size() - strm.avail_out); + } while (strm.avail_out == 0); + + assert(ret == Z_STREAM_END); + assert(strm.avail_in == 0); + + content.swap(compressed); + + deflateEnd(&strm); + return true; +} + +class decompressor { + public: + decompressor() { + std::memset(&strm, 0, sizeof(strm)); + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + // 15 is the value of wbits, which should be at the maximum possible value + // to ensure that any gzip stream can be decoded. The offset of 32 specifies + // that the stream type should be automatically detected either gzip or + // deflate. + is_valid_ = inflateInit2(&strm, 32 + 15) == Z_OK; + } + + ~decompressor() { + inflateEnd(&strm); + } + + bool is_valid() const { + return is_valid_; + } + + template + bool decompress(const char *data, size_t data_length, T callback) { + int ret = Z_OK; + + strm.avail_in = static_cast(data_length); + strm.next_in = const_cast(reinterpret_cast(data)); + + std::array buff{}; + do { + strm.avail_out = buff.size(); + strm.next_out = reinterpret_cast(buff.data()); + + ret = inflate(&strm, Z_NO_FLUSH); + assert(ret != Z_STREAM_ERROR); + switch (ret) { + case Z_NEED_DICT: + case Z_DATA_ERROR: + case Z_MEM_ERROR: + inflateEnd(&strm); + return false; + } + + if (!callback(buff.data(), buff.size() - strm.avail_out)) { + return false; + } + } while (strm.avail_out == 0); + + return ret == Z_OK || ret == Z_STREAM_END; + } + + private: + bool is_valid_; + z_stream strm; +}; +#endif + +inline bool has_header(const Headers &headers, const char *key) { + return headers.find(key) != headers.end(); +} + +inline const char *get_header_value(const Headers &headers, const char *key, size_t id = 0, const char *def = nullptr) { + auto rng = headers.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { + return it->second.c_str(); + } + return def; +} + +inline uint64_t get_header_value_uint64(const Headers &headers, const char *key, uint64_t def = 0) { + auto it = headers.find(key); + if (it != headers.end()) { + return std::strtoull(it->second.data(), nullptr, 10); + } + return def; +} + +inline void parse_header(const char *beg, const char *end, Headers &headers) { + auto p = beg; + while (p < end && *p != ':') { + p++; + } + if (p < end) { + auto key_end = p; + p++; // skip ':' + while (p < end && (*p == ' ' || *p == '\t')) { + p++; + } + if (p < end) { + auto val_begin = p; + while (p < end) { + p++; + } + headers.emplace(std::string(beg, key_end), std::string(val_begin, end)); + } + } +} + +inline bool read_headers(Stream &strm, Headers &headers) { + const auto bufsiz = 2048; + char buf[bufsiz]; + stream_line_reader line_reader(strm, buf, bufsiz); + + for (;;) { + if (!line_reader.getline()) { + return false; + } + + // Check if the line ends with CRLF. + if (line_reader.end_with_crlf()) { + // Blank line indicates end of headers. + if (line_reader.size() == 2) { + break; + } + } else { + continue; // Skip invalid line. + } + + // Skip trailing spaces and tabs. + auto end = line_reader.ptr() + line_reader.size() - 2; + while (line_reader.ptr() < end && (end[-1] == ' ' || end[-1] == '\t')) { + end--; + } + + parse_header(line_reader.ptr(), end, headers); + } + + return true; +} + +inline bool read_content_with_length(Stream &strm, uint64_t len, Progress progress, ContentReceiver out) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + + uint64_t r = 0; + while (r < len) { + auto read_len = static_cast(len - r); + auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); + if (n <= 0) { + return false; + } + + if (!out(buf, static_cast(n))) { + return false; + } + + r += static_cast(n); + + if (progress) { + if (!progress(r, len)) { + return false; + } + } + } + + return true; +} + +inline void skip_content_with_length(Stream &strm, uint64_t len) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + uint64_t r = 0; + while (r < len) { + auto read_len = static_cast(len - r); + auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ)); + if (n <= 0) { + return; + } + r += static_cast(n); + } +} + +inline bool read_content_without_length(Stream &strm, ContentReceiver out) { + char buf[CPPHTTPLIB_RECV_BUFSIZ]; + for (;;) { + auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); + if (n < 0) { + return false; + } else if (n == 0) { + return true; + } + if (!out(buf, static_cast(n))) { + return false; + } + } + + return true; +} + +inline bool read_content_chunked(Stream &strm, ContentReceiver out) { + const auto bufsiz = 16; + char buf[bufsiz]; + + stream_line_reader line_reader(strm, buf, bufsiz); + + if (!line_reader.getline()) { + return false; + } + + unsigned long chunk_len; + while (true) { + char *end_ptr; + + chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16); + + if (end_ptr == line_reader.ptr()) { + return false; + } + if (chunk_len == ULONG_MAX) { + return false; + } + + if (chunk_len == 0) { + break; + } + + if (!read_content_with_length(strm, chunk_len, nullptr, out)) { + return false; + } + + if (!line_reader.getline()) { + return false; + } + + if (strcmp(line_reader.ptr(), "\r\n")) { + break; + } + + if (!line_reader.getline()) { + return false; + } + } + + if (chunk_len == 0) { + // Reader terminator after chunks + if (!line_reader.getline() || strcmp(line_reader.ptr(), "\r\n")) return false; + } + + return true; +} + +inline bool is_chunked_transfer_encoding(const Headers &headers) { + return !strcasecmp(get_header_value(headers, "Transfer-Encoding", 0, ""), "chunked"); +} + +template +bool read_content(Stream &strm, + T &x, + size_t payload_max_length, + int &status, + Progress progress, + ContentReceiver receiver, + bool decompress) { + ContentReceiver out = [&](const char *buf, size_t n) { return receiver(buf, n); }; + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + decompressor decompressor; +#endif + + if (decompress) { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + std::string content_encoding = x.get_header_value("Content-Encoding"); + if (content_encoding.find("gzip") != std::string::npos || + content_encoding.find("deflate") != std::string::npos) { + if (!decompressor.is_valid()) { + status = 500; + return false; + } + + out = [&](const char *buf, size_t n) { + return decompressor.decompress(buf, n, [&](const char *buf, size_t n) { return receiver(buf, n); }); + }; + } +#else + if (x.get_header_value("Content-Encoding") == "gzip") { + status = 415; + return false; + } +#endif + } + + auto ret = true; + auto exceed_payload_max_length = false; + + if (is_chunked_transfer_encoding(x.headers)) { + ret = read_content_chunked(strm, out); + } else if (!has_header(x.headers, "Content-Length")) { + ret = read_content_without_length(strm, out); + } else { + auto len = get_header_value_uint64(x.headers, "Content-Length", 0); + if (len > payload_max_length) { + exceed_payload_max_length = true; + skip_content_with_length(strm, len); + ret = false; + } else if (len > 0) { + ret = read_content_with_length(strm, len, progress, out); + } + } + + if (!ret) { + status = exceed_payload_max_length ? 413 : 400; + } + return ret; +} + +template +inline ssize_t write_headers(Stream &strm, const T &info, const Headers &headers) { + ssize_t write_len = 0; + for (const auto &x : info.headers) { + if (x.first == "EXCEPTION_WHAT") { + continue; + } + auto len = strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); + if (len < 0) { + return len; + } + write_len += len; + } + for (const auto &x : headers) { + auto len = strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); + if (len < 0) { + return len; + } + write_len += len; + } + auto len = strm.write("\r\n"); + if (len < 0) { + return len; + } + write_len += len; + return write_len; +} + +inline bool write_data(Stream &strm, const char *d, size_t l) { + size_t offset = 0; + while (offset < l) { + auto length = strm.write(d + offset, l - offset); + if (length < 0) { + return false; + } + offset += static_cast(length); + } + return true; +} + +template +inline ssize_t write_content( + Stream &strm, ContentProvider content_provider, size_t offset, size_t length, T is_shutting_down) { + size_t begin_offset = offset; + size_t end_offset = offset + length; + + auto ok = true; + + DataSink data_sink; + data_sink.write = [&](const char *d, size_t l) { + if (ok) { + offset += l; + if (!write_data(strm, d, l)) { + ok = false; + } + } + }; + data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; + + while (ok && offset < end_offset && !is_shutting_down()) { + if (!content_provider(offset, end_offset - offset, data_sink)) { + return -1; + } + if (!ok) { + return -1; + } + } + + return static_cast(offset - begin_offset); +} + +template +inline ssize_t write_content_chunked(Stream &strm, ContentProvider content_provider, T is_shutting_down) { + size_t offset = 0; + auto data_available = true; + ssize_t total_written_length = 0; + + auto ok = true; + + DataSink data_sink; + data_sink.write = [&](const char *d, size_t l) { + if (ok) { + data_available = l > 0; + offset += l; + + // Emit chunked response header and footer for each chunk + auto chunk = from_i_to_hex(l) + "\r\n" + std::string(d, l) + "\r\n"; + if (write_data(strm, chunk.data(), chunk.size())) { + total_written_length += chunk.size(); + } else { + ok = false; + } + } + }; + data_sink.done = [&](void) { + data_available = false; + if (ok) { + static const std::string done_marker("0\r\n\r\n"); + if (write_data(strm, done_marker.data(), done_marker.size())) { + total_written_length += done_marker.size(); + } else { + ok = false; + } + } + }; + data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; + + while (data_available && !is_shutting_down()) { + if (!content_provider(offset, 0, data_sink)) { + return -1; + } + if (!ok) { + return -1; + } + } + + return total_written_length; +} + +template +inline bool redirect(T &cli, const Request &req, Response &res, const std::string &path) { + Request new_req = req; + new_req.path = path; + new_req.redirect_count -= 1; + + if (res.status == 303 && (req.method != "GET" && req.method != "HEAD")) { + new_req.method = "GET"; + new_req.body.clear(); + new_req.headers.clear(); + } + + Response new_res; + + auto ret = cli.send(new_req, new_res); + if (ret) { + res = new_res; + } + return ret; +} + +inline std::string encode_url(const std::string &s) { + std::string result; + + for (size_t i = 0; s[i]; i++) { + switch (s[i]) { + case ' ': + result += "%20"; + break; + case '+': + result += "%2B"; + break; + case '\r': + result += "%0D"; + break; + case '\n': + result += "%0A"; + break; + case '\'': + result += "%27"; + break; + case ',': + result += "%2C"; + break; + // case ':': result += "%3A"; break; // ok? probably... + case ';': + result += "%3B"; + break; + default: + auto c = static_cast(s[i]); + if (c >= 0x80) { + result += '%'; + char hex[4]; + auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c); + assert(len == 2); + result.append(hex, static_cast(len)); + } else { + result += s[i]; + } + break; + } + } + + return result; +} + +inline std::string decode_url(const std::string &s, bool convert_plus_to_space) { + std::string result; + + for (size_t i = 0; i < s.size(); i++) { + if (s[i] == '%' && i + 1 < s.size()) { + if (s[i + 1] == 'u') { + int val = 0; + if (from_hex_to_i(s, i + 2, 4, val)) { + // 4 digits Unicode codes + char buff[4]; + size_t len = to_utf8(val, buff); + if (len > 0) { + result.append(buff, len); + } + i += 5; // 'u0000' + } else { + result += s[i]; + } + } else { + int val = 0; + if (from_hex_to_i(s, i + 1, 2, val)) { + // 2 digits hex codes + result += static_cast(val); + i += 2; // '00' + } else { + result += s[i]; + } + } + } else if (convert_plus_to_space && s[i] == '+') { + result += ' '; + } else { + result += s[i]; + } + } + + return result; +} + +inline std::string params_to_query_str(const Params ¶ms) { + std::string query; + + for (auto it = params.begin(); it != params.end(); ++it) { + if (it != params.begin()) { + query += "&"; + } + query += it->first; + query += "="; + query += detail::encode_url(it->second); + } + + return query; +} + +inline void parse_query_text(const std::string &s, Params ¶ms) { + split(&s[0], &s[s.size()], '&', [&](const char *b, const char *e) { + std::string key; + std::string val; + split(b, e, '=', [&](const char *b2, const char *e2) { + if (key.empty()) { + key.assign(b2, e2); + } else { + val.assign(b2, e2); + } + }); + params.emplace(decode_url(key, true), decode_url(val, true)); + }); +} + +inline bool parse_multipart_boundary(const std::string &content_type, std::string &boundary) { + auto pos = content_type.find("boundary="); + if (pos == std::string::npos) { + return false; + } + boundary = content_type.substr(pos + 9); + if (boundary.length() >= 2 && boundary.front() == '"' && boundary.back() == '"') { + boundary = boundary.substr(1, boundary.size() - 2); + } + return !boundary.empty(); +} + +inline bool parse_range_header(const std::string &s, Ranges &ranges) { + static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))"); + std::smatch m; + if (std::regex_match(s, m, re_first_range)) { + auto pos = static_cast(m.position(1)); + auto len = static_cast(m.length(1)); + bool all_valid_ranges = true; + split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) { + if (!all_valid_ranges) return; + static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))"); + std::cmatch cm; + if (std::regex_match(b, e, cm, re_another_range)) { + ssize_t first = -1; + if (!cm.str(1).empty()) { + first = static_cast(std::stoll(cm.str(1))); + } + + ssize_t last = -1; + if (!cm.str(2).empty()) { + last = static_cast(std::stoll(cm.str(2))); + } + + if (first != -1 && last != -1 && first > last) { + all_valid_ranges = false; + return; + } + ranges.emplace_back(std::make_pair(first, last)); + } + }); + return all_valid_ranges; + } + return false; +} + +class MultipartFormDataParser { + public: + MultipartFormDataParser() = default; + + void set_boundary(std::string &&boundary) { + boundary_ = boundary; + } + + bool is_valid() const { + return is_valid_; + } + + template + bool parse(const char *buf, size_t n, T content_callback, U header_callback) { + static const std::regex re_content_type(R"(^Content-Type:\s*(.*?)\s*$)", std::regex_constants::icase); + + static const std::regex re_content_disposition( + "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename=" + "\"(.*?)\")?\\s*$", + std::regex_constants::icase); + static const std::string dash_ = "--"; + static const std::string crlf_ = "\r\n"; + + buf_.append(buf, n); // TODO: performance improvement + + while (!buf_.empty()) { + switch (state_) { + case 0: { // Initial boundary + auto pattern = dash_ + boundary_ + crlf_; + if (pattern.size() > buf_.size()) { + return true; + } + auto pos = buf_.find(pattern); + if (pos != 0) { + return false; + } + buf_.erase(0, pattern.size()); + off_ += pattern.size(); + state_ = 1; + break; + } + case 1: { // New entry + clear_file_info(); + state_ = 2; + break; + } + case 2: { // Headers + auto pos = buf_.find(crlf_); + while (pos != std::string::npos) { + // Empty line + if (pos == 0) { + if (!header_callback(file_)) { + is_valid_ = false; + return false; + } + buf_.erase(0, crlf_.size()); + off_ += crlf_.size(); + state_ = 3; + break; + } + + auto header = buf_.substr(0, pos); + { + std::smatch m; + if (std::regex_match(header, m, re_content_type)) { + file_.content_type = m[1]; + } else if (std::regex_match(header, m, re_content_disposition)) { + file_.name = m[1]; + file_.filename = m[2]; + } + } + + buf_.erase(0, pos + crlf_.size()); + off_ += pos + crlf_.size(); + pos = buf_.find(crlf_); + } + if (state_ != 3) { + return true; + } + break; + } + case 3: { // Body + { + auto pattern = crlf_ + dash_; + if (pattern.size() > buf_.size()) { + return true; + } + + auto pos = buf_.find(pattern); + if (pos == std::string::npos) { + pos = buf_.size(); + while (pos > 0) { + auto c = buf_[pos - 1]; + if (c != '\r' && c != '\n' && c != '-') { + break; + } + pos--; + } + } + + if (!content_callback(buf_.data(), pos)) { + is_valid_ = false; + return false; + } + + off_ += pos; + buf_.erase(0, pos); + } + + { + auto pattern = crlf_ + dash_ + boundary_; + if (pattern.size() > buf_.size()) { + return true; + } + + auto pos = buf_.find(pattern); + if (pos != std::string::npos) { + if (!content_callback(buf_.data(), pos)) { + is_valid_ = false; + return false; + } + + off_ += pos + pattern.size(); + buf_.erase(0, pos + pattern.size()); + state_ = 4; + } else { + if (!content_callback(buf_.data(), pattern.size())) { + is_valid_ = false; + return false; + } + + off_ += pattern.size(); + buf_.erase(0, pattern.size()); + } + } + break; + } + case 4: { // Boundary + if (crlf_.size() > buf_.size()) { + return true; + } + if (buf_.find(crlf_) == 0) { + buf_.erase(0, crlf_.size()); + off_ += crlf_.size(); + state_ = 1; + } else { + auto pattern = dash_ + crlf_; + if (pattern.size() > buf_.size()) { + return true; + } + if (buf_.find(pattern) == 0) { + buf_.erase(0, pattern.size()); + off_ += pattern.size(); + is_valid_ = true; + state_ = 5; + } else { + return true; + } + } + break; + } + case 5: { // Done + is_valid_ = false; + return false; + } + } + } + + return true; + } + + private: + void clear_file_info() { + file_.name.clear(); + file_.filename.clear(); + file_.content_type.clear(); + } + + std::string boundary_; + + std::string buf_; + size_t state_ = 0; + bool is_valid_ = false; + size_t off_ = 0; + MultipartFormData file_; +}; + +inline std::string to_lower(const char *beg, const char *end) { + std::string out; + auto it = beg; + while (it != end) { + out += static_cast(::tolower(*it)); + it++; + } + return out; +} + +inline std::string make_multipart_data_boundary() { + static const char data[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + std::random_device seed_gen; + std::mt19937 engine(seed_gen()); + + std::string result = "--cpp-httplib-multipart-data-"; + + for (auto i = 0; i < 16; i++) { + result += data[engine() % (sizeof(data) - 1)]; + } + + return result; +} + +inline std::pair get_range_offset_and_length(const Request &req, size_t content_length, size_t index) { + auto r = req.ranges[index]; + + if (r.first == -1 && r.second == -1) { + return std::make_pair(0, content_length); + } + + auto slen = static_cast(content_length); + + if (r.first == -1) { + r.first = slen - r.second; + r.second = slen - 1; + } + + if (r.second == -1) { + r.second = slen - 1; + } + + return std::make_pair(r.first, r.second - r.first + 1); +} + +inline std::string make_content_range_header_field(size_t offset, size_t length, size_t content_length) { + std::string field = "bytes "; + field += std::to_string(offset); + field += "-"; + field += std::to_string(offset + length - 1); + field += "/"; + field += std::to_string(content_length); + return field; +} + +template +bool process_multipart_ranges_data(const Request &req, + Response &res, + const std::string &boundary, + const std::string &content_type, + SToken stoken, + CToken ctoken, + Content content) { + for (size_t i = 0; i < req.ranges.size(); i++) { + ctoken("--"); + stoken(boundary); + ctoken("\r\n"); + if (!content_type.empty()) { + ctoken("Content-Type: "); + stoken(content_type); + ctoken("\r\n"); + } + + auto offsets = get_range_offset_and_length(req, res.body.size(), i); + auto offset = offsets.first; + auto length = offsets.second; + + ctoken("Content-Range: "); + stoken(make_content_range_header_field(offset, length, res.body.size())); + ctoken("\r\n"); + ctoken("\r\n"); + if (!content(offset, length)) { + return false; + } + ctoken("\r\n"); + } + + ctoken("--"); + stoken(boundary); + ctoken("--\r\n"); + + return true; +} + +inline std::string make_multipart_ranges_data(const Request &req, + Response &res, + const std::string &boundary, + const std::string &content_type) { + std::string data; + + process_multipart_ranges_data(req, + res, + boundary, + content_type, + [&](const std::string &token) { data += token; }, + [&](const char *token) { data += token; }, + [&](size_t offset, size_t length) { + data += res.body.substr(offset, length); + return true; + }); + + return data; +} + +inline size_t get_multipart_ranges_data_length(const Request &req, + Response &res, + const std::string &boundary, + const std::string &content_type) { + size_t data_length = 0; + + process_multipart_ranges_data(req, + res, + boundary, + content_type, + [&](const std::string &token) { data_length += token.size(); }, + [&](const char *token) { data_length += strlen(token); }, + [&](size_t /*offset*/, size_t length) { + data_length += length; + return true; + }); + + return data_length; +} + +template +inline bool write_multipart_ranges_data(Stream &strm, + const Request &req, + Response &res, + const std::string &boundary, + const std::string &content_type, + T is_shutting_down) { + return process_multipart_ranges_data( + req, + res, + boundary, + content_type, + [&](const std::string &token) { strm.write(token); }, + [&](const char *token) { strm.write(token); }, + [&](size_t offset, size_t length) { + return write_content(strm, res.content_provider_, offset, length, is_shutting_down) >= 0; + }); +} + +inline std::pair get_range_offset_and_length(const Request &req, const Response &res, size_t index) { + auto r = req.ranges[index]; + + if (r.second == -1) { + r.second = static_cast(res.content_length_) - 1; + } + + return std::make_pair(r.first, r.second - r.first + 1); +} + +inline bool expect_content(const Request &req) { + if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || req.method == "PRI" || + (req.method == "DELETE" && req.has_header("Content-Length"))) { + return true; + } + // TODO: check if Content-Length is set + return false; +} + +inline bool has_crlf(const char *s) { + auto p = s; + while (*p) { + if (*p == '\r' || *p == '\n') { + return true; + } + p++; + } + return false; +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +template +inline std::string message_digest(const std::string &s, Init init, Update update, Final final, size_t digest_length) { + using namespace std; + + std::vector md(digest_length, 0); + CTX ctx; + init(&ctx); + update(&ctx, s.data(), s.size()); + final(md.data(), &ctx); + + stringstream ss; + for (auto c : md) { + ss << setfill('0') << setw(2) << hex << (unsigned int) c; + } + return ss.str(); +} + +inline std::string MD5(const std::string &s) { + return message_digest(s, MD5_Init, MD5_Update, MD5_Final, MD5_DIGEST_LENGTH); +} + +inline std::string SHA_256(const std::string &s) { + return message_digest(s, SHA256_Init, SHA256_Update, SHA256_Final, SHA256_DIGEST_LENGTH); +} + +inline std::string SHA_512(const std::string &s) { + return message_digest(s, SHA512_Init, SHA512_Update, SHA512_Final, SHA512_DIGEST_LENGTH); +} +#endif + +#ifdef _WIN32 +class WSInit { + public: + WSInit() { + WSADATA wsaData; + WSAStartup(0x0002, &wsaData); + } + + ~WSInit() { + WSACleanup(); + } +}; + +static WSInit wsinit_; +#endif + +} // namespace detail + +// Header utilities +inline std::pair make_range_header(Ranges ranges) { + std::string field = "bytes="; + auto i = 0; + for (auto r : ranges) { + if (i != 0) { + field += ", "; + } + if (r.first != -1) { + field += std::to_string(r.first); + } + field += '-'; + if (r.second != -1) { + field += std::to_string(r.second); + } + i++; + } + return std::make_pair("Range", field); +} + +inline std::pair make_basic_authentication_header(const std::string &username, + const std::string &password, + bool is_proxy = false) { + auto field = "Basic " + detail::base64_encode(username + ":" + password); + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + return std::make_pair(key, field); +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +inline std::pair make_digest_authentication_header( + const Request &req, + const std::map &auth, + size_t cnonce_count, + const std::string &cnonce, + const std::string &username, + const std::string &password, + bool is_proxy = false) { + using namespace std; + + string nc; + { + stringstream ss; + ss << setfill('0') << setw(8) << hex << cnonce_count; + nc = ss.str(); + } + + auto qop = auth.at("qop"); + if (qop.find("auth-int") != std::string::npos) { + qop = "auth-int"; + } else { + qop = "auth"; + } + + std::string algo = "MD5"; + if (auth.find("algorithm") != auth.end()) { + algo = auth.at("algorithm"); + } + + string response; + { + auto H = algo == "SHA-256" ? detail::SHA_256 : algo == "SHA-512" ? detail::SHA_512 : detail::MD5; + + auto A1 = username + ":" + auth.at("realm") + ":" + password; + + auto A2 = req.method + ":" + req.path; + if (qop == "auth-int") { + A2 += ":" + H(req.body); + } + + response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce + ":" + qop + ":" + H(A2)); + } + + auto field = "Digest username=\"" + username + "\", realm=\"" + auth.at("realm") + "\", nonce=\"" + + auth.at("nonce") + "\", uri=\"" + req.path + "\", algorithm=" + algo + ", qop=" + qop + ", nc=\"" + + nc + "\", cnonce=\"" + cnonce + "\", response=\"" + response + "\""; + + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + return std::make_pair(key, field); +} +#endif + +inline bool parse_www_authenticate(const Response &res, std::map &auth, bool is_proxy) { + auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate"; + if (res.has_header(auth_key)) { + static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~"); + auto s = res.get_header_value(auth_key); + auto pos = s.find(' '); + if (pos != std::string::npos) { + auto type = s.substr(0, pos); + if (type == "Basic") { + return false; + } else if (type == "Digest") { + s = s.substr(pos + 1); + auto beg = std::sregex_iterator(s.begin(), s.end(), re); + for (auto i = beg; i != std::sregex_iterator(); ++i) { + auto m = *i; + auto key = s.substr(static_cast(m.position(1)), static_cast(m.length(1))); + auto val = m.length(2) > 0 + ? s.substr(static_cast(m.position(2)), static_cast(m.length(2))) + : s.substr(static_cast(m.position(3)), static_cast(m.length(3))); + auth[key] = val; + } + return true; + } + } + } + return false; +} + +// https://stackoverflow.com/questions/440133/how-do-i-create-a-random-alpha-numeric-string-in-c/440240#answer-440240 +inline std::string random_string(size_t length) { + auto randchar = []() -> char { + const char charset[] = "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + const size_t max_index = (sizeof(charset) - 1); + return charset[static_cast(rand()) % max_index]; + }; + std::string str(length, 0); + std::generate_n(str.begin(), length, randchar); + return str; +} + +// Request implementation +inline bool Request::has_header(const char *key) const { + return detail::has_header(headers, key); +} + +inline std::string Request::get_header_value(const char *key, size_t id) const { + return detail::get_header_value(headers, key, id, ""); +} + +inline size_t Request::get_header_value_count(const char *key) const { + auto r = headers.equal_range(key); + return static_cast(std::distance(r.first, r.second)); +} + +inline void Request::set_header(const char *key, const char *val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val)) { + headers.emplace(key, val); + } +} + +inline void Request::set_header(const char *key, const std::string &val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) { + headers.emplace(key, val); + } +} + +inline bool Request::has_param(const char *key) const { + return params.find(key) != params.end(); +} + +inline std::string Request::get_param_value(const char *key, size_t id) const { + auto rng = params.equal_range(key); + auto it = rng.first; + std::advance(it, static_cast(id)); + if (it != rng.second) { + return it->second; + } + return std::string(); +} + +inline size_t Request::get_param_value_count(const char *key) const { + auto r = params.equal_range(key); + return static_cast(std::distance(r.first, r.second)); +} + +inline bool Request::is_multipart_form_data() const { + const auto &content_type = get_header_value("Content-Type"); + return !content_type.find("multipart/form-data"); +} + +inline bool Request::has_file(const char *key) const { + return files.find(key) != files.end(); +} + +inline MultipartFormData Request::get_file_value(const char *key) const { + auto it = files.find(key); + if (it != files.end()) { + return it->second; + } + return MultipartFormData(); +} + +// Response implementation +inline bool Response::has_header(const char *key) const { + return headers.find(key) != headers.end(); +} + +inline std::string Response::get_header_value(const char *key, size_t id) const { + return detail::get_header_value(headers, key, id, ""); +} + +inline size_t Response::get_header_value_count(const char *key) const { + auto r = headers.equal_range(key); + return static_cast(std::distance(r.first, r.second)); +} + +inline void Response::set_header(const char *key, const char *val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val)) { + headers.emplace(key, val); + } +} + +inline void Response::set_header(const char *key, const std::string &val) { + if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) { + headers.emplace(key, val); + } +} + +inline void Response::set_redirect(const char *url, int stat) { + if (!detail::has_crlf(url)) { + set_header("Location", url); + if (300 <= stat && stat < 400) { + this->status = stat; + } else { + this->status = 302; + } + } +} + +inline void Response::set_content(const char *s, size_t n, const char *content_type) { + body.assign(s, n); + set_header("Content-Type", content_type); +} + +inline void Response::set_content(std::string s, const char *content_type) { + body = std::move(s); + set_header("Content-Type", content_type); +} + +inline void Response::set_content_provider(size_t in_length, + ContentProvider provider, + std::function resource_releaser) { + assert(in_length > 0); + content_length_ = in_length; + content_provider_ = [provider](size_t offset, size_t length, DataSink &sink) { + return provider(offset, length, sink); + }; + content_provider_resource_releaser_ = resource_releaser; +} + +inline void Response::set_chunked_content_provider(ChunkedContentProvider provider, + std::function resource_releaser) { + content_length_ = 0; + content_provider_ = [provider](size_t offset, size_t, DataSink &sink) { return provider(offset, sink); }; + content_provider_resource_releaser_ = resource_releaser; +} + +// Rstream implementation +inline ssize_t Stream::write(const char *ptr) { + return write(ptr, strlen(ptr)); +} + +inline ssize_t Stream::write(const std::string &s) { + return write(s.data(), s.size()); +} + +template +inline ssize_t Stream::write_format(const char *fmt, const Args &... args) { + std::array buf; + +#if defined(_MSC_VER) && _MSC_VER < 1900 + auto sn = _snprintf_s(buf, bufsiz, buf.size() - 1, fmt, args...); +#else + auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...); +#endif + if (sn <= 0) { + return sn; + } + + auto n = static_cast(sn); + + if (n >= buf.size() - 1) { + std::vector glowable_buf(buf.size()); + + while (n >= glowable_buf.size() - 1) { + glowable_buf.resize(glowable_buf.size() * 2); +#if defined(_MSC_VER) && _MSC_VER < 1900 + n = static_cast( + _snprintf_s(&glowable_buf[0], glowable_buf.size(), glowable_buf.size() - 1, fmt, args...)); +#else + n = static_cast(snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...)); +#endif + } + return write(&glowable_buf[0], n); + } else { + return write(buf.data(), n); + } +} + +namespace detail { + +// Socket stream implementation +inline SocketStream::SocketStream(socket_t sock, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec) + : sock_(sock), + read_timeout_sec_(read_timeout_sec), + read_timeout_usec_(read_timeout_usec), + write_timeout_sec_(write_timeout_sec), + write_timeout_usec_(write_timeout_usec) {} + +inline SocketStream::~SocketStream() {} + +inline bool SocketStream::is_readable() const { + return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; +} + +inline bool SocketStream::is_writable() const { + return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0; +} + +inline ssize_t SocketStream::read(char *ptr, size_t size) { + if (!is_readable()) { + return -1; + } + +#ifdef _WIN32 + if (size > static_cast((std::numeric_limits::max)())) { + return -1; + } + return recv(sock_, ptr, static_cast(size), 0); +#else + return handle_EINTR([&]() { return recv(sock_, ptr, size, 0); }); +#endif +} + +inline ssize_t SocketStream::write(const char *ptr, size_t size) { + if (!is_writable()) { + return -1; + } + +#ifdef _WIN32 + if (size > static_cast((std::numeric_limits::max)())) { + return -1; + } + return send(sock_, ptr, static_cast(size), 0); +#else + return handle_EINTR([&]() { return send(sock_, ptr, size, 0); }); +#endif +} + +inline void SocketStream::get_remote_ip_and_port(std::string &ip, int &port) const { + return detail::get_remote_ip_and_port(sock_, ip, port); +} + +// Buffer stream implementation +inline bool BufferStream::is_readable() const { + return true; +} + +inline bool BufferStream::is_writable() const { + return true; +} + +inline ssize_t BufferStream::read(char *ptr, size_t size) { +#if defined(_MSC_VER) && _MSC_VER < 1900 + auto len_read = buffer._Copy_s(ptr, size, size, position); +#else + auto len_read = buffer.copy(ptr, size, position); +#endif + position += static_cast(len_read); + return static_cast(len_read); +} + +inline ssize_t BufferStream::write(const char *ptr, size_t size) { + buffer.append(ptr, size); + return static_cast(size); +} + +inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/, int & /*port*/) const {} + +inline const std::string &BufferStream::get_buffer() const { + return buffer; +} + +} // namespace detail + +/* + * SSL Implementation + */ +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +namespace detail { + +template +inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex, U SSL_connect_or_accept, V setup) { + SSL *ssl = nullptr; + { + std::lock_guard guard(ctx_mutex); + ssl = SSL_new(ctx); + } + + if (ssl) { + auto bio = BIO_new_socket(static_cast(sock), BIO_NOCLOSE); + SSL_set_bio(ssl, bio, bio); + + if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) { + SSL_shutdown(ssl); + { + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); + } + return nullptr; + } + } + + return ssl; +} + +inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, bool process_socket_ret) { + if (process_socket_ret) { + SSL_shutdown(ssl); // shutdown only if not already closed by remote + } + + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); +} + +template +inline bool process_client_socket_ssl(SSL *ssl, + socket_t sock, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec, + T callback) { + SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec); + return callback(strm); +} + +#if OPENSSL_VERSION_NUMBER < 0x10100000L +static std::shared_ptr> openSSL_locks_; + +class SSLThreadLocks { + public: + SSLThreadLocks() { + openSSL_locks_ = std::make_shared>(CRYPTO_num_locks()); + CRYPTO_set_locking_callback(locking_callback); + } + + ~SSLThreadLocks() { + CRYPTO_set_locking_callback(nullptr); + } + + private: + static void locking_callback(int mode, int type, const char * /*file*/, int /*line*/) { + auto &lk = (*openSSL_locks_)[static_cast(type)]; + if (mode & CRYPTO_LOCK) { + lk.lock(); + } else { + lk.unlock(); + } + } +}; + +#endif + +class SSLInit { + public: + SSLInit() { +#if OPENSSL_VERSION_NUMBER < 0x1010001fL + SSL_load_error_strings(); + SSL_library_init(); +#else + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); +#endif + } + + ~SSLInit() { +#if OPENSSL_VERSION_NUMBER < 0x1010001fL + ERR_free_strings(); +#endif + } + + private: +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SSLThreadLocks thread_init_; +#endif +}; + +// SSL socket stream implementation +inline SSLSocketStream::SSLSocketStream(socket_t sock, + SSL *ssl, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec) + : sock_(sock), + ssl_(ssl), + read_timeout_sec_(read_timeout_sec), + read_timeout_usec_(read_timeout_usec), + write_timeout_sec_(write_timeout_sec), + write_timeout_usec_(write_timeout_usec) {} + +inline SSLSocketStream::~SSLSocketStream() {} + +inline bool SSLSocketStream::is_readable() const { + return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; +} + +inline bool SSLSocketStream::is_writable() const { + return detail::select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0; +} + +inline ssize_t SSLSocketStream::read(char *ptr, size_t size) { + if (SSL_pending(ssl_) > 0 || is_readable()) { + return SSL_read(ssl_, ptr, static_cast(size)); + } + return -1; +} + +inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) { + if (is_writable()) { + return SSL_write(ssl_, ptr, static_cast(size)); + } + return -1; +} + +inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip, int &port) const { + detail::get_remote_ip_and_port(sock_, ip, port); +} + +static SSLInit sslinit_; + +} // namespace detail + +class SSLClient : public Client { + public: + explicit SSLClient(const std::string &host); + + explicit SSLClient(const std::string &host, int port); + + explicit SSLClient(const std::string &host, + int port, + const std::string &client_cert_path, + const std::string &client_key_path); + + explicit SSLClient(const std::string &host, int port, X509 *client_cert, EVP_PKEY *client_key); + + ~SSLClient() override; + + bool is_valid() const override; + + void set_ca_cert_path(const char *ca_cert_file_path, const char *ca_cert_dir_path = nullptr); + + void set_ca_cert_store(X509_STORE *ca_cert_store); + + void enable_server_certificate_verification(bool enabled); + + long get_openssl_verify_result() const; + + SSL_CTX *ssl_context() const; + + private: + bool create_and_connect_socket(Socket &socket) override; + void close_socket(Socket &socket, bool process_socket_ret) override; + + bool process_socket(Socket &socket, std::function callback) override; + bool is_ssl() const override; + + bool connect_with_proxy(Socket &sock, Response &res, bool &success); + bool initialize_ssl(Socket &socket); + + bool verify_host(X509 *server_cert) const; + bool verify_host_with_subject_alt_name(X509 *server_cert) const; + bool verify_host_with_common_name(X509 *server_cert) const; + bool check_host_name(const char *pattern, size_t pattern_len) const; + + SSL_CTX *ctx_; + std::mutex ctx_mutex_; + std::vector host_components_; + + std::string ca_cert_file_path_; + std::string ca_cert_dir_path_; + X509_STORE *ca_cert_store_ = nullptr; + bool server_certificate_verification_ = false; + long verify_result_ = 0; + + friend class Client; +}; + +// SSL HTTP client implementation +inline SSLClient::SSLClient(const std::string &host) : SSLClient(host, 443, std::string(), std::string()) {} + +inline SSLClient::SSLClient(const std::string &host, int port) : SSLClient(host, port, std::string(), std::string()) {} + +inline SSLClient::SSLClient(const std::string &host, + int port, + const std::string &client_cert_path, + const std::string &client_key_path) + : Client(host, port, client_cert_path, client_key_path) { + ctx_ = SSL_CTX_new(SSLv23_client_method()); + + detail::split(&host_[0], &host_[host_.size()], '.', [&](const char *b, const char *e) { + host_components_.emplace_back(std::string(b, e)); + }); + if (!client_cert_path.empty() && !client_key_path.empty()) { + if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(), SSL_FILETYPE_PEM) != 1 || + SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(), SSL_FILETYPE_PEM) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } + } +} + +inline SSLClient::SSLClient(const std::string &host, int port, X509 *client_cert, EVP_PKEY *client_key) + : Client(host, port) { + ctx_ = SSL_CTX_new(SSLv23_client_method()); + + detail::split(&host_[0], &host_[host_.size()], '.', [&](const char *b, const char *e) { + host_components_.emplace_back(std::string(b, e)); + }); + if (client_cert != nullptr && client_key != nullptr) { + if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 || SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } + } +} + +inline SSLClient::~SSLClient() { + if (ctx_) { + SSL_CTX_free(ctx_); + } +} + +inline bool SSLClient::is_valid() const { + return ctx_; +} + +inline void SSLClient::set_ca_cert_path(const char *ca_cert_file_path, const char *ca_cert_dir_path) { + if (ca_cert_file_path) { + ca_cert_file_path_ = ca_cert_file_path; + } + if (ca_cert_dir_path) { + ca_cert_dir_path_ = ca_cert_dir_path; + } +} + +inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) { + if (ca_cert_store) { + ca_cert_store_ = ca_cert_store; + } +} + +inline void SSLClient::enable_server_certificate_verification(bool enabled) { + server_certificate_verification_ = enabled; +} + +inline long SSLClient::get_openssl_verify_result() const { + return verify_result_; +} + +inline SSL_CTX *SSLClient::ssl_context() const { + return ctx_; +} + +inline bool SSLClient::create_and_connect_socket(Socket &socket) { + return is_valid() && Client::create_and_connect_socket(socket); +} + +inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res, bool &success) { + success = true; + Response res2; + + if (!detail::process_client_socket(socket.sock, + read_timeout_sec_, + read_timeout_usec_, + write_timeout_sec_, + write_timeout_usec_, + [&](Stream &strm) { + Request req2; + req2.method = "CONNECT"; + req2.path = host_and_port_; + return process_request(strm, req2, res2, false); + })) { + close_socket(socket, true); + success = false; + return false; + } + + if (res2.status == 407) { + if (!proxy_digest_auth_username_.empty() && !proxy_digest_auth_password_.empty()) { + std::map auth; + if (parse_www_authenticate(res2, auth, true)) { + Response res3; + if (!detail::process_client_socket(socket.sock, + read_timeout_sec_, + read_timeout_usec_, + write_timeout_sec_, + write_timeout_usec_, + [&](Stream &strm) { + Request req3; + req3.method = "CONNECT"; + req3.path = host_and_port_; + req3.headers.insert(make_digest_authentication_header( + req3, + auth, + 1, + random_string(10), + proxy_digest_auth_username_, + proxy_digest_auth_password_, + true)); + return process_request(strm, req3, res3, false); + })) { + close_socket(socket, true); + success = false; + return false; + } + } + } else { + res = res2; + return false; + } + } + + return true; +} + +inline bool SSLClient::initialize_ssl(Socket &socket) { + auto ssl = detail::ssl_new(socket.sock, + ctx_, + ctx_mutex_, + [&](SSL *ssl) { + if (ca_cert_file_path_.empty() && ca_cert_store_ == nullptr) { + SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, nullptr); + } else if (!ca_cert_file_path_.empty()) { + if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(), nullptr)) { + return false; + } + SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER, nullptr); + } else if (ca_cert_store_ != nullptr) { + if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store_) { + SSL_CTX_set_cert_store(ctx_, ca_cert_store_); + } + SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER, nullptr); + } + + if (SSL_connect(ssl) != 1) { + return false; + } + + if (server_certificate_verification_) { + verify_result_ = SSL_get_verify_result(ssl); + + if (verify_result_ != X509_V_OK) { + return false; + } + + auto server_cert = SSL_get_peer_certificate(ssl); + + if (server_cert == nullptr) { + return false; + } + + if (!verify_host(server_cert)) { + X509_free(server_cert); + return false; + } + X509_free(server_cert); + } + + return true; + }, + [&](SSL *ssl) { + SSL_set_tlsext_host_name(ssl, host_.c_str()); + return true; + }); + + if (ssl) { + socket.ssl = ssl; + return true; + } + + close_socket(socket, false); + return false; +} + +inline void SSLClient::close_socket(Socket &socket, bool process_socket_ret) { + detail::close_socket(socket.sock); + socket_.sock = INVALID_SOCKET; + if (socket.ssl) { + detail::ssl_delete(ctx_mutex_, socket.ssl, process_socket_ret); + socket_.ssl = nullptr; + } +} + +inline bool SSLClient::process_socket(Socket &socket, std::function callback) { + assert(socket.ssl); + return detail::process_client_socket_ssl(socket.ssl, + socket.sock, + read_timeout_sec_, + read_timeout_usec_, + write_timeout_sec_, + write_timeout_usec_, + callback); +} + +inline bool SSLClient::is_ssl() const { + return true; +} + +inline bool SSLClient::verify_host(X509 *server_cert) const { + /* Quote from RFC2818 section 3.1 "Server Identity" + + If a subjectAltName extension of type dNSName is present, that MUST + be used as the identity. Otherwise, the (most specific) Common Name + field in the Subject field of the certificate MUST be used. Although + the use of the Common Name is existing practice, it is deprecated and + Certification Authorities are encouraged to use the dNSName instead. + + Matching is performed using the matching rules specified by + [RFC2459]. If more than one identity of a given type is present in + the certificate (e.g., more than one dNSName name, a match in any one + of the set is considered acceptable.) Names may contain the wildcard + character * which is considered to match any single domain name + component or component fragment. E.g., *.a.com matches foo.a.com but + not bar.foo.a.com. f*.com matches foo.com but not bar.com. + + In some cases, the URI is specified as an IP address rather than a + hostname. In this case, the iPAddress subjectAltName must be present + in the certificate and must exactly match the IP in the URI. + + */ + return verify_host_with_subject_alt_name(server_cert) || verify_host_with_common_name(server_cert); +} + +inline bool SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const { + auto ret = false; + + auto type = GEN_DNS; + + struct in6_addr addr6; + struct in_addr addr; + size_t addr_len = 0; + +#ifndef __MINGW32__ + if (inet_pton(AF_INET6, host_.c_str(), &addr6)) { + type = GEN_IPADD; + addr_len = sizeof(struct in6_addr); + } else if (inet_pton(AF_INET, host_.c_str(), &addr)) { + type = GEN_IPADD; + addr_len = sizeof(struct in_addr); + } +#endif + + auto alt_names = static_cast( + X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr)); + + if (alt_names) { + auto dsn_matched = false; + auto ip_mached = false; + + auto count = sk_GENERAL_NAME_num(alt_names); + + for (decltype(count) i = 0; i < count && !dsn_matched; i++) { + auto val = sk_GENERAL_NAME_value(alt_names, i); + if (val->type == type) { + auto name = (const char *) ASN1_STRING_get0_data(val->d.ia5); + auto name_len = (size_t) ASN1_STRING_length(val->d.ia5); + + if (strlen(name) == name_len) { + switch (type) { + case GEN_DNS: + dsn_matched = check_host_name(name, name_len); + break; + + case GEN_IPADD: + if (!memcmp(&addr6, name, addr_len) || !memcmp(&addr, name, addr_len)) { + ip_mached = true; + } + break; + } + } + } + } + + if (dsn_matched || ip_mached) { + ret = true; + } + } + + GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *) alt_names); + return ret; +} + +inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const { + const auto subject_name = X509_get_subject_name(server_cert); + + if (subject_name != nullptr) { + char name[BUFSIZ]; + auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName, name, sizeof(name)); + + if (name_len != -1) { + return check_host_name(name, static_cast(name_len)); + } + } + + return false; +} + +inline bool SSLClient::check_host_name(const char *pattern, size_t pattern_len) const { + if (host_.size() == pattern_len && host_ == pattern) { + return true; + } + + // Wildcard match + // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484 + std::vector pattern_components; + detail::split(&pattern[0], &pattern[pattern_len], '.', [&](const char *b, const char *e) { + pattern_components.emplace_back(std::string(b, e)); + }); + + if (host_components_.size() != pattern_components.size()) { + return false; + } + + auto itr = pattern_components.begin(); + for (const auto &h : host_components_) { + auto &p = *itr; + if (p != h && p != "*") { + auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' && !p.compare(0, p.size() - 1, h)); + if (!partial_match) { + return false; + } + } + ++itr; + } + + return true; +} +#endif + +// ---------------------------------------------------------------------------- + +} // namespace httplib diff --git a/core-tests/include/httplib_server.h b/core-tests/include/httplib_server.h new file mode 100644 index 00000000000..489893f2837 --- /dev/null +++ b/core-tests/include/httplib_server.h @@ -0,0 +1,1060 @@ +/** + * httplib.h + * + * Copyright (c) 2020 Yuji Hirose. All rights reserved. + * MIT License + * GitHub: https://github.com/yhirose/cpp-httplib + */ + +#pragma once + +#include "swoole_coroutine_system.h" +#include "swoole_coroutine_socket.h" +#include "httplib_client.h" + +using swoole::Coroutine; +using swoole::coroutine::Socket; +using swoole::coroutine::System; +using swoole::network::Address; + +namespace httplib { + +class ContentReader { + public: + using Reader = std::function; + using MultipartReader = std::function; + + ContentReader(Reader reader, MultipartReader multipart_reader) + : reader_(reader), multipart_reader_(multipart_reader) {} + + bool operator()(MultipartContentHeader header, ContentReceiver receiver) const { + return multipart_reader_(header, receiver); + } + + bool operator()(ContentReceiver receiver) const { + return reader_(receiver); + } + + Reader reader_; + MultipartReader multipart_reader_; +}; + +class Server { + public: + using Handler = std::function; + using HandlerWithContentReader = + std::function; + using Expect100ContinueHandler = std::function; + + Server(); + + virtual ~Server(); + + virtual bool is_valid() const; + + Server &Get(const char *pattern, Handler handler); + Server &Post(const char *pattern, Handler handler); + Server &Post(const char *pattern, HandlerWithContentReader handler); + Server &Put(const char *pattern, Handler handler); + Server &Put(const char *pattern, HandlerWithContentReader handler); + Server &Patch(const char *pattern, Handler handler); + Server &Patch(const char *pattern, HandlerWithContentReader handler); + Server &Delete(const char *pattern, Handler handler); + Server &Delete(const char *pattern, HandlerWithContentReader handler); + Server &Options(const char *pattern, Handler handler); + + CPPHTTPLIB_DEPRECATED bool set_base_dir(const char *dir, const char *mount_point = nullptr); + bool set_mount_point(const char *mount_point, const char *dir); + bool remove_mount_point(const char *mount_point); + void set_file_extension_and_mimetype_mapping(const char *ext, const char *mime); + void set_file_request_handler(Handler handler); + + void set_error_handler(Handler handler); + void set_expect_100_continue_handler(Expect100ContinueHandler handler); + void set_logger(Logger logger); + + void set_tcp_nodelay(bool on); + void set_socket_options(SocketOptions socket_options); + + void set_keep_alive_max_count(size_t count); + void set_read_timeout(time_t sec, time_t usec = 0); + void set_write_timeout(time_t sec, time_t usec = 0); + void set_idle_interval(time_t sec, time_t usec = 0); + + void set_payload_max_length(size_t length); + + bool bind_to_port(const char *host, int port, int socket_flags = 0); + int bind_to_any_port(const char *host, int socket_flags = 0); + bool listen_after_bind(); + + bool listen(const char *host, int port, int socket_flags = 0); + + inline void BeforeListen(std::function fn) { + before_listen_callback_ = fn; + } + + bool is_running() const; + void stop(); + + protected: + bool process_request(Stream &strm, + bool close_connection, + bool &connection_closed, + const std::function &setup_request); + + Socket *svr_sock_; + size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT; + time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; + time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; + time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND; + time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND; + time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND; + time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND; + size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH; + + std::function before_listen_callback_ = nullptr; + + private: + using Handlers = std::vector>; + using HandlersForContentReader = std::vector>; + + Socket *create_server_socket(const char *host, int port, int socket_flags, SocketOptions socket_options) const; + int bind_internal(const char *host, int port, int socket_flags); + bool listen_internal(); + + bool routing(Request &req, Response &res, Stream &strm); + bool handle_file_request(Request &req, Response &res, bool head = false); + bool dispatch_request(Request &req, Response &res, Handlers &handlers); + bool dispatch_request_for_content_reader(Request &req, + Response &res, + ContentReader content_reader, + HandlersForContentReader &handlers); + + bool parse_request_line(const char *s, Request &req); + bool write_response(Stream &strm, bool close_connection, const Request &req, Response &res); + bool write_content_with_provider( + Stream &strm, const Request &req, Response &res, const std::string &boundary, const std::string &content_type); + bool read_content(Stream &strm, Request &req, Response &res); + bool read_content_with_content_receiver(Stream &strm, + Request &req, + Response &res, + ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver); + bool read_content_core(Stream &strm, + Request &req, + Response &res, + ContentReceiver receiver, + MultipartContentHeader mulitpart_header, + ContentReceiver multipart_receiver); + + virtual bool process_and_close_socket(Socket *sock); + + std::atomic is_running_; + std::vector> base_dirs_; + std::map file_extension_and_mimetype_map_; + Handler file_request_handler_; + Handlers get_handlers_; + Handlers post_handlers_; + HandlersForContentReader post_handlers_for_content_reader_; + Handlers put_handlers_; + HandlersForContentReader put_handlers_for_content_reader_; + Handlers patch_handlers_; + HandlersForContentReader patch_handlers_for_content_reader_; + Handlers delete_handlers_; + HandlersForContentReader delete_handlers_for_content_reader_; + Handlers options_handlers_; + Handler error_handler_; + Logger logger_; + Expect100ContinueHandler expect_100_continue_handler_; + + bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY; + SocketOptions socket_options_ = default_socket_options; +}; + +// HTTP server implementation +inline Server::Server() : svr_sock_(nullptr), is_running_(false) { +#ifndef _WIN32 + signal(SIGPIPE, SIG_IGN); +#endif +} + +inline Server::~Server() { + if (svr_sock_) { + delete svr_sock_; + } +} + +inline Server &Server::Get(const char *pattern, Handler handler) { + get_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Post(const char *pattern, Handler handler) { + post_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Post(const char *pattern, HandlerWithContentReader handler) { + post_handlers_for_content_reader_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Put(const char *pattern, Handler handler) { + put_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Put(const char *pattern, HandlerWithContentReader handler) { + put_handlers_for_content_reader_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Patch(const char *pattern, Handler handler) { + patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Patch(const char *pattern, HandlerWithContentReader handler) { + patch_handlers_for_content_reader_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Delete(const char *pattern, Handler handler) { + delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Delete(const char *pattern, HandlerWithContentReader handler) { + delete_handlers_for_content_reader_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server &Server::Options(const char *pattern, Handler handler) { + options_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline bool Server::set_base_dir(const char *dir, const char *mount_point) { + return set_mount_point(mount_point, dir); +} + +inline bool Server::set_mount_point(const char *mount_point, const char *dir) { + if (detail::is_dir(dir)) { + std::string mnt = mount_point ? mount_point : "/"; + if (!mnt.empty() && mnt[0] == '/') { + base_dirs_.emplace_back(mnt, dir); + return true; + } + } + return false; +} + +inline bool Server::remove_mount_point(const char *mount_point) { + for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) { + if (it->first == mount_point) { + base_dirs_.erase(it); + return true; + } + } + return false; +} + +inline void Server::set_file_extension_and_mimetype_mapping(const char *ext, const char *mime) { + file_extension_and_mimetype_map_[ext] = mime; +} + +inline void Server::set_file_request_handler(Handler handler) { + file_request_handler_ = std::move(handler); +} + +inline void Server::set_error_handler(Handler handler) { + error_handler_ = std::move(handler); +} + +inline void Server::set_tcp_nodelay(bool on) { + tcp_nodelay_ = on; +} + +inline void Server::set_socket_options(SocketOptions socket_options) { + socket_options_ = socket_options; +} + +inline void Server::set_logger(Logger logger) { + logger_ = std::move(logger); +} + +inline void Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) { + expect_100_continue_handler_ = std::move(handler); +} + +inline void Server::set_keep_alive_max_count(size_t count) { + keep_alive_max_count_ = count; +} + +inline void Server::set_read_timeout(time_t sec, time_t usec) { + read_timeout_sec_ = sec; + read_timeout_usec_ = usec; +} + +inline void Server::set_write_timeout(time_t sec, time_t usec) { + write_timeout_sec_ = sec; + write_timeout_usec_ = usec; +} + +inline void Server::set_idle_interval(time_t sec, time_t usec) { + idle_interval_sec_ = sec; + idle_interval_usec_ = usec; +} + +inline void Server::set_payload_max_length(size_t length) { + payload_max_length_ = length; +} + +inline bool Server::bind_to_port(const char *host, int port, int socket_flags) { + if (bind_internal(host, port, socket_flags) < 0) return false; + return true; +} +inline int Server::bind_to_any_port(const char *host, int socket_flags) { + return bind_internal(host, 0, socket_flags); +} + +inline bool Server::listen_after_bind() { + return listen_internal(); +} + +inline bool Server::listen(const char *host, int port, int socket_flags) { + return bind_to_port(host, port, socket_flags) && listen_internal(); +} + +inline bool Server::is_running() const { + return is_running_; +} + +inline void Server::stop() { + if (is_running_) { + is_running_ = false; + svr_sock_->cancel(SW_EVENT_READ); + } +} + +inline bool Server::parse_request_line(const char *s, Request &req) { + const static std::regex re("(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI) " + "(([^?]+)(?:\\?(.*?))?) (HTTP/1\\.[01])\r\n"); + + std::cmatch m; + if (std::regex_match(s, m, re)) { + req.version = std::string(m[5]); + req.method = std::string(m[1]); + req.target = std::string(m[2]); + req.path = detail::decode_url(m[3], false); + + // Parse query text + auto len = std::distance(m[4].first, m[4].second); + if (len > 0) { + detail::parse_query_text(m[4], req.params); + } + + return true; + } + + return false; +} + +inline bool Server::write_response(Stream &strm, bool close_connection, const Request &req, Response &res) { + assert(res.status != -1); + + if (400 <= res.status && error_handler_) { + error_handler_(req, res); + } + + detail::BufferStream bstrm; + + // Response line + if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status, detail::status_message(res.status))) { + return false; + } + + // Headers + if (close_connection || req.get_header_value("Connection") == "close") { + res.set_header("Connection", "close"); + } + + if (!close_connection && req.get_header_value("Connection") == "Keep-Alive") { + res.set_header("Connection", "Keep-Alive"); + } + + if (!res.has_header("Content-Type") && (!res.body.empty() || res.content_length_ > 0)) { + res.set_header("Content-Type", "text/plain"); + } + + if (!res.has_header("Accept-Ranges") && req.method == "HEAD") { + res.set_header("Accept-Ranges", "bytes"); + } + + std::string content_type; + std::string boundary; + + if (req.ranges.size() > 1) { + boundary = detail::make_multipart_data_boundary(); + + auto it = res.headers.find("Content-Type"); + if (it != res.headers.end()) { + content_type = it->second; + res.headers.erase(it); + } + + res.headers.emplace("Content-Type", "multipart/byteranges; boundary=" + boundary); + } + + if (res.body.empty()) { + if (res.content_length_ > 0) { + size_t length = 0; + if (req.ranges.empty()) { + length = res.content_length_; + } else if (req.ranges.size() == 1) { + auto offsets = detail::get_range_offset_and_length(req, res.content_length_, 0); + auto offset = offsets.first; + length = offsets.second; + auto content_range = detail::make_content_range_header_field(offset, length, res.content_length_); + res.set_header("Content-Range", content_range); + } else { + length = detail::get_multipart_ranges_data_length(req, res, boundary, content_type); + } + res.set_header("Content-Length", std::to_string(length)); + } else { + if (res.content_provider_) { + res.set_header("Transfer-Encoding", "chunked"); + } else { + res.set_header("Content-Length", "0"); + } + } + } else { + if (req.ranges.empty()) { + ; + } else if (req.ranges.size() == 1) { + auto offsets = detail::get_range_offset_and_length(req, res.body.size(), 0); + auto offset = offsets.first; + auto length = offsets.second; + auto content_range = detail::make_content_range_header_field(offset, length, res.body.size()); + res.set_header("Content-Range", content_range); + res.body = res.body.substr(offset, length); + } else { + res.body = detail::make_multipart_ranges_data(req, res, boundary, content_type); + } + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + // TODO: 'Accept-Encoding' has gzip, not gzip;q=0 + const auto &encodings = req.get_header_value("Accept-Encoding"); + if (encodings.find("gzip") != std::string::npos && detail::can_compress(res.get_header_value("Content-Type"))) { + if (detail::compress(res.body)) { + res.set_header("Content-Encoding", "gzip"); + } + } +#endif + + auto length = std::to_string(res.body.size()); + res.set_header("Content-Length", length); + } + + if (!detail::write_headers(bstrm, res, Headers())) { + return false; + } + + // Flush buffer + auto &data = bstrm.get_buffer(); + strm.write(data.data(), data.size()); + + // Body + if (req.method != "HEAD") { + if (!res.body.empty()) { + if (!strm.write(res.body)) { + return false; + } + } else if (res.content_provider_) { + if (!write_content_with_provider(strm, req, res, boundary, content_type)) { + return false; + } + } + } + + // Log + if (logger_) { + logger_(req, res); + } + + return true; +} + +inline bool Server::write_content_with_provider( + Stream &strm, const Request &req, Response &res, const std::string &boundary, const std::string &content_type) { + auto is_shutting_down = [this]() { return this->svr_sock_ == nullptr; }; + + if (res.content_length_) { + if (req.ranges.empty()) { + if (detail::write_content(strm, res.content_provider_, 0, res.content_length_, is_shutting_down) < 0) { + return false; + } + } else if (req.ranges.size() == 1) { + auto offsets = detail::get_range_offset_and_length(req, res.content_length_, 0); + auto offset = offsets.first; + auto length = offsets.second; + if (detail::write_content(strm, res.content_provider_, offset, length, is_shutting_down) < 0) { + return false; + } + } else { + if (!detail::write_multipart_ranges_data(strm, req, res, boundary, content_type, is_shutting_down)) { + return false; + } + } + } else { + if (detail::write_content_chunked(strm, res.content_provider_, is_shutting_down) < 0) { + return false; + } + } + return true; +} + +inline bool Server::read_content(Stream &strm, Request &req, Response &res) { + MultipartFormDataMap::iterator cur; + if (read_content_core(strm, + req, + res, + // Regular + [&](const char *buf, size_t n) { + if (req.body.size() + n > req.body.max_size()) { + return false; + } + req.body.append(buf, n); + return true; + }, + // Multipart + [&](const MultipartFormData &file) { + cur = req.files.emplace(file.name, file); + return true; + }, + [&](const char *buf, size_t n) { + auto &content = cur->second.content; + if (content.size() + n > content.max_size()) { + return false; + } + content.append(buf, n); + return true; + })) { + const auto &content_type = req.get_header_value("Content-Type"); + if (!content_type.find("application/x-www-form-urlencoded")) { + detail::parse_query_text(req.body, req.params); + } + return true; + } + return false; +} + +inline bool Server::read_content_with_content_receiver(Stream &strm, + Request &req, + Response &res, + ContentReceiver receiver, + MultipartContentHeader multipart_header, + ContentReceiver multipart_receiver) { + return read_content_core(strm, req, res, receiver, multipart_header, multipart_receiver); +} + +inline bool Server::read_content_core(Stream &strm, + Request &req, + Response &res, + ContentReceiver receiver, + MultipartContentHeader mulitpart_header, + ContentReceiver multipart_receiver) { + detail::MultipartFormDataParser multipart_form_data_parser; + ContentReceiver out; + + if (req.is_multipart_form_data()) { + const auto &content_type = req.get_header_value("Content-Type"); + std::string boundary; + if (!detail::parse_multipart_boundary(content_type, boundary)) { + res.status = 400; + return false; + } + + multipart_form_data_parser.set_boundary(std::move(boundary)); + out = [&](const char *buf, size_t n) { + /* For debug + size_t pos = 0; + while (pos < n) { + auto read_size = std::min(1, n - pos); + auto ret = multipart_form_data_parser.parse( + buf + pos, read_size, multipart_receiver, mulitpart_header); + if (!ret) { return false; } + pos += read_size; + } + return true; + */ + return multipart_form_data_parser.parse(buf, n, multipart_receiver, mulitpart_header); + }; + } else { + out = receiver; + } + + if (!detail::read_content(strm, req, payload_max_length_, res.status, Progress(), out, true)) { + return false; + } + + if (req.is_multipart_form_data()) { + if (!multipart_form_data_parser.is_valid()) { + res.status = 400; + return false; + } + } + + return true; +} + +inline bool Server::handle_file_request(Request &req, Response &res, bool head) { + for (const auto &kv : base_dirs_) { + const auto &mount_point = kv.first; + const auto &base_dir = kv.second; + + // Prefix match + if (!req.path.find(mount_point)) { + std::string sub_path = "/" + req.path.substr(mount_point.size()); + if (detail::is_valid_path(sub_path)) { + auto path = base_dir + sub_path; + if (path.back() == '/') { + path += "index.html"; + } + + if (detail::is_file(path)) { + detail::read_file(path, res.body); + auto type = detail::find_content_type(path, file_extension_and_mimetype_map_); + if (type) { + res.set_header("Content-Type", type); + } + res.status = 200; + if (!head && file_request_handler_) { + file_request_handler_(req, res); + } + return true; + } + } + } + } + return false; +} + +inline Socket *Server::create_server_socket(const char *host, + int port, + int socket_flags, + SocketOptions socket_options) const { + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = socket_flags; + hints.ai_protocol = 0; + + auto service = std::to_string(port); + + if (swoole_coroutine_getaddrinfo(host, service.c_str(), &hints, &result)) { + return nullptr; + } + + Socket *sock = nullptr; + for (auto rp = result; rp; rp = rp->ai_next) { + sock = new Socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sock->get_fd() == INVALID_SOCKET) { + delete sock; + continue; + } + if (tcp_nodelay_) { + sock->set_option(IPPROTO_TCP, TCP_NODELAY, 1); + } + if (socket_options) { + socket_options(sock->get_fd()); + } + if (rp->ai_family == AF_INET6) { + sock->set_option(IPPROTO_IPV6, IPV6_V6ONLY, 0); + } + if (!sock->bind(rp->ai_addr, static_cast(rp->ai_addrlen))) { + delete sock; + return nullptr; + } + if (!sock->listen(512)) { + delete sock; + return nullptr; + } + break; + } + + freeaddrinfo(result); + return sock; +} + +inline int Server::bind_internal(const char *host, int port, int socket_flags) { + if (!is_valid()) { + return -1; + } + + svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_); + if (svr_sock_ == nullptr) { + return -1; + } + + if (port == 0) { + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + if (getsockname(svr_sock_->get_fd(), reinterpret_cast(&addr), &addr_len) == -1) { + return -1; + } + if (addr.ss_family == AF_INET) { + return ntohs(reinterpret_cast(&addr)->sin_port); + } else if (addr.ss_family == AF_INET6) { + return ntohs(reinterpret_cast(&addr)->sin6_port); + } else { + return -1; + } + } else { + return port; + } +} + +struct CoroutineArg { + Socket *client_socket; + Server *this_; +}; + +inline bool Server::listen_internal() { + if (!svr_sock_) { + return false; + } + + is_running_ = true; + + if (before_listen_callback_) { + before_listen_callback_(); + } + + while (is_running_) { + auto client_sock = svr_sock_->accept(); + if (client_sock) { + auto arg = new CoroutineArg; + arg->client_socket = client_sock; + arg->this_ = this; + Coroutine::create( + [](void *arg) { + CoroutineArg *_arg = (CoroutineArg *) arg; + _arg->this_->process_and_close_socket(_arg->client_socket); + delete _arg; + }, + arg); + continue; + } + if (svr_sock_->errCode == EMFILE || svr_sock_->errCode == ENFILE) { + System::sleep(SW_ACCEPT_RETRY_TIME); + continue; + } else if (svr_sock_->errCode == ETIMEDOUT || svr_sock_->errCode == SW_ERROR_SSL_BAD_CLIENT) { + continue; + } else if (svr_sock_->errCode == ECANCELED) { + break; + } else { + swoole_warning("accept failed, Error: %s[%d]", svr_sock_->errMsg, svr_sock_->errCode); + break; + } + } + + is_running_ = false; + return true; +} + +inline bool Server::routing(Request &req, Response &res, Stream &strm) { + // File handler + bool is_head_request = req.method == "HEAD"; + if ((req.method == "GET" || is_head_request) && handle_file_request(req, res, is_head_request)) { + return true; + } + + if (detail::expect_content(req)) { + // Content reader handler + { + ContentReader reader( + [&](ContentReceiver receiver) { + return read_content_with_content_receiver(strm, req, res, receiver, nullptr, nullptr); + }, + [&](MultipartContentHeader header, ContentReceiver receiver) { + return read_content_with_content_receiver(strm, req, res, nullptr, header, receiver); + }); + + if (req.method == "POST") { + if (dispatch_request_for_content_reader(req, res, reader, post_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "PUT") { + if (dispatch_request_for_content_reader(req, res, reader, put_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "PATCH") { + if (dispatch_request_for_content_reader(req, res, reader, patch_handlers_for_content_reader_)) { + return true; + } + } else if (req.method == "DELETE") { + if (dispatch_request_for_content_reader(req, res, reader, delete_handlers_for_content_reader_)) { + return true; + } + } + } + + // Read content into `req.body` + if (!read_content(strm, req, res)) { + return false; + } + } + + // Regular handler + if (req.method == "GET" || req.method == "HEAD") { + return dispatch_request(req, res, get_handlers_); + } else if (req.method == "POST") { + return dispatch_request(req, res, post_handlers_); + } else if (req.method == "PUT") { + return dispatch_request(req, res, put_handlers_); + } else if (req.method == "DELETE") { + return dispatch_request(req, res, delete_handlers_); + } else if (req.method == "OPTIONS") { + return dispatch_request(req, res, options_handlers_); + } else if (req.method == "PATCH") { + return dispatch_request(req, res, patch_handlers_); + } + + res.status = 400; + return false; +} + +inline bool Server::dispatch_request(Request &req, Response &res, Handlers &handlers) { + try { + for (const auto &x : handlers) { + const auto &pattern = x.first; + const auto &handler = x.second; + + if (std::regex_match(req.path, req.matches, pattern)) { + handler(req, res); + return true; + } + } + } catch (const std::exception &ex) { + res.status = 500; + res.set_header("EXCEPTION_WHAT", ex.what()); + } catch (...) { + res.status = 500; + res.set_header("EXCEPTION_WHAT", "UNKNOWN"); + } + return false; +} + +inline bool Server::dispatch_request_for_content_reader(Request &req, + Response &res, + ContentReader content_reader, + HandlersForContentReader &handlers) { + for (const auto &x : handlers) { + const auto &pattern = x.first; + const auto &handler = x.second; + + if (std::regex_match(req.path, req.matches, pattern)) { + handler(req, res, content_reader); + return true; + } + } + return false; +} + +inline bool Server::process_request(Stream &strm, + bool close_connection, + bool &connection_closed, + const std::function &setup_request) { + std::array buf{}; + + detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); + + // Connection has been closed on client + if (!line_reader.getline()) { + return false; + } + + Request req; + Response res; + + res.version = "HTTP/1.1"; + + // Check if the request URI doesn't exceed the limit + if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) { + Headers dummy; + detail::read_headers(strm, dummy); + res.status = 414; + return write_response(strm, close_connection, req, res); + } + + // Request line and headers + if (!parse_request_line(line_reader.ptr(), req) || !detail::read_headers(strm, req.headers)) { + res.status = 400; + return write_response(strm, close_connection, req, res); + } + + if (req.get_header_value("Connection") == "close") { + connection_closed = true; + } + + if (req.version == "HTTP/1.0" && req.get_header_value("Connection") != "Keep-Alive") { + connection_closed = true; + } + + strm.get_remote_ip_and_port(req.remote_addr, req.remote_port); + req.set_header("REMOTE_ADDR", req.remote_addr); + req.set_header("REMOTE_PORT", std::to_string(req.remote_port)); + + if (req.has_header("Range")) { + const auto &range_header_value = req.get_header_value("Range"); + if (!detail::parse_range_header(range_header_value, req.ranges)) { + // TODO: error + } + } + + if (setup_request) { + setup_request(req); + } + + if (req.get_header_value("Expect") == "100-continue") { + auto status = 100; + if (expect_100_continue_handler_) { + status = expect_100_continue_handler_(req, res); + } + switch (status) { + case 100: + case 417: + strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status, detail::status_message(status)); + break; + default: + return write_response(strm, close_connection, req, res); + } + } + + // Rounting + if (routing(req, res, strm)) { + if (res.status == -1) { + res.status = req.ranges.empty() ? 200 : 206; + } + } else { + if (res.status == -1) { + res.status = 404; + } + } + + return write_response(strm, close_connection, req, res); +} + +inline bool Server::is_valid() const { + return true; +} + +namespace detail { + +template +inline bool process_server_socket_core(socket_t sock, size_t keep_alive_max_count, T callback) { + assert(keep_alive_max_count > 0); + auto ret = false; + auto count = keep_alive_max_count; + while (count > 0 && keep_alive(sock)) { + auto close_connection = count == 1; + auto connection_closed = false; + ret = callback(close_connection, connection_closed); + if (!ret || connection_closed) { + break; + } + count--; + } + return ret; +}; + +template +inline bool process_server_socket(socket_t sock, + size_t keep_alive_max_count, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec, + T callback) { + return process_server_socket_core(sock, keep_alive_max_count, [&](bool close_connection, bool connection_closed) { + SocketStream strm(sock, read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec); + return callback(strm, close_connection, connection_closed); + }); +} + +} // namespace detail + +class CoSocketStream : public detail::SocketStream { + public: + CoSocketStream(Socket *sock, + time_t read_timeout_sec, + time_t read_timeout_usec, + time_t write_timeout_sec, + time_t write_timeout_usec) + : detail::SocketStream( + sock->get_fd(), read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec) { + sock_ = sock; + sock->set_timeout((double) read_timeout_sec + ((double) read_timeout_usec / 1000000), Socket::TIMEOUT_READ); + sock->set_timeout((double) write_timeout_sec + ((double) write_timeout_usec / 1000000), Socket::TIMEOUT_WRITE); + } + ~CoSocketStream() {} + bool is_readable() const { + return true; + } + bool is_writable() const { + return true; + } + ssize_t read(char *ptr, size_t size) { + return sock_->recv_with_buffer(ptr, size); + } + ssize_t write(const char *ptr, size_t size) { + return sock_->write(ptr, size); + } + void get_remote_ip_and_port(std::string &ip, int &port) const { + Address sa; + sock_->getpeername(&sa); + ip = std::string(sa.get_ip()); + port = sa.get_port(); + } + + private: + Socket *sock_; +}; + +inline bool Server::process_and_close_socket(Socket *sock) { + size_t keep_alive_max_count = keep_alive_max_count_; + time_t read_timeout_sec = read_timeout_sec_; + time_t read_timeout_usec = read_timeout_usec_; + time_t write_timeout_sec = write_timeout_sec_; + time_t write_timeout_usec = write_timeout_usec_; + + CoSocketStream strm(sock, read_timeout_sec, read_timeout_usec, write_timeout_sec, write_timeout_usec); + + assert(keep_alive_max_count > 0); + + auto ret = false; + auto count = keep_alive_max_count; + + do { + auto close_connection = count == 1; + auto connection_closed = false; + ret = process_request(strm, close_connection, connection_closed, nullptr); + if (!ret || connection_closed) { + break; + } + count--; + } while (count > 0 && sock->check_liveness()); + + sock->shutdown(); + sock->close(); + delete sock; + + return ret; +} + +} // namespace httplib diff --git a/core-tests/include/process.h b/core-tests/include/process.h deleted file mode 100644 index 7a1be55b6ac..00000000000 --- a/core-tests/include/process.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include -#include "swoole/swoole.h" - -using namespace std; - -namespace swoole { namespace test { - -class process -{ - -private: - std::function handler; - -public: - swWorker worker; - - process(std::function fn); - ~process(); - pid_t start(); - ssize_t write(const void *__buf, size_t __n); - ssize_t read(void *__buf, size_t __nbytes); -}; -} -} \ No newline at end of file diff --git a/core-tests/include/redis_client.h b/core-tests/include/redis_client.h new file mode 100644 index 00000000000..fdcbf6a450f --- /dev/null +++ b/core-tests/include/redis_client.h @@ -0,0 +1,67 @@ +#pragma once + +#include "swoole.h" + +#include +#include +#include + +#include "hiredis.h" + +namespace swoole { + +class RedisReply { + private: + redisReply *ptr_; + + public: + RedisReply(void *ptr) : ptr_(reinterpret_cast(ptr)) {} + RedisReply(RedisReply &&_o) { + ptr_ = _o.ptr_; + _o.ptr_ = nullptr; + } + ~RedisReply() { + if (ptr_) { + freeReplyObject(ptr_); + } + } + redisReply *operator->() { + return ptr_; + } + + inline bool empty() { + return ptr_ == nullptr; + } + inline const char *str() { + return ptr_->str; + } + inline size_t len() { + return ptr_->len; + } + inline size_t type() { + return ptr_->type; + } +}; + +class RedisClient { + private: + redisContext *ctx = nullptr; + + public: + RedisClient() = default; + + ~RedisClient() { + if (ctx) { + redisFree(ctx); + } + } + + RedisReply Request(const std::vector &args); + RedisReply Request(int argc, const char **argv, const size_t *argvlen); + + bool Connect(const std::string &host = "127.0.0.1", int port = 6379, struct timeval timeout = {}); + std::string Get(const std::string &key); + bool Set(const std::string &key, const std::string &value); +}; + +} // namespace swoole diff --git a/core-tests/include/test_core.h b/core-tests/include/test_core.h new file mode 100644 index 00000000000..21feb2da8b6 --- /dev/null +++ b/core-tests/include/test_core.h @@ -0,0 +1,64 @@ +#pragma once + +#include "swoole_api.h" +#include "swoole_client.h" + +#include + +#include +#include +#include +#include +#include +#include +#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_JPG_FILE "/examples/test.jpg" +#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 { + +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 new file mode 100644 index 00000000000..8843f5f0028 --- /dev/null +++ b/core-tests/include/test_coroutine.h @@ -0,0 +1,73 @@ +#pragma once + +#include "test_core.h" + +#include "swoole_coroutine.h" +#include "swoole_coroutine_channel.h" +#include "swoole_coroutine_system.h" +#include "swoole_coroutine_socket.h" +#include "swoole_coroutine_c_api.h" + +namespace swoole { +namespace test { + +class coroutine { + public: + coroutine(const CoroutineFunc &_fn, void *_arg, int *_complete_num) + : fn(_fn), arg(_arg), complete_num(_complete_num) {} + + void start() { + fn(arg); + (*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); + ASSERT_GT(cid, 0); + } + + inline static void run(std::initializer_list> args) { + int complete_num = 0; + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + 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) { + int complete_num = 0; + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + Coroutine::activate(); + for (const auto &fn : fns) { + create(fn, nullptr, &complete_num); + } + swoole_event_wait(); + Coroutine::deactivate(); + } + + inline static void run(const CoroutineFunc &fn, void *arg = nullptr) { + int complete_num = 0; + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + Coroutine::activate(); + create(fn, arg, &complete_num); + swoole_event_wait(); + Coroutine::deactivate(); + } + + private: + CoroutineFunc fn; + void *arg; + int *complete_num; +}; +} // namespace test +} // namespace swoole diff --git a/core-tests/include/test_process.h b/core-tests/include/test_process.h new file mode 100644 index 00000000000..b680861b960 --- /dev/null +++ b/core-tests/include/test_process.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "test_core.h" +#include "swoole_process_pool.h" + +using namespace std; + +namespace swoole { namespace test { +//------------------------------------------------------------------------------- +class Process +{ + +private: + std::function handler; + +public: + Worker worker = {}; + + Process(std::function fn, int pipe_type = SOCK_DGRAM); + ~Process(); + pid_t start(); + ssize_t write(const void *__buf, size_t __n); + ssize_t read(void *__buf, size_t __nbytes); +}; +//------------------------------------------------------------------------------- +}} diff --git a/core-tests/include/test_server.h b/core-tests/include/test_server.h new file mode 100644 index 00000000000..6199b4cf8ff --- /dev/null +++ b/core-tests/include/test_server.h @@ -0,0 +1,66 @@ +#pragma once + +#include "test_core.h" +#include "swoole_server.h" + +#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 + +typedef void (*_onStart)(swServer *serv); +typedef void (*_onShutdown)(swServer *serv); +typedef void (*_onPipeMessage)(swServer *, swEventData *data); +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 *); +typedef void (*_onConnect)(swServer *serv, swDataHead *); + +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 { +//-------------------------------------------------------------------------------------------------------- +class Server { + private: + swoole::Server serv; + std::vector ports; + std::unordered_map private_data; + std::string host; + int port; + int mode; + int type; + + 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 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) { + auto it = private_data.find(key); + if (it == private_data.end()) { + return nullptr; + } else { + return it->second; + } + } + + inline void set_private_data(const std::string &key, void *data) { + private_data[key] = data; + } +}; +//-------------------------------------------------------------------------------------------------------- +} // namespace test +} // namespace swoole diff --git a/core-tests/include/tests.h b/core-tests/include/tests.h deleted file mode 100755 index ab5cda3a415..00000000000 --- a/core-tests/include/tests.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "wrapper/coroutine.h" - -#include "swoole/swoole.h" -#include "swoole/client.h" -#include "swoole/server.h" - -#include - -#include -#include -#include -#include -#include -#include -#include - -#define TEST_HOST "127.0.0.1" -#define TEST_PORT 9501 - -void create_test_server(swServer *serv); \ No newline at end of file diff --git a/core-tests/include/wrapper/coroutine.h b/core-tests/include/wrapper/coroutine.h deleted file mode 100644 index 6011a8be9c3..00000000000 --- a/core-tests/include/wrapper/coroutine.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "swoole/coroutine.h" -#include - -namespace swoole { namespace test { - -class coroutine -{ -public: - coroutine(coroutine_func_t _fn, void *_arg, int *_complete_num) : - fn(_fn), arg(_arg), complete_num(_complete_num) { } - - void run() - { - fn(arg); - (*complete_num)++; - } - - inline static void create(coroutine_func_t fn, void *arg, int *complete_num) - { - auto test = new coroutine(fn, arg, complete_num); - - long cid = swoole::Coroutine::create([](void *arg) - { - ((coroutine *) arg)->run(); - delete (coroutine *) arg; - }, test); - ASSERT_GT(cid, 0); - } - - inline static void test(std::initializer_list> args) - { - int complete_num = 0; - swoole_event_init(); - SwooleTG.reactor->wait_exit = 1; - for (const auto &arg : args) - { - create(arg.first, arg.second, &complete_num); - } - swoole_event_wait(); - } - - inline static void test(std::initializer_list fns) - { - int complete_num = 0; - swoole_event_init(); - SwooleTG.reactor->wait_exit = 1; - for (const auto &fn : fns) - { - create(fn, nullptr, &complete_num); - } - swoole_event_wait(); - } - - inline static void test(coroutine_func_t fn, void *arg = nullptr) - { - int complete_num = 0; - swoole_event_init(); - SwooleTG.reactor->wait_exit = 1; - create(fn, arg, &complete_num); - swoole_event_wait(); - } - -private: - coroutine_func_t fn; - void *arg; - int *complete_num; -}; -} -} - - diff --git a/core-tests/include/wrapper/server.h b/core-tests/include/wrapper/server.h deleted file mode 100644 index ca9155f1ecd..00000000000 --- a/core-tests/include/wrapper/server.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include "tests.h" - -using namespace swoole; - -#define SERVER_THIS ((server *) serv->ptr2) - -#define ON_WORKERSTART_PARAMS swServer *serv, int worker_id -#define ON_PACKET_PARAMS swServer *serv, swEventData *req -#define ON_RECEIVE_PARAMS swServer *serv, swEventData *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 int (*_onReceive)(swServer *, swEventData *); -typedef int (*_onPacket)(swServer *, swEventData *); -typedef void (*_onClose)(swServer *serv, swDataHead *); -typedef void (*_onConnect)(swServer *serv, swDataHead *); - -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 { -class server -{ -private: - swServer serv; - std::vector ports; - std::string host; - int port; - int mode; - int type; - -public: - swDgramPacket *packet = nullptr; - - server(std::string _host, int _port, int _mode, int _type); - ~server(); - void on(std::string event, void *fn); - bool start(); - bool listen(std::string host, int port, enum swSocket_type type); - size_t get_packet(swEventData *req, char **data_ptr); - int send(int session_id, void *data, uint32_t length); - ssize_t sendto(swSocketAddress *address, const char *__buf, size_t __n, int server_socket = -1); - int close(int session_id, int reset); -}; -} -} diff --git a/core-tests/run.sh b/core-tests/run.sh index 5a7e60da3b8..eac533dc4d8 100755 --- a/core-tests/run.sh +++ b/core-tests/run.sh @@ -1,2 +1,32 @@ #!/bin/bash -cmake . && make -j8 && ./bin/core_tests +__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 + +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 74067cc6258..00000000000 --- a/core-tests/samples/s1.cc +++ /dev/null @@ -1,60 +0,0 @@ -#include "swoole.h" -#include "swoole_api.h" -#include "client.h" -#include "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(); - /** - * 协程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 new file mode 100644 index 00000000000..3f6f3fe1395 --- /dev/null +++ b/core-tests/src/_lib/http.cpp @@ -0,0 +1,921 @@ +#include "test_core.h" +#include "httplib_client.h" +#include "swoole_http.h" + +namespace websocket = swoole::websocket; + +namespace httplib { + +bool Client::Upgrade(const char *_path, Headers &_headers) { + set_keep_alive(true); + _headers.emplace("Connection", "Upgrade"); + _headers.emplace("Upgrade", "websocket"); + _headers.emplace("Sec-Websocket-Key", "sN9cRrP/n9NdMgdcy2VJFQ=="); + _headers.emplace("Sec-WebSocket-Version", "13"); + + auto resp = Get(_path, _headers); + if (resp == nullptr or resp->status != SW_HTTP_SWITCHING_PROTOCOLS) { + return false; + } + + return true; +} + +bool Client::Push(const char *data, size_t length, int opcode) { + if (!socket_.is_open()) { + return false; + } + return process_socket(socket_, [&](Stream &strm) { + swString buffer = {}; + char buf[32]; + buffer.size = sizeof(buf); + buffer.str = buf; + + 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; + }); +} + +std::shared_ptr Client::Recv() { + auto msg = std::make_shared(); + auto retval = process_socket(socket_, [&](Stream &strm) { + swProtocol proto = {}; + proto.package_length_size = SW_WEBSOCKET_HEADER_LEN; + proto.get_package_length = websocket::get_package_length; + proto.package_max_length = SW_INPUT_BUFFER_SIZE; + + char buf[1024]; + ssize_t packet_len; + + if (strm.read(buf, SW_WEBSOCKET_HEADER_LEN) <= 0) { + return false; + } + 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, pl.header_len - SW_WEBSOCKET_HEADER_LEN) <= 0) { + return false; + } + pl.buf_size = pl.header_len; + packet_len = proto.get_package_length(&proto, nullptr, &pl); + if (packet_len <= 0) { + return false; + } + } + + char *data = (char *) malloc(packet_len + 1); + if (data == nullptr) { + return false; + } + data[packet_len] = 0; + + 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; + while (read_bytes < packet_len) { + auto n_read = strm.read(data + read_bytes, packet_len - read_bytes); + if (n_read <= 0) { + free(data); + return false; + } + read_bytes += n_read; + } + + return websocket::decode(msg.get(), data, packet_len); + }); + + return retval ? msg : nullptr; +} + +// HTTP client implementation +Client::Client(const std::string &host) : Client(host, 80, std::string(), std::string()) {} + +Client::Client(const std::string &host, int port) : Client(host, port, std::string(), std::string()) {} + +Client::Client(const std::string &host, + int port, + const std::string &client_cert_path, + const std::string &client_key_path) + : host_(host), + port_(port), + host_and_port_(host_ + ":" + std::to_string(port_)), + client_cert_path_(client_cert_path), + client_key_path_(client_key_path) {} + +Client::~Client() { + stop(); +} + +bool Client::is_valid() const { + return true; +} + +socket_t Client::create_client_socket() const { + if (!proxy_host_.empty()) { + return detail::create_client_socket(proxy_host_.c_str(), + proxy_port_, + tcp_nodelay_, + socket_options_, + connection_timeout_sec_, + connection_timeout_usec_, + interface_); + } + return detail::create_client_socket(host_.c_str(), + port_, + tcp_nodelay_, + socket_options_, + connection_timeout_sec_, + connection_timeout_usec_, + interface_); +} + +bool Client::create_and_connect_socket(Socket &socket) { + auto sock = create_client_socket(); + if (sock == INVALID_SOCKET) { + return false; + } + socket.sock = sock; + return true; +} + +void Client::close_socket(Socket &socket, bool /*process_socket_ret*/) { + detail::close_socket(socket.sock); + socket_.sock = INVALID_SOCKET; +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + socket_.ssl = nullptr; +#endif +} + +bool Client::read_response_line(Stream &strm, Response &res) { + std::array buf; + + detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); + + if (!line_reader.getline()) { + return false; + } + + const static std::regex re("(HTTP/1\\.[01]) (\\d+?) .*\r\n"); + + std::cmatch m; + if (std::regex_match(line_reader.ptr(), m, re)) { + res.version = std::string(m[1]); + res.status = std::stoi(std::string(m[2])); + } + + return true; +} + +bool Client::send(const Request &req, Response &res) { + std::lock_guard request_mutex_guard(request_mutex_); + + { + std::lock_guard guard(socket_mutex_); + + auto is_alive = false; + if (socket_.is_open()) { + is_alive = detail::select_write(socket_.sock, 0, 0) > 0; + if (!is_alive) { + close_socket(socket_, false); + } + } + + if (!is_alive) { + if (!create_and_connect_socket(socket_)) { + return false; + } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + // TODO: refactoring + if (is_ssl()) { + auto &scli = static_cast(*this); + if (!proxy_host_.empty()) { + bool success = false; + if (!scli.connect_with_proxy(socket_, res, success)) { + return success; + } + } + + if (!scli.initialize_ssl(socket_)) { + return false; + } + } +#endif + } + } + + auto close_connection = !keep_alive_; + + auto ret = process_socket(socket_, [&](Stream &strm) { return handle_request(strm, req, res, close_connection); }); + + if (close_connection) { + stop(); + } + + return ret; +} + +bool Client::handle_request(Stream &strm, const Request &req, Response &res, bool close_connection) { + if (req.path.empty()) { + return false; + } + + bool ret; + + if (!is_ssl() && !proxy_host_.empty()) { + auto req2 = req; + req2.path = "https://" + host_and_port_ + req.path; + ret = process_request(strm, req2, res, close_connection); + } else { + ret = process_request(strm, req, res, close_connection); + } + + if (!ret) { + return false; + } + + if (300 < res.status && res.status < 400 && follow_location_) { + ret = redirect(req, res); + } + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + if ((res.status == 401 || res.status == 407) && req.authorization_count_ < 5) { + auto is_proxy = res.status == 407; + const auto &username = is_proxy ? proxy_digest_auth_username_ : digest_auth_username_; + const auto &password = is_proxy ? proxy_digest_auth_password_ : digest_auth_password_; + + if (!username.empty() && !password.empty()) { + std::map auth; + if (parse_www_authenticate(res, auth, is_proxy)) { + Request new_req = req; + new_req.authorization_count_ += 1; + auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; + new_req.headers.erase(key); + new_req.headers.insert(make_digest_authentication_header( + req, auth, new_req.authorization_count_, random_string(10), username, password, is_proxy)); + + Response new_res; + + ret = send(new_req, new_res); + if (ret) { + res = new_res; + } + } + } + } +#endif + + return ret; +} + +bool Client::redirect(const Request &req, Response &res) { + if (req.redirect_count == 0) { + return false; + } + + auto location = res.get_header_value("location"); + if (location.empty()) { + return false; + } + + const static std::regex re(R"(^(?:(https?):)?(?://([^:/?#]*)(?::(\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)"); + + std::smatch m; + if (!std::regex_match(location, m, re)) { + return false; + } + + auto scheme = is_ssl() ? "https" : "http"; + + auto next_scheme = m[1].str(); + auto next_host = m[2].str(); + auto port_str = m[3].str(); + auto next_path = m[4].str(); + + auto next_port = port_; + if (!port_str.empty()) { + next_port = std::stoi(port_str); + } else if (!next_scheme.empty()) { + next_port = next_scheme == "https" ? 443 : 80; + } + + if (next_scheme.empty()) { + next_scheme = scheme; + } + if (next_host.empty()) { + next_host = host_; + } + if (next_path.empty()) { + next_path = "/"; + } + + if (next_scheme == scheme && next_host == host_ && next_port == port_) { + return detail::redirect(*this, req, res, next_path); + } else { + if (next_scheme == "https") { +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + SSLClient cli(next_host.c_str(), next_port); + cli.copy_settings(*this); + return detail::redirect(cli, req, res, next_path); +#else + return false; +#endif + } else { + Client cli(next_host.c_str(), next_port); + cli.copy_settings(*this); + return detail::redirect(cli, req, res, next_path); + } + } +} + +bool Client::write_request(Stream &strm, const Request &req, bool close_connection) { + detail::BufferStream bstrm; + + // Request line + const auto &path = detail::encode_url(req.path); + + bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str()); + + // Additonal headers + Headers headers; + if (close_connection) { + headers.emplace("Connection", "close"); + } + + if (!req.has_header("Host")) { + if (is_ssl()) { + if (port_ == 443) { + headers.emplace("Host", host_); + } else { + headers.emplace("Host", host_and_port_); + } + } else { + if (port_ == 80) { + headers.emplace("Host", host_); + } else { + headers.emplace("Host", host_and_port_); + } + } + } + + if (!req.has_header("Accept")) { + headers.emplace("Accept", "*/*"); + } + + if (!req.has_header("User-Agent")) { + headers.emplace("User-Agent", USER_AGENT); + } + + if (req.body.empty()) { + if (req.content_provider) { + auto length = std::to_string(req.content_length); + headers.emplace("Content-Length", length); + } else { + headers.emplace("Content-Length", "0"); + } + } else { + if (!req.has_header("Content-Type")) { + headers.emplace("Content-Type", "text/plain"); + } + + if (!req.has_header("Content-Length")) { + auto length = std::to_string(req.body.size()); + headers.emplace("Content-Length", length); + } + } + + if (!basic_auth_username_.empty() && !basic_auth_password_.empty()) { + headers.insert(make_basic_authentication_header(basic_auth_username_, basic_auth_password_, false)); + } + + if (!proxy_basic_auth_username_.empty() && !proxy_basic_auth_password_.empty()) { + headers.insert(make_basic_authentication_header(proxy_basic_auth_username_, proxy_basic_auth_password_, true)); + } + + detail::write_headers(bstrm, req, headers); + + // Flush buffer + auto &data = bstrm.get_buffer(); + if (!detail::write_data(strm, data.data(), data.size())) { + return false; + } + + // Body + if (req.body.empty()) { + if (req.content_provider) { + size_t offset = 0; + size_t end_offset = req.content_length; + + bool ok = true; + + DataSink data_sink; + data_sink.write = [&](const char *d, size_t l) { + if (ok) { + if (detail::write_data(strm, d, l)) { + offset += l; + } else { + ok = false; + } + } + }; + data_sink.is_writable = [&](void) { return ok && strm.is_writable(); }; + + while (offset < end_offset) { + if (!req.content_provider(offset, end_offset - offset, data_sink)) { + return false; + } + if (!ok) { + return false; + } + } + } + } else { + return detail::write_data(strm, req.body.data(), req.body.size()); + } + + return true; +} + +std::shared_ptr Client::send_with_content_provider(const char *method, + const char *path, + const Headers &headers, + const std::string &body, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + Request req; + req.method = method; + req.headers = headers; + req.path = path; + + if (content_type) { + req.headers.emplace("Content-Type", content_type); + } + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + if (compress_) { + if (content_provider) { + size_t offset = 0; + + DataSink data_sink; + data_sink.write = [&](const char *data, size_t data_len) { + req.body.append(data, data_len); + offset += data_len; + }; + data_sink.is_writable = [&](void) { return true; }; + + while (offset < content_length) { + if (!content_provider(offset, content_length - offset, data_sink)) { + return nullptr; + } + } + } else { + req.body = body; + } + + if (!detail::compress(req.body)) { + return nullptr; + } + req.headers.emplace("Content-Encoding", "gzip"); + } else +#endif + { + if (content_provider) { + req.content_length = content_length; + req.content_provider = content_provider; + } else { + req.body = body; + } + } + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +bool Client::process_request(Stream &strm, const Request &req, Response &res, bool close_connection) { + // Send request + if (!write_request(strm, req, close_connection)) { + return false; + } + + // Receive response and headers + if (!read_response_line(strm, res) || !detail::read_headers(strm, res.headers)) { + return false; + } + + if (req.response_handler) { + if (!req.response_handler(res)) { + return false; + } + } + + // Body + if (req.method != "HEAD" && req.method != "CONNECT") { + auto out = + req.content_receiver + ? static_cast([&](const char *buf, size_t n) { return req.content_receiver(buf, n); }) + : static_cast([&](const char *buf, size_t n) { + if (res.body.size() + n > res.body.max_size()) { + return false; + } + res.body.append(buf, n); + return true; + }); + + int dummy_status; + if (!detail::read_content( + strm, res, (std::numeric_limits::max)(), dummy_status, req.progress, out, decompress_)) { + return false; + } + } + + if (res.get_header_value("Connection") == "close" || res.version == "HTTP/1.0") { + stop(); + } + + // Log + if (logger_) { + logger_(req, res); + } + + return true; +} + +bool Client::process_socket(Socket &socket, std::function callback) { + return detail::process_client_socket( + socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, callback); +} + +bool Client::is_ssl() const { + return false; +} + +std::shared_ptr Client::Get(const char *path) { + return Get(path, Headers(), Progress()); +} + +std::shared_ptr Client::Get(const char *path, Progress progress) { + return Get(path, Headers(), std::move(progress)); +} + +std::shared_ptr Client::Get(const char *path, const Headers &headers) { + return Get(path, headers, Progress()); +} + +std::shared_ptr Client::Get(const char *path, const Headers &headers, Progress progress) { + Request req; + req.method = "GET"; + req.path = path; + req.headers = headers; + req.progress = std::move(progress); + + auto res = std::make_shared(); + return send(req, *res) ? res : nullptr; +} + +std::shared_ptr Client::Get(const char *path, ContentReceiver content_receiver) { + return Get(path, Headers(), nullptr, std::move(content_receiver), Progress()); +} + +std::shared_ptr Client::Get(const char *path, ContentReceiver content_receiver, Progress progress) { + return Get(path, Headers(), nullptr, std::move(content_receiver), std::move(progress)); +} + +std::shared_ptr Client::Get(const char *path, const Headers &headers, ContentReceiver content_receiver) { + return Get(path, headers, nullptr, std::move(content_receiver), Progress()); +} + +std::shared_ptr Client::Get(const char *path, + const Headers &headers, + ContentReceiver content_receiver, + Progress progress) { + return Get(path, headers, nullptr, std::move(content_receiver), std::move(progress)); +} + +std::shared_ptr Client::Get(const char *path, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver) { + return Get(path, headers, std::move(response_handler), content_receiver, Progress()); +} + +std::shared_ptr Client::Get(const char *path, + const Headers &headers, + ResponseHandler response_handler, + ContentReceiver content_receiver, + Progress progress) { + Request req; + req.method = "GET"; + req.path = path; + req.headers = headers; + req.response_handler = std::move(response_handler); + req.content_receiver = std::move(content_receiver); + req.progress = std::move(progress); + + auto res = std::make_shared(); + return send(req, *res) ? res : nullptr; +} + +std::shared_ptr Client::Head(const char *path) { + return Head(path, Headers()); +} + +std::shared_ptr Client::Head(const char *path, const Headers &headers) { + Request req; + req.method = "HEAD"; + req.headers = headers; + req.path = path; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +std::shared_ptr Client::Post(const char *path) { + return Post(path, std::string(), nullptr); +} + +std::shared_ptr Client::Post(const char *path, const std::string &body, const char *content_type) { + return Post(path, Headers(), body, content_type); +} + +std::shared_ptr Client::Post(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + return send_with_content_provider("POST", path, headers, body, 0, nullptr, content_type); +} + +std::shared_ptr Client::Post(const char *path, const Params ¶ms) { + return Post(path, Headers(), params); +} + +std::shared_ptr Client::Post(const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return Post(path, Headers(), content_length, content_provider, content_type); +} + +std::shared_ptr Client::Post(const char *path, + const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return send_with_content_provider( + "POST", path, headers, std::string(), content_length, content_provider, content_type); +} + +std::shared_ptr Client::Post(const char *path, const Headers &headers, const Params ¶ms) { + auto query = detail::params_to_query_str(params); + return Post(path, headers, query, "application/x-www-form-urlencoded"); +} + +std::shared_ptr Client::Post(const char *path, const MultipartFormDataItems &items) { + return Post(path, Headers(), items); +} + +std::shared_ptr Client::Post(const char *path, const Headers &headers, const MultipartFormDataItems &items) { + auto boundary = detail::make_multipart_data_boundary(); + + std::string body; + + for (const auto &item : items) { + body += "--" + boundary + "\r\n"; + body += "Content-Disposition: form-data; name=\"" + item.name + "\""; + if (!item.filename.empty()) { + body += "; filename=\"" + item.filename + "\""; + } + body += "\r\n"; + if (!item.content_type.empty()) { + body += "Content-Type: " + item.content_type + "\r\n"; + } + body += "\r\n"; + body += item.content + "\r\n"; + } + + body += "--" + boundary + "--\r\n"; + + std::string content_type = "multipart/form-data; boundary=" + boundary; + return Post(path, headers, body, content_type.c_str()); +} + +std::shared_ptr Client::Put(const char *path) { + return Put(path, std::string(), nullptr); +} + +std::shared_ptr Client::Put(const char *path, const std::string &body, const char *content_type) { + return Put(path, Headers(), body, content_type); +} + +std::shared_ptr Client::Put(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + return send_with_content_provider("PUT", path, headers, body, 0, nullptr, content_type); +} + +std::shared_ptr Client::Put(const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return Put(path, Headers(), content_length, content_provider, content_type); +} + +std::shared_ptr Client::Put(const char *path, + const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return send_with_content_provider( + "PUT", path, headers, std::string(), content_length, content_provider, content_type); +} + +std::shared_ptr Client::Put(const char *path, const Params ¶ms) { + return Put(path, Headers(), params); +} + +std::shared_ptr Client::Put(const char *path, const Headers &headers, const Params ¶ms) { + auto query = detail::params_to_query_str(params); + return Put(path, headers, query, "application/x-www-form-urlencoded"); +} + +std::shared_ptr Client::Patch(const char *path, const std::string &body, const char *content_type) { + return Patch(path, Headers(), body, content_type); +} + +std::shared_ptr Client::Patch(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + return send_with_content_provider("PATCH", path, headers, body, 0, nullptr, content_type); +} + +std::shared_ptr Client::Patch(const char *path, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return Patch(path, Headers(), content_length, content_provider, content_type); +} + +std::shared_ptr Client::Patch(const char *path, + const Headers &headers, + size_t content_length, + ContentProvider content_provider, + const char *content_type) { + return send_with_content_provider( + "PATCH", path, headers, std::string(), content_length, content_provider, content_type); +} + +std::shared_ptr Client::Delete(const char *path) { + return Delete(path, Headers(), std::string(), nullptr); +} + +std::shared_ptr Client::Delete(const char *path, const std::string &body, const char *content_type) { + return Delete(path, Headers(), body, content_type); +} + +std::shared_ptr Client::Delete(const char *path, const Headers &headers) { + return Delete(path, headers, std::string(), nullptr); +} + +std::shared_ptr Client::Delete(const char *path, + const Headers &headers, + const std::string &body, + const char *content_type) { + Request req; + req.method = "DELETE"; + req.headers = headers; + req.path = path; + + if (content_type) { + req.headers.emplace("Content-Type", content_type); + } + req.body = body; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +std::shared_ptr Client::Options(const char *path) { + return Options(path, Headers()); +} + +std::shared_ptr Client::Options(const char *path, const Headers &headers) { + Request req; + req.method = "OPTIONS"; + req.path = path; + req.headers = headers; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +size_t Client::is_socket_open() const { + std::lock_guard guard(socket_mutex_); + return socket_.is_open(); +} + +void Client::stop() { + std::lock_guard guard(socket_mutex_); + if (socket_.is_open()) { + detail::shutdown_socket(socket_.sock); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + close_socket(socket_, true); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } +} + +void Client::set_timeout_sec(time_t timeout_sec) { + set_connection_timeout(timeout_sec, 0); +} + +void Client::set_connection_timeout(time_t sec, time_t usec) { + connection_timeout_sec_ = sec; + connection_timeout_usec_ = usec; +} + +void Client::set_read_timeout(time_t sec, time_t usec) { + read_timeout_sec_ = sec; + read_timeout_usec_ = usec; +} + +void Client::set_write_timeout(time_t sec, time_t usec) { + write_timeout_sec_ = sec; + write_timeout_usec_ = usec; +} + +void Client::set_basic_auth(const char *username, const char *password) { + basic_auth_username_ = username; + basic_auth_password_ = password; +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +void Client::set_digest_auth(const char *username, const char *password) { + digest_auth_username_ = username; + digest_auth_password_ = password; +} +#endif + +void Client::set_keep_alive(bool on) { + keep_alive_ = on; +} + +void Client::set_follow_location(bool on) { + follow_location_ = on; +} + +void Client::set_tcp_nodelay(bool on) { + tcp_nodelay_ = on; +} + +void Client::set_socket_options(SocketOptions socket_options) { + socket_options_ = socket_options; +} + +void Client::set_compress(bool on) { + compress_ = on; +} + +void Client::set_decompress(bool on) { + decompress_ = on; +} + +void Client::set_interface(const char *intf) { + interface_ = intf; +} + +void Client::set_proxy(const char *host, int port) { + proxy_host_ = host; + proxy_port_ = port; +} + +void Client::set_proxy_basic_auth(const char *username, const char *password) { + proxy_basic_auth_username_ = username; + proxy_basic_auth_password_ = password; +} + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +void Client::set_proxy_digest_auth(const char *username, const char *password) { + proxy_digest_auth_username_ = username; + proxy_digest_auth_password_ = password; +} +#endif + +void Client::set_logger(Logger logger) { + logger_ = std::move(logger); +} + +} // namespace httplib diff --git a/core-tests/src/_lib/process.cpp b/core-tests/src/_lib/process.cpp new file mode 100644 index 00000000000..f81b4fec296 --- /dev/null +++ b/core-tests/src/_lib/process.cpp @@ -0,0 +1,48 @@ +#include "test_process.h" + +using swoole::test::Process; +using swoole::UnixSocket; + +Process::Process(std::function fn, int pipe_type) : handler(fn) { + if (pipe_type > 0) { + auto pipe = new UnixSocket(true, SOCK_DGRAM); + + worker.pipe_master = pipe->get_socket(true); + worker.pipe_worker = pipe->get_socket(false); + + worker.pipe_object = pipe; + worker.pipe_current = worker.pipe_master; + } +} + +Process::~Process() { + if (worker.pipe_object) { + delete worker.pipe_object; + } +} + +pid_t Process::start() { + // std::system("ls /proc/self/task"); + pid_t pid = swoole_fork(0); + if (pid < 0) { + printf("[Worker] Fatal Error: fork() failed"); + exit(1); + } else if (pid == 0) { + worker.child_process = 1; + worker.pipe_current = worker.pipe_worker; + handler(this); + exit(0); + } else { + worker.pid = pid; + worker.child_process = 0; + return pid; + } +} + +ssize_t Process::write(const void *__buf, size_t __n) { + return worker.pipe_current->write(__buf, __n); +} + +ssize_t Process::read(void *__buf, size_t __nbytes) { + return worker.pipe_current->read(__buf, __nbytes); +} diff --git a/core-tests/src/_lib/redis.cpp b/core-tests/src/_lib/redis.cpp new file mode 100644 index 00000000000..b64a6794fab --- /dev/null +++ b/core-tests/src/_lib/redis.cpp @@ -0,0 +1,74 @@ +#include "redis_client.h" +#include "test_core.h" + +#include + +using namespace std; + +namespace swoole { +bool RedisClient::Connect(const string &host, int port, struct timeval timeout) { + redisContext *c = redisConnectWithTimeout(host.c_str(), port, timeout); + if (c == NULL) { + printf("Connection error: can't allocate redis context\n"); + return false; + } + + if (c->err) { + printf("Connection error: %s\n", c->errstr); + redisFree(c); + return false; + } + + ctx = c; + return true; +} + +string RedisClient::Get(const string &key) { + const char *argv[] = {"GET", key.c_str()}; + size_t argvlen[] = {strlen(argv[0]), key.length()}; + + auto reply = Request(SW_ARRAY_SIZE(argv), argv, argvlen); + if (!reply.empty() && reply->str) { + return string(reply->str, reply->len); + } else { + return ""; + } +} + +bool RedisClient::Set(const string &key, const string &value) { + const char *argv[] = {"SET", key.c_str(), value.c_str()}; + size_t argvlen[] = {strlen(argv[0]), key.length(), value.length()}; + + auto reply = Request(SW_ARRAY_SIZE(argv), argv, argvlen); + if (!reply.empty() && reply->type == REDIS_REPLY_STATUS && strncmp(reply->str, "OK", 2) == 0) { + return true; + } else { + return false; + } +} + +RedisReply RedisClient::Request(int argc, const char **argv, const size_t *argvlen) { + return redisCommandArgv(ctx, argc, argv, argvlen); +} + +RedisReply RedisClient::Request(const vector &args) { + ctx->err = 0; + + size_t n = args.size(); + const char **argv = new const char *[n]; + size_t *argvlen = new size_t[n]; + + for (size_t i = 0; i < args.size(); i++) { + argv[i] = args[i].c_str(); + argvlen[i] = args[i].length(); + } + + auto reply = Request(args.size(), (const char **) argv, (const size_t *) argvlen); + + delete[] argv; + delete[] argvlen; + + return reply; +} + +} // namespace swoole diff --git a/core-tests/src/_lib/server.cpp b/core-tests/src/_lib/server.cpp new file mode 100644 index 00000000000..dac4afe9093 --- /dev/null +++ b/core-tests/src/_lib/server.cpp @@ -0,0 +1,104 @@ +/* + +----------------------------------------------------------------------+ + | 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_server.h" +#include "swoole_memory.h" + +using namespace swoole::test; +using swoole::network::Address; + +Server::Server(std::string _host, int _port, swoole::Server::Mode _mode, int _type) + : serv(_mode), host(_host), port(_port), mode(_mode), type(_type) { + serv.worker_num = 1; + + if (mode == swoole::Server::MODE_BASE) { + serv.reactor_num = 1; + serv.worker_num = 1; + } + + serv.dispatch_mode = 2; + serv.private_data_2 = this; + + 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) { + swoole_warning("create server fail[error=%d].", errno); + exit(0); + } +} + +Server::~Server() {} + +void Server::on(std::string event, void *fn) { + if (event == "Start") { + serv.onStart = (_onStart) fn; + } else if (event == "onShutdown") { + serv.onShutdown = (_onShutdown) fn; + } else if (event == "onPipeMessage") { + serv.onPipeMessage = (_onPipeMessage) fn; + } else if (event == "onWorkerStart") { + serv.onWorkerStart = (_onWorkerStart) fn; + } else if (event == "onWorkerStop") { + serv.onWorkerStop = (_onWorkerStop) fn; + } else if (event == "onReceive") { + serv.onReceive = (_onReceive) fn; + } else if (event == "onPacket") { + serv.onPacket = (_onPacket) fn; + } else if (event == "onClose") { + serv.onClose = (_onClose) fn; + } else { + serv.onConnect = (_onConnect) fn; + } +} + +bool Server::start() { + return serv.start() == 0; +} + +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; + } + + ports.push_back(ls); + return true; +} + +int Server::send(int session_id, const void *data, uint32_t length) { + return serv.send(session_id, data, length); +} + +ssize_t Server::sendto(const Address &address, const char *__buf, size_t __n, int server_socket_fd) { + network::Socket *server_socket; + if (server_socket_fd < 0) { + server_socket = serv.udp_socket_ipv6 ? serv.udp_socket_ipv6 : serv.udp_socket_ipv4; + } else { + server_socket = serv.get_server_socket(server_socket_fd); + } + return server_socket->sendto(address, __buf, __n, 0); +} + +int Server::close(int session_id, int reset) { + return serv.close(session_id, reset); +} diff --git a/core-tests/src/client/client.cpp b/core-tests/src/client/client.cpp deleted file mode 100644 index 8f39277296a..00000000000 --- a/core-tests/src/client/client.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include "tests.h" -#include "process.h" -#include "wrapper/server.h" - -#define GREETER "Hello Swoole" -#define GREETER_SIZE sizeof(GREETER) - -using swoole::test::process; -using swoole::test::server; - -TEST(client, tcp) -{ - int ret; - swClient cli; - char buf[128]; - - pid_t pid; - - process proc([](process *proc) - { - on_receive_lambda_type receive_fn = [](ON_RECEIVE_PARAMS) - { - char *data_ptr = NULL; - size_t data_len = SERVER_THIS->get_packet(req, (char **) &data_ptr); - - SERVER_THIS->send(req->info.fd, data_ptr, data_len); - }; - - server serv(TEST_HOST, TEST_PORT, SW_MODE_BASE, SW_SOCK_TCP); - serv.on("onReceive", (void *) receive_fn); - serv.start(); - }); - - pid = proc.start(); - - sleep(1); // wait for the test server to start - - ret = swClient_create(&cli, SW_SOCK_TCP, SW_SOCK_SYNC); - ASSERT_EQ(ret, 0); - ret = cli.connect(&cli, TEST_HOST, TEST_PORT, -1, 0); - ASSERT_EQ(ret, 0); - ret = cli.send(&cli, SW_STRS(GREETER), 0); - ASSERT_GT(ret, 0); - ret = cli.recv(&cli, buf, 128, 0); - ASSERT_EQ(ret, GREETER_SIZE); - ASSERT_STREQ(GREETER, buf); - cli.close(&cli); - kill(pid, SIGKILL); -} - -TEST(client, udp) -{ - int ret; - swClient cli; - char buf[128]; - - pid_t pid; - - process proc([](process *proc) - { - on_packet_lambda_type packet_fn = [](ON_PACKET_PARAMS) - { - swDgramPacket *packet = nullptr; - SERVER_THIS->get_packet(req, (char **) &packet); - - SERVER_THIS->sendto(&packet->socket_addr, packet->data, packet->length, req->info.server_fd); - }; - - server serv(TEST_HOST, TEST_PORT, SW_MODE_BASE, SW_SOCK_UDP); - serv.on("onPacket", (void *) packet_fn); - serv.start(); - }); - - pid = proc.start(); - - sleep(1); // wait for the test server to start - - ret = swClient_create(&cli, SW_SOCK_UDP, SW_SOCK_SYNC); - ASSERT_EQ(ret, 0); - ret = cli.connect(&cli, TEST_HOST, TEST_PORT, -1, 0); - ASSERT_EQ(ret, 0); - ret = cli.send(&cli, SW_STRS(GREETER), 0); - ASSERT_GT(ret, 0); - ret = cli.recv(&cli, buf, 128, 0); - ASSERT_EQ(ret, GREETER_SIZE); - ASSERT_STREQ(GREETER, buf); - cli.close(&cli); - kill(pid, SIGKILL); -} diff --git a/core-tests/src/core/base.cpp b/core-tests/src/core/base.cpp new file mode 100644 index 00000000000..671a0aa7235 --- /dev/null +++ b/core-tests/src/core/base.cpp @@ -0,0 +1,311 @@ +/* + +----------------------------------------------------------------------+ + | 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_file.h" +#include "swoole_util.h" +#include "swoole.h" +#include "swoole_config.h" + +using namespace swoole; +using namespace std; + +static const string test_data("hello world\n"); + +TEST(base, datahead_dump) { + swDataHead data = {}; + 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); +} + +TEST(base, dec2hex) { + auto result = swoole_dec2hex(2684326179, 16); + ASSERT_STREQ(result, "9fff9123"); + sw_free(result); +} + +TEST(base, hex2dec) { + size_t n_parsed; + ASSERT_EQ(swoole_hex2dec("9fff9123", &n_parsed), 2684326179); + ASSERT_EQ(n_parsed, 8); + ASSERT_EQ(swoole_hex2dec("0x9fff9123", &n_parsed), 2684326179); + ASSERT_EQ(n_parsed, 10); + ASSERT_EQ(swoole_hex2dec("f", &n_parsed), 15); + ASSERT_EQ(n_parsed, 1); +} + +TEST(base, random_string) { + char buf[1024] = {}; + swoole_random_string(buf, sizeof(buf) - 1); + ASSERT_EQ(strlen(buf), sizeof(buf) - 1); +} + +TEST(base, file_put_contents) { + char buf[65536]; + swoole_random_string(buf, sizeof(buf) - 1); + ASSERT_TRUE(file_put_contents(TEST_TMP_FILE, buf, sizeof(buf))); + auto result = file_get_contents(TEST_TMP_FILE); + ASSERT_STREQ(buf, result->value()); +} + +TEST(base, file_get_size) { + File f(TEST_TMP_FILE, File::WRITE | File::CREATE); + char buf[65536]; + swoole_random_string(buf, sizeof(buf) - 1); + + ASSERT_TRUE(f.ready()); + f.truncate(0); + f.set_offset(0); + f.write(buf, sizeof(buf) - 1); + f.close(); + + ASSERT_EQ(file_get_size(TEST_TMP_FILE), sizeof(buf) - 1); +} + +TEST(base, version_compare) { + ASSERT_EQ(swoole_version_compare("1.2.1", "1.2.0"), 1); + ASSERT_EQ(swoole_version_compare("1.2.3", "1.3.0"), -1); + ASSERT_EQ(swoole_version_compare("1.2.3", "1.2.9"), -1); + ASSERT_EQ(swoole_version_compare("1.2.0", "1.2.0"), 0); +} + +TEST(base, common_divisor) { + ASSERT_EQ(swoole_common_divisor(16, 12), 4); + ASSERT_EQ(swoole_common_divisor(6, 15), 3); + ASSERT_EQ(swoole_common_divisor(32, 16), 16); +} + +TEST(base, common_multiple) { + ASSERT_EQ(swoole_common_multiple(16, 12), 48); + ASSERT_EQ(swoole_common_multiple(6, 15), 30); + ASSERT_EQ(swoole_common_multiple(32, 16), 32); +} + +TEST(base, shell_exec) { + pid_t pid; + string str = "md5sum " + test::get_jpg_file(); + int _pipe = swoole_shell_exec(str.c_str(), &pid, 0); + ASSERT_GT(_pipe, 0); + ASSERT_GT(pid, 0); + char buf[1024] = {}; + ssize_t n = read(_pipe, buf, sizeof(buf) - 1); + 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) { + auto file = test::get_jpg_file(); + ssize_t file_size = file_get_size(file); + ASSERT_GT(file_size, 0); + auto fp = fopen(file.c_str(), "r+"); + ASSERT_TRUE(fp); + ASSERT_EQ(file_get_size(fp), file_size); + fclose(fp); +} + +TEST(base, eventdata_pack) { + EventData ed1{}; + + ASSERT_TRUE(Server::task_pack(&ed1, test_data.c_str(), test_data.length())); + ASSERT_EQ(string(ed1.data, ed1.info.len), test_data); + + EventData ed2{}; + ASSERT_EQ(swoole_random_bytes(sw_tg_buffer()->str, SW_BUFFER_SIZE_BIG), SW_BUFFER_SIZE_BIG); + ASSERT_TRUE(Server::task_pack(&ed2, sw_tg_buffer()->str, SW_BUFFER_SIZE_BIG)); + + String _buffer(SW_BUFFER_SIZE_BIG); + PacketPtr packet; + ASSERT_TRUE(Server::task_unpack(&ed2, &_buffer, &packet)); + ASSERT_EQ(memcmp(sw_tg_buffer()->str, _buffer.str, SW_BUFFER_SIZE_BIG), 0); +} + +TEST(base, stack_defer_fn) { + int count = 0; + + ON_SCOPE_EXIT { + count++; + ASSERT_EQ(count, 2); + }; + + ON_SCOPE_EXIT { + count++; + ASSERT_EQ(count, 1); + }; +} + +TEST(base, string_format) { + char *data = swoole_string_format(128, "hello %d world, %s is best.", 2020, "swoole"); + ASSERT_STREQ(data, "hello 2020 world, swoole is best."); + sw_free(data); +} + +TEST(base, dirname) { + ASSERT_EQ(dirname("/hello/world/index.html.abc"), "/hello/world"); + ASSERT_EQ(dirname("/hello/world"), "/hello"); + ASSERT_EQ(dirname("/root"), "/"); + ASSERT_EQ(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(); + ASSERT_TRUE(fp.ready()); + + char buf[128]; + swoole_random_string(buf, sizeof(buf) - 2); + buf[sizeof(buf) - 2] = '\n'; + + fp.write(buf, sizeof(buf) - 1); + fp.close(); + + ASSERT_EQ(swoole::dirname(fp.get_path()), tmpdir); + ASSERT_STREQ(swoole::file_get_contents(fp.get_path())->str, buf); + + unlink(fp.get_path().c_str()); + rmdir(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, hook) { + int count = 0; + 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); +} + +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"}; + ASSERT_EQ("index.php", swoole::intersection(vec1, vec2)); + + std::set vec3{"a", "zh中", "、r\n"}; + ASSERT_EQ("", swoole::intersection(vec1, vec3)); +} + +TEST(base, itoa) { + char buf[128]; + long value = 123456987; + int n = swoole_itoa(buf, value); + + 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 new file mode 100644 index 00000000000..ec756f092e4 --- /dev/null +++ b/core-tests/src/core/channel.cpp @@ -0,0 +1,123 @@ +/* + +----------------------------------------------------------------------+ + | 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_channel.h" + +using namespace std; +using namespace swoole; + +const int N = 10000000; + +TEST(channel, push) { + auto *c = Channel::make(128 * 1024, 8192, SW_CHAN_LOCK | SW_CHAN_NOTIFY); + map m; + + size_t bytes = 0; + int index = 0; + + while (bytes < N) { + char buf[8000]; + int n = swoole_random_bytes(buf, (rand() % (sizeof(buf) / 2)) + (sizeof(buf) / 2)); + if (n <= 0) { + swoole_trace("no enough data, n=%d, errno=%d\n", n, errno); + continue; + } + m[index++] = string(buf, n); + bytes += n; + } + + swoole_trace("size=%lu", m.size()); + + thread t1([&]() { + auto next = m.find(0); + int index = 1; + size_t bytes = 0; + + while (bytes < N) { + if (c->push(next->second.c_str(), next->second.length()) == SW_OK) { + swoole_trace("[PUSH] index=%d, size=%lu", index, next->second.length()); + bytes += next->second.length(); + next = m.find(index++); + if (next == m.end()) { + break; + } + } else { + usleep(10); + } + } + }); + + thread t2([&]() { + char buf[8000]; + size_t bytes = 0; + int index = 0; + while (bytes < N) { + int retval = c->pop(buf, sizeof(buf)); + if (retval > 0) { + swoole_trace("[POP] index=%d, size=%d", index, retval); + string &_data = m[index++]; + bytes += retval; + ASSERT_EQ(_data, string(buf, retval)); + } else { + usleep(10); + } + } + }); + + t1.join(); + t2.join(); + + 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 new file mode 100644 index 00000000000..7d7f1cd5ebb --- /dev/null +++ b/core-tests/src/core/hash.cpp @@ -0,0 +1,39 @@ +/* + +----------------------------------------------------------------------+ + | 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_hash.h" + +static const char *data = "hello world, PHP the best."; + +TEST(hash, crc32) { + ASSERT_EQ(swoole_crc32(data, strlen(data)), 2962796788); +} + +TEST(hash, php) { + ASSERT_GT(swoole_hash_php(data, strlen(data)), 0); +} + +TEST(hash, jenkins) { + ASSERT_GT(swoole_hash_jenkins(data, strlen(data)), 0); +} + +TEST(hash, austin) { + ASSERT_GT(swoole_hash_austin(data, strlen(data)), 0); +} diff --git a/core-tests/src/core/heap.cpp b/core-tests/src/core/heap.cpp new file mode 100644 index 00000000000..226c9838031 --- /dev/null +++ b/core-tests/src/core/heap.cpp @@ -0,0 +1,39 @@ +#include "test_core.h" +#include "swoole_heap.h" +#include + +typedef struct node_t { + int pri; + int val; +} node_t; + +#define SIZE 100 + +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 * 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 new file mode 100644 index 00000000000..af76a7483cb --- /dev/null +++ b/core-tests/src/core/log.cpp @@ -0,0 +1,215 @@ +#include "test_core.h" +#include "swoole_file.h" +#include +#include + +using namespace swoole; + +const char *file = "/tmp/swoole_log_test.log"; + +TEST(log, level) { + std::vector processTypes = {SW_PROCESS_MASTER, SW_PROCESS_MANAGER, SW_PROCESS_WORKER, SW_PROCESS_TASKWORKER}; + + 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); + + 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")); + + 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"))); + + SwooleG.process_type = originType; + } +} + +TEST(log, date_format) { + sw_logger()->reset(); + sw_logger()->set_date_format("day %d of %B in the year %Y. Time: %I:%S %p"); + sw_logger()->open(file); + + sw_logger()->put(SW_LOG_WARNING, SW_STRL("hello world")); + auto content = file_get_contents(file); + + sw_logger()->close(); + unlink(file); + + int data[16]; + char *month = nullptr; + char *am = nullptr; + + int n = std::sscanf(content->value(), + "[day %d of %s in the year %d. Time: %d:%d %s @%d.%d]\tWARNING\thello world", + data, + month, + data + 1, + data + 2, + data + 3, + am, + data + 4, + data + 5); + + ASSERT_TRUE(n); +} + +TEST(log, date_format_long_string) { + sw_logger()->reset(); + sw_logger()->set_level(SW_LOG_ERROR); + std::unique_ptr content(new String(256)); + auto str = content.get(); + + str->repeat("x", 1, 120); + str->append(SW_STRL("day %d of %B in the year %Y. Time: %I:%S %p")); + + bool retval = sw_logger()->set_date_format(str->str); + + ASSERT_FALSE(retval); + ASSERT_EQ(swoole_get_last_error(), SW_ERROR_INVALID_PARAMS); +} + +TEST(log, date_with_microseconds) { + sw_logger()->reset(); + sw_logger()->set_date_with_microseconds(true); + sw_logger()->open(file); + + sw_logger()->put(SW_LOG_WARNING, SW_STRL("hello world")); + auto content = file_get_contents(file); + + sw_logger()->close(); + unlink(file); + + std::regex e("\\[\\S+\\s\\d{2}:\\d{2}:\\d{2}\\<\\.(\\d+)\\>\\s@\\d+\\.\\d+\\]\tWARNING\thello world"); + ASSERT_TRUE(std::regex_search(content->value(), e)); +} + +TEST(log, rotation) { + 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) { + 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); + ASSERT_NE(content.get(), nullptr); + + sw_logger()->close(); + ASSERT_TRUE(sw_logger()->redirect_stdout_and_stderr(0)); + unlink(sw_logger()->get_real_file()); + + 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 new file mode 100644 index 00000000000..6e285616e34 --- /dev/null +++ b/core-tests/src/core/string.cpp @@ -0,0 +1,273 @@ +#include "test_core.h" +#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]; + strcpy(buf, "hello world "); + swoole::rtrim(buf, strlen(buf)); + ASSERT_EQ(strcmp("hello world", buf), 0); + ASSERT_NE(strcmp("hello world ", buf), 0); + + 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) { + { + string haystack = "hello world"; + string needle = " "; + int pos; + + pos = swoole_strnpos(haystack.c_str(), haystack.length(), needle.c_str(), needle.length()); + ASSERT_EQ(pos, 5); + } + { + string haystack = "hello world"; + string needle = "*"; + int pos; + + pos = swoole_strnpos(haystack.c_str(), haystack.length(), needle.c_str(), needle.length()); + ASSERT_EQ(-1, pos); + } +} + +TEST(string, strnstr) { + { + string haystack = "hello world"; + string needle = " "; + 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 = 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); + } +} + +TEST(string, explode) { + string haystack = "hello world"; + string needle = " "; + + String str; + str.str = (char *) haystack.c_str(); + str.length = haystack.length(); + + int value_1 = 0; + + const char *explode_str = nullptr; + size_t explode_length = 0; + + str.split(needle.c_str(), needle.length(), [&](const char *data, size_t length) -> int { + explode_str = data; + explode_length = length; + value_1 = 5; + return false; + }); + + ASSERT_EQ(haystack, explode_str); + ASSERT_EQ(6, explode_length); + ASSERT_EQ(5, value_1); +} + +TEST(string, explode_2) { + string haystack = "hello,world,swoole,php,last"; + string needle = ","; + + String str; + str.str = (char *) haystack.c_str(); + str.length = haystack.length(); + + int count = 0; + vector list; + + size_t n = str.split(needle.c_str(), needle.length(), [&](const char *data, size_t length) -> int { + list.push_back(string(data, length - 1)); + count++; + return true; + }); + + ASSERT_EQ(list[0], string("hello")); + ASSERT_EQ(list[1], string("world")); + ASSERT_EQ(list[2], string("swoole")); + ASSERT_EQ(list[3], string("php")); + ASSERT_EQ("last", string(str.str + n, str.length - n)); + ASSERT_EQ(4, count); + ASSERT_EQ(list.size(), count); +} + +static const int init_size = 1024; +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); + + char *str_1 = str->str; + + const int len_1 = 11; + str->append(test_data.c_str(), test_data.length()); + str->offset = len_1; + char *str_2 = str->pop(init_size); + + EXPECT_EQ(str_1, str_2); + EXPECT_EQ(string("hello,world"), string(str_2, len_1)); + EXPECT_EQ(string(",swoole,php,last"), string(str->str, str->length)); + EXPECT_EQ(init_size, str->size); + + str->allocator->free(str_1); +} + +TEST(string, pop_2) { + auto str = swoole::make_string(init_size); + std::unique_ptr s(str); + + char *str_1 = str->str; + + const int len_1 = test_data.length(); + str->append(test_data.c_str(), test_data.length()); + str->offset = len_1; + char *str_2 = str->pop(init_size); + + EXPECT_EQ(str_1, str_2); + EXPECT_EQ(test_data, string(str_2, len_1)); + EXPECT_EQ(str->length, 0); + EXPECT_EQ(init_size, str->size); + + str->allocator->free(str_1); +} + +TEST(string, reduce_1) { + auto str = swoole::make_string(init_size); + std::unique_ptr s(str); + + const int len_1 = 11; + str->append(test_data.c_str(), test_data.length()); + str->offset = len_1; + + str->reduce(str->offset); + + EXPECT_EQ(string(",swoole,php,last"), string(str->str, str->length)); +} + +TEST(string, reduce_2) { + auto str = swoole::make_string(init_size); + std::unique_ptr s(str); + + str->append(test_data.c_str(), test_data.length()); + str->offset = str->length; + + str->reduce(str->offset); + + EXPECT_EQ(str->length, 0); +} + +TEST(string, reduce_3) { + auto str = swoole::make_string(init_size); + std::unique_ptr s(str); + + str->append(test_data.c_str(), test_data.length()); + str->offset = 0; + + str->reduce(str->offset); + + EXPECT_EQ(str->length, test_data.length()); +} + +const auto FORMAT_INT = 999999999999999; +const auto FORMAT_STR = "hello world"; + +TEST(string, format_1) { + String str1(1024); + str1.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"); + + 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()); + + 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) { + const char *str1 = "hello: swoole & world"; + 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 new file mode 100644 index 00000000000..80107f62d24 --- /dev/null +++ b/core-tests/src/core/time.cpp @@ -0,0 +1,28 @@ +#include "test_core.h" +#include "swoole_util.h" + +TEST(time, get_ms) { + const int us = 3000; + long ms1 = swoole::time(); + usleep(us); + long ms2 = swoole::time(); + EXPECT_GE(ms2 - ms1, us / 1000); +} + +TEST(time, get_ms_steady) { + const int us = 3000; + long ms1 = swoole::time(true); + usleep(us); + long ms2 = swoole::time(true); + EXPECT_GE(ms2 - ms1, us / 1000); +} + +TEST(time, get_seconds) { + long sec1 = swoole::time(); + 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/core/wheel_timer.cpp b/core-tests/src/core/wheel_timer.cpp new file mode 100644 index 00000000000..1fa688a02e6 --- /dev/null +++ b/core-tests/src/core/wheel_timer.cpp @@ -0,0 +1,31 @@ +#include "test_core.h" +#include "swoole_wheel_timer.h" + +using namespace swoole; +using namespace std; + + +TEST(wheel_timer, next) { + constexpr int size = 5; + WheelTimer wt(size); + + auto t1 = wt.add([&](WheelTimerNode *){ + ASSERT_EQ(wt.get_round(), 6); + }); + ASSERT_EQ(t1->index_, size - 1); + wt.next(); + + auto t2 = wt.add([&](WheelTimerNode *){ + ASSERT_EQ(wt.get_round(), 9); + }); + ASSERT_EQ(t2->index_, 0); + wt.update(t1); + + for (int i = 0; i < 10; i++) { + if (i < 4) { + wt.update(t2); + } + wt.next(); + } +} + diff --git a/core-tests/src/coroutine/async.cpp b/core-tests/src/coroutine/async.cpp index 495bdec904a..4de8af590bf 100644 --- a/core-tests/src/coroutine/async.cpp +++ b/core-tests/src/coroutine/async.cpp @@ -1,39 +1,38 @@ -#include "tests.h" +#include "test_coroutine.h" +#include "swoole_async.h" #include #include using namespace std; -using namespace swoole::coroutine; +using swoole::AsyncEvent; using swoole::test::coroutine; const int magic_code = 0x7009501; -TEST(coroutine_async, usleep) -{ - coroutine::test([](void *arg) - { - swAio_event ev; - bool retval = async([](swAio_event *event) { - usleep(1000); - event->ret = magic_code; - }, ev); +TEST(coroutine_async, usleep) { + coroutine::run([](void *arg) { + AsyncEvent ev = {}; + bool retval = swoole::coroutine::async( + [](AsyncEvent *event) { + usleep(1000); + event->retval = magic_code; + }, + ev); ASSERT_EQ(retval, true); - ASSERT_EQ(ev.ret, magic_code); + ASSERT_EQ(ev.retval, magic_code); }); } -TEST(coroutine_async, gethostbyname) -{ - coroutine::test([](void *arg) - { +TEST(coroutine_async, gethostbyname) { + coroutine::run([](void *arg) { string domain("www.baidu.com"), ip; - bool retval = async([&]() { + bool retval = swoole::coroutine::async([&]() { char buf[128]; - if (swoole_gethostbyname(AF_INET, domain.c_str(), buf) == SW_OK) { + if (swoole::network::gethostbyname(AF_INET, domain.c_str(), buf) == SW_OK) { char addr[128]; - inet_ntop( AF_INET, buf, addr, sizeof(addr)); + inet_ntop(AF_INET, buf, addr, sizeof(addr)); ip = addr; } else { ip = "unknown"; @@ -42,14 +41,21 @@ TEST(coroutine_async, gethostbyname) ASSERT_EQ(retval, true); match_results result; - try - { + try { const regex pattern("(\\d{1,3}).(\\d{1,3}).(\\d{1,3}).(\\d{1,3})"); ASSERT_EQ(regex_match(ip, result, pattern), true); - } - catch(std::exception &ex) - { + } catch (std::exception &ex) { std::cerr << "regex error: gcc version must be 4.9+" << std::endl; } }); } + +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 85e27697812..442bb477d6f 100644 --- a/core-tests/src/coroutine/base.cpp +++ b/core-tests/src/coroutine/base.cpp @@ -1,103 +1,118 @@ -#include "tests.h" +#include "test_coroutine.h" using namespace swoole; +using swoole::coroutine::System; -TEST(coroutine_base, create) -{ +TEST(coroutine_base, create) { long _cid; - long cid = Coroutine::create([](void *arg) - { - *(long *) arg = Coroutine::get_current_cid(); - }, &_cid); + long cid = Coroutine::create([](void *arg) { *(long *) arg = Coroutine::get_current_cid(); }, &_cid); ASSERT_GT(cid, 0); ASSERT_EQ(cid, _cid); } -TEST(coroutine_base, get_current) -{ +TEST(coroutine_base, get_current) { long _cid; - long cid = Coroutine::create([](void *arg) - { - auto co = Coroutine::get_current(); - *(long *) arg = co->get_cid(); - }, &_cid); + long cid = Coroutine::create( + [](void *arg) { + auto co = Coroutine::get_current(); + *(long *) arg = co->get_cid(); + }, + &_cid); ASSERT_GT(cid, 0); ASSERT_EQ(cid, _cid); } -TEST(coroutine_base, yield_resume) -{ +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->yield(); - *(long *) arg = Coroutine::get_current_cid(); - }, &_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(); + }, + &_cid); ASSERT_GT(cid, 0); Coroutine::get_by_cid(cid)->resume(); + Coroutine::set_on_close(nullptr); ASSERT_EQ(cid, _cid); } -TEST(coroutine_base, get_cid) -{ - Coroutine::create([](void *arg) - { +TEST(coroutine_base, get_cid) { + Coroutine::create([](void *arg) { auto co = Coroutine::get_current(); long cid = co->get_cid(); - + ASSERT_GT(cid, 0); }); } -TEST(coroutine_base, get_origin) -{ - Coroutine::create([](void *arg) - { +TEST(coroutine_base, get_origin) { + Coroutine::create([](void *arg) { auto *co = Coroutine::get_current(); - Coroutine::create([](void *arg) - { - auto current_co = Coroutine::get_current(); - auto origin_co = current_co->get_origin(); + Coroutine::create( + [](void *arg) { + auto current_co = Coroutine::get_current(); + auto origin_co = current_co->get_origin(); - ASSERT_EQ(arg, origin_co); - }, co); + ASSERT_EQ(arg, origin_co); + }, + co); }); } -TEST(coroutine_base, get_origin_cid) -{ - Coroutine::create([](void *arg) - { +TEST(coroutine_base, get_origin_cid) { + Coroutine::create([](void *arg) { auto _cid = Coroutine::get_current_cid(); - Coroutine::create([](void *arg) - { - auto origin_cid = Coroutine::get_current()->get_origin_cid(); + Coroutine::create( + [](void *arg) { + auto origin_cid = Coroutine::get_current()->get_origin_cid(); - ASSERT_EQ(*(long *) arg, origin_cid); - }, &_cid); + ASSERT_EQ(*(long *) arg, origin_cid); + }, + &_cid); }); } -TEST(coroutine_base, is_end) -{ - Coroutine::create([](void *_arg) - { +TEST(coroutine_base, is_end) { + Coroutine::create([](void *_arg) { auto co = Coroutine::get_current(); ASSERT_FALSE(co->is_end()); }); } -TEST(coroutine_base, set_task) -{ - Coroutine::create([](void *_arg) - { +TEST(coroutine_base, set_task) { + Coroutine::create([](void *_arg) { int task; auto co = Coroutine::get_current(); co->set_task(&task); @@ -106,10 +121,8 @@ TEST(coroutine_base, set_task) }); } -TEST(coroutine_base, get_current_task) -{ - Coroutine::create([](void *_arg) - { +TEST(coroutine_base, get_current_task) { + Coroutine::create([](void *_arg) { int task; auto co = Coroutine::get_current(); co->set_task(&task); @@ -118,30 +131,25 @@ TEST(coroutine_base, get_current_task) }); } -TEST(coroutine_base, get_current_cid) -{ - Coroutine::create([](void *_arg) - { +TEST(coroutine_base, get_current_cid) { + Coroutine::create([](void *_arg) { 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()); }); } -TEST(coroutine_base, get_by_cid) -{ - Coroutine::create([](void *_arg) - { +TEST(coroutine_base, get_by_cid) { + Coroutine::create([](void *_arg) { auto actual = Coroutine::get_current(); auto cid = actual->get_cid(); ASSERT_EQ(actual, Coroutine::get_by_cid(cid)); }); } -TEST(coroutine_base, get_task_by_cid) -{ - Coroutine::create([](void *_arg) - { +TEST(coroutine_base, get_task_by_cid) { + Coroutine::create([](void *_arg) { int task; auto co = Coroutine::get_current(); co->set_task(&task); @@ -150,41 +158,79 @@ TEST(coroutine_base, get_task_by_cid) }); } -TEST(coroutine_base, get_last_cid) -{ - Coroutine::create([](void *_arg) - { - }); - Coroutine::create([](void *_arg) - { - }); - long cid = Coroutine::create([](void *_arg) - { - }); +TEST(coroutine_base, get_last_cid) { + Coroutine::create([](void *_arg) {}); + Coroutine::create([](void *_arg) {}); + long cid = Coroutine::create([](void *_arg) {}); ASSERT_EQ(Coroutine::get_last_cid(), cid); } -TEST(coroutine_base, count) -{ - Coroutine::create([](void *_arg) - { +TEST(coroutine_base, count) { + Coroutine::create([](void *_arg) { ASSERT_EQ(Coroutine::count(), 1); - Coroutine::create([](void *_arg) - { - ASSERT_EQ(Coroutine::count(), 2); - }); + Coroutine::create([](void *_arg) { ASSERT_EQ(Coroutine::count(), 2); }); }); ASSERT_EQ(Coroutine::count(), 0); } -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_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 a1efd609b0d..cb24673e5c0 100644 --- a/core-tests/src/coroutine/channel.cpp +++ b/core-tests/src/coroutine/channel.cpp @@ -1,16 +1,12 @@ -#include "tests.h" -#include "swoole/coroutine_channel.h" +#include "test_coroutine.h" using swoole::coroutine::Channel; -using namespace swoole; using namespace std; using namespace swoole::test; -TEST(coroutine_channel, push_pop) -{ - test::coroutine::test([](void *arg) - { +TEST(coroutine_channel, push_pop) { + coroutine::run([](void *arg) { Channel chan(1); int i = 1; bool ret; @@ -21,63 +17,59 @@ TEST(coroutine_channel, push_pop) }); } -TEST(coroutine_channel, push_yield) -{ +TEST(coroutine_channel, push_yield) { Channel chan(1); - test::coroutine::test({ - make_pair([](void *arg) - { - auto chan = (Channel *) arg; - int i = 1; - bool ret; - - ret = chan->push(new int(i)); - ASSERT_TRUE(ret); - ret = chan->push(new int(i)); - ASSERT_TRUE(ret); - }, &chan), - - make_pair([](void *arg) - { - auto chan = (Channel *) arg; - ASSERT_EQ(*(int *) chan->pop(), 1); - ASSERT_EQ(*(int *) chan->pop(), 1); - }, &chan) - }); + coroutine::run({make_pair( + [](void *arg) { + auto chan = (Channel *) arg; + int i = 1; + bool ret; + + ret = chan->push(new int(i)); + ASSERT_TRUE(ret); + ret = chan->push(new int(i)); + ASSERT_TRUE(ret); + }, + &chan), + + make_pair( + [](void *arg) { + auto chan = (Channel *) arg; + ASSERT_EQ(*(int *) chan->pop(), 1); + ASSERT_EQ(*(int *) chan->pop(), 1); + }, + &chan)}); } -TEST(coroutine_channel, pop_yield) -{ +TEST(coroutine_channel, pop_yield) { Channel chan(1); - test::coroutine::test({ - make_pair([](void *arg) - { - auto chan = (Channel *) arg; - - ASSERT_EQ(*(int *) chan->pop(), 1); - ASSERT_EQ(*(int *) chan->pop(), 1); - }, &chan), - - make_pair([](void *arg) - { - auto chan = (Channel *) arg; - int i = 1; - bool ret; - - ret = chan->push(&i); - ASSERT_TRUE(ret); - ret = chan->push(&i); - ASSERT_TRUE(ret); - }, &chan) - }); + coroutine::run({make_pair( + [](void *arg) { + auto chan = (Channel *) arg; + + ASSERT_EQ(*(int *) chan->pop(), 1); + ASSERT_EQ(*(int *) chan->pop(), 1); + }, + &chan), + + make_pair( + [](void *arg) { + auto chan = (Channel *) arg; + int i = 1; + bool ret; + + ret = chan->push(&i); + ASSERT_TRUE(ret); + ret = chan->push(&i); + ASSERT_TRUE(ret); + }, + &chan)}); } -TEST(coroutine_channel, push_timeout) -{ - test::coroutine::test([](void *arg) - { +TEST(coroutine_channel, push_timeout) { + coroutine::run([](void *arg) { Channel chan(1); bool ret; @@ -88,10 +80,8 @@ TEST(coroutine_channel, push_timeout) }); } -TEST(coroutine_channel, pop_timeout) -{ - test::coroutine::test([](void *arg) - { +TEST(coroutine_channel, pop_timeout) { + coroutine::run([](void *arg) { Channel chan(1); void *ret; @@ -99,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 82d1dc95530..042999d61af 100644 --- a/core-tests/src/coroutine/gethostbyname.cpp +++ b/core-tests/src/coroutine/gethostbyname.cpp @@ -1,42 +1,46 @@ -#include "tests.h" -#include "swoole/coroutine_system.h" +#include "test_coroutine.h" using swoole::Coroutine; +using swoole::Timer; using swoole::coroutine::System; using swoole::test::coroutine; -TEST(coroutine_gethostbyname, resolve_cache) -{ - coroutine::test([](void *arg) - { +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 = swTimer_get_absolute_msec(); - for (int i = 0; i < 100; ++i) - { - std::string addr2 = System::gethostbyname("www.baidu.com", AF_INET); + int64_t with_cache = Timer::get_absolute_msec(); + for (int i = 0; i < 100; ++i) { + std::string addr2 = System::gethostbyname(domain_baidu, AF_INET); ASSERT_EQ(addr1, addr2); } - with_cache = swTimer_get_absolute_msec() - with_cache; + with_cache = Timer::get_absolute_msec() - with_cache; System::set_dns_cache_capacity(0); - int64_t without_cache = swTimer_get_absolute_msec(); - for (int i = 0; i < 5; ++i) - { - std::string addr2 = System::gethostbyname("www.baidu.com", AF_INET); + int64_t without_cache = Timer::get_absolute_msec(); + for (int i = 0; i < 5; ++i) { + std::string addr2 = System::gethostbyname(domain_baidu, AF_INET); ASSERT_NE(addr2, ""); } - without_cache = swTimer_get_absolute_msec() - without_cache; + without_cache = Timer::get_absolute_msec() - without_cache; ASSERT_GT(without_cache, with_cache); }); } -TEST(coroutine_gethostbyname, resolve_cache_inet4_and_inet6) -{ - coroutine::test([](void *arg) - { +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); std::string addr1 = System::gethostbyname("ipv6.sjtu.edu.cn", AF_INET); @@ -47,10 +51,9 @@ TEST(coroutine_gethostbyname, resolve_cache_inet4_and_inet6) ASSERT_EQ(addr1.find(":"), addr1.npos); ASSERT_NE(addr2.find(":"), addr2.npos); - int64_t start = swTimer_get_absolute_msec(); + int64_t start = Timer::get_absolute_msec(); - for (int i = 0; i < 100; ++i) - { + for (int i = 0; i < 100; ++i) { std::string addr3 = System::gethostbyname("ipv6.sjtu.edu.cn", AF_INET); std::string addr4 = System::gethostbyname("ipv6.sjtu.edu.cn", AF_INET6); @@ -58,6 +61,26 @@ TEST(coroutine_gethostbyname, resolve_cache_inet4_and_inet6) ASSERT_EQ(addr2, addr4); } - ASSERT_LT(swTimer_get_absolute_msec() - start, 5); + 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 new file mode 100644 index 00000000000..1aee8368af9 --- /dev/null +++ b/core-tests/src/coroutine/hook.cpp @@ -0,0 +1,611 @@ +/* + +----------------------------------------------------------------------+ + | 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_coroutine.h" +#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; + +const char *host_1 = "www.baidu.com"; +const char *host_2 = "www.xxxxxxxxxxxxxxxxxxxxx00000xxxxxxxxx----not_found.com"; +static const char *test_file = "/tmp/swoole-core-test"; + +TEST(coroutine_hook, file) { + coroutine::run([](void *arg) { + char buf[8192]; + size_t n_buf = sizeof(buf); + ASSERT_EQ(swoole_random_bytes(buf, n_buf), n_buf); + + int fd = swoole_coroutine_open(test_file, O_WRONLY | O_TRUNC | O_CREAT, 0666); + ASSERT_EQ(swoole_coroutine_write(fd, buf, n_buf), n_buf); + swoole_coroutine_close(fd); + + fd = swoole_coroutine_open(test_file, O_RDONLY, 0); + char data[8192]; + ASSERT_EQ(swoole_coroutine_read(fd, data, n_buf), n_buf); + ASSERT_EQ(std::string(buf, n_buf), std::string(data, n_buf)); + swoole_coroutine_close(fd); + + ASSERT_EQ(swoole_coroutine_unlink(test_file), 0); + }); +} + +TEST(coroutine_hook, gethostbyname) { + coroutine::run([](void *arg) { + auto result1 = swoole_coroutine_gethostbyname(host_1); + ASSERT_NE(result1, nullptr); + + auto result2 = swoole_coroutine_gethostbyname(host_2); + ASSERT_EQ(result2, nullptr); + ASSERT_EQ(h_errno, HOST_NOT_FOUND); + }); +} + +TEST(coroutine_hook, getaddrinfo) { + coroutine::run([](void *arg) { + struct addrinfo hints; + sw_memset_zero(&hints, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + struct addrinfo *result, *curr; + int count; + + result = nullptr; + auto result1 = swoole_coroutine_getaddrinfo(host_1, "http", &hints, &result); + ASSERT_EQ(result1, 0); + + curr = result; + count = 0; + while (curr && curr->ai_addr) { + curr = curr->ai_next; + count++; + } + ASSERT_GE(count, 1); + freeaddrinfo(result); + + result = nullptr; + auto result2 = swoole_coroutine_getaddrinfo(host_2, nullptr, &hints, &result); + ASSERT_EQ(result2, EAI_NONAME); + ASSERT_EQ(result, nullptr); + freeaddrinfo(result); + }); +} + +TEST(coroutine_hook, fstat) { + coroutine::run([](void *arg) { + int fd = swoole_coroutine_open(TEST_TMP_FILE, O_RDONLY, 0); + struct stat statbuf_1; + swoole_coroutine_fstat(fd, &statbuf_1); + + struct stat statbuf_2; + fstat(fd, &statbuf_2); + + ASSERT_EQ(memcmp(&statbuf_1, &statbuf_2, sizeof(statbuf_2)), 0); + + swoole_coroutine_close(fd); + }); +} + +TEST(coroutine_hook, statvfs) { + coroutine::run([](void *arg) { + struct statvfs statbuf_1; + swoole_coroutine_statvfs("/tmp", &statbuf_1); + + struct statvfs statbuf_2; + statvfs("/tmp", &statbuf_2); + + ASSERT_EQ(memcmp(&statbuf_1, &statbuf_2, sizeof(statbuf_2)), 0); + }); +} + +TEST(coroutine_hook, dir) { + coroutine::run([](void *arg) { + ASSERT_EQ(swoole_coroutine_mkdir(TEST_TMP_DIR, 0666), 0); + ASSERT_EQ(swoole_coroutine_access(TEST_TMP_DIR, R_OK), 0); + ASSERT_EQ(swoole_coroutine_rmdir(TEST_TMP_DIR), 0); + ASSERT_EQ(access(TEST_TMP_DIR, R_OK), -1); + }); +} + +TEST(coroutine_hook, socket) { + coroutine::run([](void *arg) { + int sock = swoole_coroutine_socket(AF_INET, SOCK_STREAM, 0); + ASSERT_GT(sock, 0); + swoole::network::Address sa; + std::string ip = System::gethostbyname("www.baidu.com", AF_INET, 10); + sa.assign(SW_SOCK_TCP, ip, 80); + ASSERT_EQ(swoole_coroutine_connect(sock, &sa.addr.ss, sa.len), 0); + ASSERT_EQ(swoole_coroutine_socket_wait_event(sock, SW_EVENT_WRITE, 5), SW_OK); + + const char req[] = "GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\nKeepAlive: off\r\n\r\n"; + ASSERT_EQ(swoole_coroutine_send(sock, req, strlen(req), 0), strlen(req)); + + swoole::String resp(1024); + + while (1) { + ssize_t n = swoole_coroutine_recv(sock, resp.value() + resp.length, resp.size - resp.length, 0); + if (n <= 0) { + break; + } + resp.length += n; + if (resp.length == resp.size) { + resp.reserve(resp.size * 2); + } + } + + ASSERT_GT(resp.length, 100); + ASSERT_TRUE(resp.contains("baidu.com")); + swoole_coroutine_close(sock); + }); +} + +TEST(coroutine_hook, rename) { + coroutine::run([](void *arg) { + char buf[8192]; + size_t n_buf = sizeof(buf); + ASSERT_EQ(swoole_random_bytes(buf, n_buf), n_buf); + + int fd = swoole_coroutine_open(test_file, O_WRONLY | O_TRUNC | O_CREAT, 0666); + ASSERT_EQ(swoole_coroutine_write(fd, buf, n_buf), n_buf); + swoole_coroutine_close(fd); + + std::string to_file_name = std::string(test_file, ".bak"); + ASSERT_EQ(swoole_coroutine_rename(test_file, to_file_name.c_str()), 0); + ASSERT_EQ(access(TEST_TMP_DIR, F_OK), -1); + ASSERT_EQ(access(to_file_name.c_str(), F_OK), 0); + + swoole_coroutine_unlink(to_file_name.c_str()); + }); +} + +TEST(coroutine_hook, flock) { + long start_time = swoole::time(); + coroutine::run([&](void *arg) { + swoole::Coroutine::create([&](void *arg) { + int fd = swoole_coroutine_open(TEST_TMP_FILE, O_WRONLY, 0); + ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_EX), 0); + System::sleep(0.1); + ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_UN), 0); + + ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_SH), 0); + ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_UN), 0); + ASSERT_LE(swoole::time() - start_time, 1000); + swoole_coroutine_close(fd); + }); + swoole::Coroutine::create([&](void *arg) { + int fd = swoole_coroutine_open(TEST_TMP_FILE, O_WRONLY, 0); + ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_SH), 0); + System::sleep(2); + ASSERT_EQ(swoole_coroutine_flock(fd, LOCK_UN), 0); + swoole_coroutine_close(fd); + }); + }); + // LOCK_NB + coroutine::run([](void *arg) { + int fd1 = swoole_coroutine_open(TEST_TMP_FILE, O_WRONLY, 0); + ASSERT_EQ(swoole_coroutine_flock(fd1, LOCK_EX), 0); + int fd2 = swoole_coroutine_open(TEST_TMP_FILE, O_WRONLY, 0); + ASSERT_EQ(swoole_coroutine_flock(fd2, LOCK_EX | LOCK_NB), -1); + ASSERT_EQ(swoole_coroutine_flock(fd1, LOCK_UN), 0); + swoole_coroutine_close(fd1); + swoole_coroutine_close(fd2); + }); +} + +TEST(coroutine_hook, read_dir) { + 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, dir1.c_str()); + + entry = swoole_coroutine_readdir(fp); + ASSERT_NE(entry, nullptr); + ASSERT_STREQ(entry->d_name, dir2.c_str()); + + swoole_coroutine_closedir(fp); + }; + + coroutine::run([&](void *arg) { fn(); }); + fn(); +} + +TEST(coroutine_hook, readlink) { + auto fn = []() { + char buf1[1024] = {}; + char buf2[1024] = {}; + + auto retval = swoole_coroutine_readlink("/proc/self/cwd", buf1, sizeof(buf1)); + ASSERT_NE(retval, -1); + + 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 new file mode 100644 index 00000000000..373d2d8afb1 --- /dev/null +++ b/core-tests/src/coroutine/http_server.cpp @@ -0,0 +1,101 @@ +/* + +----------------------------------------------------------------------+ + | 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_coroutine.h" +#include "httplib_server.h" + +using namespace swoole::test; +using namespace httplib; +using namespace std; + +TEST(coroutine_http_server, get) { + Server svr; + mutex lock; + int port = swoole::test::get_random_port(); + lock.lock(); + + thread t1([&lock, port]() { + lock.lock(); + Client cli(TEST_HOST, port); + auto resp1 = cli.Get("/hi"); + EXPECT_EQ(resp1->status, 200); + EXPECT_EQ(resp1->body, string("Hello World!")); + + auto resp2 = cli.Get("/stop"); + EXPECT_EQ(resp2->status, 200); + EXPECT_EQ(resp2->body, string("Stop Server!")); + }); + + 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) { + res.set_content("Stop Server!", "text/plain"); + svr.stop(); + }); + + svr.Post("/post", [](const Request &req, Response &res) { res.set_content("Hello World!", "text/plain"); }); + + svr.BeforeListen([&lock]() { lock.unlock(); }); + + ASSERT_TRUE(svr.listen(TEST_HOST, port)); + }); + + t1.join(); +} + +TEST(coroutine_http_server, post) { + Server svr; + mutex lock; + int port = swoole::test::get_random_port(); + lock.lock(); + + std::thread t1([&lock, port]() { + lock.lock(); + + Client cli(TEST_HOST, port); + + httplib::Params params; + params.emplace("name", "john"); + params.emplace("note", "coder"); + + auto resp1 = cli.Post("/post", params); + EXPECT_EQ(resp1->status, 200); + EXPECT_EQ(resp1->body, string("Hello World!")); + + auto resp2 = cli.Get("/stop"); + EXPECT_EQ(resp2->status, 200); + EXPECT_EQ(resp2->body, string("Stop Server!")); + }); + + 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(); + }); + + svr.Post("/post", [](const Request &req, Response &res) { res.set_content("Hello World!", "text/plain"); }); + + svr.BeforeListen([&lock]() { lock.unlock(); }); + + svr.listen(TEST_HOST, port); + }); + + t1.join(); +} diff --git a/core-tests/src/coroutine/socket.cpp b/core-tests/src/coroutine/socket.cpp index ad390edb7d7..e4787c7cd5b 100644 --- a/core-tests/src/coroutine/socket.cpp +++ b/core-tests/src/coroutine/socket.cpp @@ -1,30 +1,54 @@ -#include "tests.h" -#include "swoole/coroutine_socket.h" -#include "process.h" -#include "wrapper/server.h" +/* + +----------------------------------------------------------------------+ + | 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_process.h" +#include "test_coroutine.h" +#include "test_server.h" 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; -TEST(coroutine_socket, connect_refused) -{ - swoole_event_init(); - SwooleTG.reactor->wait_exit = 1; +const std::string host = "www.baidu.com"; - test::coroutine::test([](void *arg) - { +TEST(coroutine_socket, connect_refused) { + coroutine::run([](void *arg) { Socket sock(SW_SOCK_TCP); - bool retval = sock.connect("127.0.0.1", 9801, 0.5); + bool retval = sock.connect("127.0.0.1", 9801); ASSERT_EQ(retval, false); ASSERT_EQ(sock.errCode, ECONNREFUSED); }); } -TEST(coroutine_socket, connect_timeout) -{ - test::coroutine::test([](void *arg) - { +TEST(coroutine_socket, connect_timeout) { + coroutine::run([](void *arg) { Socket sock(SW_SOCK_TCP); sock.set_timeout(0.5); bool retval = sock.connect("192.0.0.1", 9801); @@ -33,44 +57,54 @@ TEST(coroutine_socket, connect_timeout) }); } -TEST(coroutine_socket, connect_with_dns) -{ - test::coroutine::test([](void *arg) - { +TEST(coroutine_socket, connect_with_dns) { + coroutine::run([](void *arg) { Socket sock(SW_SOCK_TCP); - bool retval = sock.connect("www.baidu.com", 80, 0.5); + bool retval = sock.connect(host, 80); ASSERT_EQ(retval, true); ASSERT_EQ(sock.errCode, 0); }); } -TEST(coroutine_socket, recv_success) -{ - pid_t pid; +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); + }); +} - process proc([](process *proc) - { - on_receive_lambda_type receive_fn = [](ON_RECEIVE_PARAMS) - { - char *data_ptr = NULL; - size_t data_len = SERVER_THIS->get_packet(req, (char **) &data_ptr); +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); + }); +} - SERVER_THIS->send(req->info.fd, data_ptr, data_len); +TEST(coroutine_socket, recv_success) { + pid_t pid; + int port = swoole::test::get_random_port(); + + 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, SW_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(); }); pid = proc.start(); - sleep(1); // wait for the test server to start + sleep(1); // wait for the test server to start - test::coroutine::test([](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")); @@ -80,33 +114,30 @@ TEST(coroutine_socket, recv_success) ASSERT_EQ(strcmp(buf, "hello world\n"), 0); }); - kill(pid, SIGKILL); + kill(pid, SIGTERM); + int status; + wait(&status); } -TEST(coroutine_socket, recv_fail) -{ +TEST(coroutine_socket, recv_fail) { pid_t pid; + int port = swoole::test::get_random_port(); - process proc([](process *proc) - { - on_receive_lambda_type receive_fn = [](ON_RECEIVE_PARAMS) - { - SERVER_THIS->close(req->info.fd, 0); - }; + 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, SW_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(); }); pid = proc.start(); - sleep(1); // wait for the test server to start + sleep(1); // wait for the test server to start - test::coroutine::test([](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); @@ -115,34 +146,38 @@ TEST(coroutine_socket, recv_fail) ASSERT_EQ(n, 0); }); - kill(pid, SIGKILL); + kill(pid, SIGTERM); + int status; + wait(&status); } -TEST(coroutine_socket, bind_success) -{ - test::coroutine::test([](void *arg) - { +TEST(coroutine_socket, bind_success) { + coroutine::run([](void *arg) { 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); }); } -TEST(coroutine_socket, bind_fail) -{ - test::coroutine::test([](void *arg) - { +TEST(coroutine_socket, bind_fail) { + coroutine::run([](void *arg) { Socket sock(SW_SOCK_TCP); 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); }); } -TEST(coroutine_socket, listen) -{ - test::coroutine::test([](void *arg) - { +TEST(coroutine_socket, listen) { + coroutine::run([](void *arg) { Socket sock(SW_SOCK_TCP); bool retval = sock.bind("127.0.0.1", 9909); ASSERT_EQ(retval, true); @@ -150,27 +185,1047 @@ TEST(coroutine_socket, listen) }); } -TEST(coroutine_socket, accept) -{ - test::coroutine::test({ - [](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); +TEST(coroutine_socket, accept) { + 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(); + ASSERT_NE(conn, nullptr); + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9909, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + sock.close(); + }}); +} + +#define CRLF "\r\n" +#define EOF_PACKET "hello world" CRLF +#define EOF_PACKET_2 "php&swoole, java&golang" CRLF +#define RECV_TIMEOUT 10.0 + +static void socket_set_eof_protocol(Socket &sock) { + memcpy(sock.protocol.package_eof, SW_STRL(CRLF)); + sock.protocol.package_eof_len = 2; + sock.open_eof_check = true; +} + +TEST(coroutine_socket, eof_1) { + 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(); + char buf[1024]; + ssize_t l = conn->recv(buf, sizeof(buf)); + EXPECT_EQ(string(buf, l), string("start\r\n")); + conn->send(EOF_PACKET); + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9909, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + sock.send("start\r\n"); + + socket_set_eof_protocol(sock); + + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + size_t eof_packet_len = strlen(EOF_PACKET); + auto buf = sock.get_read_buffer(); + + ASSERT_EQ(l, eof_packet_len); + ASSERT_EQ(string(buf->str, l), string(EOF_PACKET)); + ASSERT_EQ(buf->length, eof_packet_len); + ASSERT_EQ(buf->offset, eof_packet_len); + }}); +} + +TEST(coroutine_socket, eof_2) { + 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(); + char buf[1024]; + ssize_t l = conn->recv(buf, sizeof(buf)); + EXPECT_EQ(string(buf, l), string("start\r\n")); + conn->send(EOF_PACKET EOF_PACKET_2); + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9909, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + sock.send("start\r\n"); + + socket_set_eof_protocol(sock); + + // packet 1 + { + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + size_t eof_packet_len = strlen(EOF_PACKET); + auto buf = sock.get_read_buffer(); + + ASSERT_EQ(l, eof_packet_len); + ASSERT_EQ(string(buf->str, l), string(EOF_PACKET)); + ASSERT_EQ(buf->length, strlen(EOF_PACKET EOF_PACKET_2)); + ASSERT_EQ(buf->offset, eof_packet_len); + } + // packet 2 + { + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + size_t eof_packet_len = strlen(EOF_PACKET_2); + auto buf = sock.get_read_buffer(); + + ASSERT_EQ(l, eof_packet_len); + ASSERT_EQ(string(buf->str, l), string(EOF_PACKET_2)); + ASSERT_EQ(buf->length, strlen(EOF_PACKET_2)); + ASSERT_EQ(buf->offset, eof_packet_len); + } + }}); +} + +TEST(coroutine_socket, eof_3) { + 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(); + char buf[1024]; + ssize_t l = conn->recv(buf, sizeof(buf)); + EXPECT_EQ(string(buf, l), string("start\r\n")); + conn->shutdown(); + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9909, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + sock.send("start\r\n"); + + socket_set_eof_protocol(sock); + + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + ASSERT_EQ(l, 0); + }}); +} + +TEST(coroutine_socket, eof_4) { + 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(); + char buf[1024]; + ssize_t l = conn->recv(buf, sizeof(buf)); + EXPECT_EQ(string(buf, l), string("start\r\n")); + conn->send(EOF_PACKET, strlen(EOF_PACKET) - strlen(CRLF)); // no eof + conn->shutdown(); + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9909, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + sock.send("start\r\n"); + + socket_set_eof_protocol(sock); + + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + ASSERT_EQ(l, 0); + + auto buf = sock.get_read_buffer(); + ASSERT_EQ(string(buf->str, 10), string(EOF_PACKET, 10)); + }}); +} + +TEST(coroutine_socket, eof_5) { + 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(); + char buf[1024]; + ssize_t l = conn->recv(buf, sizeof(buf)); + EXPECT_EQ(string(buf, l), string("start\r\n")); + + swString *s = swoole::make_string(128 * 1024); + s->repeat("A", 1, 128 * 1024 - 16); + s->append(SW_STRL(CRLF)); + + conn->send_all(s->str, s->length); + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9909, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + sock.send("start\r\n"); + + socket_set_eof_protocol(sock); + + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + ASSERT_EQ(l, 128 * 1024 - 14); + }}); +} + +TEST(coroutine_socket, eof_6) { + 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(); + char buf[1024]; + ssize_t l = conn->recv(buf, sizeof(buf)); + EXPECT_EQ(string(buf, l), string("start\r\n")); + + swString s(128 * 1024); + s.repeat("A", 1, 128 * 1024 - 16); + s.append(SW_STRL(CRLF)); + + conn->send_all(s.value(), s.get_length()); + }, + + [](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect("127.0.0.1", 9909, -1); + ASSERT_EQ(retval, true); + ASSERT_EQ(sock.errCode, 0); + sock.send("start\r\n"); + + socket_set_eof_protocol(sock); + sock.protocol.package_max_length = 1024 * 64; + + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + ASSERT_EQ(l, -1); + ASSERT_EQ(sock.errCode, SW_ERROR_PACKAGE_LENGTH_TOO_LARGE); + }}); +} + +static void socket_set_length_protocol_1(Socket &sock) { + sock.protocol = {}; + + sock.protocol.package_length_type = 'n'; + sock.protocol.package_length_size = swoole_type_size(sock.protocol.package_length_type); + sock.protocol.package_body_offset = 2; + sock.protocol.get_package_length = Protocol::default_length_func; + sock.protocol.package_max_length = 65535; + + sock.open_length_check = true; +} + +static void socket_set_length_protocol_2(Socket &sock) { + sock.protocol = {}; + + sock.protocol.package_length_type = 'N'; + sock.protocol.package_length_size = swoole_type_size(sock.protocol.package_length_type); + sock.protocol.package_body_offset = 4; + sock.protocol.get_package_length = Protocol::default_length_func; + sock.protocol.package_max_length = 2 * 1024 * 1024; + + sock.open_length_check = true; +} + +TEST(coroutine_socket, length_1) { + 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(); + char buf[1024]; + ssize_t l = swoole_random_bytes(buf + 2, sizeof(buf) - 2); + *(uint16_t *) buf = htons(l); + + conn->send(buf, l + 2); + }, + + [](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); + + socket_set_length_protocol_1(sock); + + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + auto buf = sock.get_read_buffer(); + + ASSERT_EQ(l, 1024); + ASSERT_EQ(buf->length, l); + ASSERT_EQ(buf->offset, l); + }}); +} + +TEST(coroutine_socket, length_2) { + 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(); + char buf[1024]; + *(uint16_t *) buf = htons(0); + + conn->send(buf, 2); + }, + + [](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); + + socket_set_length_protocol_1(sock); + + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + auto buf = sock.get_read_buffer(); + + ASSERT_EQ(l, 2); + ASSERT_EQ(buf->length, 2); + ASSERT_EQ(buf->offset, 2); + }}); +} + +TEST(coroutine_socket, length_3) { + 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(); + char buf[1024]; + memset(buf, 'A', sizeof(buf)); + *(uint16_t *) buf = htons(65530); + + conn->send(buf, sizeof(buf)); + }, + + [](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); + + socket_set_length_protocol_1(sock); + sock.protocol.package_max_length = 4096; + + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + ASSERT_EQ(l, -1); + ASSERT_EQ(sock.errCode, SW_ERROR_PACKAGE_LENGTH_TOO_LARGE); + }}); +} + +static string pkt_1; +static string pkt_2; + +static void length_protocol_server_func(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(); + String strbuf(256 * 1024); + + uint32_t pack_len; + + size_t l_1 = swoole_rand(65536, 65536 * 2); + pack_len = htonl(l_1); + strbuf.append((char *) &pack_len, sizeof(pack_len)); + strbuf.append_random_bytes(l_1); + + pkt_1 = string(strbuf.str, l_1 + 4); + + size_t l_2 = swoole_rand(65536, 65536 * 2); + pack_len = htonl(l_2); + strbuf.append((char *) &pack_len, sizeof(pack_len)); + strbuf.append_random_bytes(l_2); + + pkt_2 = string(strbuf.str + pkt_1.length(), l_2 + 4); + + conn->send_all(strbuf.str, strbuf.length); +} + +TEST(coroutine_socket, length_4) { + coroutine::run({length_protocol_server_func, + + [](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); + + socket_set_length_protocol_2(sock); + + size_t bytes = 0; + for (int i = 0; i < 2; i++) { + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + bytes += l; + auto buf = sock.get_read_buffer(); + uint32_t unpack_len = ntohl(*(uint32_t *) buf->str); + + if (i == 0) { + ASSERT_EQ(pkt_1, string(buf->str, buf->length)); + } else { + ASSERT_EQ(pkt_2, string(buf->str, buf->length)); + } + + ASSERT_EQ(unpack_len, l - 4); + ASSERT_EQ(buf->length, l); + ASSERT_EQ(buf->offset, l); + } + ASSERT_GE(bytes, 65536 * 2); + }}); +} + +TEST(coroutine_socket, length_5) { + coroutine::run({length_protocol_server_func, + + [](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); + + socket_set_length_protocol_2(sock); + + size_t bytes = 0; + for (int i = 0; i < 2; i++) { + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + bytes += l; + char *data = sock.pop_packet(); + uint32_t unpack_len = ntohl(*(uint32_t *) data); + ASSERT_EQ(unpack_len, l - 4); + + if (i == 0) { + ASSERT_EQ(pkt_1, string(data, l)); + } else { + ASSERT_EQ(pkt_2, string(data, l)); + } + } + ASSERT_GE(bytes, 65536 * 2); + }}); +} + +TEST(coroutine_socket, length_7) { + 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(); + char buf[1024]; + *(uint32_t *) buf = htons(0); + + conn->send(buf, 2); + System::sleep(0.01); + conn->send(buf + 2, 2); + }, + + [](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); + + socket_set_length_protocol_2(sock); + + ssize_t l = sock.recv_packet(RECV_TIMEOUT); + auto buf = sock.get_read_buffer(); + + ASSERT_EQ(l, 4); + ASSERT_EQ(buf->length, 4); + ASSERT_EQ(buf->offset, 4); + }}); +} + +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.connect(host, 80); + ASSERT_EQ(retval, true); + + Address sa; + bool result = sock.getsockname(&sa); + sock.close(); + ASSERT_EQ(result, true); + }); +} + +TEST(coroutine_socket, check_liveness) { + coroutine::run([](void *arg) { + Socket sock(SW_SOCK_TCP); + bool retval = sock.connect(host, 80); + ASSERT_EQ(retval, true); + + 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); + }); + + 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); - Socket *conn = sock.accept(); - ASSERT_NE(conn, nullptr); - }, + 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); - [](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); + 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 new file mode 100644 index 00000000000..0552a909bfe --- /dev/null +++ b/core-tests/src/coroutine/system.cpp @@ -0,0 +1,280 @@ +/* + +----------------------------------------------------------------------+ + | 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_coroutine.h" +#include "swoole_pipe.h" + +using namespace swoole; +using namespace swoole::test; + +using swoole::coroutine::Socket; +using swoole::coroutine::System; + +static const char *test_file = "/tmp/swoole-core-test"; + +static constexpr int DATA_SIZE = 8 * 1024 * 1024; +static constexpr int DATA_SIZE_2 = 64 * 1024; + +TEST(coroutine_system, file) { + test::coroutine::run([](void *arg) { + std::shared_ptr buf = std::make_shared(DATA_SIZE); + ASSERT_EQ(swoole_random_bytes(buf->str, buf->size - 1), buf->size - 1); + buf->str[buf->size - 1] = 0; + ASSERT_EQ(System::write_file(test_file, buf->str, buf->size, true, 0), buf->size); + auto data = System::read_file(test_file, true); + ASSERT_TRUE(data.get()); + ASSERT_STREQ(buf->str, data->str); + unlink(test_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; + + 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 *) { + int fd = swoole_coroutine_open(test_file, File::READ, 0); + ASSERT_TRUE(fd > 0); + swoole_coroutine_flock_ex(test_file, fd, LOCK_SH); + String read_buf(DATA_SIZE_2); + auto rn = swoole_coroutine_read(fd, read_buf.str, read_buf.size - 1); + ASSERT_EQ(rn, read_buf.size - 1); + read_buf.str[read_buf.size - 1] = 0; + swoole_coroutine_flock_ex(test_file, fd, LOCK_UN); + EXPECT_STREQ(read_buf.str, buf->str); + swoole_coroutine_close(fd); + }); + } + + auto wn = swoole_coroutine_write(fd, buf->str, buf->size - 1); + ASSERT_EQ(wn, buf->size - 1); + swoole_coroutine_flock_ex(test_file, fd, LOCK_UN); + swoole_coroutine_close(fd); + }); + + 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/hashmap.cpp b/core-tests/src/hashmap.cpp deleted file mode 100644 index 2d877643412..00000000000 --- a/core-tests/src/hashmap.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "tests.h" - -#include -#include - -typedef struct -{ - int fd; - int key; -} swFdInfo; - -bool type_eof() -{ - char eof[] = SW_DATA_EOF; - printf("SW_DATA_STREAM_EOF = %s\n", eof); - return 0; -} - -TEST(hashmap, string) -{ - swHashMap *hm = swHashMap_new(16, NULL); - swHashMap_add(hm, (char *) SW_STRL("hello"), (void *) 199); - swHashMap_add(hm, (char *) SW_STRL("swoole22"), (void *) 8877); - swHashMap_add(hm, (char *) SW_STRL("hello2"), (void *) 200); - swHashMap_add(hm, (char *) SW_STRL("willdel"), (void *) 888); - swHashMap_add(hm, (char *) SW_STRL("willupadte"), (void *) 9999); - swHashMap_add(hm, (char *) SW_STRL("hello3"), (void *) 78978); - - swHashMap_del(hm, (char *) SW_STRL("willdel")); - swHashMap_update(hm, (char *) SW_STRL("willupadte"), (void *) (9999 * 5555)); - - int ret1 = (int) (long) swHashMap_find(hm, (char *) SW_STRL("hello")); - ASSERT_GT(ret1, 0); - - int ret2 = (int) (long) swHashMap_find(hm, (char *) SW_STRL("hello2")); - ASSERT_GT(ret2, 0); - - int ret3 = (int) (long) swHashMap_find(hm, (char *) SW_STRL("notfound")); - ASSERT_EQ(ret3, 0); - - char *key; - int data; - - while (1) - { - data = (int) (long) swHashMap_each(hm, &key); - if (!data) - { - break; - } - } - swHashMap_free(hm); -} - -#define BUFSIZE 128 -#define MAP_SIZE 32 -char data[BUFSIZE]; - -TEST(hashmap, integer) -{ - swHashMap *ht = swHashMap_new(16, free); - swFdInfo *pkt, *tmp; - int i; - swFdInfo *lists[MAP_SIZE]; - - for (i = 0; i < MAP_SIZE; i++) - { - pkt = (swFdInfo *) malloc(sizeof(swFdInfo)); - pkt->key = i; - pkt->fd = i * 37; - swHashMap_add_int(ht, pkt->fd, pkt); - lists[i] = pkt; - } - - tmp = (swFdInfo *) swHashMap_find_int(ht, 37 * 8); - ASSERT_NE((void* )tmp, nullptr); - - tmp = (swFdInfo *) swHashMap_find_int(ht, 37 * 3); - ASSERT_NE((void* )tmp, nullptr); - - tmp = (swFdInfo *) swHashMap_find_int(ht, 36 * 3); - ASSERT_EQ((void* )tmp, nullptr); - - for (i = 0; i < 10; i++) - { - free(lists[i]); - } -} diff --git a/core-tests/src/heap.cpp b/core-tests/src/heap.cpp deleted file mode 100644 index 4147b2298b3..00000000000 --- a/core-tests/src/heap.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "swoole/swoole.h" -#include "swoole/heap.h" -#include -#include - -typedef struct node_t -{ - int pri; - int val; -} node_t; - -#define SIZE 100 - -TEST(heap, random) -{ - swHeap *pq; - node_t *ns; - node_t *n; - - pq = swHeap_new(SIZE, SW_MAX_HEAP); - ASSERT_NE(pq, nullptr); - - std::map _map; - - int i; - for (i = 0; i < SIZE - 1; i++) - { - int pri = swoole_system_random(10000, 99999); - ns = (node_t*) malloc(sizeof(node_t)); - ns->val = i; - ns->pri = pri; - swHeap_push(pq, pri, ns); - _map[i] = pri; - } - - while ((n = (node_t*) swHeap_pop(pq))) - { - ASSERT_EQ(_map[n->val], n->pri); - free(n); - } - - swHeap_free(pq); -} diff --git a/core-tests/src/lock/lock.cpp b/core-tests/src/lock/lock.cpp new file mode 100644 index 00000000000..a2da9992ede --- /dev/null +++ b/core-tests/src/lock/lock.cpp @@ -0,0 +1,217 @@ +/* + +----------------------------------------------------------------------+ + | 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_coroutine.h" +#include "swoole_lock.h" +#include "swoole_util.h" + +#include + +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; + const int N = 100000; + + auto fn = [&]() { + for (int i = 0; i < N; i++) { + ASSERT_EQ(lock.lock(), 0); + count++; + ASSERT_EQ(lock.unlock(), 0); + } + }; + + std::thread t1(fn); + std::thread t2(fn); + + t1.join(); + t2.join(); + + 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(lock, lockwait) { + Mutex lock(0); + + lock.lock(); + + std::thread t1([&lock]() { + long ms1 = swoole::time(); + const int TIMEOUT_1 = 2; + ASSERT_EQ(lock.lock_wait(TIMEOUT_1), ETIMEDOUT); + long ms2 = swoole::time(); + + ASSERT_GE(ms2 - ms1, TIMEOUT_1); + + const int TIMEOUT_2 = 10; + ASSERT_EQ(lock.lock_wait(TIMEOUT_2), 0); + long ms3 = swoole::time(); + + ASSERT_LE(ms3 - ms2, TIMEOUT_2); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + lock.unlock(); + + t1.join(); +} + +TEST(lock, shared) { + Mutex lock(Mutex::PROCESS_SHARED); + test_share_lock_fun(lock); +} + +TEST(lock, try_rd) { + Mutex lock(0); + test_lock_rd_func(lock); +} + +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); }); + }); + }; + + 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 old mode 100755 new mode 100644 index 85cc85f63ef..05f8a5c6b36 --- a/core-tests/src/main.cpp +++ b/core-tests/src/main.cpp @@ -1,14 +1,90 @@ -#include "tests.h" -#include "swoole/swoole_api.h" +#include "test_core.h" + +#include "swoole_proxy.h" using namespace swoole; +using namespace std; + +static string root_path; + +static void init_root_path(const char *); -int main(int argc, char **argv) -{ +int main(int argc, char **argv) { swoole_init(); + init_root_path(argv[0]); + + if (getenv("DISPLAY_BACKTRACE") != nullptr) { + sw_logger()->display_backtrace(); + } ::testing::InitGoogleTest(&argc, argv); int retval = RUN_ALL_TESTS(); + swoole_clean(); + return retval; } + +static void init_root_path(const char *_exec_file) { + char buf[PATH_MAX]; + 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) { + root_path = relative_root_path; + } else { + root_path = string(_realpath); + } +} + +namespace swoole { +namespace test { + +const string &get_root_path() { + return root_path; +} + +string get_jpg_file() { + return root_path + TEST_JPG_FILE; +} + +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 new file mode 100644 index 00000000000..101c7f6f86d --- /dev/null +++ b/core-tests/src/memory/buffer.cpp @@ -0,0 +1,81 @@ +/* + +----------------------------------------------------------------------+ + | 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_memory.h" +#include "swoole_buffer.h" + +using namespace std; +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; + + 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]); + unique_ptr s3(new char[v[2].iov_len]); + unique_ptr s4(new char[v[3].iov_len]); + + v[0].iov_base = s1.get(); + v[1].iov_base = s2.get(); + v[2].iov_base = s3.get(); + v[3].iov_base = s4.get(); + + memset(v[0].iov_base, 'A', v[0].iov_len); + memset(v[1].iov_base, 'B', v[1].iov_len); + memset(v[2].iov_base, 'C', v[2].iov_len); + memset(v[3].iov_base, 'D', v[3].iov_len); + + buf.append(v, iovcnt, 0); + ASSERT_EQ(buf.length(), total_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_for_offset.length()); + + 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) { + 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 new file mode 100644 index 00000000000..2a4a21cb8e5 --- /dev/null +++ b/core-tests/src/memory/fixed_pool.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 "swoole_memory.h" +#include "swoole_util.h" + +using namespace std; + +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) { + mem = pool->alloc(0); + ASSERT_TRUE(mem); + alloc_list.push_back(mem); + } else if (!alloc_list.empty()) { + 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 new file mode 100644 index 00000000000..d573caba3a6 --- /dev/null +++ b/core-tests/src/memory/global_memory.cpp @@ -0,0 +1,50 @@ +/* + +----------------------------------------------------------------------+ + | 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_memory.h" + +TEST(global_memory, alloc) { + auto pool = new swoole::GlobalMemory(2 * 1024 * 1024, false); + + char *ptr1 = (char *) pool->alloc(199); + pool->free(ptr1); + strcpy(ptr1, "hello, world, #1"); + + char *ptr2 = (char *) pool->alloc(17); + strcpy(ptr2, "hello, world, #2"); + pool->free(ptr2); + + char *ptr3 = (char *) pool->alloc(113); + strcpy(ptr3, "hello, world, #3"); + + ASSERT_TRUE(ptr1); + ASSERT_TRUE(ptr2); + ASSERT_TRUE(ptr3); + + ASSERT_GT(pool->capacity(), 2 * 1024 * 1024 - 512); + ASSERT_GT(pool->get_memory_size(), 0); + + ASSERT_STREQ(ptr1, "hello, world, #1"); + ASSERT_STREQ(ptr2, "hello, world, #2"); + ASSERT_STREQ(ptr3, "hello, world, #3"); + + pool->destroy(); + delete pool; +} diff --git a/core-tests/src/lru_cache.cpp b/core-tests/src/memory/lru_cache.cpp similarity index 86% rename from core-tests/src/lru_cache.cpp rename to core-tests/src/memory/lru_cache.cpp index 656f017e1c0..f569e3a0979 100644 --- a/core-tests/src/lru_cache.cpp +++ b/core-tests/src/memory/lru_cache.cpp @@ -1,5 +1,5 @@ -#include "tests.h" -#include "swoole/lru_cache.h" +#include "test_core.h" +#include "swoole_lru_cache.h" using namespace swoole; using namespace std; @@ -7,22 +7,16 @@ using namespace std; LRUCache cache(2); int dtor_num = 0; -class lru_cache_test_class -{ -public: - lru_cache_test_class() - { +class lru_cache_test_class { + public: + lru_cache_test_class() {} - } - - ~lru_cache_test_class() - { + ~lru_cache_test_class() { ++dtor_num; } }; -TEST(lru_cache, basic) -{ +TEST(lru_cache, basic) { shared_ptr val = make_shared("hello"); shared_ptr val1 = make_shared("hello1"); @@ -42,8 +36,7 @@ TEST(lru_cache, basic) ASSERT_EQ(cache.get("test"), nullptr); } -TEST(lru_cache, memory_free) -{ +TEST(lru_cache, memory_free) { shared_ptr val = make_shared(); cache.set("test", val); ASSERT_EQ(cache.get("test").get(), val.get()); @@ -53,8 +46,7 @@ TEST(lru_cache, memory_free) ASSERT_EQ(dtor_num, 1); } -TEST(lru_cache, lru_kick) -{ +TEST(lru_cache, lru_kick) { dtor_num = 0; shared_ptr val = make_shared(); shared_ptr val1 = make_shared(); @@ -78,7 +70,7 @@ TEST(lru_cache, lru_kick) ASSERT_EQ(cache.get("test"), nullptr); shared_ptr val_str = make_shared("hello"); - cache.set("test1", val_str); // update test1 and will del test2 + cache.set("test1", val_str); // update test1 and will del test2 ASSERT_EQ(cache.get("test1").get(), val_str.get()); ASSERT_EQ(dtor_num, 2); @@ -90,4 +82,4 @@ TEST(lru_cache, lru_kick) cache.clear(); ASSERT_EQ(dtor_num, 4); -} \ No newline at end of file +} diff --git a/core-tests/src/memory/ringbuffer.cpp b/core-tests/src/memory/ringbuffer.cpp new file mode 100644 index 00000000000..00f7d327f21 --- /dev/null +++ b/core-tests/src/memory/ringbuffer.cpp @@ -0,0 +1,127 @@ +#include "test_core.h" +#include "swoole_memory.h" +#include "swoole_pipe.h" + +using namespace swoole; + +#include + +#define READ_THREAD_N 4 +#define WRITE_N 10000 +#define PACKET_LEN 90000 +//#define PRINT_SERNUM_N 10 + +static MemoryPool *pool = NULL; + +typedef struct { + uint32_t id; + uint32_t size; + uint32_t serial_num; + void *ptr; +} pkg; + +typedef struct { + std::thread *thread; + UnixSocket *pipe; +} ThreadObject; + +static void thread_read(int i); +static void thread_write(void); +static ThreadObject threads[READ_THREAD_N]; + +TEST(ringbuffer, thread) { + int i; + pool = new RingBuffer(1024 * 1024 * 4, true); + ASSERT_NE(nullptr, pool); + + for (i = 0; i < READ_THREAD_N; i++) { + threads[i].pipe = new UnixSocket(true, SOCK_DGRAM); + ASSERT_TRUE(threads[i].pipe->ready()); + threads[i].thread = new std::thread(thread_read, i); + } + + sleep(1); + srand((unsigned int) time(NULL)); + thread_write(); + + for (i = 0; i < READ_THREAD_N; i++) { + threads[i].thread->join(); + delete threads[i].pipe; + delete threads[i].thread; + } +} + +static void thread_write(void) { + uint32_t size, yield_count = 0, yield_total_count = 0; + void *ptr; + pkg send_pkg; + sw_memset_zero(&send_pkg, sizeof(send_pkg)); + + int i; + for (i = 0; i < WRITE_N; i++) { + size = 10000 + rand() % PACKET_LEN; + // printf("[ < %d] alloc size=%d\n", i, size); + + yield_count = 0; + do { + ptr = pool->alloc(size); + if (ptr) { + break; + } else { + yield_count++; + yield_total_count++; + usleep(10); + } + } while (yield_count < 100); + + if (!ptr) { + swoole_warning("alloc failed. index=%d, break", i); + } + ASSERT_NE(ptr, nullptr); + + send_pkg.id = i; + send_pkg.ptr = ptr; + send_pkg.size = size; + send_pkg.serial_num = rand(); + + //保存长度值 + memcpy(ptr, &size, sizeof(size)); + //在指针末尾保存一个串号 + memcpy((char *) ptr + size - 4, &(send_pkg.serial_num), sizeof(send_pkg.serial_num)); + + ASSERT_FALSE( + threads[i % READ_THREAD_N].pipe->write(&send_pkg, sizeof(send_pkg)) < 0); + } + + // printf("yield_total_count=%d\n", yield_total_count); +} + +static void thread_read(int i) { + pkg recv_pkg; + uint32_t tmp; + int ret; + uint32_t recv_count = 0; + int j = 0; + UnixSocket *sock = threads[i].pipe; + int task_n = WRITE_N / READ_THREAD_N; + + for (j = 0; j < task_n; j++) { + ret = sock->read(&recv_pkg, sizeof(recv_pkg)); + ASSERT_FALSE(ret < 0); + + memcpy(&tmp, recv_pkg.ptr, sizeof(tmp)); + ASSERT_EQ(tmp, recv_pkg.size); + + memcpy(&tmp, (char *) recv_pkg.ptr + recv_pkg.size - 4, sizeof(tmp)); + ASSERT_EQ(tmp, recv_pkg.serial_num); + +#ifdef PRINT_SERNUM_N + if (j % PRINT_SERNUM_N == 0) { + printf("[ > %d] recv. recv_count=%d, serial_num=%d\n", recv_pkg.id, recv_count, tmp); + } +#endif + + pool->free(recv_pkg.ptr); + recv_count++; + } +} diff --git a/core-tests/src/memory/table.cpp b/core-tests/src/memory/table.cpp new file mode 100644 index 00000000000..d58ca0b0ac2 --- /dev/null +++ b/core-tests/src/memory/table.cpp @@ -0,0 +1,332 @@ +/* + +----------------------------------------------------------------------+ + | 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_table.h" + +using namespace swoole; + +#include +#include + +struct exception_t : public std::exception { + int code; + std::string msg; + exception_t(std::string _msg, int _code) : std::exception() { + msg = _msg; + code = _code; + } + const char *what() const throw() { + return msg.c_str(); + } +}; + +struct row_t { + std::string name; + long id; + double score; +}; + +class table_t { + private: + TableColumn *column_id; + TableColumn *column_name; + TableColumn *column_score; + + Table *table; + + public: + table_t(uint32_t rows_size, float conflict_proportion = 0.2) { + table = Table::make(rows_size, conflict_proportion); + if (!table) { + throw exception_t("alloc failed", swoole_get_last_error()); + } + + table->add_column("id", TableColumn::TYPE_INT, 0); + table->add_column("name", TableColumn::TYPE_STRING, 32); + table->add_column("score", TableColumn::TYPE_FLOAT, 0); + + if (!table->create()) { + throw exception_t("create failed", swoole_get_last_error()); + } + column_id = table->get_column("id"); + column_name = table->get_column("name"); + column_score = table->get_column("score"); + } + + bool set(const std::string &key, const row_t &value) { + TableRow *_rowlock = nullptr; + TableRow *row = table->set(key.c_str(), key.length(), &_rowlock, nullptr); + if (!row) { + _rowlock->unlock(); + return false; + } + + row->set_value(column_id, (void *) &value.id, sizeof(value.id)); + row->set_value(column_name, (void *) value.name.c_str(), value.name.length()); + row->set_value(column_score, (void *) &value.score, sizeof(value.score)); + + _rowlock->unlock(); + + return true; + } + + row_t get(const std::string &key) { + row_t result; + TableRow *_rowlock = nullptr; + TableRow *row = table->get(key.c_str(), key.length(), &_rowlock); + if (row) { + memcpy(&result.id, row->data + column_id->index, sizeof(result.id)); + memcpy(&result.score, row->data + column_score->index, sizeof(result.score)); + + TableStringLength l; + memcpy(&l, row->data + column_name->index, sizeof(l)); + result.name = std::string(row->data + column_name->index + sizeof(l), l); + } + _rowlock->unlock(); + + return result; + } + + bool del(const std::string &key) { + return table->del(key.c_str(), key.length()); + } + + bool exists(const std::string &key) { + TableRow *_rowlock = nullptr; + TableRow *row = table->get(key.c_str(), key.length(), &_rowlock); + _rowlock->unlock(); + + return row != nullptr; + } + + size_t count() { + return table->count(); + } + + Table *ptr() { + return table; + } + + ~table_t() { + if (table) { + table->destroy(); + } + } +}; + +TEST(table, create) { + 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}); + + ASSERT_EQ(table.count(), 3); + + row_t r1 = table.get("java"); + ASSERT_EQ(r1.id, 2); + ASSERT_EQ(r1.score, 3.1415926); + ASSERT_EQ(r1.name, std::string("java")); + + ASSERT_TRUE(table.exists("php")); + 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/aio_thread.cpp b/core-tests/src/network/aio_thread.cpp deleted file mode 100644 index 6f439538844..00000000000 --- a/core-tests/src/network/aio_thread.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "tests.h" -#include "swoole/async.h" - -#include - -using namespace std; - -static int callback_count; - -static void aio_callback(swAio_event *event) -{ - callback_count++; -} - -TEST(aio_thread, dispatch) -{ - atomic handle_count(0); - swAio_event event; - event.object = &handle_count; - event.canceled = 0; - event.callback = aio_callback; - - callback_count = 0; - - event.handler = [](swAio_event *event) - { - (*(atomic *) event->object)++; - }; - - swoole_event_init(); - SwooleTG.reactor->wait_exit = 1; - - for (int i = 0; i < 1000; ++i) - { - auto ret = swAio_dispatch2(&event); - ASSERT_EQ(ret->object, event.object); - ASSERT_NE(ret->task_id, event.task_id); - } - - swoole_event_wait(); - - ASSERT_EQ(handle_count, 1000); - ASSERT_EQ(callback_count, 1000); -} diff --git a/core-tests/src/network/client.cpp b/core-tests/src/network/client.cpp new file mode 100644 index 00000000000..acbee375417 --- /dev/null +++ b/core-tests/src/network/client.cpp @@ -0,0 +1,357 @@ +#include "test_core.h" +#include "test_server.h" +#include "test_process.h" + +#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; + +TEST(client, tcp) { + int ret; + char buf[128]; + + pid_t pid; + int port = swoole::test::get_random_port(); + + 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, port, swoole::Server::MODE_BASE, SW_SOCK_TCP); + serv.on("onReceive", (void *) receive_fn); + serv.start(); + }); + + pid = proc.start(); + + sleep(1); // wait for the test server to start + + Client cli(SW_SOCK_TCP, false); + ASSERT_NE(cli.socket, nullptr); + 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); + ret = cli.recv(&cli, buf, 128, 0); + ASSERT_EQ(ret, GREETER_SIZE); + ASSERT_STREQ(GREETER, buf); + + kill(pid, SIGTERM); + int status; + wait(&status); +} + +TEST(client, udp) { + int ret; + char buf[128]; + int port = swoole::test::get_random_port(); + pid_t pid; + + 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, port, swoole::Server::MODE_BASE, SW_SOCK_UDP); + serv.on("onPacket", (void *) packet_fn); + serv.start(); + }); + + pid = proc.start(); + + sleep(1); // wait for the test server to start + + Client cli(SW_SOCK_UDP, false); + ASSERT_NE(cli.socket, nullptr); + 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); + ret = cli.recv(&cli, buf, 128, 0); + ASSERT_EQ(ret, GREETER_SIZE); + ASSERT_STREQ(GREETER, buf); + + kill(pid, SIGTERM); + int status; + wait(&status); +} + +static void test_async_client_tcp(const char *host, int port) { + pid_t pid; + + Pipe p(true); + ASSERT_TRUE(p.ready()); + + 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, port, swoole::Server::MODE_BASE, SW_SOCK_TCP); + + serv.set_private_data("pipe", &p); + + serv.on("onReceive", (void *) receive_fn); + + on_workerstart_lambda_type worker_start_fn = [](ON_WORKERSTART_PARAMS) { + Pipe *p = (Pipe *) SERVER_THIS->get_private_data("pipe"); + int64_t value = 1; + p->write(&value, sizeof(value)); + }; + + serv.on("onWorkerStart", (void *) worker_start_fn); + + serv.start(); + }); + + pid = proc.start(); + int64_t value; + p.set_timeout(10); + p.read(&value, sizeof(value)); + + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + + AsyncClient ac(SW_SOCK_TCP); + + ac.on_connect([](AsyncClient *ac) { ac->send(SW_STRS(GREETER)); }); + + ac.on_close([](AsyncClient *ac) { + + }); + ac.on_error([](AsyncClient *ac) { + + }); + + ac.on_receive([](AsyncClient *ac, const char *data, size_t len) { + ASSERT_EQ(len, GREETER_SIZE); + ASSERT_STREQ(GREETER, data); + ac->close(); + }); + + bool retval = ac.connect(host, port); + EXPECT_TRUE(retval); + + swoole_event_wait(); + + kill(pid, SIGTERM); + int status; + 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, swoole::test::get_random_port(), -1, 0); + ASSERT_EQ(ret, -1); + ASSERT_EQ(swoole_get_last_error(), ECONNREFUSED); +} + +TEST(client, connect_timeout) { + int ret; + Client cli(SW_SOCK_TCP, false); + 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); +} + +TEST(client, shutdown_write) { + signal(SIGPIPE, SIG_IGN); + int ret; + Client cli(SW_SOCK_TCP, false); + ret = cli.connect(&cli, "www.baidu.com", 80, -1, 0); + ASSERT_EQ(ret, 0); + cli.shutdown(SHUT_WR); + ssize_t retval = cli.send(&cli, SW_STRL("hello world"), 0); + ASSERT_EQ(retval, -1); + ASSERT_EQ(swoole_get_last_error(), EPIPE); +} + +TEST(client, shutdown_read) { + signal(SIGPIPE, SIG_IGN); + int ret; + Client cli(SW_SOCK_TCP, false); + ret = cli.connect(&cli, "www.baidu.com", 80, -1, 0); + ASSERT_EQ(ret, 0); + + cli.shutdown(SHUT_RD); + ssize_t retval = cli.send(&cli, SW_STRL("hello world\r\n\r\n"), 0); + ASSERT_GT(retval, 0); + + char buf[1024]; + retval = cli.recv(&cli, buf, sizeof(buf), 0); + ASSERT_EQ(retval, 0); +} + +TEST(client, shutdown_all) { + signal(SIGPIPE, SIG_IGN); + int ret; + Client cli(SW_SOCK_TCP, false); + ret = cli.connect(&cli, "www.baidu.com", 80, -1, 0); + ASSERT_EQ(ret, 0); + + cli.shutdown(SHUT_RDWR); + + ssize_t retval = cli.send(&cli, SW_STRL("hello world\r\n\r\n"), 0); + ASSERT_EQ(retval, -1); + ASSERT_EQ(swoole_get_last_error(), EPIPE); + + char buf[1024]; + 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 new file mode 100644 index 00000000000..f89746600c8 --- /dev/null +++ b/core-tests/src/network/dns.cpp @@ -0,0 +1,212 @@ +/* + +----------------------------------------------------------------------+ + | 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_coroutine.h" + +#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, 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.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) { + swoole::GetaddrinfoRequest req("www.baidu.com", AF_INET, SOCK_STREAM, 0, ""); + ASSERT_EQ(swoole::network::getaddrinfo(&req), 0); + ASSERT_GT(req.count, 0); + + vector ip_list; + req.parse_result(ip_list); + + for (auto &ip : ip_list) { + 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 new file mode 100644 index 00000000000..f4b656eb96e --- /dev/null +++ b/core-tests/src/network/socket.cpp @@ -0,0 +1,382 @@ +/* + +----------------------------------------------------------------------+ + | 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 std; +using namespace swoole; + +const char test_data[] = "hello swoole, hello world, php is best"; + +TEST(socket, sendto) { + char sock1_path[] = "/tmp/udp_unix1.sock"; + char sock2_path[] = "/tmp/udp_unix2.sock"; + + unlink(sock1_path); + unlink(sock2_path); + + auto sock1 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0); + sock1->bind(sock1_path, nullptr); + + auto sock2 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0); + sock2->bind(sock2_path, nullptr); + + ASSERT_GT(sock1->sendto(sock2_path, 0, test_data, strlen(test_data)), 0); + + char buf[1024] = {}; + network::Address sa; + sa.type = SW_SOCK_UNIX_DGRAM; + ASSERT_GT(sock2->recvfrom(buf, sizeof(buf), 0, &sa), 0); + ASSERT_STREQ(test_data, buf); + ASSERT_STREQ(sa.get_ip(), sock1_path); + + sock1->free(); + sock2->free(); + unlink(sock1_path); + unlink(sock2_path); +} + +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"; + + auto sock1 = make_socket(sock_type, SW_FD_DGRAM_SERVER, 0); + sock1->bind(ip, &port1); + + auto sock2 = make_socket(sock_type, SW_FD_DGRAM_SERVER, 0); + sock2->bind(ip, &port2); + + ASSERT_GT(sock1->sendto(ip, port2, test_data, strlen(test_data)), 0); + + char buf[1024] = {}; + network::Address sa; + sa.type = sock_type; + ASSERT_GT(sock2->recvfrom(buf, sizeof(buf), 0, &sa), 0); + + ASSERT_STREQ(test_data, buf); + ASSERT_EQ(sa.get_port(), port1); + ASSERT_STREQ(sa.get_ip(), ip); + + sock1->free(); + sock2->free(); +} + +TEST(socket, sendto_ipv4) { + test_sendto(SW_SOCK_UDP); +} + +TEST(socket, sendto_ipv6) { + test_sendto(SW_SOCK_UDP6); +} + +TEST(socket, recv) { + mutex m; + m.lock(); + int port = swoole::test::get_random_port(); + + thread t1([&m, port]() { + auto svr = make_server_socket(SW_SOCK_TCP, TEST_HOST, port); + char buf[1024] = {}; + svr->set_block(); + m.unlock(); + + auto client_sock = svr->accept(); + client_sock->recv(buf, sizeof(buf), 0); + + ASSERT_STREQ(test_data, buf); + svr->free(); + }); + + thread t2([&m, port]() { + m.lock(); + auto cli = make_socket(SW_SOCK_TCP, SW_FD_STREAM_CLIENT, 0); + 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(); + }); + + t1.join(); + t2.join(); +} + +TEST(socket, recvfrom_blocking) { + mutex m; + m.lock(); + int port = swoole::test::get_random_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(); + m.unlock(); + svr->recvfrom_blocking(buf, sizeof(buf), 0, &addr); + ASSERT_STREQ(test_data, buf); + svr->free(); + }); + + 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, 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); + cli->free(); + }); + + t1.join(); + t2.join(); +} + +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, port]() { + auto svr = make_server_socket(SW_SOCK_TCP, TEST_HOST, port); + m.unlock(); + auto cli = svr->accept(); + int len; + cli->recv_blocking(&len, sizeof(len), MSG_WAITALL); + int _len = ntohl(len); + ASSERT_EQ(_len, str->get_length()); + ASSERT_LT(_len, 1024 * 1024); + std::unique_ptr data(new char[_len]); + cli->recv_blocking(data.get(), _len, MSG_WAITALL); + ASSERT_STREQ(data.get(), str->value()); + cli->free(); + svr->free(); + }); + + 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, port); + ASSERT_EQ(cli->connect(addr), SW_OK); + int len = htonl(str->get_length()); + cli->send(&len, sizeof(len), 0); + ASSERT_EQ(cli->sendfile_blocking(file.c_str(), 0, 0, -1), SW_OK); + cli->free(); + }); + + t1.join(); + t2.join(); +} + +TEST(socket, peek) { + char sock1_path[] = "/tmp/udp_unix1.sock"; + char sock2_path[] = "/tmp/udp_unix2.sock"; + + unlink(sock1_path); + unlink(sock2_path); + + auto sock1 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0); + sock1->bind(sock1_path, nullptr); + + auto sock2 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0); + sock2->bind(sock2_path, nullptr); + + ASSERT_GT(sock1->sendto(sock2_path, 0, test_data, strlen(test_data)), 0); + + char buf[1024] = {}; + ASSERT_GT(sock2->peek(buf, sizeof(buf), 0), 0); + ASSERT_STREQ(test_data, buf); + + sw_memset_zero(buf, sizeof(buf)); + ASSERT_GT(sock2->recv(buf, sizeof(buf), 0), 0); + ASSERT_STREQ(test_data, buf); + + sock1->free(); + sock2->free(); + unlink(sock1_path); + unlink(sock2_path); +} + +TEST(socket, sendto_blocking) { + char sock1_path[] = "/tmp/udp_unix1.sock"; + unlink(sock1_path); + auto sock1 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0); + sock1->bind(sock1_path, nullptr); + sock1->info.assign(SW_SOCK_UNIX_DGRAM, sock1_path, 0); + + char sock2_path[] = "/tmp/udp_unix2.sock"; + unlink(sock2_path); + auto sock2 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0); + sock2->bind(sock2_path, nullptr); + sock2->info.assign(SW_SOCK_UNIX_DGRAM, sock2_path, 0); + + char sendbuf[65536] = {}; + swoole_random_string(sendbuf, sizeof(sendbuf) - 1); + + thread t1([sock2, sendbuf]() { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + char recvbuf[65536] = {}; + while (1) { + auto retval = sock2->recv(recvbuf, sizeof(recvbuf) - 1, 0); + recvbuf[retval] = 0; + if (retval == 3) { + ASSERT_STREQ(recvbuf, "end"); + break; + } else { + ASSERT_STREQ(sendbuf, recvbuf); + } + } + }); + + for (int i = 0; i < 10; i++) { + ASSERT_GT(sock1->sendto_blocking(sock2->info, sendbuf, strlen(sendbuf)), 0); + } + ASSERT_GT(sock1->sendto_blocking(sock2->info, "end", 3), 0); + + t1.join(); + + sock1->free(); + sock2->free(); + unlink(sock1_path); + unlink(sock2_path); +} + +TEST(socket, clean) { + char sock1_path[] = "/tmp/udp_unix1.sock"; + unlink(sock1_path); + auto sock1 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0); + sock1->bind(sock1_path, nullptr); + sock1->info.assign(SW_SOCK_UNIX_DGRAM, sock1_path, 0); + + char sock2_path[] = "/tmp/udp_unix2.sock"; + unlink(sock2_path); + auto sock2 = make_socket(SW_SOCK_UNIX_DGRAM, SW_FD_DGRAM_SERVER, 0); + sock2->bind(sock2_path, nullptr); + sock2->info.assign(SW_SOCK_UNIX_DGRAM, sock2_path, 0); + + char sendbuf[65536] = {}; + swoole_random_string(sendbuf, sizeof(sendbuf) - 1); + + for (int i = 0; i < 3; i++) { + ASSERT_GT(sock1->sendto_blocking(sock2->info, sendbuf, strlen(sendbuf)), 0); + } + + sock2->clean(); + char recvbuf[1024]; + auto retval = sock2->peek(recvbuf, sizeof(recvbuf), MSG_DONTWAIT); + ASSERT_EQ(retval, -1); + + sock1->free(); + sock2->free(); + unlink(sock1_path); + unlink(sock2_path); +} + +TEST(socket, check_liveness) { + mutex m; + int svr_port = swoole::test::get_random_port(); + m.lock(); + + thread t1([&m, svr_port]() { + auto svr = make_server_socket(SW_SOCK_TCP, TEST_HOST, svr_port); + m.unlock(); + + auto cli = svr->accept(); + ASSERT_TRUE(cli); + + char buf[1024] = {}; + cli->recv(buf, sizeof(buf), 0); + ASSERT_STREQ(test_data, buf); + + ssize_t n = cli->recv(buf, sizeof(buf), 0); + buf[n] = 0; + ASSERT_STREQ("close", buf); + cli->shutdown(SHUT_RDWR); + cli->free(); + + svr->free(); + }); + + 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, svr_port), SW_OK); + + cli->send(test_data, sizeof(test_data), 0); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + ASSERT_TRUE(cli->check_liveness()); + + cli->send(SW_STRL("close"), 0); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + ASSERT_FALSE(cli->check_liveness()); + + cli->free(); + }); + + 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 new file mode 100644 index 00000000000..5dd870eefa0 --- /dev/null +++ b/core-tests/src/network/stream.cpp @@ -0,0 +1,112 @@ +/* + +----------------------------------------------------------------------+ + | 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" + +using namespace std; +using namespace swoole; +using namespace swoole::network; + +TEST(stream, send) { + 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); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, svr_port); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + port->open_length_check = true; + Stream::set_protocol(&port->protocol); + + mutex lock; + lock.lock(); + + char buf[65536]; + ASSERT_EQ(swoole_random_bytes(buf, sizeof(buf)), sizeof(buf)); + + ASSERT_EQ(serv.create(), SW_OK); + + std::thread t1([&]() { + swoole_signal_block_all(); + + lock.lock(); + + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + + // bad request + auto stream0 = Stream::create(TEST_TMP_FILE, 0, SW_SOCK_UNIX_STREAM); + ASSERT_EQ(stream0, nullptr); + + // bad request + auto stream1 = Stream::create(TEST_HOST, 39999, SW_SOCK_TCP); + ASSERT_TRUE(stream1); + stream1->response = [](Stream *stream, const char *data, uint32_t length) { + EXPECT_EQ(data, nullptr); + EXPECT_EQ(stream->errCode, ECONNREFUSED); + }; + ASSERT_EQ(stream1->send(buf, sizeof(buf)), SW_OK); + + // success requset + 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) { + string *buf = (string *) stream->private_data; + string pkt = string("Server: ") + *buf; + EXPECT_EQ(string(data, length), pkt); + delete buf; + }; + ASSERT_EQ(stream2->send(buf, sizeof(buf)), SW_OK); + + swoole_event_wait(); + + kill(getpid(), SIGTERM); + }); + + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; + + 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); + + string pkt = string("Server: ") + req_body; + int packed_len = htonl(pkt.length()); + + EXPECT_TRUE(serv->send(req->info.fd, &packed_len, sizeof(packed_len))); + EXPECT_TRUE(serv->send(req->info.fd, pkt.c_str(), pkt.length())); + + // end stream + packed_len = htonl(0); + EXPECT_TRUE(serv->send(req->info.fd, &packed_len, sizeof(packed_len))); + + return SW_OK; + }; + + serv.start(); + t1.join(); + + sw_logger()->set_level(ori_log_level); +} diff --git a/core-tests/src/os/async.cpp b/core-tests/src/os/async.cpp new file mode 100644 index 00000000000..8a122069db7 --- /dev/null +++ b/core-tests/src/os/async.cpp @@ -0,0 +1,99 @@ +/* + +----------------------------------------------------------------------+ + | 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_socket.h" +#include "swoole_async.h" + +#include + +using namespace swoole; + +static int callback_count; + +TEST(async, dispatch) { + callback_count = 0; + std::atomic handle_count(0); + AsyncEvent event = {}; + event.object = &handle_count; + event.callback = [](AsyncEvent *event) { callback_count++; }; + event.handler = [](AsyncEvent *event) { (*(std::atomic *) event->object)++; }; + + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + + for (int i = 0; i < 1000; ++i) { + auto ret = swoole::async::dispatch(&event); + EXPECT_EQ(ret->object, event.object); + } + + swoole_event_wait(); + + 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 new file mode 100644 index 00000000000..31a7a72f755 --- /dev/null +++ b/core-tests/src/os/msg_queue.cpp @@ -0,0 +1,57 @@ +/* + +----------------------------------------------------------------------+ + | 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_msg_queue.h" + +using swoole::MsgQueue; +using swoole::QueueNode; + +TEST(msg_queue, rbac) { + MsgQueue q(0x950001); + ASSERT_TRUE(q.ready()); + QueueNode in; + in.mtype = 999; + strcpy(in.mdata, "hello world"); + + if (!swoole::test::is_github_ci()) { + ASSERT_TRUE(q.set_capacity(8192)); + } + + // input data + ASSERT_TRUE(q.push(&in, strlen(in.mdata))); + + size_t queue_num, queue_bytes; + ASSERT_TRUE(q.stat(&queue_num, &queue_bytes)); + ASSERT_EQ(queue_num, 1); + ASSERT_GT(queue_bytes, 10); + + // output data + QueueNode out{}; + ASSERT_GT(q.pop(&out, sizeof(out.mdata)), 1); + + ASSERT_TRUE(q.stat(&queue_num, &queue_bytes)); + ASSERT_EQ(queue_num, 0); + ASSERT_EQ(queue_bytes, 0); + + ASSERT_EQ(out.mtype, in.mtype); + ASSERT_STREQ(out.mdata, in.mdata); + + ASSERT_TRUE(q.destroy()); +} diff --git a/core-tests/src/os/pipe.cpp b/core-tests/src/os/pipe.cpp new file mode 100644 index 00000000000..f966e0cb2ef --- /dev/null +++ b/core-tests/src/os/pipe.cpp @@ -0,0 +1,53 @@ +#include "test_core.h" +#include "swoole_pipe.h" + +using namespace swoole; + +TEST(pipe, unixsock) { + UnixSocket p(true, SOCK_DGRAM); + ASSERT_TRUE(p.ready()); + + char buf[1024]; + + int ret = p.write((void *) SW_STRS("hello world1")); + ASSERT_GT(ret, 0); + ret = p.write((void *) SW_STRS("hello world2")); + ASSERT_GT(ret, 0); + ret = p.write((void *) SW_STRS("hello world3")); + ASSERT_GT(ret, 0); + + // 1 + ret = p.read(buf, sizeof(buf)); + if (ret < 0) { + swoole_sys_warning("read() failed."); + } + ASSERT_GT(ret, 0); + ASSERT_EQ(strcmp("hello world1", buf), 0); + // 2 + ret = p.read(buf, sizeof(buf)); + ASSERT_GT(ret, 0); + ASSERT_EQ(strcmp("hello world2", buf), 0); + // 3 + ret = p.read(buf, sizeof(buf)); + ASSERT_GT(ret, 0); + ASSERT_EQ(strcmp("hello world3", buf), 0); +} + +TEST(pipe, base) { + int ret; + char data[256]; + + Pipe p(true); + ASSERT_TRUE(p.ready()); + + + ret = p.write((void *) SW_STRL("hello world\n")); + ASSERT_GT(ret, 0); + ret = p.write((void *) SW_STRL("你好中国。\n")); + ASSERT_GT(ret, 0); + + sw_memset_zero(data, 256); + ret = p.read(data, 255); + ASSERT_GT(ret, 0); + ASSERT_EQ(strcmp("hello world\n你好中国。\n", data), 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 c2dea989f34..a2f2527a14a 100644 --- a/core-tests/src/os/signal.cpp +++ b/core-tests/src/os/signal.cpp @@ -1,40 +1,34 @@ -#include "tests.h" +#include "test_core.h" +#include "swoole_signal.h" #ifdef HAVE_SIGNALFD -static void sig_usr1(int signo ){ +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(); - SwooleTG.reactor->wait_exit = 1; + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); sigemptyset(&curset); sigprocmask(SIG_BLOCK, NULL, &curset); - ret = sigismember(&curset,SIGUSR1); + ret = sigismember(&curset, SIGUSR1); ASSERT_EQ(ret, 0); - swSignalfd_init(); - swSignal_add(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); + ret = sigismember(&curset, SIGUSR1); ASSERT_EQ(ret, 1); - swSignal_add(SIGUSR1, NULL); - swSignal_clear(); + swoole_signal_set(SIGUSR1, NULL); + swoole_signal_clear(); sigemptyset(&curset); sigprocmask(SIG_BLOCK, NULL, &curset); - ret = sigismember(&curset,SIGUSR1); + ret = sigismember(&curset, SIGUSR1); ASSERT_EQ(ret, 0); swoole_event_wait(); diff --git a/core-tests/src/os/timer.cpp b/core-tests/src/os/timer.cpp new file mode 100644 index 00000000000..98dfdef8d80 --- /dev/null +++ b/core-tests/src/os/timer.cpp @@ -0,0 +1,152 @@ +/* + +----------------------------------------------------------------------+ + | 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" +#include "swoole_timer.h" + +using swoole::Timer; +using swoole::TimerNode; + +TEST(timer, sys) { + int timer1_count = 0; + int timer2_count = 0; + int timer_running = true; + + uint64_t ms1 = swoole::time(); + + swoole_timer_add( + 20L, false, [&](Timer *, TimerNode *) { timer1_count++; }, nullptr); + + swoole_timer_add( + 100L, + true, + [&](Timer *, TimerNode *tnode) { + timer2_count++; + if (timer2_count == 5) { + swoole_timer_del(tnode); + timer_running = false; + } + }, + nullptr); + + while (1) { + sleep(10); + if (SwooleG.signal_alarm) { + swoole_timer_select(); + if (!timer_running) { + break; + } + } + } + + uint64_t ms2 = swoole::time(); + + swoole_timer_free(); + + 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 3d7f3f2c366..84e94689b8a 100644 --- a/core-tests/src/os/wait.cpp +++ b/core-tests/src/os/wait.cpp @@ -1,22 +1,35 @@ -#include "tests.h" -#include "swoole/coroutine_c_api.h" +#include "test_coroutine.h" using namespace swoole; using namespace swoole::test; +using swoole::coroutine::System; -TEST(os_wait, waitpid_before_child_exit) -{ - test::coroutine::test([](void *arg) - { - pid_t pid = fork(); - ASSERT_NE(pid, -1); - - if (pid == 0) - { - usleep(100000); - exit(0); - } +static pid_t fork_child() { + pid_t pid = fork(); + EXPECT_NE(pid, -1); + + 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); @@ -24,19 +37,9 @@ TEST(os_wait, waitpid_before_child_exit) }); } -TEST(os_wait, waitpid_after_child_exit) -{ - test::coroutine::test([](void *arg) - { - pid_t pid = fork(); - ASSERT_NE(pid, -1); - - if (pid == 0) - { - exit(0); - } - - usleep(100000); +TEST(os_wait, waitpid_after_child_exit) { + test::coroutine::run([](void *arg) { + pid_t pid = fork_child2(); int status = -1; pid_t pid2 = swoole_coroutine_waitpid(pid, &status, 0); ASSERT_EQ(status, 0); @@ -44,27 +47,15 @@ TEST(os_wait, waitpid_after_child_exit) }); } -TEST(os_wait, wait_before_child_exit) -{ - test::coroutine::test([](void *arg) - { - pid_t pid = fork(); - ASSERT_NE(pid, -1); - - if (pid == 0) - { - usleep(100000); - exit(0); - } - +TEST(os_wait, wait_before_child_exit) { + test::coroutine::run([](void *arg) { + pid_t pid = fork_child(); int status = -1; pid_t pid2 = -1; - for (;;) - { + for (;;) { pid2 = swoole_coroutine_wait(&status); - if (pid2 == pid) - { + if (pid2 == pid) { break; } } @@ -73,27 +64,15 @@ TEST(os_wait, wait_before_child_exit) }); } -TEST(os_wait, wait_after_child_exit) -{ - test::coroutine::test([](void *arg) - { - pid_t pid = fork(); - ASSERT_NE(pid, -1); - - if (pid == 0) - { - exit(0); - } - - usleep(100000); +TEST(os_wait, wait_after_child_exit) { + test::coroutine::run([](void *arg) { + pid_t pid = fork_child2(); int status = -1; pid_t pid2 = -1; - for (;;) - { + for (;;) { pid2 = swoole_coroutine_wait(&status); - if (pid2 == pid) - { + if (pid2 == pid) { break; } } @@ -101,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/pipe.cpp b/core-tests/src/pipe.cpp deleted file mode 100644 index 172640efa76..00000000000 --- a/core-tests/src/pipe.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "tests.h" - -TEST(pipe, unixsock) -{ - swPipe p; - char buf[1024]; - bzero(&p, sizeof(p)); - int ret = swPipeUnsock_create(&p, 1, SOCK_DGRAM); - ASSERT_EQ(ret, 0); - - ret = p.write(&p, (void*) SW_STRS("hello world1")); - ASSERT_GT(ret, 0); - ret = p.write(&p, (void*) SW_STRS("hello world2")); - ASSERT_GT(ret, 0); - ret = p.write(&p, (void*) SW_STRS("hello world3")); - ASSERT_GT(ret, 0); - - //1 - ret = p.read(&p, buf, sizeof(buf)); - if (ret < 0) - { - swSysWarn("read() failed."); - } - ASSERT_GT(ret, 0); - ASSERT_EQ(strcmp("hello world1", buf), 0); - //2 - ret = p.read(&p, buf, sizeof(buf)); - ASSERT_GT(ret, 0); - ASSERT_EQ(strcmp("hello world2", buf), 0); - //3 - ret = p.read(&p, buf, sizeof(buf)); - ASSERT_GT(ret, 0); - ASSERT_EQ(strcmp("hello world3", buf), 0); -} - -TEST(pipe, base) -{ - swPipe p; - int ret; - char data[256]; - - ret = swPipeBase_create(&p, 1); - ASSERT_EQ(ret, 0); - ret = p.write(&p, (void *) SW_STRL("hello world\n")); - ASSERT_GT(ret, 0); - ret = p.write(&p, (void *) SW_STRL("你好中国。\n")); - ASSERT_GT(ret, 0); - - bzero(data, 256); - ret = p.read(&p, data, 255); - ASSERT_GT(ret, 0); - ASSERT_EQ(strcmp("hello world\n你好中国。\n", data), 0); -} diff --git a/core-tests/src/process/process.cpp b/core-tests/src/process/process.cpp deleted file mode 100644 index 31f6a5c6462..00000000000 --- a/core-tests/src/process/process.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "process.h" - -using swoole::test::process; - -process::process(std::function fn): - handler(fn) -{ - swPipe *pipe = (swPipe *) malloc(sizeof(swPipe)); - swPipeUnsock_create(pipe, 1, SOCK_DGRAM); - - worker.pipe_master = pipe->getSocket(pipe, SW_PIPE_MASTER); - worker.pipe_worker = pipe->getSocket(pipe, SW_PIPE_WORKER); - - worker.pipe_object = pipe; - worker.pipe_current = worker.pipe_master; -} - -process::~process() -{ - worker.pipe_object->close(worker.pipe_object); - free(worker.pipe_object); -} - -pid_t process::start() -{ - pid_t pid = fork(); - - if (pid < 0) - { - printf("[Worker] Fatal Error: fork() failed"); - exit(1); - } - else if (pid == 0) // child - { - worker.child_process = 1; - worker.pipe_current = worker.pipe_worker; - handler(this); - - exit(0); - } - else // parent - { - worker.pid = pid; - worker.child_process = 0; - - return pid; - } -} - -ssize_t process::write(const void *__buf, size_t __n) -{ - return ::write(worker.pipe_current->fd, __buf, __n); -} - -ssize_t process::read(void *__buf, size_t __nbytes) -{ - return ::read(worker.pipe_current->fd, __buf, __nbytes); -} diff --git a/core-tests/src/protocol/base.cpp b/core-tests/src/protocol/base.cpp new file mode 100644 index 00000000000..d815e87b551 --- /dev/null +++ b/core-tests/src/protocol/base.cpp @@ -0,0 +1,87 @@ +/* + +----------------------------------------------------------------------+ + | 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_redis.h" + +using namespace swoole; +using namespace std; + +constexpr int PKG_N = 32; +constexpr int MAX_SIZE = 128000; +constexpr int MIN_SIZE = 512; + +TEST(protocol, eof) { + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + + String pkgs[PKG_N]; + + for (int i = 0; i < PKG_N; i++) { + pkgs[i].append_random_bytes(swoole_rand(MIN_SIZE, MAX_SIZE), true); + pkgs[i].append("\r\n"); + } + + sw_logger()->set_level(SW_LOG_WARNING); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + ASSERT_TRUE(port); + port->set_eof_protocol("\r\n", false); + + mutex lock; + lock.lock(); + serv.create(); + + thread t1([&]() { + lock.lock(); + + network::Client cli(SW_SOCK_TCP, false); + EXPECT_EQ(cli.connect(&cli, TEST_HOST, port->port, 1, 0), 0); + + for (int i = 0; i < PKG_N; i++) { + EXPECT_EQ(cli.send(&cli, pkgs[i].str, pkgs[i].length, 0), pkgs[i].length); + } + }); + + serv.onWorkerStart = [&lock](Server *serv, Worker *worker) { lock.unlock(); }; + + int recv_count = 0; + + 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); + + EXPECT_EQ(memcmp(req->data, pkgs[recv_count].str, req->info.len), 0); + + recv_count++; + + if (recv_count == PKG_N) { + kill(serv->get_master_pid(), SIGTERM); + } + + return SW_OK; + }; + + serv.start(); + + t1.join(); +} diff --git a/core-tests/src/protocol/base64.cpp b/core-tests/src/protocol/base64.cpp new file mode 100644 index 00000000000..3328a3e66b1 --- /dev/null +++ b/core-tests/src/protocol/base64.cpp @@ -0,0 +1,38 @@ +/* + +----------------------------------------------------------------------+ + | 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_base64.h" + +TEST(base64, encode) { + char inbuf[1024]; + char outbuf[2048]; + + auto n = swoole_random_bytes(inbuf, sizeof(inbuf) - 1); + auto n2 = swoole::base64_encode((uchar *) inbuf, n, outbuf); + ASSERT_GT(n2, n); +} + +TEST(base64, decode) { + const char *inbuf = "aGVsbG8gd29ybGQ="; + char outbuf[2048]; + + auto n2 = swoole::base64_decode(inbuf, strlen(inbuf), outbuf); + ASSERT_EQ(std::string(outbuf, n2), "hello world"); +} 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 new file mode 100644 index 00000000000..eabd700f828 --- /dev/null +++ b/core-tests/src/protocol/mime_type.cpp @@ -0,0 +1,54 @@ +/* + +----------------------------------------------------------------------+ + | 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_mime_type.h" + +using namespace swoole; + +TEST(mime_type, get) { + auto result = mime_type::get("test.html.json"); + ASSERT_EQ(result, "application/json"); +} + +TEST(mime_type, exists) { + ASSERT_TRUE(mime_type::exists("test.html.json")); +} + +TEST(mime_type, set) { + std::string test_mime_type("application/swoole-core-test"); + mime_type::set("swoole_test", test_mime_type); + + auto result = mime_type::get("test.swoole_test"); + ASSERT_EQ(result, test_mime_type); +} + +TEST(mime_type, add) { + std::string test_mime_type("application/swoole-core-test2"); + ASSERT_TRUE(mime_type::add("swoole_test2", test_mime_type)); + ASSERT_FALSE(mime_type::add("swoole_test2", test_mime_type)); + + auto result = mime_type::get("test.swoole_test2"); + ASSERT_EQ(result, test_mime_type); +} + +TEST(mime_type, del) { + ASSERT_TRUE(mime_type::del("json")); + ASSERT_FALSE(mime_type::exists("test.html.json")); +} diff --git a/core-tests/src/protocol/redis.cpp b/core-tests/src/protocol/redis.cpp new file mode 100644 index 00000000000..0ab9f144480 --- /dev/null +++ b/core-tests/src/protocol/redis.cpp @@ -0,0 +1,88 @@ +/* + +----------------------------------------------------------------------+ + | 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_redis.h" + +using namespace swoole; +using namespace std; + +const std::string REDIS_TEST_KEY = "key-swoole"; +const std::string REDIS_TEST_VALUE = "value-swoole"; + +TEST(redis, get) { + test::coroutine::run([](void *arg) { + RedisClient redis; + ASSERT_TRUE(redis.Connect("127.0.0.1", 6379)); + ASSERT_TRUE(redis.Set(REDIS_TEST_KEY, REDIS_TEST_VALUE)); + ASSERT_EQ(redis.Get(REDIS_TEST_KEY), REDIS_TEST_VALUE); + }); +} + +TEST(redis, server) { + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + serv.enable_static_handler = true; + + sw_logger()->set_level(SW_LOG_WARNING); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + ASSERT_TRUE(port); + port->open_redis_protocol = true; + + serv.create(); + + serv.onWorkerStart = [&](Server *serv, Worker *worker) { + if (worker->id != 0) { + return; + } + swoole::Coroutine::create( + [](void *arg) { + Server *serv = reinterpret_cast(arg); + RedisClient redis; + ASSERT_TRUE(redis.Connect("127.0.0.1", serv->get_primary_port()->port)); + ASSERT_TRUE(redis.Set(REDIS_TEST_KEY, REDIS_TEST_VALUE)); + ASSERT_EQ(redis.Get(REDIS_TEST_KEY), REDIS_TEST_VALUE); + kill(serv->gs->master_pid, SIGTERM); + }, + serv); + }; + + serv.onReceive = [](Server *serv, RecvData *req) -> int { + int session_id = req->info.fd; + auto list = redis::parse(req->data, req->info.len); + + String *buffer = sw_tg_buffer(); + buffer->clear(); + + if (strcasecmp(list[0].c_str(), "GET") == 0) { + 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) { + redis::format(buffer, redis::REPLY_STATUS, "OK"); + serv->send(session_id, buffer->str, buffer->length); + } + + return SW_OK; + }; + + serv.start(); +} diff --git a/core-tests/src/reactor/base.cpp b/core-tests/src/reactor/base.cpp index cbd2b6e2f54..aa018aee9d7 100644 --- a/core-tests/src/reactor/base.cpp +++ b/core-tests/src/reactor/base.cpp @@ -1,111 +1,103 @@ -#include "tests.h" -#include "swoole/swoole_api.h" - -TEST(reactor, swReactor_create) -{ - swReactor reactor; - - int ret = swReactor_create(&reactor, SW_REACTOR_MAXEVENTS); - ASSERT_EQ(ret, SW_OK); - - ASSERT_NE(reactor.object, nullptr); - ASSERT_EQ(reactor.max_event_num, SW_REACTOR_MAXEVENTS); - - ASSERT_NE(reactor.add, nullptr); - ASSERT_NE(reactor.set, nullptr); - ASSERT_NE(reactor.del, nullptr); - ASSERT_NE(reactor.wait, nullptr); - ASSERT_NE(reactor.free, nullptr); - - ASSERT_EQ(reactor.running, 1); - ASSERT_NE(reactor.onFinish, nullptr); - ASSERT_NE(reactor.onTimeout, nullptr); - ASSERT_NE(reactor.is_empty, nullptr); - ASSERT_EQ(reactor.can_exit, nullptr); // set in PHP_METHOD(swoole_coroutine_scheduler, set) - ASSERT_NE(reactor.write, nullptr); - ASSERT_NE(reactor.close, nullptr); - ASSERT_NE(reactor.defer, nullptr); - ASSERT_EQ(reactor.defer_tasks, nullptr); - ASSERT_NE(reactor.default_write_handler, nullptr); +/* + +----------------------------------------------------------------------+ + | 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_pipe.h" +#include "swoole_util.h" + +using namespace std; +using namespace swoole; + +TEST(reactor, create) { + swoole_event_init(0); + + Reactor *reactor = SwooleTG.reactor; + + ASSERT_EQ(reactor->max_event_num, SW_REACTOR_MAXEVENTS); + + ASSERT_TRUE(reactor->running); + ASSERT_NE(reactor->write, nullptr); + ASSERT_NE(reactor->close, nullptr); + ASSERT_EQ(reactor->defer_tasks, nullptr); + ASSERT_NE(reactor->default_write_handler, nullptr); /** * coroutine socket reactor */ - ASSERT_NE(reactor.read_handler[swReactor_fdtype(SW_FD_CORO_SOCKET | SW_EVENT_READ)], nullptr); - ASSERT_NE(reactor.write_handler[swReactor_fdtype(SW_FD_CORO_SOCKET | SW_EVENT_WRITE)], nullptr); - ASSERT_NE(reactor.error_handler[swReactor_fdtype(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[swReactor_fdtype(SW_FD_CORO_POLL | SW_EVENT_READ)], nullptr); - ASSERT_NE(reactor.write_handler[swReactor_fdtype(SW_FD_CORO_POLL | SW_EVENT_WRITE)], nullptr); - ASSERT_NE(reactor.error_handler[swReactor_fdtype(SW_FD_CORO_POLL | SW_EVENT_ERROR)], nullptr); - - ASSERT_NE(reactor.read_handler[swReactor_fdtype(SW_FD_CORO_EVENT | SW_EVENT_READ)], nullptr); - ASSERT_NE(reactor.write_handler[swReactor_fdtype(SW_FD_CORO_EVENT | SW_EVENT_WRITE)], nullptr); - ASSERT_NE(reactor.error_handler[swReactor_fdtype(SW_FD_CORO_EVENT | 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[swReactor_fdtype(SW_FD_AIO | SW_EVENT_READ)], 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); -TEST(reactor, swReactor_set_handler) -{ - swReactor reactor; + ASSERT_NE(reactor->read_handler[Reactor::get_fd_type(SW_FD_AIO | SW_EVENT_READ)], nullptr); - swReactor_set_handler(&reactor, SW_EVENT_READ, (swReactor_handler) 0x1); - ASSERT_EQ(reactor.read_handler[swReactor_fdtype(SW_EVENT_READ)], (swReactor_handler) 0x1); - - swReactor_set_handler(&reactor, SW_EVENT_WRITE, (swReactor_handler) 0x2); - ASSERT_EQ(reactor.write_handler[swReactor_fdtype(SW_EVENT_WRITE)], (swReactor_handler) 0x2); - - swReactor_set_handler(&reactor, SW_EVENT_ERROR, (swReactor_handler) 0x3); - ASSERT_EQ(reactor.error_handler[swReactor_fdtype(SW_EVENT_ERROR)], (swReactor_handler) 0x3); + swoole_event_free(); } -TEST(reactor, swReactor_close) -{ - swReactor reactor; +TEST(reactor, set_handler) { + Reactor reactor; - swSocket *socket = swSocket_new(10, SW_FD_SESSION); - socket->in_buffer = swBuffer_new(SW_BUFFER_MIN_SIZE); - socket->out_buffer = swBuffer_new(SW_SEND_BUFFER_SIZE); + reactor.set_handler(SW_EVENT_READ, (ReactorHandler) 0x1); + ASSERT_EQ(reactor.read_handler[Reactor::get_fd_type(SW_EVENT_READ)], (ReactorHandler) 0x1); - swReactor_close(&reactor, socket); + reactor.set_handler(SW_EVENT_WRITE, (ReactorHandler) 0x2); + ASSERT_EQ(reactor.write_handler[Reactor::get_fd_type(SW_EVENT_WRITE)], (ReactorHandler) 0x2); - ASSERT_EQ(socket->in_buffer, nullptr); - ASSERT_EQ(socket->out_buffer, nullptr); + reactor.set_handler(SW_EVENT_ERROR, (ReactorHandler) 0x3); + ASSERT_EQ(reactor.error_handler[Reactor::get_fd_type(SW_EVENT_ERROR)], (ReactorHandler) 0x3); } -TEST(reactor, swReactor_wait) -{ +TEST(reactor, wait) { int ret; - swPipe p; + UnixSocket p(true, SOCK_DGRAM); + ASSERT_TRUE(p.ready()); - ret = swoole_event_init(); + ret = swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); ASSERT_EQ(ret, SW_OK); ASSERT_NE(SwooleTG.reactor, nullptr); - ret = swPipeUnsock_create(&p, 1, SOCK_DGRAM); - ASSERT_EQ(ret, SW_OK); - - swoole_event_set_handler(SW_FD_PIPE | SW_EVENT_READ, [](swReactor *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)); EXPECT_EQ(sizeof("hello world"), n); EXPECT_STREQ("hello world", buffer); - reactor->del(reactor, ev->socket); - reactor->wait_exit = 1; + reactor->del(ev->socket); return SW_OK; }); - ret = swoole_event_add(p.worker_socket, SW_EVENT_READ); + 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(&p, (void *) SW_STRS("hello world")); + ret = p.write((void *) SW_STRS("hello world")); ASSERT_EQ(ret, sizeof("hello world")); ret = swoole_event_wait(); @@ -113,38 +105,271 @@ TEST(reactor, swReactor_wait) ASSERT_EQ(SwooleTG.reactor, nullptr); } -TEST(reactor, swReactor_write) -{ +TEST(reactor, write) { int ret; - swPipe p; + UnixSocket p(true, SOCK_DGRAM); + ASSERT_TRUE(p.ready()); + p.set_blocking(false); + p.set_buffer_size(65536); - ret = swoole_event_init(); + ret = swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); ASSERT_EQ(ret, SW_OK); ASSERT_NE(SwooleTG.reactor, nullptr); - ret = swPipeUnsock_create(&p, 1, SOCK_DGRAM); - ASSERT_EQ(ret, SW_OK); - - swoole_event_set_handler(SW_FD_PIPE | SW_EVENT_READ, [](swReactor *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)); EXPECT_EQ(sizeof("hello world"), n); EXPECT_STREQ("hello world", buffer); - reactor->del(reactor, ev->socket); - reactor->wait_exit = 1; - + reactor->del(ev->socket); + return SW_OK; }); - ret = swoole_event_add(p.worker_socket, SW_EVENT_READ); + ret = swoole_event_add(p.get_socket(false), SW_EVENT_READ); ASSERT_EQ(ret, SW_OK); - ret = swoole_event_write(p.master_socket, (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, Event *event) -> int { + char buf[1024]; + size_t l = strlen(pkt); + size_t n = read(event->fd, buf, sizeof(buf)); + EXPECT_EQ(n, l); + buf[n] = 0; + EXPECT_EQ(std::string(buf, n), std::string(pkt)); + reactor->del(event->socket); + + return SW_OK; + }); + 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); + + return SW_OK; + }); + reactor->add(p.get_socket(false), SW_EVENT_READ); + reactor->add(p.get_socket(true), SW_EVENT_WRITE); + reactor->wait(nullptr); +} + +TEST(reactor, poll) { + Reactor reactor(1024, Reactor::TYPE_POLL); + reactor.wait_exit = true; + reactor_test_func(&reactor); +} + +TEST(reactor, select) { + Reactor reactor(1024, Reactor::TYPE_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/ringbuffer.cpp b/core-tests/src/ringbuffer.cpp deleted file mode 100644 index 8f2c5f34217..00000000000 --- a/core-tests/src/ringbuffer.cpp +++ /dev/null @@ -1,139 +0,0 @@ -#include "tests.h" - -#include - -#define READ_THREAD_N 4 -#define WRITE_N 10000 -#define PACKET_LEN 90000 -//#define PRINT_SERNUM_N 10 - -static swMemoryPool *pool = NULL; - -typedef struct -{ - uint32_t id; - uint32_t size; - uint32_t serial_num; - void* ptr; -} pkg; - -typedef struct -{ - std::thread *thread; - swPipe pipe; -} ThreadObject; - -static void thread_read(int i); -static void thread_write(void); -static ThreadObject threads[READ_THREAD_N]; - -TEST(ringbuffer, thread) -{ - int i; - pool = swRingBuffer_new(1024 * 1024 * 4, 1); - ASSERT_NE(pool, nullptr); - - for (i = 0; i < READ_THREAD_N; i++) - { - int ret = swPipeUnsock_create(&threads[i].pipe, 1, SOCK_DGRAM); - ASSERT_EQ(ret, 0); - threads[i].thread = new std::thread(thread_read, i); - } - - sleep(1); - srand((unsigned int) time(NULL)); - thread_write(); - - for (i = 0; i < READ_THREAD_N; i++) - { - threads[i].thread->join(); - threads[i].pipe.close(&threads[i].pipe); - delete threads[i].thread; - } -} - -static void thread_write(void) -{ - uint32_t size, yield_count = 0, yield_total_count = 0; - void *ptr; - pkg send_pkg; - bzero(&send_pkg, sizeof(send_pkg)); - - int i; - for (i = 0; i < WRITE_N; i++) - { - size = 10000 + rand() % PACKET_LEN; - //printf("[ < %d] alloc size=%d\n", i, size); - - yield_count = 0; - do - { - ptr = pool->alloc(pool, size); - if (ptr) - { - break; - } - else - { - yield_count++; - yield_total_count++; - usleep(10); - } - } while (yield_count < 100); - - if (!ptr) - { - printf("alloc failed. index=%d, break\n", i); - break; - } - - ASSERT_NE(ptr, nullptr); - - send_pkg.id = i; - send_pkg.ptr = ptr; - send_pkg.size = size; - send_pkg.serial_num = rand(); - - //保存长度值 - memcpy(ptr, &size, sizeof(size)); - //在指针末尾保存一个串号 - memcpy((char*) ptr + size - 4, &(send_pkg.serial_num), sizeof(send_pkg.serial_num)); - - ASSERT_FALSE(threads[i % READ_THREAD_N].pipe.write(&threads[i % READ_THREAD_N].pipe, &send_pkg, sizeof(send_pkg)) < 0); - } - -// printf("yield_total_count=%d\n", yield_total_count); -} - -static void thread_read(int i) -{ - pkg recv_pkg; - uint32_t tmp; - int ret; - uint32_t recv_count = 0; - int j = 0; - swPipe *sock = &threads[i].pipe; - int task_n = WRITE_N / READ_THREAD_N; - - for (j = 0; j < task_n; j++) - { - ret = sock->read(sock, &recv_pkg, sizeof(recv_pkg)); - ASSERT_FALSE(ret < 0); - - memcpy(&tmp, recv_pkg.ptr, sizeof(tmp)); - ASSERT_EQ(tmp, recv_pkg.size); - - memcpy(&tmp, (char*) recv_pkg.ptr + recv_pkg.size - 4, sizeof(tmp)); - ASSERT_EQ(tmp, recv_pkg.serial_num); - -#ifdef PRINT_SERNUM_N - if (j % PRINT_SERNUM_N == 0) - { - printf("[ > %d] recv. recv_count=%d, serial_num=%d\n", recv_pkg.id, recv_count, tmp); - } -#endif - - pool->free(pool, recv_pkg.ptr); - recv_count++; - } -} diff --git a/core-tests/src/server.cpp b/core-tests/src/server.cpp deleted file mode 100755 index 3cbaf247592..00000000000 --- a/core-tests/src/server.cpp +++ /dev/null @@ -1,201 +0,0 @@ -#include "swoole/swoole.h" -#include "swoole/server.h" - -namespace swoole_test -{ -static int my_onPacket(swServer *serv, swEventData *req); -static int my_onReceive(swServer *serv, swEventData *req); -static void my_onStart(swServer *serv); -static void my_onShutdown(swServer *serv); -static void my_onConnect(swServer *serv, swDataHead *info); -static void my_onClose(swServer *serv, swDataHead *info); -static void my_onWorkerStart(swServer *serv, int worker_id); -static void my_onWorkerStop(swServer *serv, int worker_id); - -static int g_receive_count = 0; - -int server_test() -{ - int ret; - swServer serv; - swServer_init(&serv); - - serv.reactor_num = 4; - serv.worker_num = 2; - - serv.factory_mode = SW_MODE_BASE; - //serv.factory_mode = SW_MODE_SINGLE; //SW_MODE_PROCESS/SW_MODE_THREAD/SW_MODE_BASE/SW_MODE_SINGLE - serv.max_connection = 10000; - //serv.open_cpu_affinity = 1; - //serv.open_tcp_nodelay = 1; - //serv.daemonize = 1; -// memcpy(serv.log_file, SW_STRS("/tmp/swoole.log")); //日志 - - serv.dispatch_mode = 2; -// serv.open_tcp_keepalive = 1; - -#ifdef HAVE_OPENSSL - //serv.ssl_cert_file = "tests/ssl/ssl.crt"; - //serv.ssl_key_file = "tests/ssl/ssl.key"; - //serv.open_ssl = 1; -#endif - - serv.onStart = my_onStart; - serv.onShutdown = my_onShutdown; - serv.onConnect = my_onConnect; - serv.onReceive = my_onReceive; - serv.onPacket = my_onPacket; - serv.onClose = my_onClose; - serv.onWorkerStart = my_onWorkerStart; - serv.onWorkerStop = my_onWorkerStop; - -// swSignal_add(SIGINT, user_signal); - - //create Server - ret = swServer_create(&serv); - if (ret < 0) - { - swTrace("create server fail[error=%d].\n", ret); - exit(1); - } - - swListenPort *port = swServer_add_port(&serv, SW_SOCK_TCP, "127.0.0.1", 9501); - port->open_eof_check = 0; - //config - port->backlog = 128; - memcpy(port->protocol.package_eof, SW_STRL("\r\n\r\n")); //开启eof检测,启用buffer区 - - swServer_add_port(&serv, SW_SOCK_UDP, "0.0.0.0", 9502); - swServer_add_port(&serv, SW_SOCK_TCP6, "::", 9503); - swServer_add_port(&serv, SW_SOCK_UDP6, "::", 9504); - - ret = swServer_start(&serv); - if (ret < 0) - { - swTrace("start server fail[error=%d].\n", ret); - exit(1); - } - return 0; -} - -void my_onWorkerStart(swServer *serv, int worker_id) -{ - printf("WorkerStart[%d]PID=%d\n", worker_id, getpid()); -} - -void my_onWorkerStop(swServer *serv, int worker_id) -{ - printf("WorkerStop[%d]PID=%d\n", worker_id, getpid()); -} - -int my_onReceive(swServer *serv, swEventData *req) -{ - int ret; - char resp_data[SW_IPC_BUFFER_SIZE]; - - g_receive_count++; - - swConnection *conn = swWorker_get_connection(serv, req->info.fd); - swoole_rtrim(req->data, req->info.len); - printf("onReceive[%d]: ip=%s|port=%d Data=%s|Len=%d\n", g_receive_count, - swSocket_get_ip(conn->socket_type, &conn->info), - swSocket_get_port(conn->socket_type, &conn->info), - req->data, req->info.len); - - int n = snprintf(resp_data, SW_IPC_BUFFER_SIZE, "Server: %.*s\n", req->info.len, req->data); - ret = serv->send(serv, req->info.fd, resp_data, n); - if (ret < 0) - { - printf("send to client fail. errno=%d\n", errno); - } - else - { - printf("send %d bytes to client success. data=%s\n", n, resp_data); - } - return SW_OK; -} - -int my_onPacket(swServer *serv, swEventData *req) -{ - int serv_sock = req->info.server_fd; - char *data; - serv->get_packet(serv, req, &data); - swDgramPacket *packet = (swDgramPacket *) data; - - int length; - char address[256]; - int port = 0; - int ret; - - if (packet->socket_type == SW_SOCK_UDP) - { - inet_ntop(AF_INET6, &packet->socket_addr.addr.inet_v4.sin_addr, address, sizeof(address)); - data = packet->data; - length = packet->length; - 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)); - data = packet->data; - length = packet->length; - port = ntohs(packet->socket_addr.addr.inet_v6.sin6_port); - } - else if (packet->socket_type == SW_SOCK_UNIX_DGRAM) - { - strcpy(address, packet->socket_addr.addr.un.sun_path); - data = packet->data; - length = packet->length; - } - - printf("Packet[client=%s:%d, %d bytes]: data=%.*s\n", address, port, length, length, data); - - char resp_data[SW_IPC_BUFFER_SIZE]; - int n = snprintf(resp_data, SW_IPC_BUFFER_SIZE, "Server: %.*s", length, data); - - if (packet->socket_type == SW_SOCK_UDP) - { - ret = swSocket_udp_sendto(serv_sock, address, port, resp_data, n); - } - else if (packet->socket_type == SW_SOCK_UDP6) - { - ret = swSocket_udp_sendto6(serv_sock, address, port, resp_data, n); - } - else if (packet->socket_type == SW_SOCK_UNIX_DGRAM) - { - ret = swSocket_unix_sendto(serv_sock, address, resp_data, n); - } - - if (ret < 0) - { - printf("send to client fail. errno=%d\n", errno); - } - else - { - printf("send %d bytes to client success. data=%s\n", n, resp_data); - } - - return SW_OK; -} - -void my_onStart(swServer *serv) -{ - swNotice("Server is running"); -} - -void my_onShutdown(swServer *serv) -{ - swNotice("Server is shutdown"); -} - -void my_onConnect(swServer *serv, swDataHead *info) -{ - printf("PID=%d\tConnect fd=%d|reactor_id=%d\n", getpid(), info->fd, info->reactor_id); -} - -void my_onClose(swServer *serv, swDataHead *info) -{ - printf("PID=%d\tClose fd=%d|reactor_id=%d\n", getpid(), info->fd, info->reactor_id); -} - -} diff --git a/core-tests/src/server/base.cpp b/core-tests/src/server/base.cpp deleted file mode 100644 index 4cbbdf97291..00000000000 --- a/core-tests/src/server/base.cpp +++ /dev/null @@ -1,136 +0,0 @@ -#include "tests.h" -#include "wrapper/server.h" - -using swoole::test::server; - -server::server(std::string _host, int _port, int _mode, int _type): - host(_host), port(_port), mode(_mode), type(_type) -{ - swServer_init(&serv); - - serv.worker_num = 1; - - if (mode == SW_MODE_BASE) - { - serv.reactor_num = 1; - serv.worker_num = 1; - } - - serv.factory_mode = (uint8_t) mode; - serv.dispatch_mode = 2; - - //create Server - int ret = swServer_create(&serv); - if (ret < 0) - { - swTrace("create server fail[error=%d].\n", ret); - exit(0); - } - this->listen(host, port, (swSocket_type) type); -} - -server::~server() -{ -} - -void server::on(std::string event, void *fn) -{ - if (event == "Start") - { - serv.onStart = (_onStart) fn; - } - else if (event == "onShutdown") - { - serv.onShutdown = (_onShutdown) fn; - } - else if (event == "onPipeMessage") - { - serv.onPipeMessage = (_onPipeMessage) fn; - } - else if (event == "onWorkerStart") - { - serv.onWorkerStart = (_onWorkerStart) fn; - } - else if (event == "onWorkerStop") - { - serv.onWorkerStop = (_onWorkerStop) fn; - } - else if (event == "onReceive") - { - serv.onReceive = (_onReceive) fn; - } - else if (event == "onPacket") - { - serv.onPacket = (_onPacket) fn; - } - else if (event == "onClose") - { - serv.onClose = (_onClose) fn; - } - else - { - serv.onConnect = (_onConnect) fn; - } -} - -bool server::start() -{ - serv.ptr2 = this; - - int ret = swServer_start(&serv); - if (ret < 0) - { - swTrace("start server fail[error=%d].\n", ret); - return false; - } - return true; -} - -bool server::listen(std::string host, int port, enum swSocket_type type) -{ - swListenPort *ls = swServer_add_port(&serv, type, (char *) host.c_str(), port); - if (ls == nullptr) - { - return false; - } - - ports.push_back(ls); - return true; -} - -size_t server::get_packet(swEventData *req, char **data_ptr) -{ - return serv.get_packet(&serv, req, data_ptr); -} - -int server::send(int session_id, void *data, uint32_t length) -{ - return serv.send(&serv, session_id, data, length); -} - -ssize_t server::sendto(swSocketAddress *address, const char *__buf, size_t __n, int server_socket) -{ - char ip[256]; - uint16_t port; - - inet_ntop(AF_INET, (void *) &address->addr.inet_v4.sin_addr, ip, sizeof(ip)); - port = ntohs(address->addr.inet_v4.sin_port); - - return swSocket_udp_sendto(server_socket, ip, port, __buf, __n); -} - -int server::close(int session_id, int reset) -{ - return serv.close(&serv, session_id, reset); -} - -void create_test_server(swServer *serv) -{ - swServer_init(serv); - - swServer_create(serv); - - SwooleG.memory_pool = swMemoryGlobal_new(SW_GLOBAL_MEMORY_PAGESIZE, 1); - serv->workers = (swWorker *) SwooleG.memory_pool->alloc(SwooleG.memory_pool, serv->worker_num * sizeof(swWorker)); - swFactoryProcess_create(&serv->factory, serv->worker_num); -} \ No newline at end of file diff --git a/core-tests/src/server/buffer.cpp b/core-tests/src/server/buffer.cpp new file mode 100644 index 00000000000..9c0df8f346f --- /dev/null +++ b/core-tests/src/server/buffer.cpp @@ -0,0 +1,83 @@ +/* + +----------------------------------------------------------------------+ + | 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" + +using namespace std; +using namespace swoole; + +static const char *packet = "hello world\n"; + +TEST(server, send_buffer) { + 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); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + ASSERT_EQ(serv.create(), SW_OK); + + mutex lock; + lock.lock(); + + std::thread t1([&]() { + swoole_signal_block_all(); + + lock.lock(); + + swoole::network::SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, port->port); + c.send(packet, strlen(packet)); + char buf[4096]; + + while (1) { + ssize_t retval = c.recv(buf, sizeof(buf)); + if (retval <= 0) { + break; + } + usleep(100); + } + + 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)); + + swString resp(1024 * 1024 * 16); + resp.repeat("A", 1, resp.capacity()); + EXPECT_TRUE(serv->send(req->info.fd, resp.value(), resp.get_length())); + EXPECT_TRUE(serv->close(req->info.fd, 0)); + + return SW_OK; + }; + + serv.start(); + t1.join(); +} diff --git a/core-tests/src/server/http.cpp b/core-tests/src/server/http.cpp new file mode 100644 index 00000000000..1fc7058478e --- /dev/null +++ b/core-tests/src/server/http.cpp @@ -0,0 +1,1242 @@ +/* + +----------------------------------------------------------------------+ + | 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 "httplib_client.h" +#include "llhttp.h" +#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; + unordered_map response_headers; + string url; + string current_key; + Server *server; + int fd; + bool completed; + + void setHeader(string key, string value) { + response_headers[key] = value; + } + + 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) { + 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(": ")); + buf->append(kv.second.c_str(), kv.second.length()); + buf->append(SW_STRL("\r\n")); + } + buf->append(SW_STRL("\r\n")); + server->send(fd, buf->str, buf->length); + delete buf; + } +}; + +static int handle_on_message_complete(llhttp_t *parser) { + http_context *ctx = reinterpret_cast(parser->data); + ctx->completed = true; + return 0; +} + +static int handle_on_header_field(llhttp_t *parser, const char *at, size_t length) { + http_context *ctx = reinterpret_cast(parser->data); + ctx->current_key = string(at, length); + return 0; +} + +static int handle_on_header_value(llhttp_t *parser, const char *at, size_t length) { + http_context *ctx = reinterpret_cast(parser->data); + ctx->headers[ctx->current_key] = string(at, length); + return 0; +} + +static int handle_on_url(llhttp_t *parser, const char *at, size_t length) { + http_context *ctx = reinterpret_cast(parser->data); + ctx->url = std::string(at, length); + return 0; +} + +static void test_base_server(function fn) { + thread child_thread; + 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; + serv.set_document_root(test::get_root_path()); + serv.add_static_handler_location("/examples"); + + 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; + port->open_websocket_protocol = 1; + + serv.create(); + + serv.onWorkerStart = [&child_thread](Server *serv, Worker *worker) { + function fn = *(function *) serv->private_data_2; + child_thread = thread(fn, serv); + }; + + 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) { + sw_tg_buffer()->clear(); + std::string resp = "Swoole: " + string(req->data, req->info.len); + 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; + } + + 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_PAUSED_UPGRADE) { + ctx.setHeader("Connection", "Upgrade"); + ctx.setHeader("Sec-WebSocket-Accept", "IIRiohCjop4iJrmvySrFcwcXpHo="); + ctx.setHeader("Sec-WebSocket-Version", "13"); + ctx.setHeader("Upgrade", "websocket"); + ctx.setHeader("Content-Length", "0"); + + ctx.response(SW_HTTP_SWITCHING_PROTOCOLS); + + conn->websocket_status = websocket::STATUS_ACTIVE; + + return SW_OK; + } + + if (err != HPE_OK) { + fprintf(stderr, "Parse error: %s %s\n", llhttp_errno_name(err), parser.reason); + return SW_ERR; + } + EXPECT_EQ(err, HPE_OK); + + 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); + + return SW_OK; + }; + + serv.start(); + 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_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_base_server([](Server *serv) { + swoole_signal_block_all(); + + auto port = serv->get_primary_port(); + + httplib::Client cli(TEST_HOST, port->port); + httplib::Params params; + params.emplace("name", "john"); + params.emplace("note", "coder"); + auto resp = cli.Post("/index.html", params); + EXPECT_EQ(resp->status, 200); + EXPECT_EQ(resp->body, string("hello world")); + + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, static_get) { + 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("/examples/test.jpg"); + EXPECT_EQ(resp->status, 200); + + string file = test::get_root_path() + "/examples/test.jpg"; + File fp(file, O_RDONLY); + EXPECT_TRUE(fp.ready()); + + auto str = fp.read_content(); + + 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); + + httplib::Headers headers; + EXPECT_TRUE(cli.Upgrade("/websocket", headers)); + EXPECT_TRUE(cli.Push(data, length)); + + auto msg = cli.Recv(); + EXPECT_EQ(string(msg->payload, msg->payload_length), string("Swoole: ") + string(data, length)); +} + +TEST(http_server, websocket_small) { + 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_base_server([](Server *serv) { + swoole_signal_block_all(); + + String str(8192); + str.repeat("A", 1, 8192); + websocket_test(serv->get_primary_port()->get_port(), str.value(), str.get_length()); + + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, websocket_big) { + test_base_server([](Server *serv) { + swoole_signal_block_all(); + + String str(128 * 1024); + str.repeat("A", 1, str.capacity() - 1); + websocket_test(serv->get_primary_port()->get_port(), str.value(), str.get_length()); + + kill(getpid(), SIGTERM); + }); +} + +TEST(http_server, parser1) { + std::thread t; + auto server = http_server::listen(":0", [](Context &ctx) { + EXPECT_EQ(ctx.form_data.size(), 3); + ctx.end("DONE"); + }); + server->worker_num = 1; + server->onWorkerStart = [&t](Server *server, Worker *worker) { + t = std::thread([server]() { + swoole_signal_block_all(); + string file = test::get_root_path() + "/core-tests/fuzz/cases/req1.bin"; + File fp(file, O_RDONLY); + EXPECT_TRUE(fp.ready()); + auto str = fp.read_content(); + SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, server->get_primary_port()->port); + c.send(str->value(), str->get_length()); + char buf[1024]; + auto n = c.recv(buf, sizeof(buf)); + c.close(); + std::string resp(buf, n); + + EXPECT_TRUE(resp.find("200 OK") != resp.npos); + + kill(server->get_master_pid(), SIGTERM); + }); + }; + server->start(); + t.join(); +} + +TEST(http_server, parser2) { + std::thread t; + auto server = http_server::listen(":0", [](Context &ctx) { + EXPECT_EQ(ctx.form_data.size(), 3); + ctx.end("DONE"); + }); + server->worker_num = 1; + server->get_primary_port()->set_package_max_length(64 * 1024); + server->upload_max_filesize = 1024 * 1024; + server->onWorkerStart = [&t](Server *server, Worker *worker) { + t = std::thread([server]() { + swoole_signal_block_all(); + string file = test::get_root_path() + "/core-tests/fuzz/cases/req2.bin"; + File fp(file, O_RDONLY); + EXPECT_TRUE(fp.ready()); + auto str = fp.read_content(); + SyncClient c(SW_SOCK_TCP); + c.connect(TEST_HOST, server->get_primary_port()->port); + c.send(str->value(), str->get_length()); + char buf[1024]; + auto n = c.recv(buf, sizeof(buf)); + c.close(); + std::string resp(buf, n); + + EXPECT_TRUE(resp.find("200 OK") != resp.npos); + + kill(server->get_master_pid(), SIGTERM); + }); + }; + server->start(); + t.join(); +} + +TEST(http_server, heartbeat) { + Server *server = test_process_server(); + server->heartbeat_check_interval = 0; + auto port = server->get_primary_port(); + port->set_package_max_length(1024); + port->heartbeat_idle_time = 2; + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + sleep(1); + port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/"); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); + sleep(10); + resp = cli.Get("/"); + ASSERT_EQ(resp, nullptr); + } +} + +TEST(http_server, overflow) { + Server *server = test_process_server(); + auto port = server->get_primary_port(); + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + sleep(1); + port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/"); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); + resp = cli.Get("/overflow"); + ASSERT_EQ(resp, nullptr); + } +} + +TEST(http_server, process) { + Server *server = test_process_server(); + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/"); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); + + resp = cli.Get("/"); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); + } +} + +TEST(http_server, process1) { + Server *server = test_process_server(); + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/index.html"); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); + + sleep(1); + resp = cli.Get("/examples/test.jpg"); + ASSERT_EQ(resp->status, 200); + } +} + +TEST(http_server, redundant_callback) { + Server *server = test_process_server(Server::DISPATCH_IDLE_WORKER); + server->onConnect = [](Server *serv, DataHead *info) -> int { return 0; }; + server->onClose = [](Server *serv, DataHead *info) -> int { return 0; }; + server->onBufferFull = [](Server *serv, DataHead *info) -> int { return 0; }; + server->onBufferEmpty = [](Server *serv, DataHead *info) -> int { return 0; }; + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + ASSERT_EQ(server->onConnect, nullptr); + ASSERT_EQ(server->onClose, nullptr); + ASSERT_EQ(server->onBufferFull, nullptr); + ASSERT_EQ(server->onBufferEmpty, nullptr); + exit(0); + } + + if (pid > 0) { + sleep(2); + kill(server->get_master_pid(), SIGTERM); + } +} + +TEST(http_server, pause) { + Server *server = test_process_server(); + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_keep_alive(true); + auto resp = cli.Get("/pause"); + ASSERT_EQ(resp->status, 200); + ASSERT_EQ(resp->body, string("hello world")); + + resp = cli.Get("/"); + ASSERT_EQ(resp, nullptr); + } +} + +TEST(http_server, sni) { + Server *server = test_process_server(Server::DISPATCH_FDMOD, true); + ListenPort *port = server->get_primary_port(); + port->ssl_set_cert_file(test::get_root_path() + "/tests/include/ssl_certs/server.crt"); + port->ssl_set_key_file(test::get_root_path() + "/tests/include/ssl_certs/server.key"); + SSLContext *context = new SSLContext(); + *context = *port->ssl_context; + context->cert_file = test::get_root_path() + "/tests/include/ssl_certs/sni_server_cs_cert.pem"; + context->key_file = test::get_root_path() + "/tests/include/ssl_certs/sni_server_cs_key.pem"; + port->ssl_add_sni_cert("localhost", context); + port->ssl_context->protocols = 0; + port->ssl_init(); + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + string port_num = to_string(server->get_primary_port()->port); + + sleep(1); + pid_t pid2; + string command = "curl https://localhost:" + port_num + " -k -vvv --stderr /tmp/wwwsnitestcom.txt"; + swoole_shell_exec(command.c_str(), &pid2, 0); + sleep(1); + + stringstream buffer; + ifstream wwwsnitestcom; + wwwsnitestcom.open("/tmp/wwwsnitestcom.txt"); + ASSERT_TRUE(wwwsnitestcom.is_open()); + buffer << wwwsnitestcom.rdbuf(); + wwwsnitestcom.close(); + string response(buffer.str()); + ASSERT_TRUE(response.find("CN=cs.php.net") != string::npos); + + string command2 = "curl https://127.0.0.1:" + port_num + " -k -vvv --stderr /tmp/wwwsnitest2com.txt"; + swoole_shell_exec(command2.c_str(), &pid2, 0); + sleep(1); + + stringstream buffer2; + ifstream wwwsnitest2com; + wwwsnitest2com.open("/tmp/wwwsnitest2com.txt"); + ASSERT_TRUE(wwwsnitest2com.is_open()); + buffer2 << wwwsnitest2com.rdbuf(); + string response2(buffer2.str()); + wwwsnitest2com.close(); + ASSERT_TRUE(response2.find("CN=127.0.0.1") != string::npos); + } +} + +TEST(http_server, bad_request) { + Server *server = test_process_server(); + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + sleep(1); + + string str_1 = "curl -X UNKNOWN http://"; + string str_2 = ":"; + string str_3 = " -k -vvv --stderr /tmp/bad_request.txt"; + string host = TEST_HOST; + string port = to_string(server->get_primary_port()->port); + string command = str_1 + host + str_2 + port + str_3; + + pid_t pid2; + swoole_shell_exec(command.c_str(), &pid2, 0); + sleep(1); + + stringstream buffer; + ifstream bad_request; + bad_request.open("/tmp/bad_request.txt"); + ASSERT_TRUE(bad_request.is_open()); + buffer << bad_request.rdbuf(); + string response(buffer.str()); + bad_request.close(); + ASSERT_TRUE(response.find("400 Bad Request") != string::npos); + } +} + +TEST(http_server, chunked) { + Server *server = test_process_server(); + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + sleep(1); + + string jpg_path = test::get_jpg_file(); + string str_1 = "curl -H 'Transfer-Encoding: chunked' -F \"file=@" + jpg_path + "\" http://"; + string str_2 = ":"; + string host = TEST_HOST; + string port = to_string(server->get_primary_port()->port); + string command = str_1 + host + str_2 + port; + + pid_t pid2; + int pipe = swoole_shell_exec(command.c_str(), &pid2, 0); + sleep(1); + + char buf[1024] = {}; + read(pipe, buf, sizeof(buf) - 1); + ASSERT_STREQ(buf, "hello world"); + } +} + +TEST(http_server, max_queued_bytes) { + Server *server = test_process_server(); + server->max_queued_bytes = 100; + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + sleep(1); + + string jpg_path = test::get_jpg_file(); + string str_1 = "curl -H 'Transfer-Encoding: chunked' -F \"file=@" + jpg_path + "\" http://"; + string str_2 = ":"; + string host = TEST_HOST; + string port = to_string(server->get_primary_port()->port); + string command = str_1 + host + str_2 + port; + + pid_t pid2; + int pipe = swoole_shell_exec(command.c_str(), &pid2, 0); + sleep(1); + + char buf[1024] = {}; + read(pipe, buf, sizeof(buf) - 1); + ASSERT_STREQ(buf, "hello world"); + } +} + +TEST(http_server, dispatch_func_return_error_worker_id) { + Server *server = test_process_server(); + server->dispatch_func = [](Server *serv, Connection *conn, SendData *data) -> int { + return data->info.fd % 2 == 0 ? Server::DISPATCH_RESULT_DISCARD_PACKET + : Server::DISPATCH_RESULT_CLOSE_CONNECTION; + }; + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + }; + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + sleep(1); + auto port = server->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + cli.set_read_timeout(1, 0); + auto resp = cli.Get("/"); + ASSERT_EQ(resp, nullptr); + resp = cli.Get("/"); + ASSERT_EQ(resp, nullptr); + } +} + +TEST(http_server, client_ca) { + Server *server = test_process_server(Server::DISPATCH_FDMOD, true); + ListenPort *port = server->get_primary_port(); + port->ssl_set_cert_file(test::get_root_path() + "/tests/include/api/ssl-ca/server-cert.pem"); + port->ssl_set_key_file(test::get_root_path() + "/tests/include/api/ssl-ca/server-key.pem"); + port->ssl_context->verify_peer = true; + port->ssl_context->allow_self_signed = true; + port->ssl_context->client_cert_file = test::get_root_path() + "/tests/include/api/ssl-ca/ca-cert.pem"; + port->ssl_init(); + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + string port_num = to_string(server->get_primary_port()->port); + + sleep(1); + pid_t pid2; + string client_cert = " --cert " + test::get_root_path() + "/tests/include/api/ssl-ca/client-cert.pem "; + string client_key = "--key " + test::get_root_path() + "/tests/include/api/ssl-ca/client-key.pem"; + string command = "curl https://127.0.0.1:" + port_num + " " + client_cert + client_key + + " -k -vvv --stderr /tmp/client_ca.txt"; + swoole_shell_exec(command.c_str(), &pid2, 0); + sleep(1); + + stringstream buffer; + ifstream client_ca; + client_ca.open("/tmp/client_ca.txt"); + ASSERT_TRUE(client_ca.is_open()); + buffer << client_ca.rdbuf(); + client_ca.close(); + string response(buffer.str()); + ASSERT_TRUE(response.find("200 OK") != response.npos); + } +} + +static bool request_with_if_range_header(const char *date_format, std::string port) { + struct stat file_stat; + std::string file_path = test::get_root_path() + "/docs/swoole-logo.svg"; + stat(file_path.c_str(), &file_stat); + time_t file_mtime = file_stat.st_mtim.tv_sec; + struct tm *time_info = gmtime(&file_mtime); + + char temp[128] = {0}; + strftime(temp, sizeof(temp), date_format, time_info); + + string str_1 = "curl http://"; + string host = TEST_HOST; + string str_2 = ":"; + string str_3 = "/docs/swoole-logo.svg -k -vvv --stderr /tmp/http_range.txt "; + string headers = "-H 'Range: bytes=0-500' -H 'If-Range: "; + string command = str_1 + host + str_2 + port + str_3 + headers + string(temp) + "'"; + + pid_t pid; + close(swoole_shell_exec(command.c_str(), &pid, 0)); + sleep(2); + + stringstream buffer; + ifstream http_range; + http_range.open("/tmp/http_range.txt"); + if (!http_range.is_open()) { + return false; + } + + buffer << http_range.rdbuf(); + string response(buffer.str()); + http_range.close(); + return response.find("206 Partial Content") != string::npos && response.find("Content-Length: 501") != string::npos; +} + +TEST(http_server, http_range) { + Server *server = test_process_server(); + server->http_autoindex = true; + server->add_static_handler_location("/docs"); + + pid_t pid = fork(); + + if (pid == 0) { + server->start(); + exit(0); + } + + if (pid > 0) { + sleep(1); + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + string port = to_string(server->get_primary_port()->port); + ASSERT_TRUE(request_with_if_range_header(SW_HTTP_RFC1123_DATE_GMT, port)); + ASSERT_TRUE(request_with_if_range_header(SW_HTTP_RFC1123_DATE_UTC, port)); + ASSERT_TRUE(request_with_if_range_header(SW_HTTP_RFC850_DATE, port)); + ASSERT_TRUE(request_with_if_range_header(SW_HTTP_ASCTIME_DATE, port)); + } +} + +static bool request_with_diff_range(std::string port, std::string range) { + string str_1 = "curl -X GET http://"; + string host = TEST_HOST; + string str_2 = ":"; + string str_3 = "/docs/swoole-logo.svg -k -vvv --stderr /tmp/http_range.txt "; + string headers = "-H 'Range: bytes=" + range; + string command = str_1 + host + str_2 + port + str_3 + headers + "'"; + + pid_t pid; + close(swoole_shell_exec(command.c_str(), &pid, 0)); + + sleep(2); + stringstream buffer; + ifstream http_range; + http_range.open("/tmp/http_range.txt"); + if (!http_range.is_open()) { + return false; + } + + buffer << http_range.rdbuf(); + string response(buffer.str()); + http_range.close(); + return response.find("206 Partial Content") != string::npos; +} + +TEST(http_server, http_range2) { + Server *server = test_process_server(); + server->add_static_handler_location("/docs"); + server->add_static_handler_index_files("swoole-logo.svg"); + + pid_t pid = fork(); + + if (pid > 0) { + server->start(); + exit(0); + } + + if (pid == 0) { + sleep(1); + ON_SCOPE_EXIT { + kill(server->get_master_pid(), SIGTERM); + }; + + ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), "0-15")); + ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), "16-31")); + ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), "-16")); + ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), "128-")); + ASSERT_TRUE(request_with_diff_range(to_string(server->get_primary_port()->port), "0-0,-1")); + + } +} + +// it is always last test +TEST(http_server, abort_connection) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = 2; + SwooleG.max_sockets = 2; + serv.set_max_connection(1); + sw_logger()->set_level(SW_LOG_WARNING); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + port->open_http_protocol = 1; + serv.create(); + + serv.onWorkerStart = [](Server *serv, Worker *worker) { + auto port = serv->get_primary_port(); + httplib::Client cli(TEST_HOST, port->port); + auto resp = cli.Get("/"); + EXPECT_EQ(resp, nullptr); + + if (worker->id == 0) { + sleep(1); + kill(serv->get_master_pid(), SIGTERM); + } + }; + + serv.onReceive = [&](Server *server, RecvData *req) -> int { return SW_OK; }; + serv.start(); +} diff --git a/core-tests/src/server/http_parser.cpp b/core-tests/src/server/http_parser.cpp new file mode 100644 index 00000000000..f7e3b6e0d95 --- /dev/null +++ b/core-tests/src/server/http_parser.cpp @@ -0,0 +1,575 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | Author NathanFreeman | + +----------------------------------------------------------------------+ + */ +#include "test_core.h" +#include "swoole_http_parser.h" + +using namespace std; + +static int http_request_on_path(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_query_string(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_body(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_header_field(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_header_value(swoole_http_parser *parser, const char *at, size_t length); +static int http_request_on_headers_complete(swoole_http_parser *parser); +static int http_request_message_complete(swoole_http_parser *parser); + +// clang-format off +static const swoole_http_parser_settings http_parser_settings = +{ + nullptr, + http_request_on_path, + http_request_on_query_string, + nullptr, + nullptr, + http_request_on_header_field, + http_request_on_header_value, + http_request_on_headers_complete, + http_request_on_body, + http_request_message_complete +}; +// clang-format on + +typedef struct { + long fd; + uchar completed : 1; + uchar end_ : 1; + uchar send_header_ : 1; + + uchar send_chunked : 1; + uchar recv_chunked : 1; + uchar send_trailer_ : 1; + uchar keepalive : 1; + uchar websocket : 1; + + uchar upgrade : 1; + uchar detached : 1; + uchar parse_cookie : 1; + uchar parse_body : 1; + uchar parse_files : 1; + uchar co_socket : 1; + uchar http2 : 1; + + swoole_http_parser parser; + + uint16_t input_var_num; + char *current_header_name; + size_t current_header_name_len; + char *current_input_name; + size_t current_input_name_len; + char *current_form_data_name; + size_t current_form_data_name_len; + + vector header_fields; + vector header_values; + string query_string; +} HttpContext; + +static swoole_http_parser *swoole_http_parser_create(swoole_http_parser_type type = PHP_HTTP_REQUEST) { + HttpContext *ctx = new HttpContext(); + swoole_http_parser *parser = &ctx->parser; + swoole_http_parser_init(parser, type); + parser->data = ctx; + return parser; +} + +static void swoole_http_destroy_context(swoole_http_parser *parser) { + delete (HttpContext *) parser->data; + return; +} + +static int swoole_http_parser_method(string protocol) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, protocol.c_str(), protocol.length()); + + int ret = parser->method; + swoole_http_destroy_context(parser); + return ret; +} + +static int http_request_on_path(swoole_http_parser *parser, const char *at, size_t length) { + return 0; +} + +static int http_request_on_query_string(swoole_http_parser *parser, const char *at, size_t length) { + HttpContext *ctx = (HttpContext *) parser->data; + ctx->query_string = string(at, length); + return 0; +} + +static int http_request_on_header_field(swoole_http_parser *parser, const char *at, size_t length) { + HttpContext *ctx = (HttpContext *) parser->data; + ctx->header_fields.push_back(string(at, length)); + return 0; +} + +static int http_request_on_header_value(swoole_http_parser *parser, const char *at, size_t length) { + HttpContext *ctx = (HttpContext *) parser->data; + ctx->header_values.push_back(string(at, length)); + return 0; +} + +static int http_request_on_headers_complete(swoole_http_parser *parser) { + return 0; +} + +static int http_request_on_body(swoole_http_parser *parser, const char *at, size_t length) { + return 0; +} + +static int http_request_message_complete(swoole_http_parser *parser) { + return 0; +} + +static const string request_get = "GET /get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_http2 = "GET /get HTTP/2\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_http09 = "GET /index.html\r\n"; + +static const string request_head = "HEAD /get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_query_string = "GET /get?a=foo&b=bar&c=456%26789#frag=123 HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_query_string2 = "GET /get? HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_query_string3 = "GET /index.html?a=123\r"; + +static const string request_get_with_query_string4 = "GET /index.html?a=123\n"; + +static const string request_get_with_query_string5 = "GET /get#frag=123 HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_schema = "GET http://127.0.0.1:8081/get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_proxy_connection = "GET /get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "Proxy-Connection: keep-alive\r\n" + "\r\n"; + +static const string request_get_with_connection_close = "GET /get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: close\r\n" + "\r\n"; + +static const string request_get_http10 = "GET /get HTTP/1.0\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "\r\n"; + +static const string request_get_http10_with_keep_alive = "GET /get HTTP/1.0\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Connection: keep-alive\r\n" + "\r\n"; + +static const string request_post = "POST /api/build/v1/foo HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Content-Length: 7\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "\r\n" + "foo=bar"; + +static const string request_upgrade = "GET /get HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "upgrade: websocket\r\n" + "User-Agent: curl/7.64.1\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"; + +static const string request_dead = "POST /dead HTTP/1.1\r\n" + "Host: www.maria.com\r\n" + "User-Agent: curl/7.64.1\r\n" + "Accept: */*\r\n" + "Content-Length: abcd\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "\r\n" + "foo=bar"; + +static const string response_200 = "HTTP/1.1 200 OK\r\n" + "server: CLOUD ELB 1.0.0\r\n" + "date: Sat, 04 Feb 2023 08:47:14 GMT\r\n" + "content-type: application/json\r\n" + "content-length: 19\r\n" + "Connection: close\r\n" + "\r\n" + "{\"name\" : \"guoji\"}"; + +static const string response_200_without_ok = "HTTP/1.1 200\r\n" + "server: CLOUD ELB 1.0.0\r\n" + "date: Sat, 04 Feb 2023 08:47:14 GMT\r\n" + "content-type: application/json\r\n" + "content-length: 19\r\n" + "Connection: close\r\n" + "\r\n" + "{\"name\" : \"guoji\"}"; + +static const string response_chunk = "HTTP/1.1 200 OK\r\n" + "server: CLOUD ELB 1.0.0\r\n" + "date: Sat, 04 Feb 2023 08:47:14 GMT\r\n" + "content-type: application/json\r\n" + "Transfer-Encoding: chunked\r\n" + "Connection: close\r\n" + "\r\n" + "19\r\n" + "{\"name\" : \"guoji\"}\r\n" + "19\r\n" + "{\"name\" : \"guoji\"}\r\n" + "19\r\n" + "{\"name\" : \"guoji\"}\r\n" + "19\r\n" + "{\"name\" : \"guoji\"}\r\n" + "0\r\n" + "\r\n"; + +TEST(http_parser, method_name) { + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_DELETE), "DELETE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_GET), "GET"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_HEAD), "HEAD"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_POST), "POST"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_PUT), "PUT"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_PATCH), "PATCH"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_CONNECT), "CONNECT"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_OPTIONS), "OPTIONS"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_TRACE), "TRACE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_COPY), "COPY"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_LOCK), "LOCK"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MKCOL), "MKCOL"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MOVE), "MOVE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MKCALENDAR), "MKCALENDAR"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_PROPFIND), "PROPFIND"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_PROPPATCH), "PROPPATCH"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_SEARCH), "SEARCH"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_UNLOCK), "UNLOCK"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_REPORT), "REPORT"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MKACTIVITY), "MKACTIVITY"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_CHECKOUT), "CHECKOUT"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MERGE), "MERGE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_MSEARCH), "M-SEARCH"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_NOTIFY), "NOTIFY"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_SUBSCRIBE), "SUBSCRIBE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_UNSUBSCRIBE), "UNSUBSCRIBE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_PURGE), "PURGE"); + ASSERT_STREQ(swoole_http_method_str(PHP_HTTP_NOT_IMPLEMENTED), "NOTIMPLEMENTED"); +} + +TEST(http_parser, http_version) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get.c_str(), request_get.length()); + ASSERT_TRUE(parser->http_major == 1); + ASSERT_TRUE(parser->http_minor == 1); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get_http2.c_str(), request_get_http2.length()); + ASSERT_TRUE(parser->http_major == 2); + ASSERT_TRUE(parser->http_minor == 0); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get_http09.c_str(), request_get_http09.length()); + ASSERT_TRUE(parser->http_major == 0); + ASSERT_TRUE(parser->http_minor == 9); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get_http10.c_str(), request_get_http10.length()); + ASSERT_TRUE(parser->http_major == 1); + ASSERT_TRUE(parser->http_minor == 0); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + parser->state = s_start_req_or_res; + swoole_http_parser_execute(parser, &http_parser_settings, request_get.c_str(), request_get.length()); + ASSERT_TRUE(parser->http_major == 1); + ASSERT_TRUE(parser->http_minor == 1); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, should_keep_alive) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get.c_str(), request_get.length()); + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, + &http_parser_settings, + request_get_with_connection_close.c_str(), + request_get_with_connection_close.length()); + ASSERT_FALSE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get_http10.c_str(), request_get_http10.length()); + ASSERT_FALSE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, + &http_parser_settings, + request_get_http10_with_keep_alive.c_str(), + request_get_http10_with_keep_alive.length()); + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_get_http10.c_str(), request_get_http10.length()); + ASSERT_FALSE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_schema.c_str(), request_get_with_schema.length()); + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + parser->state = s_start_req_or_res; + swoole_http_parser_execute(parser, &http_parser_settings, request_head.c_str(), request_head.length()); + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, upgrade) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_upgrade.c_str(), request_upgrade.length()); + ASSERT_TRUE(parser->upgrade == 1); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, dead) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, &http_parser_settings, request_dead.c_str(), request_dead.length()); + ASSERT_TRUE(parser->state == s_dead); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, zero) { + swoole_http_parser *parser = swoole_http_parser_create(); + int ret = swoole_http_parser_execute(parser, &http_parser_settings, "", 0); + ASSERT_TRUE(ret == 0); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, methods) { + ASSERT_EQ(swoole_http_parser_method("COPY /get HTTP/1.1\r\n\r\n"), PHP_HTTP_COPY); + ASSERT_EQ(swoole_http_parser_method("CHECKOUT /get HTTP/1.1\r\n\r\n"), PHP_HTTP_CHECKOUT); + ASSERT_EQ(swoole_http_parser_method("HEAD /get HTTP/1.1\r\n\r\n"), PHP_HTTP_HEAD); + ASSERT_EQ(swoole_http_parser_method("LOCK /get HTTP/1.1\r\n\r\n"), PHP_HTTP_LOCK); + ASSERT_EQ(swoole_http_parser_method("MOVE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_MOVE); + ASSERT_EQ(swoole_http_parser_method("MKCALENDAR /get HTTP/1.1\r\n\r\n"), PHP_HTTP_MKCALENDAR); + ASSERT_EQ(swoole_http_parser_method("MKACTIVITY /get HTTP/1.1\r\n\r\n"), PHP_HTTP_MKACTIVITY); + ASSERT_EQ(swoole_http_parser_method("MERGE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_MERGE); + ASSERT_EQ(swoole_http_parser_method("M-SEARCH /get HTTP/1.1\r\n\r\n"), PHP_HTTP_MSEARCH); + ASSERT_EQ(swoole_http_parser_method("NOTIFY /get HTTP/1.1\r\n\r\n"), PHP_HTTP_NOTIFY); + ASSERT_EQ(swoole_http_parser_method("OPTIONS /get HTTP/1.1\r\n\r\n"), PHP_HTTP_OPTIONS); + ASSERT_EQ(swoole_http_parser_method("REPORT /get HTTP/1.1\r\n\r\n"), PHP_HTTP_REPORT); + ASSERT_EQ(swoole_http_parser_method("SEARCH /get HTTP/1.1\r\n\r\n"), PHP_HTTP_SEARCH); + ASSERT_EQ(swoole_http_parser_method("SUBSCRIBE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_SUBSCRIBE); + ASSERT_EQ(swoole_http_parser_method("UNSUBSCRIBE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_UNSUBSCRIBE); + ASSERT_EQ(swoole_http_parser_method("TRACE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_TRACE); + ASSERT_EQ(swoole_http_parser_method("UNLOCK /get HTTP/1.1\r\n\r\n"), PHP_HTTP_UNLOCK); + ASSERT_EQ(swoole_http_parser_method("PURGE /get HTTP/1.1\r\n\r\n"), PHP_HTTP_PURGE); + ASSERT_EQ(swoole_http_parser_method("POST /get HTTP/1.1\r\n\r\n"), PHP_HTTP_POST); + ASSERT_EQ(swoole_http_parser_method("PROPFIND /get HTTP/1.1\r\n\r\n"), PHP_HTTP_PROPFIND); + ASSERT_EQ(swoole_http_parser_method("PROPPATCH /get HTTP/1.1\r\n\r\n"), PHP_HTTP_PROPPATCH); + ASSERT_EQ(swoole_http_parser_method("PUT /get HTTP/1.1\r\n\r\n"), PHP_HTTP_PUT); + ASSERT_EQ(swoole_http_parser_method("PATCH /get HTTP/1.1\r\n\r\n"), PHP_HTTP_PATCH); + ASSERT_EQ(swoole_http_parser_method("UNKNOWN /get HTTP/1.1\r\n\r\n"), PHP_HTTP_NOT_IMPLEMENTED); +} + +TEST(http_parser, proxy_connection) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, + &http_parser_settings, + request_get_with_proxy_connection.c_str(), + request_get_with_proxy_connection.length()); + + HttpContext *ctx = (HttpContext *) parser->data; + ASSERT_STREQ(ctx->header_fields[4].c_str(), "Proxy-Connection"); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, header_field_and_value) { + string header = "User-Agent: curl/7.64.1\r\n" + "\r\n"; + + swoole_http_parser *parser = swoole_http_parser_create(); + parser->state = s_header_field; + swoole_http_parser_execute(parser, &http_parser_settings, header.c_str(), header.length()); + + HttpContext *ctx = (HttpContext *) parser->data; + ASSERT_STREQ(ctx->header_fields[0].c_str(), "User-Agent"); + swoole_http_destroy_context(parser); + + header = "curl/7.64.1\r\n" + "\r\n"; + parser = swoole_http_parser_create(); + parser->state = s_header_value; + swoole_http_parser_execute(parser, &http_parser_settings, header.c_str(), header.length()); + ASSERT_STREQ(ctx->header_values[0].c_str(), "curl/7.64.1"); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, response) { + swoole_http_parser *parser = swoole_http_parser_create(PHP_HTTP_RESPONSE); + swoole_http_parser_execute(parser, &http_parser_settings, response_200.c_str(), response_200.length()); + + ASSERT_EQ(parser->status_code, 200); + ASSERT_TRUE(parser->http_major == 1); + ASSERT_TRUE(parser->http_minor == 1); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(PHP_HTTP_RESPONSE); + parser->state = s_start_req_or_res; + swoole_http_parser_execute(parser, &http_parser_settings, response_200.c_str(), response_200.length()); + + ASSERT_EQ(parser->status_code, 200); + ASSERT_TRUE(parser->http_major == 1); + ASSERT_TRUE(parser->http_minor == 1); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(PHP_HTTP_RESPONSE); + parser->state = s_start_req_or_res; + swoole_http_parser_execute( + parser, &http_parser_settings, response_200_without_ok.c_str(), response_200_without_ok.length()); + + ASSERT_EQ(parser->status_code, 200); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(PHP_HTTP_RESPONSE); + parser->state = s_start_req_or_res; + swoole_http_parser_execute(parser, &http_parser_settings, response_chunk.c_str(), response_chunk.length()); + + ASSERT_EQ(parser->status_code, 200); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, query_string) { + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_query_string.c_str(), request_get_with_query_string.length()); + + HttpContext *ctx = (HttpContext *) parser->data; + ASSERT_STREQ(ctx->query_string.c_str(), "a=foo&b=bar&c=456%26789"); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_query_string2.c_str(), request_get_with_query_string2.length()); + + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_query_string3.c_str(), request_get_with_query_string3.length()); + + ASSERT_TRUE(parser->http_major == 0); + ASSERT_TRUE(parser->http_minor == 9); + ctx = (HttpContext *) parser->data; + ASSERT_STREQ(ctx->query_string.c_str(), "a=123"); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_query_string4.c_str(), request_get_with_query_string4.length()); + + ASSERT_TRUE(parser->http_major == 0); + ASSERT_TRUE(parser->http_minor == 9); + ctx = (HttpContext *) parser->data; + ASSERT_STREQ(ctx->query_string.c_str(), "a=123"); + swoole_http_destroy_context(parser); + + parser = swoole_http_parser_create(); + swoole_http_parser_execute( + parser, &http_parser_settings, request_get_with_query_string5.c_str(), request_get_with_query_string5.length()); + + ASSERT_TRUE(swoole_http_should_keep_alive(parser)); + swoole_http_destroy_context(parser); +} + +TEST(http_parser, http09) { + string request_get_with_query_string_http09 = "GET /index.html\r"; + swoole_http_parser *parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, + &http_parser_settings, + request_get_with_query_string_http09.c_str(), + request_get_with_query_string_http09.length()); + + ASSERT_TRUE(parser->http_major == 0); + ASSERT_TRUE(parser->http_minor == 9); + swoole_http_destroy_context(parser); + + request_get_with_query_string_http09 = "GET /index.html\n"; + parser = swoole_http_parser_create(); + swoole_http_parser_execute(parser, + &http_parser_settings, + request_get_with_query_string_http09.c_str(), + request_get_with_query_string_http09.length()); + + ASSERT_TRUE(parser->http_major == 0); + ASSERT_TRUE(parser->http_minor == 9); + swoole_http_destroy_context(parser); +} diff --git a/core-tests/src/server/message_bus.cpp b/core-tests/src/server/message_bus.cpp new file mode 100644 index 00000000000..52f96e78a4f --- /dev/null +++ b/core-tests/src/server/message_bus.cpp @@ -0,0 +1,198 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#include "test_core.h" + +#include "swoole_server.h" +#include "swoole_memory.h" +#include "swoole_signal.h" +#include "swoole_lock.h" + +using namespace std; +using namespace swoole; + +constexpr int DATA_SIZE = 2 * SW_NUM_MILLION; + +struct TestPacket { + SessionId fd; + std::string data; +}; + +struct TestMB { + std::vector q; + MessageBus mb; + std::function read_func; + + bool send_empty_packet(network::Socket *sock) { + SendData _data4; + _data4.data = "hello world"; + _data4.info.fd = 4; + _data4.info.len = 0; + if (!mb.write(sock, &_data4)) { + return false; + } + + SendData _data5; + _data5.data = nullptr; + _data5.info.fd = 5; + _data5.info.len = 10; + if (!mb.write(sock, &_data5)) { + return false; + } + + return true; + } + + int read(Event *ev) { + auto retval = read_func(ev->socket); + if (retval == 0) { + return SW_OK; + } else if (retval < 0) { + swoole_event_del(ev->socket); + return SW_ERR; + } + + auto packet = mb.get_packet(); + + q.push_back(TestPacket{ + mb.get_buffer()->info.fd, + std::string(packet.data, packet.length), + }); + + if (q.size() == 5) { + swoole_event_del(ev->socket); + } + + return SW_OK; + } +}; + +#define MB_SEND(i, s) \ + String pkt##i(s); \ + pkt##i.append_random_bytes(pkt##i.size - 1, false); \ + pkt##i.append('\0'); \ + \ + SendData _data##i{}; \ + _data##i.data = pkt##i.value(); \ + _data##i.info.fd = i; \ + _data##i.info.len = pkt##i.get_length(); \ + ASSERT_TRUE(tmb.mb.write(p.get_socket(true), &_data##i)); + +#define MB_ASSERT(i) \ + auto r##i = tmb.q.at(i - 1); \ + ASSERT_EQ(r##i.fd, i); \ + ASSERT_STREQ(r##i.data.c_str(), pkt##i.value()); + +TEST(message_bus, read) { + UnixSocket p(true, SOCK_STREAM); + ASSERT_TRUE(p.ready()); + + ASSERT_EQ(swoole_event_init(SW_EVENTLOOP_WAIT_EXIT), SW_OK); + p.set_blocking(false); + p.set_buffer_size(65536); + + uint64_t msg_id = 0; + + TestMB tmb{}; + tmb.mb.set_buffer_size(65536); + tmb.mb.set_id_generator([&msg_id]() { return msg_id++; }); + tmb.mb.alloc_buffer(); + + tmb.read_func = [&tmb](network::Socket *sock) { + return tmb.mb.read(sock); + }; + + sw_reactor()->ptr = &tmb; + + ASSERT_EQ(swoole_event_add(p.get_socket(false), SW_EVENT_READ), SW_OK); + + swoole_event_set_handler(SW_FD_PIPE | SW_EVENT_READ, [](Reactor *reactor, Event *ev) -> int { + TestMB *tmb = (TestMB *) reactor->ptr; + return tmb->read(ev); + }); + + MB_SEND(1, DATA_SIZE); + MB_SEND(2, tmb.mb.get_buffer_size()); + MB_SEND(3, 2341); + + tmb.send_empty_packet(p.get_socket(true)); + + ASSERT_EQ(swoole_event_wait(), SW_OK); + + MB_ASSERT(1); + MB_ASSERT(2); + MB_ASSERT(3); + + auto r4 = tmb.q.at(3); + ASSERT_EQ(r4.fd, 4); + ASSERT_STREQ(r4.data.c_str(), ""); + + auto r5 = tmb.q.at(4); + ASSERT_EQ(r5.fd, 5); + ASSERT_STREQ(r5.data.c_str(), ""); +} + +TEST(message_bus, read_with_buffer) { + UnixSocket p(true, SOCK_DGRAM); + ASSERT_TRUE(p.ready()); + + ASSERT_EQ(swoole_event_init(SW_EVENTLOOP_WAIT_EXIT), SW_OK); + p.set_blocking(false); + p.set_buffer_size(65536); + + uint64_t msg_id = 0; + + TestMB tmb{}; + tmb.mb.set_buffer_size(65536); + tmb.mb.set_id_generator([&msg_id]() { return msg_id++; }); + tmb.mb.alloc_buffer(); + + tmb.read_func = [&tmb](network::Socket *sock) { + return tmb.mb.read_with_buffer(sock); + }; + + sw_reactor()->ptr = &tmb; + + ASSERT_EQ(swoole_event_add(p.get_socket(false), SW_EVENT_READ), SW_OK); + + swoole_event_set_handler(SW_FD_PIPE | SW_EVENT_READ, [](Reactor *reactor, Event *ev) -> int { + TestMB *tmb = (TestMB *) reactor->ptr; + return tmb->read(ev); + }); + + MB_SEND(1, DATA_SIZE); + MB_SEND(2, tmb.mb.get_buffer_size()); + MB_SEND(3, 2341); + + tmb.send_empty_packet(p.get_socket(true)); + + ASSERT_EQ(swoole_event_wait(), SW_OK); + + MB_ASSERT(1); + MB_ASSERT(2); + MB_ASSERT(3); + + auto r4 = tmb.q.at(3); + ASSERT_EQ(r4.fd, 4); + ASSERT_STREQ(r4.data.c_str(), ""); + + auto r5 = tmb.q.at(4); + ASSERT_EQ(r5.fd, 5); + ASSERT_STREQ(r5.data.c_str(), ""); +} diff --git a/core-tests/src/server/multipart_parser.cpp b/core-tests/src/server/multipart_parser.cpp new file mode 100644 index 00000000000..aac4e30ef2f --- /dev/null +++ b/core-tests/src/server/multipart_parser.cpp @@ -0,0 +1,255 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | Author NathanFreeman | + +----------------------------------------------------------------------+ + */ +#include "test_core.h" +#include "multipart_parser.h" + +struct MppResult { + std::string data; + std::string header_field; + std::string header_value; + bool header_complete; + bool body_end; +}; + +static int multipart_on_header_field(multipart_parser *p, const char *at, size_t length) { + swoole_trace("on_header_field: at=%.*s, length=%lu", (int) length, at, length); + auto res = (MppResult *) p->data; + res->header_field = std::string(at, length); + return 0; +} + +static int multipart_on_header_value(multipart_parser *p, const char *at, size_t length) { + swoole_trace("on_header_value: at=%.*s, length=%lu", (int) length, at, length); + auto res = (MppResult *) p->data; + res->header_value = std::string(at, length); + return 0; +} + +static int multipart_on_data(multipart_parser *p, const char *at, size_t length) { + swoole_trace("on_data: length=%lu", length); + auto res = (MppResult *) p->data; + res->data.append(at, length); + return 0; +} + +static int multipart_on_header_complete(multipart_parser *p) { + swoole_trace("on_header_complete"); + auto res = (MppResult *) p->data; + res->header_complete = true; + return 0; +} + +static int multipart_on_data_end(multipart_parser *p) { + swoole_trace("on_data_end"); + return 0; +} + +static int multipart_on_part_begin(multipart_parser *p) { + swoole_trace("on_part_begin"); + return 0; +} + +static int multipart_on_body_end(multipart_parser *p) { + swoole_trace("on_body_end"); + auto res = (MppResult *) p->data; + res->body_end = true; + return 0; +} + +static multipart_parser_settings _settings{ + multipart_on_header_field, + multipart_on_header_value, + multipart_on_data, + multipart_on_part_begin, + multipart_on_header_complete, + multipart_on_data_end, + multipart_on_body_end, +}; + +static const std::string boundary = "--WebKitFormBoundaryeGOz80A8JnaO6kuw"; + +static multipart_parser *create_parser() { + return multipart_parser_init(boundary.c_str(), boundary.length(), &_settings); +} + +static void create_error(multipart_parser *parser, multipart_error error_reason, const char *error) { + size_t length = 1024; + char buf[length]; + + parser->error_reason = error_reason; + int result_len = multipart_parser_error_msg(parser, buf, length); + ASSERT_GT(result_len, 0); + buf[result_len] = '\0'; + + std::string response(buf, result_len); + ASSERT_TRUE(response.find(error) != response.npos); +} + +TEST(multipart_parser, error_message) { + size_t length = 1024; + char buf[length]; + auto parser = create_parser(); + + parser->error_reason = MPPE_OK; + ASSERT_EQ(multipart_parser_error_msg(parser, buf, length), 0); + + parser->error_expected = '\0'; + create_error(parser, MPPE_PAUSED, "parser paused"); + create_error(parser, MPPE_BOUNDARY_END_NO_CRLF, "no CRLF at first boundary end: "); + create_error(parser, MPPE_BAD_START_BOUNDARY, "first boundary mismatching: "); + create_error(parser, MPPE_INVALID_HEADER_FIELD_CHAR, "invalid char in header field: "); + create_error(parser, MPPE_INVALID_HEADER_VALUE_CHAR, "invalid char in header value: "); + create_error(parser, MPPE_BAD_PART_END, "no next part or final hyphen: expecting CR or '-' "); + create_error(parser, MPPE_END_BOUNDARY_NO_DASH, "bad final hyphen: "); + + parser->error_expected = '\r'; + create_error(parser, MPPE_PAUSED, "parser paused"); + create_error(parser, MPPE_BOUNDARY_END_NO_CRLF, "no CRLF at first boundary end: "); + create_error(parser, MPPE_BAD_START_BOUNDARY, "first boundary mismatching: "); + create_error(parser, MPPE_INVALID_HEADER_FIELD_CHAR, "invalid char in header field: "); + create_error(parser, MPPE_INVALID_HEADER_VALUE_CHAR, "invalid char in header value: "); + create_error(parser, MPPE_BAD_PART_END, "no next part or final hyphen: expecting CR or '-' "); + create_error(parser, MPPE_END_BOUNDARY_NO_DASH, "bad final hyphen: "); + + parser->error_expected = '\n'; + create_error(parser, MPPE_PAUSED, "parser paused"); + create_error(parser, MPPE_BOUNDARY_END_NO_CRLF, "no CRLF at first boundary end: "); + create_error(parser, MPPE_BAD_START_BOUNDARY, "first boundary mismatching: "); + create_error(parser, MPPE_INVALID_HEADER_FIELD_CHAR, "invalid char in header field: "); + create_error(parser, MPPE_INVALID_HEADER_VALUE_CHAR, "invalid char in header value: "); + create_error(parser, MPPE_BAD_PART_END, "no next part or final hyphen: expecting CR or '-' "); + create_error(parser, MPPE_END_BOUNDARY_NO_DASH, "bad final hyphen: "); + + parser->error_expected = 'a'; + create_error(parser, MPPE_PAUSED, "parser paused"); + create_error(parser, MPPE_BOUNDARY_END_NO_CRLF, "no CRLF at first boundary end: "); + create_error(parser, MPPE_BAD_START_BOUNDARY, "first boundary mismatching: "); + create_error(parser, MPPE_INVALID_HEADER_FIELD_CHAR, "invalid char in header field: "); + create_error(parser, MPPE_INVALID_HEADER_VALUE_CHAR, "invalid char in header value: "); + create_error(parser, MPPE_BAD_PART_END, "no next part or final hyphen: expecting CR or '-' "); + create_error(parser, MPPE_END_BOUNDARY_NO_DASH, "bad final hyphen: "); +} + +TEST(multipart_parser, header_field) { + auto parser = create_parser(); + ssize_t ret; + + // header party + swoole::String header(1024); + header.append("--"); + header.append(boundary); + header.append("\r\n"); + header.append("Content-Disposition: form-data; name=\"test\"\r\n\r\n"); + MppResult result; + parser->data = &result; + + ret = multipart_parser_execute(parser, header.value(), header.get_length()); + ASSERT_EQ(ret, header.length); + + ASSERT_STREQ(result.header_field.c_str(), "Content-Disposition"); + ASSERT_TRUE(result.header_value.find("test") != result.header_value.npos); + + std::string boundary_str(parser->boundary, parser->boundary_length); + ASSERT_EQ(multipart_parser_execute(parser, SW_STRL("\r\n")), 2); + ASSERT_EQ(multipart_parser_execute(parser, boundary_str.c_str(), boundary_str.length()), boundary_str.length()); + ASSERT_EQ(multipart_parser_execute(parser, "--\r\n\r\n", 6), 6); +} + +TEST(multipart_parser, header_error) { + auto parser = create_parser(); + ssize_t ret; + + // header party + swoole::String header(1024); + header.append("--"); + header.append(boundary); + header.append("\r\n"); + header.append("Content-Disposition: form-data; name=\"test\""); + MppResult result; + parser->data = &result; + + ret = multipart_parser_execute(parser, header.value(), header.get_length()); + ASSERT_EQ(ret, -1); + ASSERT_EQ(parser->error_reason, MPPE_HEADER_VALUE_INCOMPLETE); + ASSERT_EQ(parser->error_expected, '\r'); +} + +TEST(multipart_parser, data) { + auto parser = create_parser(); + ssize_t ret; + + // header party + swoole::String header(1024); + header.append("--"); + header.append(boundary); + header.append("\r\n"); + header.append("Content-Disposition: form-data; name=\"test\"\r\n\r\n"); + MppResult result; + parser->data = &result; + ret = multipart_parser_execute(parser, header.value(), header.get_length()); + ASSERT_EQ(ret, header.length); + + std::string boundary_str(parser->boundary, parser->boundary_length); + + // data part + swoole::String data(128); + data.append_random_bytes(swoole_rand(60, 120), true); + data.append("\r"); + data.append_random_bytes(swoole_rand(60, 120), true); + data.append("\r\n"); + data.append_random_bytes(swoole_rand(60, 120), true); + data.append("\r\n"); + data.append(boundary_str.substr(0, swoole_rand(1, parser->boundary_length - 2))); + data.append_random_bytes(swoole_rand(60, 120), true); + ASSERT_EQ(multipart_parser_execute(parser, data.value(), data.get_length()), data.get_length()); + + auto append_data = [&]() { + size_t offset = data.length; + data.append_random_bytes(swoole_rand(60, 120), true); + size_t len = data.length - offset; + ASSERT_EQ(multipart_parser_execute(parser, data.value() + offset, len), len); + }; + + append_data(); + data.append("\r"); + ASSERT_EQ(multipart_parser_execute(parser, SW_STRL("\r")), 1); + + append_data(); + + data.append("\r\n"); + ASSERT_EQ(multipart_parser_execute(parser, SW_STRL("\r\n")), 2); + + append_data(); + + { + size_t offset = data.length; + data.append(boundary_str.substr(0, swoole_rand(1, parser->boundary_length - 2))); + size_t len = data.length - offset; + ASSERT_EQ(multipart_parser_execute(parser, data.value() + offset, len), len); + } + + ASSERT_EQ(multipart_parser_execute(parser, SW_STRL("\r\n")), 2); + ASSERT_EQ(multipart_parser_execute(parser, boundary_str.c_str(), boundary_str.length()), boundary_str.length()); + ASSERT_EQ(multipart_parser_execute(parser, "--\r\n\r\n", 6), 6); + + ASSERT_MEMEQ(data.value(), result.data.c_str(), result.data.length()); + + ASSERT_TRUE(result.header_complete); + ASSERT_TRUE(result.body_end); +} diff --git a/core-tests/src/server/server.cpp b/core-tests/src/server/server.cpp index 9457b99d49f..7d75e54d4aa 100644 --- a/core-tests/src/server/server.cpp +++ b/core-tests/src/server/server.cpp @@ -1,20 +1,1229 @@ -#include "tests.h" -#include "swoole/swoole_cxx.h" +/* + +----------------------------------------------------------------------+ + | 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" +#include "swoole_util.h" using namespace std; +using namespace swoole; + +int beforeReloadPid = 0; -TEST(server, create_pipe_buffers) -{ +TEST(server, schedule) { int ret; - swServer serv; + Server serv(Server::MODE_PROCESS); + 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(); + } + + 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"; + +TEST(server, base) { + 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(); - create_test_server(&serv); + lock.lock(); - ret = swServer_create_pipe_buffers(&serv); - 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]); + 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->send(req->info.fd, resp.c_str(), resp.length()); + + return SW_OK; + }; + + serv.start(); + t1.join(); +} + +TEST(server, process) { + 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(SW_SOCK_TCP, TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); } -} \ No newline at end of file + + 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](Server *serv) { + thread t1([=]() { + swoole_signal_block_all(); + + lock->lock(); + + ListenPort *port = serv->get_primary_port(); + + 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)); + 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, 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) { + Server serv; + serv.worker_num = 1; + serv.task_worker_num = 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.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; + }; + + ASSERT_EQ(serv.create(), SW_OK); + ASSERT_EQ(serv.create_task_workers(), SW_OK); + + 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); + + EventData buf; + memset(&buf.info, 0, sizeof(buf.info)); + + buf.info.ext_flags = SW_TASK_NOREPLY; + buf.info.len = strlen(packet); + memcpy(buf.data, packet, strlen(packet)); + + int _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) { + Server serv; + + serv.set_max_connection(0); + ASSERT_EQ(serv.get_max_connection(), SW_MIN(SW_MAX_CONNECTION, SwooleG.max_sockets)); + + serv.set_max_connection(SwooleG.max_sockets + 13); + ASSERT_EQ(serv.get_max_connection(), SwooleG.max_sockets); + + serv.set_max_connection(SwooleG.max_sockets - 13); + ASSERT_EQ(serv.get_max_connection(), SwooleG.max_sockets - 13); + + uint32_t last_value = serv.get_max_connection(); + + serv.create(); + + serv.set_max_connection(100); + ASSERT_EQ(serv.get_max_connection(), last_value); +} + +TEST(server, min_connection) { + Server serv; + + serv.task_worker_num = 14; + serv.worker_num = 5; + + serv.add_port(SW_SOCK_TCP, TEST_HOST, 0); + + serv.set_max_connection(15); + serv.create(); + ASSERT_EQ(serv.get_max_connection(), SwooleG.max_sockets); +} + +TEST(server, worker_num) { + Server serv; + + serv.worker_num = SW_CPU_NUM * SW_MAX_WORKER_NCPU + 99; + serv.task_worker_num = SW_CPU_NUM * SW_MAX_WORKER_NCPU + 99; + serv.create(); + + ASSERT_EQ(serv.worker_num, SW_CPU_NUM * SW_MAX_WORKER_NCPU); + ASSERT_EQ(serv.task_worker_num, SW_CPU_NUM * SW_MAX_WORKER_NCPU); +} + +TEST(server, reactor_num_base) { + Server serv(Server::MODE_BASE); + serv.reactor_num = SW_CPU_NUM * SW_MAX_THREAD_NCPU + 99; + serv.create(); + + ASSERT_EQ(serv.reactor_num, serv.worker_num); +} + +TEST(server, reactor_num_large) { + Server serv(Server::MODE_PROCESS); + serv.worker_num = SW_CPU_NUM * SW_MAX_WORKER_NCPU; + serv.reactor_num = SW_CPU_NUM * SW_MAX_THREAD_NCPU + 99; + serv.create(); + + ASSERT_EQ(serv.reactor_num, SW_CPU_NUM * SW_MAX_THREAD_NCPU); +} + +TEST(server, reactor_num_large2) { + Server serv(Server::MODE_PROCESS); + serv.reactor_num = SW_CPU_NUM * SW_MAX_THREAD_NCPU + 99; + serv.create(); + + ASSERT_EQ(serv.reactor_num, serv.worker_num); +} + +TEST(server, reactor_num_zero) { + Server serv; + serv.reactor_num = 0; + serv.create(); + + 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/core-tests/src/socket.cpp b/core-tests/src/socket.cpp deleted file mode 100644 index 4a82d78316e..00000000000 --- a/core-tests/src/socket.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "tests.h" -#ifndef _WIN32 -TEST(socket, swSocket_unix_sendto) -{ - int fd1,fd2,ret; - struct sockaddr_un un1,un2; - char sock1_path[] = "/tmp/udp_unix1.sock"; - char sock2_path[] = "/tmp/udp_unix2.sock"; - char test_data[] = "swoole"; - - bzero(&un1,sizeof(struct sockaddr_un)); - bzero(&un2,sizeof(struct sockaddr_un)); - - un1.sun_family = AF_UNIX; - un2.sun_family = AF_UNIX; - - unlink(sock1_path); - unlink(sock2_path); - - fd1 = socket(AF_UNIX,SOCK_DGRAM,0); - strncpy(un1.sun_path, sock1_path, sizeof(un1.sun_path) - 1); - bind(fd1,(struct sockaddr *)&un1,sizeof(un1)); - - fd2 = socket(AF_UNIX,SOCK_DGRAM,0); - strncpy(un2.sun_path, sock2_path, sizeof(un2.sun_path) - 1); - bind(fd2,(struct sockaddr *)&un2,sizeof(un2)); - - ret = swSocket_unix_sendto(fd1,sock2_path,test_data,strlen(test_data)); - ASSERT_GT(ret, 0); - - unlink(sock1_path); - unlink(sock2_path); -} -#endif diff --git a/core-tests/src/string.cpp b/core-tests/src/string.cpp deleted file mode 100644 index e3761a17572..00000000000 --- a/core-tests/src/string.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "tests.h" -#include "swoole/swoole_cxx.h" - -using namespace std; - -TEST(string, rtrim) -{ - char buf[1024]; - strcpy(buf, "hello world "); - swoole_rtrim(buf, strlen(buf)); - ASSERT_EQ(strcmp("hello world", buf), 0); - ASSERT_NE(strcmp("hello world ", buf), 0); - - strcpy(buf, " "); - swoole_rtrim(buf, strlen(buf)); - ASSERT_EQ(strlen(buf), 0); -} - -TEST(string, strnpos) -{ - { - string haystack= "hello world"; - string needle = " "; - int pos; - - pos = swoole_strnpos(haystack.c_str(), haystack.length(), needle.c_str(), needle.length()); - ASSERT_EQ(pos, 5); - } - { - string haystack= "hello world"; - string needle = "*"; - int pos; - - pos = swoole_strnpos(haystack.c_str(), haystack.length(), needle.c_str(), needle.length()); - ASSERT_EQ(-1, pos); - } -} - -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()); - 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()); - ASSERT_EQ(NULL, pos); - } -} - -TEST(string, explode) -{ - string haystack= "hello world"; - string needle = " "; - - swString str; - swString_clear(&str); - str.str = (char*) haystack.c_str(); - str.length = haystack.length(); - - int value_1 = 0; - - const char *explode_str = nullptr; - size_t explode_length = 0; - - swoole::string_split(&str, needle.c_str(), needle.length(), [&](char *data, size_t length) -> int - { - explode_str = data; - explode_length = length; - value_1 = 5; - return false; - }); - - ASSERT_EQ(haystack, explode_str); - ASSERT_EQ(6, explode_length); - ASSERT_EQ(5, value_1); -} - -TEST(string, explode_2) -{ - string haystack= "hello,world,swoole,php,last"; - string needle = ","; - - swString str; - swString_clear(&str); - str.str = (char*) haystack.c_str(); - str.length = haystack.length(); - - int count = 0; - vector list; - - size_t n = swoole::string_split(&str, needle.c_str(), needle.length(), [&](char *data, size_t length) -> int - { - list.push_back(string(data, length-1)); - count ++; - return true; - }); - - ASSERT_EQ(list[0], string("hello")); - ASSERT_EQ(list[1], string("world")); - ASSERT_EQ(list[2], string("swoole")); - ASSERT_EQ(list[3], string("php")); - ASSERT_EQ("last", string(str.str + n, str.length -n)); - ASSERT_EQ(4, count); - ASSERT_EQ(list.size(), count); -} diff --git a/core-tests/src/thread_pool.cpp b/core-tests/src/thread_pool.cpp deleted file mode 100755 index 5f234e8cba2..00000000000 --- a/core-tests/src/thread_pool.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef __MACH__ -#include "tests.h" -#include - -static swThreadPool pool; -static int _pipe; -const static int N = 10000; - -static int thread_onTask(swThreadPool *pool, void *task, int task_len) -{ - sw_atomic_long_t *n = (sw_atomic_long_t *) task; - sw_atomic_fetch_add(n, 1); - if (*n == N) - { - write(_pipe, (void*) n, sizeof(long)); - } - return SW_OK; -} - -TEST(thread_pool, dispatch) -{ - ASSERT_EQ(swThreadPool_create(&pool, 4), SW_OK); - pool.onTask = thread_onTask; - ASSERT_EQ(swThreadPool_run(&pool), SW_OK); - sw_atomic_long_t result = 0; - - _pipe = eventfd(0, 0); - - for (long i = 0; i < N; i++) - { - ASSERT_EQ(swThreadPool_dispatch(&pool, (void*) &result, sizeof(result)), SW_OK); - } - - long buf; - read(_pipe, (void*) &buf, sizeof(buf)); - close(_pipe); - - ASSERT_EQ(swThreadPool_free(&pool), SW_OK); - ASSERT_EQ(result, N); -} -#endif - 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/docs/CODE-STYLE.md b/docs/CODE-STYLE.md new file mode 100644 index 00000000000..f6eaf6c3a89 --- /dev/null +++ b/docs/CODE-STYLE.md @@ -0,0 +1,6 @@ +Code Style +======= +* Google C++ Style +* Indent adjusted to 4 characters +* Line width adjusted to 120 characters + diff --git a/docs/CPPLINT.cfg b/docs/CPPLINT.cfg new file mode 100644 index 00000000000..e5e00361fef --- /dev/null +++ b/docs/CPPLINT.cfg @@ -0,0 +1,4 @@ +set noparent +filter=-build/include_alpha,-build/include_subdir,-build/include_what_you_use,-legal/copyright,-readability/nolint +braces=4 +linelength=120 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/docs/google-style.xml b/docs/google-style.xml new file mode 100644 index 00000000000..e0559fa71aa --- /dev/null +++ b/docs/google-style.xml @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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/proc_open.php b/examples/coroutine/proc_open.php new file mode 100644 index 00000000000..f98bd81bad2 --- /dev/null +++ b/examples/coroutine/proc_open.php @@ -0,0 +1,20 @@ + array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("pipe", "w"), + ); + + $process = proc_open('unknown', $descriptorspec, $pipes); + + var_dump($pipes); + + var_dump(fread($pipes[2], 8192)); + + $return_value = proc_close($process); + + echo "command returned $return_value\n"; +}); \ No newline at end of file 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 74ee9bc5743..b3bc0fab6ce 100644 --- a/examples/cpp/Makefile +++ b/examples/cpp/Makefile @@ -1,4 +1,10 @@ +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 + rm -f co repeat test_server diff --git a/examples/cpp/co.cc b/examples/cpp/co.cc index 6c0d022092f..ffd18c82b05 100644 --- a/examples/cpp/co.cc +++ b/examples/cpp/co.cc @@ -2,111 +2,110 @@ #include #include #include -#include "coroutine.h" -#include "coroutine_socket.h" -#include "coroutine_system.h" + +#include "swoole_coroutine.h" +#include "swoole_coroutine_socket.h" +#include "swoole_coroutine_system.h" using swoole::Coroutine; -using swoole::coroutine::System; using swoole::coroutine::Socket; +using swoole::coroutine::System; using namespace std; list q; list slaves; size_t qs; -int main(int argc, char **argv) -{ - swoole_event_init(); - SwooleTG.reactor->wait_exit = 1; - +int main(int argc, char **argv) { signal(SIGPIPE, SIG_IGN); - Coroutine::create([](void *arg) { - System::sleep(2.0); - cout << "CO1\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 << "CO2\n"; - }); + Coroutine::create([](void *arg) { + System::sleep(1); + cout << "CO-2, sleep 1\n"; + }); - Coroutine::create([](void *arg) { - 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 { + auto data = q.front(); + q.pop_front(); + qs -= data.length(); + client->send(data.c_str(), data.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 { - it++; + string resp("ERROR\n"); + client->send(resp.c_str(), resp.length()); } } } - else if (strncasecmp("pop", buf, 3) == 0) { - if (q.empty()) { - string resp("EMPTY\n"); - client->send(resp.c_str(), resp.length()); - } else { - auto data = q.front(); - q.pop_front(); - qs -= data.length(); - client->send(data.c_str(), data.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()); - } - slaves.push_back(_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(); + if (_client == nullptr) { + break; + } + for (auto data : q) { + _client->send(data.c_str(), data.length()); + } + slaves.push_back(_client); + } + }); }); - swoole_event_wait(); - return 0; } diff --git a/examples/cpp/repeat.cc b/examples/cpp/repeat.cc new file mode 100644 index 00000000000..6d79537be48 --- /dev/null +++ b/examples/cpp/repeat.cc @@ -0,0 +1,66 @@ +#include "swoole_server.h" +using namespace swoole; + +int main(int argc, char **argv) { + swoole_init(); + + enum Server::Mode factory_mode; + if (argc > 1) { + factory_mode = Server::MODE_PROCESS; + } else { + factory_mode = Server::MODE_BASE; + } + + for (int i = 0; i < 2; i++) { + Server serv(factory_mode); + + serv.reactor_num = 1; + serv.worker_num = 1; + + serv.onReceive = [](Server *serv, RecvData *req) { return SW_OK; }; + + serv.onPacket = [](Server *serv, RecvData *req) { return SW_OK; }; + + 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) { + printf("timer=%p\n", tnode); + if (serv->is_base_mode()) { + kill(getpid(), SIGTERM); + } else { + kill(serv->gs->master_pid, SIGTERM); + } + }, + nullptr); + }; + + serv.add_port(SW_SOCK_UDP, "0.0.0.0", 9502); + serv.add_port(SW_SOCK_TCP6, "::", 9503); + serv.add_port(SW_SOCK_UDP6, "::", 9504); + + ListenPort *port = serv.add_port(SW_SOCK_TCP, "127.0.0.1", 9501); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + port->open_eof_check = 0; + // config + port->backlog = 128; + memcpy(port->protocol.package_eof, SW_STRL("\r\n\r\n")); + + if (serv.create()) { + swoole_warning("create server fail[error=%d]", swoole_get_last_error()); + exit(1); + } + + if (serv.start() < 0) { + swoole_warning("start server fail[error=%d]", swoole_get_last_error()); + exit(3); + } + } + + return 0; +} diff --git a/examples/cpp/test_server.cc b/examples/cpp/test_server.cc index 3bf2adcc0a7..32e44d2c826 100644 --- a/examples/cpp/test_server.cc +++ b/examples/cpp/test_server.cc @@ -3,42 +3,46 @@ * make test_server * ./bin/test_server */ -#include "server.h" +#include "swoole_server.h" +#include "swoole_util.h" -int my_onPacket(swServer *serv, swEventData *req); -int my_onReceive(swServer *serv, swEventData *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); +using namespace swoole; + +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; -int main(int argc, char **argv) -{ - int ret; - swServer serv; - swServer_init(&serv); +int main(int argc, char **argv) { + swoole_init(); + + sw_logger()->set_date_format("%F %T"); + sw_logger()->set_date_with_microseconds(true); + + Server serv(Server::MODE_BASE); serv.reactor_num = 4; - serv.worker_num = 2; + serv.worker_num = 1; - serv.factory_mode = SW_MODE_BASE; - serv.max_connection = 10000; - //serv.open_cpu_affinity = 1; - //serv.open_tcp_nodelay = 1; - //serv.daemonize = 1; - //memcpy(serv.log_file, SW_STRS("/tmp/swoole.log")); + serv.set_max_connection(10000); + // serv.open_cpu_affinity = 1; + // serv.open_tcp_nodelay = 1; + // serv.daemonize = 1; + // memcpy(serv.log_file, SW_STRS("/tmp/swoole.log")); serv.dispatch_mode = 2; - //serv.open_tcp_keepalive = 1; + // serv.open_tcp_keepalive = 1; #ifdef HAVE_OPENSSL - //serv.ssl_cert_file = "tests/ssl/ssl.crt"; - //serv.ssl_key_file = "tests/ssl/ssl.key"; - //serv.open_ssl = 1; + // serv.ssl_cert_file = "tests/ssl/ssl.crt"; + // serv.ssl_key_file = "tests/ssl/ssl.key"; + // serv.open_ssl = 1; #endif serv.onStart = my_onStart; @@ -50,165 +54,122 @@ int main(int argc, char **argv) serv.onWorkerStart = my_onWorkerStart; serv.onWorkerStop = my_onWorkerStop; - // swSignal_add(SIGINT, user_signal); + // swSignal_set(SIGINT, user_signal); - //create Server - ret = swServer_create(&serv); - if (ret < 0) - { - swWarn("create server fail[error=%d]", SwooleG.error); - exit(1); - } + serv.add_port(SW_SOCK_UDP, "0.0.0.0", 9502); + serv.add_port(SW_SOCK_TCP6, "::", 9503); + serv.add_port(SW_SOCK_UDP6, "::", 9504); - swListenPort *port = swServer_add_port(&serv, SW_SOCK_TCP, "127.0.0.1", 9501); - if (!port) - { - swWarn("listen failed, [error=%d]", SwooleG.error); + swListenPort *port = serv.add_port(SW_SOCK_TCP, "127.0.0.1", 9501); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); exit(2); } port->open_eof_check = 0; - //config + // config port->backlog = 128; memcpy(port->protocol.package_eof, SW_STRL("\r\n\r\n")); - swServer_add_port(&serv, SW_SOCK_UDP, "0.0.0.0", 9502); - swServer_add_port(&serv, SW_SOCK_TCP6, "::", 9503); - swServer_add_port(&serv, SW_SOCK_UDP6, "::", 9504); + if (serv.create()) { + swoole_warning("create server fail[error=%d]", swoole_get_last_error()); + exit(1); + } - ret = swServer_start(&serv); - if (ret < 0) - { - swWarn("start server fail[error=%d]", ret); + if (serv.start() < 0) { + 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, swEventData *req) -{ - int ret; +int my_onReceive(Server *serv, RecvData *req) { + char req_data[SW_IPC_BUFFER_SIZE]; char resp_data[SW_IPC_BUFFER_SIZE]; g_receive_count++; - swPacket_ptr *req_pkg = (swPacket_ptr *)req; - swConnection *conn = swWorker_get_connection(serv, req_pkg->info.fd); + Connection *conn = serv->get_connection_by_session_id(req->info.fd); - swoole_rtrim(req_pkg->data.str, req_pkg->data.length); - swNotice("onReceive[%d]: ip=%s|port=%d Data=%s|Len=%d", g_receive_count, - swSocket_get_ip(conn->socket_type, &conn->info), - swSocket_get_port(conn->socket_type, &conn->info), - req_pkg->data.str, req_pkg->data.length); + memcpy(req_data, req->data, req->info.len); + swoole::rtrim(req_data, req->info.len); + swoole_notice("onReceive[%d]: ip=%s|port=%d Data=%s|Len=%d", + g_receive_count, + conn->info.get_ip(), + conn->info.get_port(), + req_data, + req->info.len); - int n = sw_snprintf(resp_data, SW_IPC_BUFFER_SIZE, "Server: %.*s\n", - req_pkg->data.length, req_pkg->data.str); + int n = sw_snprintf(resp_data, SW_IPC_BUFFER_SIZE, "Server: %.*s\n", req->info.len, req_data); - ret = serv->send(serv, req->info.fd, resp_data, n); - if (ret < 0) - { - swNotice("send to client fail. errno=%d", errno); - } - else - { - swNotice("send %d bytes to client success. data=%s", n, resp_data); + if (!serv->send(req->info.fd, resp_data, n)) { + swoole_notice("send to client fail. errno=%d", errno); + } else { + swoole_notice("send %d bytes to client success. data=%s", n, resp_data); } return SW_OK; } -int my_onPacket(swServer *serv, swEventData *req) -{ - char *data; - int length; +int my_onPacket(Server *serv, RecvData *req) { char address[256]; int port = 0; - int ret; + int ret = 0; - swDgramPacket *packet; + DgramPacket *packet = (DgramPacket *) req->data; - serv->get_packet(serv, req, &data); - packet = (swDgramPacket*) data; + auto serv_socket = serv->get_server_socket(req->info.server_fd); - int serv_sock = req->info.server_fd; - - if (packet->socket_type == SW_SOCK_UDP) - { + if (packet->socket_type == SW_SOCK_UDP) { inet_ntop(AF_INET, &packet->socket_addr.addr.inet_v4.sin_addr, address, sizeof(address)); port = ntohs(packet->socket_addr.addr.inet_v4.sin_port); - } - else if (packet->socket_type == SW_SOCK_UDP6) - { + } else if (packet->socket_type == SW_SOCK_UDP6) { inet_ntop(AF_INET6, &packet->socket_addr.addr.inet_v6.sin6_addr, address, sizeof(address)); port = ntohs(packet->socket_addr.addr.inet_v6.sin6_port); - } - else if (packet->socket_type == SW_SOCK_UNIX_DGRAM) - { + } else if (packet->socket_type == SW_SOCK_UNIX_DGRAM) { strcpy(address, packet->socket_addr.addr.un.sun_path); - } - else - { + } else { abort(); } - data = packet->data; - length = packet->length; + 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); - if (packet->socket_type == SW_SOCK_UDP) - { - ret = swSocket_udp_sendto(serv_sock, address, port, resp_data, n); - } - else if (packet->socket_type == SW_SOCK_UDP6) - { - ret = swSocket_udp_sendto6(serv_sock, address, port, resp_data, n); - } - else if (packet->socket_type == SW_SOCK_UNIX_DGRAM) - { - ret = swSocket_unix_sendto(serv_sock, address, resp_data, n); - } + ret = serv_socket->sendto(address, port, resp_data, n); - if (ret < 0) - { - swNotice("send to client fail. errno=%d", errno); - } - else - { - swNotice("send %d bytes to client success. data=%s", n, resp_data); + if (ret < 0) { + swoole_notice("send to client fail. errno=%d", errno); + } else { + 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\n"); +void my_onShutdown(Server *serv) { + swoole_notice("Server is shutdown"); } -void my_onConnect(swServer *serv, swDataHead *info) -{ - swNotice("PID=%d\tConnect fd=%d|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=%d|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 new file mode 100644 index 00000000000..0a492b42e9f --- /dev/null +++ b/examples/curl/hook.php @@ -0,0 +1,34 @@ + SWOOLE_HOOK_ALL | SWOOLE_HOOK_NATIVE_CURL, ]); +//Co::set(['hook_flags' => SWOOLE_HOOK_ALL, ]); + +Co\run(function () { + $n = 3; + while($n--) { + go('test'); + } +}); + +function test() { + echo "curl init\n"; + $ch = curl_init(); +// $url = 'https://www.baidu.com/'; + $url = "http://127.0.0.1:9801/"; + + 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) { + //var_dump($ch, $strHeader); + return strlen($strHeader); + }); + + $output = curl_exec($ch); + var_dump($output); + var_dump(strlen($output)); + if ($output === false) { + echo "CURL Error:" . curl_error($ch); + } +// var_dump($output); + curl_close($ch); +} 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(['worker_num' => 8, ]); + +$http->on("start", function ($server) { + echo "Swoole http server is started at http://127.0.0.1:9501\n"; +}); + +$http->on("request", function ($request, $response) { + sleep(1); + $response->end("Hello World\n"); +}); + +$http->start(); diff --git a/examples/dtls/server.php b/examples/dtls/server.php index b79ac2053a9..d6b8e4132aa 100644 --- a/examples/dtls/server.php +++ b/examples/dtls/server.php @@ -1,5 +1,5 @@ set( [ @@ -12,7 +12,7 @@ ] ); -$server->on('Receive', function (swoole_server $serv, $fd, $tid, $data) +$server->on('Receive', function (Swoole\Server $serv, $fd, $tid, $data) { var_dump($fd, $data, $serv->getClientInfo($fd)); $serv->send($fd, "Swoole: $data\n"); diff --git a/examples/eof/async_client.php b/examples/eof/async_client.php deleted file mode 100644 index e935cb86d18..00000000000 --- a/examples/eof/async_client.php +++ /dev/null @@ -1,35 +0,0 @@ -send($_send); - echo "send ".strlen($_send)." bytes\n"; -} - -$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); //异步非阻塞 -$client->set(array('open_eof_check' => true, 'package_eof' => "\r\n\r\n")); - -$client->on("connect", function(swoole_client $cli) { - send($cli); -}); - -$client->on("receive", function (swoole_client $cli, $data) { - static $i = 0; - if ($i % 100 == 1) - { - echo "received " . strlen($data) . " bytes\n"; - } - $i ++; - //usleep(200000); - //send($cli); -}); - -$client->on("error", function(swoole_client $cli){ - echo "error\n"; -}); - -$client->on("close", function(swoole_client $cli){ - echo "Connection close\n"; -}); - -$client->connect('127.0.0.1', 9501); diff --git a/examples/eof/client.php b/examples/eof/client.php index cad67406525..a013c99a279 100644 --- a/examples/eof/client.php +++ b/examples/eof/client.php @@ -2,33 +2,28 @@ /** * 分段发送数据 * - * @param swoole_client $client - * @param string $data - * @param int $chunk_size + * @param Swoole\Client $client + * @param string $data + * @param int $chunk_size */ -function send_chunk(swoole_client $client, $data, $chunk_size = 1024) +function send_chunk(Swoole\Client $client, $data, $chunk_size = 1024) { - $len = strlen($data); - $chunk_num = intval($len / $chunk_size) + 1; - for ($i = 0; $i < $chunk_num; $i++) - { - if ($len < ($i + 1) * $chunk_size) - { - $sendn = $len - ($i * $chunk_size); - } - else - { - $sendn = $chunk_size; - } - $client->send(substr($data, $i * $chunk_size, $sendn)); - } + $len = strlen($data); + $chunk_num = intval($len / $chunk_size) + 1; + for ($i = 0; $i < $chunk_num; $i++) { + if ($len < ($i + 1) * $chunk_size) { + $sendn = $len - ($i * $chunk_size); + } else { + $sendn = $chunk_size; + } + $client->send(substr($data, $i * $chunk_size, $sendn)); + } } -$client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 -if(!$client->connect('127.0.0.1', 9501, 0.5, 0)) -{ - echo "Over flow. errno=".$client->errCode; - die("\n"); +$client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 +if (!$client->connect('127.0.0.1', 9501, 0.5, 0)) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); } //for ($i = 0; $i < 10; $i++) @@ -39,21 +34,20 @@ function send_chunk(swoole_client $client, $data, $chunk_size = 1024) //exit; $data = array( - 'name' => __FILE__, - 'content' => str_repeat('A', 8192 * rand(1, 3)), //800K + 'name' => __FILE__, + 'content' => str_repeat('A', 8192 * rand(1, 3)), //800K ); $_serialize_data = serialize($data); -$_send = $_serialize_data."__doit__"; +$_send = $_serialize_data . "__doit__"; -echo "serialize_data length=".strlen($_serialize_data)."send length=".strlen($_send)."\n"; +echo "serialize_data length=" . strlen($_serialize_data) . "send length=" . strlen($_send) . "\n"; //send_chunk($client, $_send); // -if(!$client->send($_send)) -{ - die("send failed.\n"); +if (!$client->send($_send)) { + die("send failed.\n"); } //$client->send("\r\n".substr($_serialize_data, 0, 8000)); @@ -65,9 +59,8 @@ function send_chunk(swoole_client $client, $data, $chunk_size = 1024) //usleep(500000); -if (!$client->send("\r\n\r\n")) -{ - die("send failed.\n"); +if (!$client->send("\r\n\r\n")) { + die("send failed.\n"); } echo $client->recv(); diff --git a/examples/eof/server.php b/examples/eof/server.php index a9951f231be..bbf1ab21eef 100644 --- a/examples/eof/server.php +++ b/examples/eof/server.php @@ -1,5 +1,5 @@ set(array( 'package_eof' => "\r\n\r\n", 'open_eof_check' => true, @@ -11,21 +11,18 @@ //$serv->on('connect', function ($serv, $fd) { // //echo "[#" . posix_getpid() . "]\tClient:Connect.\n"; //}); -$serv->on('receive', function (swoole_server $serv, $fd, $reactor_id, $data) -{ +$serv->on('receive', function (Swoole\Server $serv, $fd, $reactor_id, $data) { echo '#' . $serv->worker_id . " recv: " . strlen($data) . "\n"; - for ($i = 0; $i < 1000; $i++) - { + for ($i = 0; $i < 1000; $i++) { $resp = str_repeat('A', rand(10000, 50000)) . "\r\n\r\n"; $serv->send($fd, $resp); - if ($i % 100 == 1) - { + if ($i % 100 == 1) { sleep(1); - echo "send ".strlen($resp)." bytes\n"; + echo "send " . strlen($resp) . " bytes\n"; } } }); //$serv->on('close', function ($serv, $fd) { - //echo "[#" . posix_getpid() . "]\tClient: Close.\n"; +//echo "[#" . posix_getpid() . "]\tClient: Close.\n"; //}); $serv->start(); diff --git a/examples/event/cycle.php b/examples/event/cycle.php index 4282a6f2dd9..bc4a20da260 100644 --- a/examples/event/cycle.php +++ b/examples/event/cycle.php @@ -11,3 +11,5 @@ Swoole\Event::cycle(null); }); }); + +Swoole\Event::wait(); diff --git a/examples/event/inotify.php b/examples/event/inotify.php index 683b101e245..89d96619844 100644 --- a/examples/event/inotify.php +++ b/examples/event/inotify.php @@ -5,7 +5,7 @@ //监听文件,仅监听修改操作,如果想要监听所有事件可以使用IN_ALL_EVENTS $watch_descriptor = inotify_add_watch($fd, __DIR__.'/inotify.data', IN_MODIFY); -swoole_event_add($fd, function ($fd) { +Swoole\Event::add($fd, function ($fd) { $events = inotify_read($fd); if ($events) { foreach ($events as $event) { @@ -13,3 +13,5 @@ } } }); + +Swoole\Event::wait(); diff --git a/examples/event/sockets.php b/examples/event/sockets.php index b791c9ac092..65bf34583ad 100644 --- a/examples/event/sockets.php +++ b/examples/event/sockets.php @@ -3,51 +3,48 @@ * require ./configure --enable-sockets */ +use Swoole\Event; + $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("Unable to create socket\n"); socket_set_nonblock($socket) or die("Unable to set nonblock on socket\n"); function socket_onRead($socket) { - static $i = 0; - - echo socket_read($socket, 8192)."\n"; - $i ++; - if ($i > 10) - { - echo "finish\n"; - swoole_event_del($socket); - socket_close($socket); - } - else - { - sleep(1); - swoole_event_set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE); - } + static $i = 0; + + echo socket_read($socket, 8192) . "\n"; + $i++; + if ($i > 10) { + echo "finish\n"; + Event::del($socket); + socket_close($socket); + } else { + sleep(1); + Event::set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE); + } } function socket_onWrite($socket) { - socket_write($socket, "hi swoole"); - swoole_event_set($socket, null, null, SWOOLE_EVENT_READ); + socket_write($socket, "hi swoole"); + Event::set($socket, null, null, SWOOLE_EVENT_READ); } function socket_onConnect($socket) { - $err = socket_get_option($socket, SOL_SOCKET, SO_ERROR); - if ($err == 0) - { - echo "connect server success\n"; - swoole_event_set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ); - socket_write($socket, "first package\n"); - } - else - { - echo "connect server failed\n"; - swoole_event_del($socket); - socket_close($socket); - } + $err = socket_get_option($socket, SOL_SOCKET, SO_ERROR); + if ($err == 0) { + echo "connect server success\n"; + Event::set($socket, null, 'socket_onWrite', SWOOLE_EVENT_READ); + socket_write($socket, "first package\n"); + } else { + echo "connect server failed\n"; + Event::del($socket); + socket_close($socket); + } } -swoole_event_add($socket, 'socket_onRead', 'socket_onConnect', SWOOLE_EVENT_WRITE); -@socket_connect($socket, '127.0.0.1', 9501); +Event::add($socket, 'socket_onRead', 'socket_onConnect', SWOOLE_EVENT_WRITE); +socket_connect($socket, '127.0.0.1', 9501); +Event::wait(); diff --git a/examples/event/stdin.php b/examples/event/stdin.php index 6ecd4d8077e..bbf93cd9b24 100644 --- a/examples/event/stdin.php +++ b/examples/event/stdin.php @@ -1,4 +1,5 @@ setData(http_build_query(['a'=>123,'b'=>"哈哈"])); -//$cli->set(['timeout' => -1]); -//$cli->setHeaders(['Host' => 'www.baidu.com']); -//$cli->set(['http_proxy_host' => '127.0.0.1', 'http_proxy_port' => 8888,]); - -$cli->setHeaders(['User-Agent' => "swoole"]); - -$cli->get('/index.php', function ($cli) -{ - var_dump($cli); -}); - -//$cli->post('/dump.php', array("test" => 'abc'), function ($cli) { -// echo $cli->body; -// $cli->get('/index.php', function ($cli) { -// file_put_contents(__DIR__.'/t.html', $cli->body); -// $cli->download('/index.php', __DIR__.'/phpinfo.html', function ($cli) -// { -// var_dump($cli->downloadFile); -// }); -// }); -//}); diff --git a/examples/http/async_websocket.php b/examples/http/async_websocket.php deleted file mode 100644 index be19dff7dde..00000000000 --- a/examples/http/async_websocket.php +++ /dev/null @@ -1,11 +0,0 @@ -on('message', function ($_cli, $frame) { - var_dump($frame); -}); - -$cli->upgrade('/', function ($cli) { - echo $cli->body; - $cli->push("hello world"); -}); diff --git a/examples/http/client.php b/examples/http/client.php index d3f3ada40eb..cdb6dbd5309 100644 --- a/examples/http/client.php +++ b/examples/http/client.php @@ -1,5 +1,5 @@ connect('127.0.0.1', 9501); //$type = 'GET'; diff --git a/examples/http/detach.php b/examples/http/detach.php index a12ff8ccd2f..ec929b4c770 100644 --- a/examples/http/detach.php +++ b/examples/http/detach.php @@ -1,5 +1,5 @@ set(['task_worker_num' => 1, 'worker_num' => 1]); diff --git a/examples/http/event-stream.php b/examples/http/event-stream.php new file mode 100644 index 00000000000..45ab4a7ecc0 --- /dev/null +++ b/examples/http/event-stream.php @@ -0,0 +1,40 @@ +on('request', function ($req, Swoole\Http\Response $resp) use ($http) { + if ($req->server['request_uri'] == '/stream') { + $resp->header("Content-Type", "text/event-stream"); + $resp->header("Cache-Control", "no-cache"); + $resp->header("Connection", "keep-alive"); + $resp->header("X-Accel-Buffering", "no"); + $resp->header('Content-Encoding', ''); + $resp->header("Content-Length", ''); + $resp->end(); + go(function () use ($resp, $http) { + while (true) { + Co::sleep(1); + $http->send($resp->fd, 'data: ' . base64_encode(random_bytes(random_int(16, 128))). "\n\n"); + } + }); + } elseif ($req->server['request_uri'] == '/') { + $resp->end(<< + + +HTML + ); + } else { + $resp->status(404); + $resp->end(); + } +}); + +$http->start(); diff --git a/examples/http/http_proxy.php b/examples/http/http_proxy.php deleted file mode 100644 index e4642f1b635..00000000000 --- a/examples/http/http_proxy.php +++ /dev/null @@ -1,25 +0,0 @@ -set(array( - 'http_proxy_host'=>"127.0.0.1", - 'http_proxy_port'=>3128, - - )); - $cli->setHeaders([ - 'Host' => $domainName, - "User-Agent" => 'Chrome/49.0.2587.3', - ]); - $cli->get('/', function ($cli) { - echo "Length: " . strlen($cli->body) . "\n"; -$cli->close(); - // echo $cli->body; - }); -}); - - - - -?> diff --git a/examples/http/http_proxy_auth.php b/examples/http/http_proxy_auth.php deleted file mode 100644 index e4a1c40e445..00000000000 --- a/examples/http/http_proxy_auth.php +++ /dev/null @@ -1,18 +0,0 @@ -set(array( - 'http_proxy_host' => "127.0.0.1", - 'http_proxy_port' => 33080, - 'http_proxy_user' => 'test', - 'http_proxy_password' => 'test', -)); -$cli->setHeaders([ - 'Host' => "localhost", - "User-Agent" => 'Chrome/49.0.2587.3', -]); -$cli->get('/', function ($cli) { - echo "Length: " . strlen($cli->body) . ", statusCode=".$cli->statusCode."\n"; - $cli->close(); - echo $cli->body; -}); diff --git a/examples/http/https_proxy.php b/examples/http/https_proxy.php deleted file mode 100644 index eabd3758ec5..00000000000 --- a/examples/http/https_proxy.php +++ /dev/null @@ -1,21 +0,0 @@ -set(array( - 'http_proxy_host'=>"127.0.0.1", - 'http_proxy_port'=>3128, - - )); - $cli->setHeaders([ - 'Host' => $domainName, - "User-Agent" => 'Chrome/49.0.2587.3', - ]); - $cli->get('/', function ($cli) { - echo "Length: " . strlen($cli->body) . "\n"; - echo $cli->body; - }); -}); - - -?> diff --git a/examples/http/no-compression.php b/examples/http/no-compression.php new file mode 100644 index 00000000000..b1228aa80b5 --- /dev/null +++ b/examples/http/no-compression.php @@ -0,0 +1,17 @@ +on('request', function ($req, Swoole\Http\Response $resp) use ($http) { + if ($req->server['request_uri'] == '/') { + $resp->header('Content-Encoding', ''); + $resp->end(str_repeat('A', 1024)); + } elseif ($req->server['request_uri'] == '/gzip') { + $resp->end(str_repeat('A', 1024)); + } else { + $resp->status(404); + $resp->end(); + } +}); + +$http->start(); diff --git a/examples/http/redirect.php b/examples/http/redirect.php index 0536e3d8453..c06e6d0a167 100644 --- a/examples/http/redirect.php +++ b/examples/http/redirect.php @@ -1,5 +1,5 @@ on('request', function ($req, Swoole\Http\Response $resp) { $resp->redirect("http://www.baidu.com/", 301); diff --git a/examples/http/server.php b/examples/http/server.php index e44e59365d1..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 @@ -71,6 +76,7 @@ function no_chunk(swoole_http_request $request, swoole_http_response $response) if (substr($request->server['request_uri'], -8, 8) == 'test.jpg') { $response->header('Content-Type', 'image/jpeg'); $response->sendfile(dirname(__DIR__) . '/test.jpg'); + return; } elseif ($request->server['request_uri'] == '/test.txt') { $last_modified_time = filemtime(__DIR__ . '/test.txt'); $etag = md5_file(__DIR__ . '/test.txt'); @@ -83,9 +89,14 @@ function no_chunk(swoole_http_request $request, swoole_http_response $response) } else { $response->sendfile(__DIR__ . '/test.txt'); } + return; } else if ($request->server['request_uri'] == '/favicon.ico') { $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; @@ -110,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); @@ -124,8 +139,9 @@ 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; // }); } //var_dump($request); @@ -146,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); @@ -164,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.

"); } } @@ -174,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"; }); @@ -199,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 70457fd4cf7..781ae316a97 100644 --- a/examples/http2/server.php +++ b/examples/http2/server.php @@ -1,19 +1,27 @@ SWOOLE_TRACE_HTTP2, 'log_level' => 0, ]); -$key_dir = dirname(dirname(__DIR__)) . '/tests/include/api/swoole_http_server/localhost-ssl'; -$http = new swoole_http_server("0.0.0.0", 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); +$key_dir = __DIR__ . '/../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, 'document_root' => dirname(__DIR__), - 'ssl_cert_file' => $key_dir . '/server.crt', - 'ssl_key_file' => $key_dir . '/server.key', + 'ssl_cert_file' => $key_dir . '/ssl.crt', + '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 a3a1ae1b6f1..00000000000 --- a/examples/server.php +++ /dev/null @@ -1,567 +0,0 @@ - 16, // 线程数. 一般设置为CPU核数的1-4倍 - 'worker_num' => 8, // 工作进程数量. 设置为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' => 10, // 心跳检测间隔时长(秒) - 'heartbeat_idle_time' => 20, // 连接最大允许空闲的时间 - //'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被耗光而造成的丢包 - ); - - private static $buffers = array(); - - /** - * @param $fd - * @return swoole_buffer - */ - static function getBuffer($fd, $create = true) - { - if (!isset(self::$buffers[$fd])) - { - if (!$create) - { - return false; - } - self::$buffers[$fd] = new swoole_buffer(1024 * 128); - } - return self::$buffers[$fd]; - } -} - -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"); - $buffer = G::getBuffer($fd); - if ($buffer) - { - $buffer->clear(); - } - //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 == 'sendbuffer') - { - $buffer = G::getBuffer($fd); - $buffer->append("hello\n"); - $serv->send($fd, $buffer); - } - 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 303433a5807..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 new file mode 100644 index 00000000000..c2768b425f4 --- /dev/null +++ b/examples/server/eof_client.php @@ -0,0 +1,52 @@ +set( + array( + 'open_eof_split' => true, + 'package_eof' => "\r\n", + 'package_max_length' => 8 * 1024 * 1024, + ) +); + +if (!$client->connect('127.0.0.1', 9504)) { + exit("connect failed\n"); +} + +$func = "send_test" . intval(empty($argv[1]) ? 3 : $argv[1]); + +for ($l = 0; $l < 1; $l++) { + for ($i = 0; $i < 10; $i++) { + $len = rand(100000, 200000); + $func($client, $len); + } +} + +function send_test3($client, $len) +{ + $data = str_repeat('A', $len) . "\r\n"; + $chunks = str_split($data, 4000); + foreach ($chunks as $ch) { + $client->send($ch); + } + echo "send : " . strlen($data) . "\n"; +// $data = $client->recv(); +// echo "recv : " . strlen($data) . "\n"; +} + +function send_test2($client, $len) +{ + $data = pack('N', $len + 4); + $data .= str_repeat('A', $len) . rand(100000, 999999); + $client->send($data); + + $data = $client->recv(); +} + +function send_test1($client, $len) +{ + $client->send(pack('N', $len + 4)); + usleep(10); + $client->send(str_repeat('A', $len) . rand(1000, 9999)); + $data = $client->recv(); +} diff --git a/examples/server/eof_server.php b/examples/server/eof_server.php new file mode 100644 index 00000000000..d901b221d89 --- /dev/null +++ b/examples/server/eof_server.php @@ -0,0 +1,41 @@ +run('0.0.0.0', 9504); + +class SocketServer +{ + protected $serv; //swoole server + + const MAX_PACKAGE_LEN = 8000000; //max data accept + + function run($host, $port) + { + $this->serv = new Swoole\Server($host, $port, SWOOLE_BASE); + + $this->serv->set(array( + 'enable_coroutine' => false, + 'worker_num' => 1, //how much worker will start + 'open_eof_split' => true, + 'package_eof' => "\r\n", + 'package_max_length' => 8 * 1024 * 1024, + )); + + $this->serv->on('receive', array($this, 'onReceive')); + $this->serv->start(); + } + + function onReceive($serv, $fd, $tid, $data) + { + echo "recv " . strlen($data) . " bytes\n"; +// $packet = substr($data, 4); +// $result = array( +// "code" => "0", +// "msg" => "ok", +// "data" => $packet, +// ); +// $resp = json_encode($result); +// $send_data = pack('N', strlen($resp)) . $resp; +// echo "send " . strlen($send_data) . " bytes\n"; +// $serv->send($fd, $send_data); + } +} 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/fixed_header_client.php b/examples/server/fixed_header_client.php deleted file mode 100644 index c415a384b8c..00000000000 --- a/examples/server/fixed_header_client.php +++ /dev/null @@ -1,54 +0,0 @@ -set(array( - 'open_length_check' => true, - 'package_max_length' => 8 * 1024 * 1024, - 'package_length_type' => 'N', //see php pack() - 'package_length_offset' => 0, - 'package_body_offset' => 4, -)); - -if (!$client->connect('127.0.0.1', 9504)) { - exit("connect failed\n"); -} - -$func = "send_test" . intval(empty($argv[1]) ? 3 : $argv[1]); - -for ($l = 0; $l < 1; $l++) { - $data = ''; - for ($i = 0; $i < 10; $i++) { - $len = rand(100000, 200000); - echo "send : " . ($len + 4) . "\n"; - $func($client, $len); - } - sleep(1); -} - -function send_test3($client, $len) -{ - $data = pack('N', $len + 4); - $data .= str_repeat('A', $len) . rand(1000, 9999); - $chunks = str_split($data, 4000); - foreach ($chunks as $ch) { - $client->send($ch); - } - $data = $client->recv(); - echo "recv : " . strlen($data) . "\n"; -} - -function send_test2($client, $len) -{ - $data = pack('N', $len + 4); - $data .= str_repeat('A', $len) . rand(100000, 999999); - $client->send($data); - - $data = $client->recv(); -} - -function send_test1($client, $len) -{ - $client->send(pack('N', $len + 4)); - usleep(10); - $client->send(str_repeat('A', $len) . rand(1000, 9999)); - $data = $client->recv(); -} diff --git a/examples/server/fixed_header_server.php b/examples/server/fixed_header_server.php deleted file mode 100644 index c7e61a4273a..00000000000 --- a/examples/server/fixed_header_server.php +++ /dev/null @@ -1,44 +0,0 @@ -run('0.0.0.0', 9504); - -class SocketServer -{ - protected $serv; //swoole server - - const MAX_PACKAGE_LEN = 8000000; //max data accept - - function run($host, $port) - { - $this->serv = new swoole_server($host, $port); - - $this->serv->set(array( - 'enable_coroutine' => false, -// 'dispatch_mode' => 3, //who come first who is - 'worker_num' => 1, //how much worker will start - 'open_length_check' => true, - 'package_max_length' => 8 * 1024 * 1024, - 'package_length_type' => 'N', //see php pack() - 'package_length_offset' => 0, - 'package_body_offset' => 4, - )); - - $this->serv->on('receive', array($this, 'onReceive')); - $this->serv->start(); - } - - function onReceive($serv, $fd, $tid, $data) - { - echo "recv " . strlen($data) . " bytes\n"; - $packet = substr($data, 4); - $result = array( - "code" => "0", - "msg" => "ok", - "data" => $packet, - ); - $resp = json_encode($result); - $send_data = pack('N', strlen($resp)) . $resp; - echo "send " . strlen($send_data) . " bytes\n"; - $serv->send($fd, $send_data); - } -} 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 new file mode 100644 index 00000000000..4fbbb1d7618 --- /dev/null +++ b/examples/server/length_client.php @@ -0,0 +1,54 @@ +set(array( + 'open_length_check' => true, + 'package_max_length' => 8 * 1024 * 1024, + 'package_length_type' => 'N', //see php pack() + 'package_length_offset' => 0, + 'package_body_offset' => 4, +)); + +if (!$client->connect('127.0.0.1', 9504)) { + exit("connect failed\n"); +} + +$func = "send_test" . intval(empty($argv[1]) ? 3 : $argv[1]); + +for ($l = 0; $l < 1; $l++) { + $data = ''; + for ($i = 0; $i < 10; $i++) { + $len = rand(100000, 200000); + echo "send : " . ($len + 4) . "\n"; + $func($client, $len); + } + sleep(1); +} + +function send_test3($client, $len) +{ + $data = pack('N', $len + 4); + $data .= str_repeat('A', $len) . rand(1000, 9999); + $chunks = str_split($data, 4000); + foreach ($chunks as $ch) { + $client->send($ch); + } +// $data = $client->recv(); +// echo "recv : " . strlen($data) . "\n"; +} + +function send_test2($client, $len) +{ + $data = pack('N', $len + 4); + $data .= str_repeat('A', $len) . rand(100000, 999999); + $client->send($data); + + $data = $client->recv(); +} + +function send_test1($client, $len) +{ + $client->send(pack('N', $len + 4)); + usleep(10); + $client->send(str_repeat('A', $len) . rand(1000, 9999)); + $data = $client->recv(); +} diff --git a/examples/server/length_server.php b/examples/server/length_server.php new file mode 100644 index 00000000000..c494a66ad1b --- /dev/null +++ b/examples/server/length_server.php @@ -0,0 +1,44 @@ +run('0.0.0.0', 9504); + +class SocketServer +{ + protected $serv; //swoole server + + const MAX_PACKAGE_LEN = 8000000; //max data accept + + function run($host, $port) + { + $this->serv = new Swoole\Server($host, $port, SWOOLE_BASE); + + $this->serv->set(array( + 'enable_coroutine' => false, +// 'dispatch_mode' => 3, //who come first who is + 'worker_num' => 1, //how much worker will start + 'open_length_check' => true, + 'package_max_length' => 8 * 1024 * 1024, + 'package_length_type' => 'N', //see php pack() + 'package_length_offset' => 0, + 'package_body_offset' => 4, + )); + + $this->serv->on('receive', array($this, 'onReceive')); + $this->serv->start(); + } + + function onReceive($serv, $fd, $tid, $data) + { + echo "recv " . strlen($data) . " bytes\n"; +// $packet = substr($data, 4); +// $result = array( +// "code" => "0", +// "msg" => "ok", +// "data" => $packet, +// ); +// $resp = json_encode($result); +// $send_data = pack('N', strlen($resp)) . $resp; +// echo "send " . strlen($send_data) . " bytes\n"; +// $serv->send($fd, $send_data); + } +} 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 new file mode 100644 index 00000000000..9b0ed877ef4 --- /dev/null +++ b/examples/server/tcp_client.php @@ -0,0 +1,29 @@ +connect('127.0.0.1', 9504)) { + exit("connect failed\n"); +} + +$func = "send_test" . intval(empty($argv[1]) ? 3 : $argv[1]); + +for ($l = 0; $l < 1; $l++) { + $data = ''; + for ($i = 0; $i < 10; $i++) { + $len = rand(100000, 200000); + echo "send : " . ($len + 4) . "\n"; + send_test3($client, $len); + } +} + +function send_test3($client, $len) +{ + $data = pack('N', $len + 4); + $data .= str_repeat('A', $len) . rand(1000, 9999); + $chunks = str_split($data, 4000); + foreach ($chunks as $ch) { + $client->send($ch); + } +// $data = $client->recv(); +// echo "recv : " . strlen($data) . "\n"; +} diff --git a/examples/server/tcp_server.php b/examples/server/tcp_server.php new file mode 100644 index 00000000000..f3e8dc687b6 --- /dev/null +++ b/examples/server/tcp_server.php @@ -0,0 +1,38 @@ +run('0.0.0.0', 9504); + +class SocketServer +{ + protected $serv; //swoole server + + const MAX_PACKAGE_LEN = 8000000; //max data accept + + function run($host, $port) + { + $this->serv = new Swoole\Server($host, $port, SWOOLE_BASE); + + $this->serv->set(array( + 'enable_coroutine' => false, + 'worker_num' => 1, //how much worker will start + )); + + $this->serv->on('receive', array($this, 'onReceive')); + $this->serv->start(); + } + + function onReceive($serv, $fd, $tid, $data) + { + echo "recv " . strlen($data) . " bytes\n"; +// $packet = substr($data, 4); +// $result = array( +// "code" => "0", +// "msg" => "ok", +// "data" => $packet, +// ); +// $resp = json_encode($result); +// $send_data = pack('N', strlen($resp)) . $resp; +// echo "send " . strlen($send_data) . " bytes\n"; +// $serv->send($fd, $send_data); + } +} 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.c b/examples/ssl/client.c index c263324ebaa..c818f8d54f1 100644 --- a/examples/ssl/client.c +++ b/examples/ssl/client.c @@ -28,7 +28,7 @@ int OpenConnection(const char *hostname, int port) } sd = socket(PF_INET, SOCK_STREAM, 0); - bzero(&addr, sizeof(addr)); + sw_memset_zero(&addr, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = *(long*) (host->h_addr); 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/examples/wrapper/CMakeLists.txt b/examples/wrapper/CMakeLists.txt deleted file mode 100644 index a2192d7c74e..00000000000 --- a/examples/wrapper/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -cmake_minimum_required(VERSION 2.8) -project(server) - -set(SOURCE_FILES server.cpp) -set(CMAKE_BUILD_TYPE Debug) -set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}) - -add_definitions(-DHAVE_CONFIG_H) - -include_directories(./include ./ $ENV{SWOOLE_DIR}/ $ENV{SWOOLE_DIR}/include/ BEFORE) - - -link_directories($ENV{SWOOLE_DIR}/lib) -add_executable(server ${SOURCE_FILES}) -target_link_libraries(server swoole) - - - diff --git a/examples/wrapper/server.cpp b/examples/wrapper/server.cpp deleted file mode 100644 index dd20b2091a1..00000000000 --- a/examples/wrapper/server.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include "wrapper/server.hpp" -#include "swoole_api.h" -#include - -using namespace std; -using namespace swoole; - -class MyServer : public Server -{ -public: - MyServer(string _host, int _port, int _mode = SW_MODE_PROCESS, int _type = SW_SOCK_TCP) : - Server(_host, _port, _mode, _type) - { - serv.worker_num = 4; - serv.task_worker_num = 2; - } - - virtual void onStart(); - virtual void onShutdown() {}; - virtual void onWorkerStart(int worker_id); - virtual void onWorkerStop(int worker_id) {} - virtual void onPipeMessage(int src_worker_id, const DataBuffer &) {} - virtual void onReceive(int fd, const DataBuffer &data); - virtual void onConnect(int fd); - virtual void onClose(int fd); - virtual void onPacket(const DataBuffer &data, ClientInfo &clientInfo); - - virtual void onTask(int task_id, int src_worker_id, const DataBuffer &data); - virtual void onFinish(int task_id, const DataBuffer &data); -}; - -void MyServer::onReceive(int fd, const DataBuffer &data) -{ - swConnection *conn = swWorker_get_connection(&this->serv, fd); - printf("onReceive: fd=%d, ip=%s|port=%d Data=%s|Len=%ld\n", fd, swConnection_get_ip(conn), - swConnection_get_port(conn), (char *) data.buffer, data.length); - - int ret; - char resp_data[SW_BUFFER_SIZE_STD]; - int n = snprintf(resp_data, SW_BUFFER_SIZE_STD, (char *) "Server: %.*s\n", (int) data.length, (char *) data.buffer); - ret = this->send(fd, resp_data, (uint32_t) n); - if (ret < 0) - { - printf("send to client fail. errno=%d\n", errno); - } - else - { - printf("send %d bytes to client success. data=%s\n", n, resp_data); - } - DataBuffer task_data("hello world\n"); - this->task(task_data); -// this->close(fd); -} - -void MyServer::onPacket(const DataBuffer &data, ClientInfo &clientInfo) -{ - printf("recv, length=%ld, str=%s, client=%s:%d\n", data.length, (char *) data.buffer, clientInfo.address, clientInfo.port); - char resp_data[SW_BUFFER_SIZE_STD]; - int n = snprintf(resp_data, SW_BUFFER_SIZE_STD, (char *) "Server: %.*s\n", (int) data.length, (char *) data.buffer); - auto sent_data = DataBuffer(resp_data, n); - auto ip = string(clientInfo.address); - auto ret = this->sendto(ip, clientInfo.port, sent_data); - if (!ret) - { - printf("send to client failed. errno=%d\n", errno); - } - else - { - printf("send %d bytes to client success. data=%s\n", n, resp_data); - } -} - -void MyServer::onConnect(int fd) -{ - printf("PID=%d\tConnect fd=%d\n", getpid(), fd); -} - -void MyServer::onClose(int fd) -{ - printf("PID=%d\tClose fd=%d\n", getpid(), fd); -} - -void MyServer::onTask(int task_id, int src_worker_id, const DataBuffer &data) -{ - printf("PID=%d\tTaskID=%d\n", getpid(), task_id); -} - -void MyServer::onFinish(int task_id, const DataBuffer &data) -{ - printf("PID=%d\tClose fd=%d\n", getpid(), task_id); -} - -void MyServer::onStart() -{ - printf("server start\n"); -} - -static void timer1(swTimer *timer, swTimer_node *tnode) -{ - static int count = 0; - printf("#%d\thello world\n", count); - if (count > 9) - { - swoole_timer_clear(tnode->id); - } - count++; -} - -void MyServer::onWorkerStart(int worker_id) -{ -// swoole_timer_tick(1000, timer1, nullptr); -} - -int main(int argc, char **argv) -{ - if (argc < 2) - { - swoole_event_init(); - swoole_timer_tick(1000, timer1, nullptr); - swoole_event_wait(); - } - else - { - MyServer server("127.0.0.1", 9501, SW_MODE_BASE); - server.listen("127.0.0.1", 9502, SW_SOCK_UDP); - server.listen("::1", 9503, SW_SOCK_TCP6); - server.listen("::1", 9504, SW_SOCK_UDP6); - server.setEvents(EVENT_onStart|EVENT_onWorkerStart | EVENT_onReceive | EVENT_onPacket| EVENT_onClose | EVENT_onTask | EVENT_onFinish); - server.start(); - } - return 0; -} diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc new file mode 100644 index 00000000000..2398b2b9877 --- /dev/null +++ b/ext-src/php_swoole.cc @@ -0,0 +1,1630 @@ +/* + +----------------------------------------------------------------------+ + | 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_library.h" +#include "php_swoole_process.h" +#include "php_swoole_thread.h" +#include "swoole_iouring.h" + +BEGIN_EXTERN_C() +#include "zend_exceptions.h" +#include "zend_extensions.h" + +#include "ext/pcre/php_pcre.h" +#include "ext/json/php_json.h" + +#include "stubs/php_swoole_arginfo.h" +#include "stubs/php_swoole_ex_arginfo.h" +END_EXTERN_C() + +#include "swoole_mime_type.h" +#include "swoole_server.h" +#include "swoole_util.h" +#include "swoole_http2.h" + +#include +#include +#include +#include +#include + +#if defined(__MACH__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) +#include +#endif + +#ifdef SW_HAVE_ZLIB +#include +#endif +#ifdef SW_HAVE_BROTLI +#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) + +extern sapi_module_struct sapi_module; + +static swoole::CallbackManager rshutdown_callbacks; + +SW_EXTERN_C_BEGIN +static PHP_FUNCTION(swoole_version); +static PHP_FUNCTION(swoole_cpu_num); +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); +static PHP_FUNCTION(swoole_mime_type_add); +static PHP_FUNCTION(swoole_mime_type_set); +static PHP_FUNCTION(swoole_mime_type_delete); +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); +static PHP_FUNCTION(swoole_substr_json_decode); +static PHP_FUNCTION(swoole_internal_call_user_shutdown_begin); +static PHP_FUNCTION(swoole_implicit_fn); +SW_EXTERN_C_END + +// clang-format off +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) + /*------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_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_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) + PHP_FE(swoole_substr_json_decode, arginfo_swoole_substr_json_decode) + PHP_FE(swoole_internal_call_user_shutdown_begin, arginfo_swoole_internal_call_user_shutdown_begin) + // for test + PHP_FE(swoole_implicit_fn, arginfo_swoole_implicit_fn) + // for admin server + ZEND_FE(swoole_get_objects, arginfo_swoole_get_objects) + ZEND_FE(swoole_get_vm_status, arginfo_swoole_get_vm_status) + ZEND_FE(swoole_get_object_by_handle, arginfo_swoole_get_object_by_handle) + ZEND_FE(swoole_name_resolver_lookup, arginfo_swoole_name_resolver_lookup) + ZEND_FE(swoole_name_resolver_add, arginfo_swoole_name_resolver_add) + ZEND_FE(swoole_name_resolver_remove, arginfo_swoole_name_resolver_remove) + PHP_FE_END /* Must be the last line in swoole_functions[] */ +}; + +static const zend_module_dep swoole_deps[] = { + ZEND_MOD_REQUIRED("json") +#ifdef SW_USE_MYSQLND + ZEND_MOD_REQUIRED("mysqlnd") +#endif +#ifdef SW_SOCKETS + ZEND_MOD_REQUIRED("sockets") +#endif +#ifdef SW_USE_CURL + ZEND_MOD_REQUIRED("curl") +#endif +#if defined(SW_USE_PGSQL) || defined(SW_USE_ORACLE) || defined(SW_USE_SQLITE) + ZEND_MOD_REQUIRED("pdo") +#endif + + ZEND_MOD_END +}; + +zend_module_entry swoole_module_entry = { + STANDARD_MODULE_HEADER_EX, + nullptr, + swoole_deps, + "swoole", + swoole_functions, + PHP_MINIT(swoole), + PHP_MSHUTDOWN(swoole), + PHP_RINIT(swoole), //RINIT + PHP_RSHUTDOWN(swoole), //RSHUTDOWN + PHP_MINFO(swoole), + PHP_SWOOLE_VERSION, + STANDARD_MODULE_PROPERTIES +}; +// 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 + +// clang-format off +/* {{{ PHP_INI + */ + +PHP_INI_BEGIN() +/** + * enable swoole coroutine + */ +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 + */ +STD_ZEND_INI_BOOLEAN("swoole.enable_preemptive_scheduler", "Off", PHP_INI_ALL, OnUpdateBool, enable_preemptive_scheduler, zend_swoole_globals, swoole_globals) +/** + * display error + */ +STD_ZEND_INI_BOOLEAN("swoole.display_errors", "On", PHP_INI_ALL, OnUpdateBool, display_errors, zend_swoole_globals, swoole_globals) +/** + * use short class name + */ +STD_ZEND_INI_BOOLEAN("swoole.use_shortname", "On", PHP_INI_SYSTEM, OnUpdateBool, use_shortname, zend_swoole_globals, swoole_globals) +/** + * unix socket buffer size + */ +STD_PHP_INI_ENTRY("swoole.unixsock_buffer_size", ZEND_TOSTR(SW_SOCKET_BUFFER_SIZE), PHP_INI_ALL, OnUpdateLong, socket_buffer_size, zend_swoole_globals, swoole_globals) +PHP_INI_END() +// clang-format on + +static void php_swoole_init_globals(zend_swoole_globals *swoole_globals) { + 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; + shutdown_function_entry.arg_count = 0; + shutdown_function_entry.arguments = NULL; + function_name = &shutdown_function_entry.function_name; + ZVAL_STRING(function_name, function); + 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) { + zval *ztmp; + +#ifdef SW_DEBUG + if (php_swoole_array_get_value(vht, "debug_mode", ztmp) && zval_is_true(ztmp)) { + swoole_set_log_level(0); + } +#endif + // [Logger] + // ====================================================================== + if (php_swoole_array_get_value(vht, "trace_flags", ztmp)) { + swoole_set_trace_flags(zval_get_long(ztmp)); + } + if (php_swoole_array_get_value(vht, "log_file", ztmp)) { + swoole_set_log_file(zend::String(ztmp).val()); + } + if (php_swoole_array_get_value(vht, "log_level", 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()); + } + if (php_swoole_array_get_value(vht, "log_date_with_microseconds", ztmp)) { + sw_logger()->set_date_with_microseconds(zval_is_true(ztmp)); + } + if (php_swoole_array_get_value(vht, "log_rotation", ztmp)) { + sw_logger()->set_rotation(zval_get_long(ztmp)); + } + if (php_swoole_array_get_value(vht, "display_errors", ztmp)) { + SWOOLE_G(display_errors) = zval_is_true(ztmp); + } + // [DNS] + // ====================================================================== + if (php_swoole_array_get_value(vht, "dns_server", ztmp)) { + swoole_set_dns_server(zend::String(ztmp).to_std_string()); + } + // [Socket] + // ====================================================================== + auto timeout_format = [](zval *v) -> double { + double timeout = zval_get_double(v); + if (timeout <= 0 || timeout > INT_MAX) { + return INT_MAX; + } else { + return timeout; + } + }; + if (php_swoole_array_get_value(vht, "socket_dns_timeout", ztmp)) { + Socket::default_dns_timeout = timeout_format(ztmp); + } + if (php_swoole_array_get_value(vht, "socket_connect_timeout", ztmp)) { + Socket::default_connect_timeout = timeout_format(ztmp); + } + if (php_swoole_array_get_value(vht, "socket_write_timeout", ztmp) || + php_swoole_array_get_value(vht, "socket_send_timeout", ztmp)) { + Socket::default_write_timeout = timeout_format(ztmp); + } + if (php_swoole_array_get_value(vht, "socket_read_timeout", ztmp) || + php_swoole_array_get_value(vht, "socket_recv_timeout", ztmp)) { + Socket::default_read_timeout = timeout_format(ztmp); + } + if (php_swoole_array_get_value(vht, "socket_buffer_size", 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) { + rshutdown_callbacks.append(cb, private_data); +} + +SW_API bool php_swoole_is_enable_coroutine() { + if (sw_server()) { + return sw_server()->is_enable_coroutine(); + } else { + return SwooleG.enable_coroutine; + } +} + +SW_API zend_long php_swoole_parse_to_size(zval *zv) { + if (ZVAL_IS_STRING(zv)) { +#if PHP_VERSION_ID >= 80200 + zend_string *errstr; + auto size = zend_ini_parse_quantity(Z_STR_P(zv), &errstr); + if (errstr) { + php_swoole_fatal_error( + E_ERROR, "failed to parse '%s' to size, Error: %s", Z_STRVAL_P(zv), ZSTR_VAL(errstr)); + zend_string_release(errstr); + } + return size; +#else + return zend_atol(Z_STRVAL_P(zv), Z_STRLEN_P(zv)); +#endif + } else { + return zval_get_long(zv); + } +} + +SW_API zend_string *php_swoole_serialize(zval *zdata) { + php_serialize_data_t var_hash; + smart_str serialized_data = {0}; + + PHP_VAR_SERIALIZE_INIT(var_hash); + php_var_serialize(&serialized_data, zdata, &var_hash); + PHP_VAR_SERIALIZE_DESTROY(var_hash); + + zend_string *result = nullptr; + if (!EG(exception)) { + result = zend_string_init(serialized_data.s->val, serialized_data.s->len, 1); + } + smart_str_free(&serialized_data); + return result; +} + +SW_API bool php_swoole_unserialize(zend_string *data, zval *zv) { + php_unserialize_data_t var_hash; + const char *p = ZSTR_VAL(data); + size_t l = ZSTR_LEN(data); + + PHP_VAR_UNSERIALIZE_INIT(var_hash); + zend_bool unserialized = php_var_unserialize(zv, (const uchar **) &p, (const uchar *) (p + l), &var_hash); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + if (!unserialized) { + swoole_warning("unserialize() failed, Error at offset " ZEND_LONG_FMT " of %zd bytes", + (zend_long) ((char *) p - ZSTR_VAL(data)), + l); + } + return unserialized; +} + +static void fatal_error(int code, const char *format, ...) { + va_list args; + va_start(args, format); + zend_object *exception = + zend_throw_exception(swoole_error_ce, swoole::std_string::vformat(format, args).c_str(), code); + va_end(args); + + 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 + */ +PHP_MINIT_FUNCTION(swoole) { + ZEND_INIT_MODULE_GLOBALS(swoole, php_swoole_init_globals, nullptr); + REGISTER_INI_ENTRIES(); + + // clang-format off + // MUST be on the same line for the inspection tool to recognize correctly + SW_REGISTER_STRING_CONSTANT("SWOOLE_VERSION", SWOOLE_VERSION); + SW_REGISTER_LONG_CONSTANT("SWOOLE_VERSION_ID", SWOOLE_VERSION_ID); + SW_REGISTER_LONG_CONSTANT("SWOOLE_MAJOR_VERSION", SWOOLE_MAJOR_VERSION); + SW_REGISTER_LONG_CONSTANT("SWOOLE_MINOR_VERSION", SWOOLE_MINOR_VERSION); + SW_REGISTER_LONG_CONSTANT("SWOOLE_RELEASE_VERSION", SWOOLE_RELEASE_VERSION); + SW_REGISTER_STRING_CONSTANT("SWOOLE_EXTRA_VERSION", SWOOLE_EXTRA_VERSION); +#ifndef SW_DEBUG + SW_REGISTER_BOOL_CONSTANT("SWOOLE_DEBUG", 0); +#else + SW_REGISTER_BOOL_CONSTANT("SWOOLE_DEBUG", 1); +#endif + +#ifdef SW_HAVE_COMPRESSION + SW_REGISTER_BOOL_CONSTANT("SWOOLE_HAVE_COMPRESSION", 1); +#endif +#ifdef SW_HAVE_ZLIB + SW_REGISTER_BOOL_CONSTANT("SWOOLE_HAVE_ZLIB", 1); +#endif +#ifdef SW_HAVE_BROTLI + SW_REGISTER_BOOL_CONSTANT("SWOOLE_HAVE_BROTLI", 1); +#endif + SW_REGISTER_BOOL_CONSTANT("SWOOLE_USE_HTTP2", 1); + SW_REGISTER_BOOL_CONSTANT("SWOOLE_USE_SHORTNAME", SWOOLE_G(use_shortname)); + + /** + * socket type + */ + SW_REGISTER_LONG_CONSTANT("SWOOLE_SOCK_TCP", SW_SOCK_TCP); + SW_REGISTER_LONG_CONSTANT("SWOOLE_SOCK_TCP6", SW_SOCK_TCP6); + SW_REGISTER_LONG_CONSTANT("SWOOLE_SOCK_UDP", SW_SOCK_UDP); + SW_REGISTER_LONG_CONSTANT("SWOOLE_SOCK_UDP6", SW_SOCK_UDP6); + SW_REGISTER_LONG_CONSTANT("SWOOLE_SOCK_UNIX_DGRAM", SW_SOCK_UNIX_DGRAM); + SW_REGISTER_LONG_CONSTANT("SWOOLE_SOCK_UNIX_STREAM", SW_SOCK_UNIX_STREAM); + + /** + * simple socket type alias + */ + SW_REGISTER_LONG_CONSTANT("SWOOLE_TCP", SW_SOCK_TCP); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TCP6", SW_SOCK_TCP6); + SW_REGISTER_LONG_CONSTANT("SWOOLE_UDP", SW_SOCK_UDP); + SW_REGISTER_LONG_CONSTANT("SWOOLE_UDP6", SW_SOCK_UDP6); + SW_REGISTER_LONG_CONSTANT("SWOOLE_UNIX_DGRAM", SW_SOCK_UNIX_DGRAM); + SW_REGISTER_LONG_CONSTANT("SWOOLE_UNIX_STREAM", SW_SOCK_UNIX_STREAM); + + /** + * simple api + */ + SW_REGISTER_BOOL_CONSTANT("SWOOLE_SOCK_SYNC", 0); + SW_REGISTER_BOOL_CONSTANT("SWOOLE_SOCK_ASYNC", 1); + + SW_REGISTER_LONG_CONSTANT("SWOOLE_SYNC", SW_FLAG_SYNC); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ASYNC", SW_FLAG_ASYNC); + SW_REGISTER_LONG_CONSTANT("SWOOLE_KEEP", SW_FLAG_KEEP); + +#ifdef SW_USE_OPENSSL + SW_REGISTER_LONG_CONSTANT("SWOOLE_SSL", SW_SOCK_SSL); + + /** + * SSL methods + */ + SW_REGISTER_LONG_CONSTANT("SWOOLE_SSLv3_METHOD", SW_SSLv3_METHOD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_SSLv3_SERVER_METHOD", SW_SSLv3_SERVER_METHOD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_SSLv3_CLIENT_METHOD", SW_SSLv3_CLIENT_METHOD); + + SW_REGISTER_LONG_CONSTANT("SWOOLE_TLSv1_METHOD", SW_TLSv1_METHOD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TLSv1_SERVER_METHOD", SW_TLSv1_SERVER_METHOD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TLSv1_CLIENT_METHOD", SW_TLSv1_CLIENT_METHOD); + +#ifdef TLS1_1_VERSION + SW_REGISTER_LONG_CONSTANT("SWOOLE_TLSv1_1_METHOD", SW_TLSv1_1_METHOD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TLSv1_1_SERVER_METHOD", SW_TLSv1_1_SERVER_METHOD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TLSv1_1_CLIENT_METHOD", SW_TLSv1_1_CLIENT_METHOD); +#endif + +#ifdef TLS1_2_VERSION + SW_REGISTER_LONG_CONSTANT("SWOOLE_TLSv1_2_METHOD", SW_TLSv1_2_METHOD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TLSv1_2_SERVER_METHOD", SW_TLSv1_2_SERVER_METHOD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TLSv1_2_CLIENT_METHOD", SW_TLSv1_2_CLIENT_METHOD); +#endif + +#ifdef SW_SUPPORT_DTLS + SW_REGISTER_LONG_CONSTANT("SWOOLE_DTLS_SERVER_METHOD", SW_DTLS_SERVER_METHOD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_DTLS_CLIENT_METHOD", SW_DTLS_CLIENT_METHOD); +#endif + + SW_REGISTER_LONG_CONSTANT("SWOOLE_SSLv23_METHOD", SW_SSLv23_METHOD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_SSLv23_SERVER_METHOD", SW_SSLv23_SERVER_METHOD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_SSLv23_CLIENT_METHOD", SW_SSLv23_CLIENT_METHOD); + /* SSLv23_method have been renamed to TLS_method */ + SW_REGISTER_LONG_CONSTANT("SWOOLE_TLS_METHOD", SW_SSLv23_METHOD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TLS_SERVER_METHOD", SW_SSLv23_SERVER_METHOD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TLS_CLIENT_METHOD", SW_SSLv23_CLIENT_METHOD); + + /** + * SSL protocols + */ +#ifdef HAVE_SSL3 + SW_REGISTER_LONG_CONSTANT("SWOOLE_SSL_SSLv3", SW_SSL_SSLv3); +#endif + SW_REGISTER_LONG_CONSTANT("SWOOLE_SSL_TLSv1", SW_SSL_TLSv1); + +#ifdef TLS1_1_VERSION + SW_REGISTER_LONG_CONSTANT("SWOOLE_SSL_TLSv1_1", SW_SSL_TLSv1_1); +#endif + +#ifdef TLS1_2_VERSION + SW_REGISTER_LONG_CONSTANT("SWOOLE_SSL_TLSv1_2", SW_SSL_TLSv1_2); +#endif + +#ifdef TLS1_3_VERSION + SW_REGISTER_LONG_CONSTANT("SWOOLE_SSL_TLSv1_3", SW_SSL_TLSv1_3); +#endif + +#ifdef SW_SUPPORT_DTLS + SW_REGISTER_LONG_CONSTANT("SWOOLE_SSL_DTLS", SW_SSL_DTLS); +#endif + + SW_REGISTER_LONG_CONSTANT("SWOOLE_SSL_SSLv2", SW_SSL_SSLv2); +#endif + + SW_REGISTER_LONG_CONSTANT("SWOOLE_EVENT_READ", SW_EVENT_READ); + SW_REGISTER_LONG_CONSTANT("SWOOLE_EVENT_WRITE", SW_EVENT_WRITE); + + /** + * Register ERROR types + */ + SW_REGISTER_LONG_CONSTANT("SWOOLE_STRERROR_SYSTEM", SW_STRERROR_SYSTEM); + SW_REGISTER_LONG_CONSTANT("SWOOLE_STRERROR_GAI", SW_STRERROR_GAI); + SW_REGISTER_LONG_CONSTANT("SWOOLE_STRERROR_DNS", SW_STRERROR_DNS); + SW_REGISTER_LONG_CONSTANT("SWOOLE_STRERROR_SWOOLE", SW_STRERROR_SWOOLE); + + /** + * Register ERROR constants + */ + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_MALLOC_FAIL", SW_ERROR_MALLOC_FAIL); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SYSTEM_CALL_FAIL", SW_ERROR_SYSTEM_CALL_FAIL); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_PHP_FATAL_ERROR", SW_ERROR_PHP_FATAL_ERROR); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_NAME_TOO_LONG", SW_ERROR_NAME_TOO_LONG); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_INVALID_PARAMS", SW_ERROR_INVALID_PARAMS); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_QUEUE_FULL", SW_ERROR_QUEUE_FULL); + 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); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SESSION_CLOSING", SW_ERROR_SESSION_CLOSING); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SESSION_CLOSED", SW_ERROR_SESSION_CLOSED); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SESSION_NOT_EXIST", SW_ERROR_SESSION_NOT_EXIST); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SESSION_INVALID_ID", SW_ERROR_SESSION_INVALID_ID); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SESSION_DISCARD_TIMEOUT_DATA", SW_ERROR_SESSION_DISCARD_TIMEOUT_DATA); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SESSION_DISCARD_DATA", SW_ERROR_SESSION_DISCARD_DATA); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_OUTPUT_BUFFER_OVERFLOW", SW_ERROR_OUTPUT_BUFFER_OVERFLOW); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_OUTPUT_SEND_YIELD", SW_ERROR_OUTPUT_SEND_YIELD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SSL_NOT_READY", SW_ERROR_SSL_NOT_READY); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SSL_CANNOT_USE_SENFILE", SW_ERROR_SSL_CANNOT_USE_SENFILE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SSL_EMPTY_PEER_CERTIFICATE", SW_ERROR_SSL_EMPTY_PEER_CERTIFICATE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SSL_VERIFY_FAILED", SW_ERROR_SSL_VERIFY_FAILED); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SSL_BAD_CLIENT", SW_ERROR_SSL_BAD_CLIENT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SSL_BAD_PROTOCOL", SW_ERROR_SSL_BAD_PROTOCOL); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SSL_RESET", SW_ERROR_SSL_RESET); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SSL_HANDSHAKE_FAILED", SW_ERROR_SSL_HANDSHAKE_FAILED); + 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); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP2_STREAM_ID_TOO_BIG", SW_ERROR_HTTP2_STREAM_ID_TOO_BIG); + 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); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CLIENT_NO_CONNECTION", SW_ERROR_CLIENT_NO_CONNECTION); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SOCKET_CLOSED", SW_ERROR_SOCKET_CLOSED); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SOCKET_POLL_TIMEOUT", SW_ERROR_SOCKET_POLL_TIMEOUT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SOCKS5_UNSUPPORT_VERSION", SW_ERROR_SOCKS5_UNSUPPORT_VERSION); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SOCKS5_UNSUPPORT_METHOD", SW_ERROR_SOCKS5_UNSUPPORT_METHOD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SOCKS5_AUTH_FAILED", SW_ERROR_SOCKS5_AUTH_FAILED); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SOCKS5_SERVER_ERROR", SW_ERROR_SOCKS5_SERVER_ERROR); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SOCKS5_HANDSHAKE_FAILED", SW_ERROR_SOCKS5_HANDSHAKE_FAILED); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP_PROXY_HANDSHAKE_ERROR", SW_ERROR_HTTP_PROXY_HANDSHAKE_ERROR); + 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); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_INVALID_LISTEN_PORT", SW_ERROR_SERVER_INVALID_LISTEN_PORT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_TOO_MANY_LISTEN_PORT", SW_ERROR_SERVER_TOO_MANY_LISTEN_PORT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_PIPE_BUFFER_FULL", SW_ERROR_SERVER_PIPE_BUFFER_FULL); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_NO_IDLE_WORKER", SW_ERROR_SERVER_NO_IDLE_WORKER); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_ONLY_START_ONE", SW_ERROR_SERVER_ONLY_START_ONE); + 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); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_MUTEX_DOUBLE_UNLOCK", SW_ERROR_CO_MUTEX_DOUBLE_UNLOCK); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_BLOCK_OBJECT_LOCKED", SW_ERROR_CO_BLOCK_OBJECT_LOCKED); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_BLOCK_OBJECT_WAITING", SW_ERROR_CO_BLOCK_OBJECT_WAITING); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_YIELD_FAILED", SW_ERROR_CO_YIELD_FAILED); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_GETCONTEXT_FAILED", SW_ERROR_CO_GETCONTEXT_FAILED); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_SWAPCONTEXT_FAILED", SW_ERROR_CO_SWAPCONTEXT_FAILED); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_MAKECONTEXT_FAILED", SW_ERROR_CO_MAKECONTEXT_FAILED); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_IOCPINIT_FAILED", SW_ERROR_CO_IOCPINIT_FAILED); + 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 + */ + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_SERVER", SW_TRACE_SERVER); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CLIENT", SW_TRACE_CLIENT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_BUFFER", SW_TRACE_BUFFER); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CONN", SW_TRACE_CONN); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_EVENT", SW_TRACE_EVENT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_WORKER", SW_TRACE_WORKER); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_MEMORY", SW_TRACE_MEMORY); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_REACTOR", SW_TRACE_REACTOR); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_PHP", SW_TRACE_PHP); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_HTTP", SW_TRACE_HTTP); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_HTTP2", SW_TRACE_HTTP2); + 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_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); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_AIO", SW_TRACE_AIO); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_SSL", SW_TRACE_SSL); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_NORMAL", SW_TRACE_NORMAL); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CHANNEL", SW_TRACE_CHANNEL); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_TIMER", SW_TRACE_TIMER); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_SOCKET", SW_TRACE_SOCKET); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_COROUTINE", SW_TRACE_COROUTINE); + 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); + + /** + * log level + */ + SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_DEBUG", SW_LOG_DEBUG); + SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_TRACE", SW_LOG_TRACE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_INFO", SW_LOG_INFO); + SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_NOTICE", SW_LOG_NOTICE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_WARNING", SW_LOG_WARNING); + SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_ERROR", SW_LOG_ERROR); + SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_NONE", SW_LOG_NONE); + + SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_ROTATION_SINGLE", SW_LOG_ROTATION_SINGLE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_ROTATION_MONTHLY", SW_LOG_ROTATION_MONTHLY); + SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_ROTATION_DAILY", SW_LOG_ROTATION_DAILY); + SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_ROTATION_HOURLY", SW_LOG_ROTATION_HOURLY); + SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_ROTATION_EVERY_MINUTE", SW_LOG_ROTATION_EVERY_MINUTE); + + SW_REGISTER_LONG_CONSTANT("SWOOLE_IPC_NONE", SW_IPC_NONE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_IPC_UNIXSOCK", SW_IPC_UNIXSOCK); + SW_REGISTER_LONG_CONSTANT("SWOOLE_IPC_SOCKET", SW_IPC_SOCKET); + + /** + * limit + */ + 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", arginfo_swoole_coroutine_create); + SW_FUNCTION_ALIAS( + CG(function_table), "swoole_coroutine_defer", CG(function_table), "defer", arginfo_swoole_coroutine_defer); + } + + swoole_init(); + + // init bug report message + bug_report_message_init(); + + SW_INIT_CLASS_ENTRY_EX2( + swoole_exception, "Swoole\\Exception", nullptr, nullptr, zend_ce_exception, zend_get_std_object_handlers()); + + SW_INIT_CLASS_ENTRY_EX2( + swoole_error, "Swoole\\Error", nullptr, nullptr, zend_ce_error, zend_get_std_object_handlers()); + + /** **/ + php_swoole_event_minit(module_number); + // base + php_swoole_atomic_minit(module_number); + php_swoole_lock_minit(module_number); + php_swoole_process_minit(module_number); + php_swoole_process_pool_minit(module_number); + php_swoole_table_minit(module_number); + php_swoole_timer_minit(module_number); + // coroutine + 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_http2_client_coro_minit(module_number); + // 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) && __MACH__ && !defined(SW_DEBUG) + PCRE_G(jit) = 0; +#endif + + zend::known_strings_init(); + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +PHP_MSHUTDOWN_FUNCTION(swoole) { + 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; +} +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION + */ +PHP_MINFO_FUNCTION(swoole) { + char buf[64]; + php_info_print_table_start(); + php_info_print_table_header(2, "Swoole", "enabled"); + php_info_print_table_row(2, "Author", "Swoole Team "); + 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); +#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 HAVE_EPOLL + php_info_print_table_row(2, "epoll", "enabled"); +#endif +#ifdef HAVE_EVENTFD + php_info_print_table_row(2, "eventfd", "enabled"); +#endif +#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 +#ifdef SW_USE_ACCEPT4 + php_info_print_table_row(2, "accept4", "enabled"); +#endif +#ifdef HAVE_CPU_AFFINITY + php_info_print_table_row(2, "cpu_affinity", "enabled"); +#endif +#ifdef HAVE_SPINLOCK + php_info_print_table_row(2, "spinlock", "enabled"); +#endif +#ifdef HAVE_RWLOCK + php_info_print_table_row(2, "rwlock", "enabled"); +#endif +#ifdef SW_SOCKETS + php_info_print_table_row(2, "sockets", "enabled"); +#endif +#ifdef SW_USE_OPENSSL +#ifdef OPENSSL_VERSION_TEXT + php_info_print_table_row(2, "openssl", OPENSSL_VERSION_TEXT); +#else + php_info_print_table_row(2, "openssl", "enabled"); +#endif +#ifdef SW_SUPPORT_DTLS + php_info_print_table_row(2, "dtls", "enabled"); +#endif +#endif + php_info_print_table_row(2, "http2", "enabled"); + php_info_print_table_row(2, "json", "enabled"); +#ifdef SW_USE_CURL + php_info_print_table_row(2, "curl-native", "enabled"); +#endif +#ifdef SW_USE_CARES + php_info_print_table_row(2, "c-ares", ares_version(nullptr)); +#endif +#ifdef SW_HAVE_ZLIB +#ifdef ZLIB_VERSION + php_info_print_table_row(2, "zlib", ZLIB_VERSION); +#else + php_info_print_table_row(2, "zlib", "enabled"); +#endif +#endif +#ifdef SW_HAVE_BROTLI + 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 +#ifdef HAVE_PTHREAD_BARRIER + php_info_print_table_row(2, "pthread_barrier", "enabled"); +#endif +#ifdef HAVE_FUTEX + php_info_print_table_row(2, "futex", "enabled"); +#endif +#ifdef SW_USE_MYSQLND + php_info_print_table_row(2, "mysqlnd", "enabled"); +#endif +#ifdef SW_USE_JEMALLOC + php_info_print_table_row(2, "jemalloc", "enabled"); +#endif +#ifdef SW_USE_TCMALLOC + php_info_print_table_row(2, "tcmalloc", "enabled"); +#endif +#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(); + + DISPLAY_INI_ENTRIES(); +} +/* }}} */ + +static void *_sw_emalloc(size_t size) { + return emalloc(size); +} + +static void *_sw_ecalloc(size_t nmemb, size_t size) { + return ecalloc(nmemb, size); +} + +static void *_sw_erealloc(void *address, size_t size) { + return erealloc(address, size); +} + +static void _sw_efree(void *address) { + efree(address); +} + +static void *_sw_zend_string_malloc(size_t size) { + zend_string *str = zend_string_alloc(size, 0); + if (str == nullptr) { + return nullptr; + } + return str->val; +} + +static void *_sw_zend_string_calloc(size_t nmemb, size_t size) { + void *mem = _sw_zend_string_malloc(nmemb * size); + if (mem) { + sw_memset_zero(mem, size); + } + return mem; +} + +static void *_sw_zend_string_realloc(void *address, size_t size) { + zend_string *str = zend_string_realloc(zend::fetch_zend_string_by_val(address), size, 0); + if (str == nullptr) { + return nullptr; + } + return str->val; +} + +static void _sw_zend_string_free(void *address) { + zend_string_free(zend::fetch_zend_string_by_val(address)); +} + +static swoole::Allocator php_allocator{ + _sw_emalloc, + _sw_ecalloc, + _sw_erealloc, + _sw_efree, +}; + +static swoole::Allocator zend_string_allocator{ + _sw_zend_string_malloc, + _sw_zend_string_calloc, + _sw_zend_string_realloc, + _sw_zend_string_free, +}; + +const swoole::Allocator *sw_php_allocator() { + return &php_allocator; +} + +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; + + php_swoole_register_shutdown_function("swoole_internal_call_user_shutdown_begin"); + + if (SWOOLE_G(enable_library) +#ifdef ZEND_COMPILE_PRELOAD + /* avoid execution of the code during RINIT of preloader */ + && !(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 + /* Disable warning even in ZEND_DEBUG because we may register our own signal handlers */ + 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; +} + +PHP_RSHUTDOWN_FUNCTION(swoole) { + if (!SWOOLE_G(cli)) { + return SUCCESS; + } + + SWOOLE_G(req_status) = PHP_SWOOLE_RSHUTDOWN_BEGIN; + + rshutdown_callbacks.execute(); + + 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 + + swoole_event_free(); + + SWOOLE_G(req_status) = PHP_SWOOLE_RSHUTDOWN_END; + +#ifdef PHP_STREAM_FLAG_NO_CLOSE + auto php_swoole_set_stdio_no_close = [](const char *name, size_t name_len) { + zval *zstream; + php_stream *stream; + + zstream = zend_get_constant_str(name, name_len); + if (!zstream) { + return; + } + stream = (php_stream *) zend_fetch_resource2_ex((zstream), NULL, php_file_le_stream(), php_file_le_pstream()); + if (!stream) { + return; + } + stream->flags |= PHP_STREAM_FLAG_NO_CLOSE; + }; + /* do not close the stdout and stderr */ + php_swoole_set_stdio_no_close(ZEND_STRL("STDOUT")); + php_swoole_set_stdio_no_close(ZEND_STRL("STDERR")); +#endif + + return SUCCESS; +} + +static PHP_FUNCTION(swoole_version) { + RETURN_STRING(SWOOLE_VERSION); +} + +static uint32_t hashkit_one_at_a_time(const char *key, size_t key_length) { + const char *ptr = key; + uint32_t value = 0; + + while (key_length--) { + uint32_t val = (uint32_t) *ptr++; + value += val; + value += (value << 10); + value ^= (value >> 6); + } + value += (value << 3); + value ^= (value >> 11); + value += (value << 15); + + return value; +} + +static PHP_FUNCTION(swoole_hashcode) { + char *data; + size_t l_data; + zend_long type = 0; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STRING(data, l_data) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(type) + 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)); + default: + RETURN_FALSE; + } +} + +static PHP_FUNCTION(swoole_clear_error) { + swoole_set_last_error(0); +} + +PHP_FUNCTION(swoole_last_error) { + RETURN_LONG(swoole_get_last_error()); +} + +static PHP_FUNCTION(swoole_cpu_num) { + RETURN_LONG(SW_CPU_NUM); +} + +static PHP_FUNCTION(swoole_strerror) { + zend_long swoole_errno; + zend_long error_type = SW_STRERROR_SYSTEM; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_LONG(swoole_errno) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(error_type) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (error_type == SW_STRERROR_GAI) { + RETURN_STRING(gai_strerror(swoole_errno)); + } else if (error_type == SW_STRERROR_DNS) { + RETURN_STRING(hstrerror(swoole_errno)); + } else if (error_type == SW_STRERROR_SWOOLE || (swoole_errno > SW_ERROR_BEGIN && swoole_errno < SW_ERROR_END)) { + RETURN_STRING(swoole_strerror(swoole_errno)); + } else { + RETURN_STRING(strerror(swoole_errno)); + } +} + +static PHP_FUNCTION(swoole_error_log) { + char *msg; + size_t l_msg; + zend_long level; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_LONG(level) + Z_PARAM_STRING(msg, l_msg) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + 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; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(suffix) + Z_PARAM_STR(mime_type) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_BOOL(swoole::mime_type::add(ZSTR_VAL(suffix), ZSTR_VAL(mime_type))); +} + +static PHP_FUNCTION(swoole_mime_type_set) { + zend_string *suffix; + zend_string *mime_type; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(suffix) + Z_PARAM_STR(mime_type) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + swoole::mime_type::set(ZSTR_VAL(suffix), ZSTR_VAL(mime_type)); +} + +static PHP_FUNCTION(swoole_mime_type_delete) { + zend_string *suffix; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(suffix) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_BOOL(swoole::mime_type::del(ZSTR_VAL(suffix))); +} + +static PHP_FUNCTION(swoole_mime_type_get) { + zend_string *filename; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(filename) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_STRING(swoole::mime_type::get(ZSTR_VAL(filename)).c_str()); +} + +static PHP_FUNCTION(swoole_mime_type_exists) { + zend_string *filename; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(filename) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_BOOL(swoole::mime_type::exists(ZSTR_VAL(filename))); +} + +static PHP_FUNCTION(swoole_mime_type_list) { + array_init(return_value); + for (auto &i : swoole::mime_type::list()) { + add_next_index_string(return_value, i.second.c_str()); + } +} + +static PHP_FUNCTION(swoole_errno) { + RETURN_LONG(errno); +} + +PHP_FUNCTION(swoole_set_process_name) { + zend_function *cli_set_process_title = + (zend_function *) zend_hash_str_find_ptr(EG(function_table), ZEND_STRL("cli_set_process_title")); + if (!cli_set_process_title) { + php_swoole_fatal_error(E_WARNING, "swoole_set_process_name only support in CLI mode"); + RETURN_FALSE; + } + cli_set_process_title->internal_function.handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} + +static PHP_FUNCTION(swoole_get_local_ip) { + struct sockaddr_in *s4; + struct ifaddrs *ipaddrs, *ifa; + void *in_addr; + char ip[64]; + + if (getifaddrs(&ipaddrs) != 0) { + php_swoole_sys_error(E_WARNING, "getifaddrs() failed"); + RETURN_FALSE; + } + array_init(return_value); + for (ifa = ipaddrs; ifa != nullptr; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == nullptr || !(ifa->ifa_flags & IFF_UP)) { + continue; + } + + switch (ifa->ifa_addr->sa_family) { + case AF_INET: + s4 = (struct sockaddr_in *) ifa->ifa_addr; + in_addr = &s4->sin_addr; + break; + case AF_INET6: + // struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)ifa->ifa_addr; + // in_addr = &s6->sin6_addr; + continue; + default: + continue; + } + if (!inet_ntop(ifa->ifa_addr->sa_family, in_addr, ip, sizeof(ip))) { + php_error_docref(nullptr, E_WARNING, "%s: inet_ntop failed", ifa->ifa_name); + } else { + // if (ifa->ifa_addr->sa_family == AF_INET && ntohl(((struct in_addr *) in_addr)->s_addr) == + // INADDR_LOOPBACK) + if (strcmp(ip, "127.0.0.1") == 0) { + continue; + } + add_assoc_string(return_value, ifa->ifa_name, ip); + } + } + freeifaddrs(ipaddrs); +} + +static PHP_FUNCTION(swoole_get_local_mac) { + auto add_assoc_address = [](zval *zv, const char *name, const unsigned char *addr) { + char buf[32]; + sw_snprintf( + SW_STRS(buf), "%02X:%02X:%02X:%02X:%02X:%02X", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + add_assoc_string(zv, name, buf); + }; +#ifdef SIOCGIFHWADDR + struct ifconf ifc; + struct ifreq buf[16]; + + int sock; + int i = 0, num = 0; + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + php_swoole_sys_error(E_WARNING, "new socket failed"); + RETURN_FALSE; + } + array_init(return_value); + + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = (caddr_t) buf; + if (!ioctl(sock, SIOCGIFCONF, (char *) &ifc)) { + num = ifc.ifc_len / sizeof(struct ifreq); + while (i < num) { + if (!(ioctl(sock, SIOCGIFHWADDR, (char *) &buf[i]))) { + add_assoc_address(return_value, buf[i].ifr_name, (unsigned char *) buf[i].ifr_hwaddr.sa_data); + } + i++; + } + } + close(sock); +#else +#ifdef LLADDR + ifaddrs *ifas, *ifa; + if (getifaddrs(&ifas) == 0) { + array_init(return_value); + for (ifa = ifas; ifa; ifa = ifa->ifa_next) { + if ((ifa->ifa_addr->sa_family == AF_LINK) && ifa->ifa_addr) { + add_assoc_address( + return_value, ifa->ifa_name, (unsigned char *) (LLADDR((struct sockaddr_dl *) ifa->ifa_addr))); + } + } + freeifaddrs(ifas); + } +#else + php_error_docref(nullptr, E_WARNING, "swoole_get_local_mac is not supported"); + RETURN_FALSE; +#endif +#endif +} + +static PHP_FUNCTION(swoole_internal_call_user_shutdown_begin) { + if (SWOOLE_G(req_status) == PHP_SWOOLE_RINIT_END) { + SWOOLE_G(req_status) = PHP_SWOOLE_CALL_USER_SHUTDOWNFUNC_BEGIN; + RETURN_TRUE; + } else { + php_error_docref(nullptr, E_WARNING, "can not call this function in user level"); + RETURN_FALSE; + } +} + +static PHP_FUNCTION(swoole_substr_unserialize) { + zend_long offset, length = 0; + char *buf = NULL; + size_t buf_len; + zval *options = NULL; + + 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(); + + 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 || length > (zend_long) (buf_len - offset)) { + length = buf_len - offset; + } + zend::unserialize(return_value, buf + offset, length, options ? Z_ARRVAL_P(options) : NULL); +} + +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; + + ZEND_PARSE_PARAMETERS_START(2, 6) + Z_PARAM_STRING(str, str_len) + Z_PARAM_LONG(offset) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(length) + Z_PARAM_BOOL_EX(assoc, assoc_null, 1, 0) + Z_PARAM_LONG(depth) + Z_PARAM_LONG(options) + ZEND_PARSE_PARAMETERS_END(); + + if (str_len == 0) { + 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) { + php_error_docref(nullptr, E_WARNING, "Offset must be less than the length of the string"); + RETURN_NULL(); + } + 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; + } else { + options &= ~PHP_JSON_OBJECT_AS_ARRAY; + } + } + zend::json_decode(return_value, str + offset, length, options, depth); +} + +/** + * The implicit functions are intended solely for internal testing and will not be documented. + * These functions are unsafe, do not use if you are not an internal developer. + */ +static PHP_FUNCTION(swoole_implicit_fn) { + char *fn; + size_t l_fn; + zval *zargs = nullptr; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STRING(fn, l_fn) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(zargs) + ZEND_PARSE_PARAMETERS_END(); + + if (SW_STRCASEEQ(fn, l_fn, "fatal_error")) { + swoole_fatal_error(SW_ERROR_FOR_TEST, "test"); + php_printf("never be executed here\n"); + } else if (SW_STRCASEEQ(fn, l_fn, "bailout")) { + sw_php_exit(zargs ? zval_get_long(zargs) : 95); + } else if (SW_STRCASEEQ(fn, l_fn, "abort")) { + abort(); + } else if (SW_STRCASEEQ(fn, l_fn, "refcount")) { + RETURN_LONG(zval_refcount_p(zargs)); + } else if (SW_STRCASEEQ(fn, l_fn, "func_handler")) { + auto fn = zval_get_string(zargs); + zend_function *zf = (zend_function *) zend_hash_find_ptr(EG(function_table), fn); + zend_string_release(fn); + if (zf == nullptr) { + RETURN_FALSE; + } + printf("zif_handler=%p\n", zf->internal_function.handler); + } else { + zend_throw_exception_ex(swoole_exception_ce, SW_ERROR_INVALID_PARAMS, "unknown fn '%s'", fn); + } +} diff --git a/ext-src/php_swoole_call_stack.h b/ext-src/php_swoole_call_stack.h new file mode 100644 index 00000000000..14d1555e803 --- /dev/null +++ b/ext-src/php_swoole_call_stack.h @@ -0,0 +1,29 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Yurun | + +----------------------------------------------------------------------+ +*/ + +#include "php_swoole.h" + +#ifdef ZEND_CHECK_STACK_LIMIT +#define HOOK_PHP_CALL_STACK(callback) \ + void *__stack_limit = EG(stack_limit); \ + void *__stack_base = EG(stack_base); \ + EG(stack_base) = (void *) 0; \ + EG(stack_limit) = (void *) 0; \ + callback EG(stack_limit) = __stack_limit; \ + EG(stack_base) = __stack_base; +#else +#define HOOK_PHP_CALL_STACK(callback) callback +#endif diff --git a/ext-src/php_swoole_client.h b/ext-src/php_swoole_client.h new file mode 100644 index 00000000000..f899db070ee --- /dev/null +++ b/ext-src/php_swoole_client.h @@ -0,0 +1,65 @@ +/* + +----------------------------------------------------------------------+ + | 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: Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#pragma once + +#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 new file mode 100644 index 00000000000..383aefef197 --- /dev/null +++ b/ext-src/php_swoole_coroutine.h @@ -0,0 +1,316 @@ +/* + +----------------------------------------------------------------------+ + | 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: Xinyu Zhu | + | shiguangqi | + | Twosee | + | Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#pragma once + +#include "swoole_coroutine.h" +#include "swoole_coroutine_socket.h" +#include "swoole_coroutine_system.h" +#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 + +#define SW_DEFAULT_MAX_CORO_NUM 100000 +#define SW_DEFAULT_PHP_STACK_PAGE_SIZE 8192 + +#define SWOG ((zend_output_globals *) &OG(handlers)) + +SW_EXTERN_C_BEGIN +PHP_METHOD(swoole_coroutine_scheduler, set); +PHP_METHOD(swoole_coroutine_scheduler, getOptions); +SW_EXTERN_C_END + +namespace zend { +struct Function; +} + +namespace swoole { + +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; + uint32_t jit_trace_num; + zend_error_handling_t error_handling; + zend_class_entry *exception_class; + zend_object *exception; + zend_output_globals *output_ptr; +#if PHP_VERSION_ID < 80100 + /* for array_walk non-reentrancy */ + zend::Function *array_walk_fci; +#endif + /* for error control `@` */ + bool in_silence; + bool enable_scheduler; + 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; +}; + +class PHPCoroutine { + public: + struct Args { + zend_fcall_info_cache *fci_cache; + zval *argv; + uint32_t argc; + zval *callable; + }; + + struct Config { + uint64_t max_num; + uint32_t hook_flags; + bool enable_preemptive_scheduler; + bool enable_deadlock_check; + }; + + static SW_THREAD_LOCAL zend_array *options; + + enum HookType { + HOOK_NONE = 0, + HOOK_TCP = 1u << 1, + HOOK_UDP = 1u << 2, + HOOK_UNIX = 1u << 3, + HOOK_UDG = 1u << 4, + HOOK_SSL = 1u << 5, + HOOK_TLS = 1u << 6, + HOOK_STREAM_FUNCTION = 1u << 7, + HOOK_FILE = 1u << 8, + HOOK_SLEEP = 1u << 9, + HOOK_PROC = 1u << 10, + HOOK_CURL = 1u << 11, + 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 + HOOK_ALL = 0x7fffffff ^ HOOK_NATIVE_CURL, +#endif + }; + + static const uint8_t MAX_EXEC_MSEC = 10; + static void shutdown(); + static long create(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *callable); + static PHPContext *create_context(Args *args); + static void defer(zend::Function *fci); + static void deadlock_check(); + static bool enable_hook(uint32_t flags); + static bool disable_hook(); + static void disable_unsafe_function(); + static void enable_unsafe_function(); + static void interrupt_thread_stop(); + + static inline long get_cid() { + return sw_likely(activated) ? Coroutine::get_current_cid() : -1; + } + + static inline long get_pcid(long cid = 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(activated) ? Coroutine::get_elapsed(cid) : -1; + } + + static inline PHPContext *get_context() { + PHPContext *ctx = (PHPContext *) Coroutine::get_current_task(); + return ctx ? ctx : &main_context; + } + + 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_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() { + return config.max_num; + } + + static inline void set_max_num(uint64_t n) { + config.max_num = n; + } + + static inline void set_deadlock_check(bool value = true) { + config.enable_deadlock_check = value; + } + + 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 *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 *ctx = (PHPContext *) Coroutine::get_current_task(); + if (ctx && ctx->enable_scheduler) { + ctx->enable_scheduler = false; + return true; + } + return false; + } + + static void set_hook_flags(uint32_t flags); + + static inline uint32_t get_hook_flags() { + return config.hook_flags; + } + + static inline void enable_preemptive_scheduler(bool value) { + config.enable_preemptive_scheduler = value; + } + + static inline bool is_activated() { + 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 SW_THREAD_LOCAL bool activated; + static SW_THREAD_LOCAL PHPContext main_context; + static SW_THREAD_LOCAL Config config; + + 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 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 void record_last_msec(PHPContext *ctx) { + if (interrupt_thread_running) { + ctx->last_msec = Timer::get_absolute_msec(); + } + } +}; +} // namespace swoole + +/** + * for gdb + */ +zend_executor_globals *php_swoole_get_executor_globals(); diff --git a/ext-src/php_swoole_coroutine_system.h b/ext-src/php_swoole_coroutine_system.h new file mode 100644 index 00000000000..f4a75506e87 --- /dev/null +++ b/ext-src/php_swoole_coroutine_system.h @@ -0,0 +1,41 @@ +/* + +----------------------------------------------------------------------+ + | 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: Xinyu Zhu | + | shiguangqi | + | Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#include "php_swoole_cxx.h" + +// clang-format off +BEGIN_EXTERN_C() +#include "stubs/php_swoole_coroutine_system_arginfo.h" +END_EXTERN_C() +// clang-format on + +SW_EXTERN_C_BEGIN +PHP_METHOD(swoole_coroutine_system, exec); +PHP_METHOD(swoole_coroutine_system, sleep); +PHP_METHOD(swoole_coroutine_system, fread); +PHP_METHOD(swoole_coroutine_system, fgets); +PHP_METHOD(swoole_coroutine_system, fwrite); +PHP_METHOD(swoole_coroutine_system, statvfs); +PHP_METHOD(swoole_coroutine_system, getaddrinfo); +PHP_METHOD(swoole_coroutine_system, readFile); +PHP_METHOD(swoole_coroutine_system, writeFile); +PHP_METHOD(swoole_coroutine_system, wait); +PHP_METHOD(swoole_coroutine_system, waitPid); +PHP_METHOD(swoole_coroutine_system, waitSignal); +PHP_METHOD(swoole_coroutine_system, waitEvent); +SW_EXTERN_C_END 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 new file mode 100644 index 00000000000..54bee9168ea --- /dev/null +++ b/ext-src/php_swoole_cxx.cc @@ -0,0 +1,102 @@ +#include "php_swoole_cxx.h" + +//----------------------------------known string------------------------------------ + +static const char *sw_known_strings[] = { +#define _SW_ZEND_STR_DSC(id, str) str, + SW_ZEND_KNOWN_STRINGS(_SW_ZEND_STR_DSC) +#undef _SW_ZEND_STR_DSC + nullptr}; + +SW_API zend_string **sw_zend_known_strings = nullptr; + +SW_API zend_refcounted *sw_refcount_ptr; + +zend_refcounted *sw_get_refcount_ptr(zval *value) { + return (sw_refcount_ptr = value->value.counted); +} + +//----------------------------------known string------------------------------------ +namespace zend { +void known_strings_init(void) { + zend_string *str; + sw_zend_known_strings = nullptr; + + /* known strings */ + sw_zend_known_strings = (zend_string **) pemalloc( + sizeof(zend_string *) * ((sizeof(sw_known_strings) / sizeof(sw_known_strings[0]) - 1)), 1); + for (unsigned int i = 0; i < (sizeof(sw_known_strings) / sizeof(sw_known_strings[0])) - 1; i++) { + str = zend_string_init(sw_known_strings[i], strlen(sw_known_strings[i]), 1); + sw_zend_known_strings[i] = zend_new_interned_string(str); + } +} + +void known_strings_dtor(void) { + pefree(sw_zend_known_strings, 1); + sw_zend_known_strings = nullptr; +} + +namespace function { + +bool call(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *retval, const bool enable_coroutine) { + bool success; + if (enable_coroutine) { + if (retval) { + /* the coroutine has no return value */ + ZVAL_NULL(retval); + } + success = swoole::PHPCoroutine::create(fci_cache, argc, argv, nullptr) >= 0; + } else { + success = sw_zend_call_function_ex(nullptr, fci_cache, argc, argv, retval) == SUCCESS; + } + /* we have no chance to return to ZendVM to check the exception */ + if (UNEXPECTED(EG(exception))) { + zend_exception_error(EG(exception), E_ERROR); + } + return success; +} + +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()); + Variable retval; + if (call_user_function(EG(function_table), NULL, &function_name, &retval.value, argc, argv) != SUCCESS) { + ZVAL_NULL(&retval.value); + } + zval_dtor(&function_name); + /* we have no chance to return to ZendVM to check the exception */ + if (UNEXPECTED(EG(exception))) { + zend_exception_error(EG(exception), E_ERROR); + } + return retval; +} + +} // 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 new file mode 100644 index 00000000000..c3b19b27ccb --- /dev/null +++ b/ext-src/php_swoole_cxx.h @@ -0,0 +1,826 @@ +/* + +----------------------------------------------------------------------+ + | 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_private.h" +#include "php_swoole_coroutine.h" +#include "swoole_util.h" + +#include + +// clang-format off +//----------------------------------Swoole known string------------------------------------ + +#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") \ + _(SW_ZEND_STR_FD, "fd") \ + _(SW_ZEND_STR_SOCK, "sock") \ + _(SW_ZEND_STR_PIPE, "pipe") \ + _(SW_ZEND_STR_HEADERS, "headers") \ + _(SW_ZEND_STR_REQUEST_METHOD, "requestMethod") \ + _(SW_ZEND_STR_REQUEST_HEADERS, "requestHeaders") \ + _(SW_ZEND_STR_REQUEST_BODY, "requestBody") \ + _(SW_ZEND_STR_UPLOAD_FILES, "uploadFiles") \ + _(SW_ZEND_STR_COOKIES, "cookies") \ + _(SW_ZEND_STR_DOWNLOAD_FILE, "downloadFile") \ + _(SW_ZEND_STR_DOWNLOAD_OFFSET, "downloadOffset") \ + _(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") \ + _(SW_ZEND_STR_DATA, "data") \ + _(SW_ZEND_STR_PIPELINE, "pipeline") \ + _(SW_ZEND_STR_USE_PIPELINE_READ, "usePipelineRead") \ + _(SW_ZEND_STR_TRAILER, "trailer") \ + _(SW_ZEND_STR_MASTER_PID, "master_pid") \ + _(SW_ZEND_STR_CALLBACK, "callback") \ + _(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, +SW_ZEND_KNOWN_STRINGS(_SW_ZEND_STR_ID) +#undef _SW_ZEND_STR_ID + SW_ZEND_STR_LAST_KNOWN +} sw_zend_known_string_id; + +// clang-format on + +#define SW_ZSTR_KNOWN(idx) sw_zend_known_strings[idx] +extern zend_string **sw_zend_known_strings; + +//----------------------------------Swoole known string------------------------------------ + +#define SW_SET_CLASS_CREATE_WITH_ITS_OWN_HANDLERS(module) \ + module##_ce->create_object = [](zend_class_entry *ce) { return sw_zend_create_object(ce, &module##_handlers); } + +/** + * It is safe across coroutines, + * add reference count, prevent the socket pointer being released + */ +#define SW_CLIENT_GET_SOCKET_SAFE(__sock, __zsocket) \ + Socket *__sock = nullptr; \ + zend::Variable tmp_socket; \ + if (ZVAL_IS_OBJECT(__zsocket)) { \ + __sock = php_swoole_get_socket(__zsocket); \ + tmp_socket.assign(__zsocket); \ + } + +#define SW_CLIENT_PRESERVE_SOCKET(__zsocket) \ + zend::Variable tmp_socket; \ + if (ZVAL_IS_OBJECT(__zsocket)) { \ + tmp_socket.assign(__zsocket); \ + } + +SW_API bool php_swoole_is_enable_coroutine(); +SW_API zend_object *php_swoole_create_socket(enum swSocketType type); +SW_API zend_object *php_swoole_create_socket_from_fd(int fd, enum swSocketType type); +SW_API zend_object *php_swoole_create_socket_from_fd(int fd, int _domain, int _type, int _protocol); +SW_API bool php_swoole_export_socket(zval *zobject, swoole::coroutine::Socket *_socket); +SW_API zend_object *php_swoole_dup_socket(int fd, enum swSocketType type); +SW_API void php_swoole_init_socket_object(zval *zobject, swoole::coroutine::Socket *socket); +SW_API swoole::coroutine::Socket *php_swoole_get_socket(zval *zobject); +SW_API bool php_swoole_socket_is_closed(zval *zobject); +#ifdef SW_USE_OPENSSL +SW_API bool php_swoole_socket_set_ssl(swoole::coroutine::Socket *sock, zval *zset); +#endif +SW_API bool php_swoole_socket_set_protocol(swoole::coroutine::Socket *sock, zval *zset); +SW_API bool php_swoole_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() { + return PG(last_error_message) && (PG(last_error_type) & E_FATAL_ERRORS); +} + +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_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) +#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); +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); + +#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 { + public: + String() { + str = nullptr; + } + + String(const char *_str, size_t len) { + str = zend_string_init(_str, len, 0); + } + + String(const std::string &_str) { + str = zend_string_init(_str.c_str(), _str.length(), 0); + } + + String(zval *v) { + str = zval_get_string(v); + } + + String(zend_string *v, bool copy) { + if (copy) { + str = zend_string_copy(v); + } else { + str = v; + } + } + + String(const String &o) { + str = zend_string_copy(o.str); + } + + String(String &&o) { + str = o.str; + o.str = nullptr; + } + + void operator=(zval *v) { + 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; + } + + char *val() { + return ZSTR_VAL(str); + } + + size_t len() { + return ZSTR_LEN(str); + } + + zend_string *get() { + return str; + } + + void rtrim() { + ZSTR_LEN(str) = swoole::rtrim(val(), len()); + } + + const std::string to_std_string() { + return std::string(val(), len()); + } + + char *dup() { + return sw_likely(len() > 0) ? sw_strndup(val(), len()) : nullptr; + } + + char *edup() { + return sw_likely(len() > 0) ? estrndup(val(), len()) : nullptr; + } + + void release() { + if (str) { + zend_string_release(str); + str = nullptr; + } + } + + ~String() { + release(); + } + + private: + zend_string *str; +}; + +class KeyValue { + public: + zend_ulong index; + zend_string *key; + zval zvalue; + + KeyValue(zend_ulong _index, zend_string *_key, zval *_zvalue) { + index = _index; + key = _key ? zend_string_copy(_key) : nullptr; + ZVAL_DEREF(_zvalue); + zvalue = *_zvalue; + Z_TRY_ADDREF(zvalue); + } + + 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); + } + + ~KeyValue() { + if (key) { + zend_string_release(key); + } + zval_ptr_dtor(&zvalue); + } +}; + +class ArrayIterator { + public: + ArrayIterator(Bucket *p) { + _ptr = p; + _key = _ptr->key; + _val = &_ptr->val; + _index = _ptr->h; + pe = p; + } + + ArrayIterator(Bucket *p, Bucket *_pe) { + _ptr = p; + _key = _ptr->key; + _val = &_ptr->val; + _index = _ptr->h; + 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; + } + + private: + void skipUndefBucket() { + while (_ptr != pe) { + _val = &_ptr->val; + if (_val && Z_TYPE_P(_val) == IS_INDIRECT) { + _val = Z_INDIRECT_P(_val); + } + if (UNEXPECTED(Z_TYPE_P(_val) == IS_UNDEF)) { + ++_ptr; + continue; + } + if (_ptr->key) { + _key = _ptr->key; + _index = 0; + } else { + _index = _ptr->h; + _key = nullptr; + } + break; + } + } + + zval *_val; + zend_string *_key; + Bucket *_ptr; + Bucket *pe; + zend_ulong _index; +}; + +class Array { + public: + zval *arr; + + Array(zval *_arr) { + assert(Z_TYPE_P(_arr) == IS_ARRAY); + arr = _arr; + } + + size_t count() { + return zend_hash_num_elements(Z_ARRVAL_P(arr)); + } + + bool set(zend_ulong index, zval *value) { + return add_index_zval(arr, index, value) == SUCCESS; + } + + bool append(zval *value) { + return add_next_index_zval(arr, value) == SUCCESS; + } + + bool set(zend_ulong index, zend_resource *res) { + zval tmp; + ZVAL_RES(&tmp, res); + return set(index, &tmp); + } + + ArrayIterator begin() { + return ArrayIterator(Z_ARRVAL_P(arr)->arData, Z_ARRVAL_P(arr)->arData + Z_ARRVAL_P(arr)->nNumUsed); + } + + ArrayIterator end() { + return ArrayIterator(Z_ARRVAL_P(arr)->arData + Z_ARRVAL_P(arr)->nNumUsed); + } +}; + +enum PipeType { + PIPE_TYPE_NONE = 0, + PIPE_TYPE_STREAM = 1, + PIPE_TYPE_DGRAM = 2, +}; + +class Process { + public: + zend_object *zsocket = nullptr; + enum PipeType pipe_type; + bool enable_coroutine; + + Process(enum PipeType pipe_type, bool enable_coroutine) + : pipe_type(pipe_type), enable_coroutine(enable_coroutine) {} + + ~Process() { + if (zsocket) { + OBJ_RELEASE(zsocket); + } + } +}; + +class Variable { + public: + zval value; + + Variable() { + value = {}; + } + + Variable(zval *zvalue) { + assign(zvalue); + } + + Variable(const char *str, size_t l_str) { + ZVAL_STRINGL(&value, str, l_str); + } + + Variable(const char *str) { + ZVAL_STRING(&value, str); + } + + Variable(const Variable &&src) { + value = src.value; + add_ref(); + } + + Variable(Variable &&src) { + value = src.value; + src.reset(); + } + + void operator=(zval *zvalue) { + assign(zvalue); + } + + void operator=(const Variable &src) { + value = src.value; + add_ref(); + } + + void assign(zval *zvalue) { + value = *zvalue; + add_ref(); + } + + zval *ptr() { + return &value; + } + + void reset() { + ZVAL_UNDEF(&value); + } + + void add_ref() { + Z_TRY_ADDREF_P(&value); + } + + void del_ref() { + Z_TRY_DELREF_P(&value); + } + + ~Variable() { + zval_ptr_dtor(&value); + } +}; + +class CharPtr { + private: + char *str_; + + public: + CharPtr() { + str_ = nullptr; + } + + CharPtr(char *str) { + str_ = estrndup(str, strlen(str)); + } + + CharPtr(char *str, size_t len) { + str_ = estrndup(str, len); + } + + void operator=(char *str) { + assign(str, strlen(str)); + } + + void release() { + if (str_) { + efree(str_); + str_ = nullptr; + } + } + + void assign(char *str, size_t len) { + release(); + str_ = estrndup(str, len); + } + + void assign_tolower(const char *str, size_t len) { + release(); + str_ = zend_str_tolower_dup(str, len); + } + + ~CharPtr() { + release(); + } + + char *get() { + return str_; + } +}; + +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; + + bool call(zval *retval, const bool enable_coroutine) { + return function::call(&fci_cache, fci.param_count, fci.params, retval, enable_coroutine); + } +}; + +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); + +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 new file mode 100644 index 00000000000..bf378da0323 --- /dev/null +++ b/ext-src/php_swoole_http.h @@ -0,0 +1,480 @@ +/* + +----------------------------------------------------------------------+ + | 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: 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 +#define SW_ZLIB_ENCODING_RAW -0xf +#define SW_ZLIB_ENCODING_GZIP 0x1f +#define SW_ZLIB_ENCODING_DEFLATE 0x0f +#define SW_ZLIB_ENCODING_ANY 0x2f +#endif + +#ifdef SW_HAVE_BROTLI +#include +#include +#endif + +#ifdef SW_HAVE_ZSTD +#include +#endif + +#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_CONTENT_ENCODING = 1u << 8, +}; + +enum swHttpCompressMethod { + HTTP_COMPRESS_NONE, + HTTP_COMPRESS_GZIP, + HTTP_COMPRESS_DEFLATE, + HTTP_COMPRESS_BR, + HTTP_COMPRESS_ZSTD, +}; + +namespace swoole { +class Server; +class Coroutine; +namespace http2 { +class Stream; +class Session; +} // namespace http2 + +namespace http { + +struct Request { + int version; + char *path; + uint32_t path_len; + const char *ext; + uint32_t ext_len; + uint8_t post_form_urlencoded; + + zval zdata; + const char *body_at; + size_t body_length; + String *chunked_body; + String *h2_data_buffer; + + // Notice: Do not change the order + zval *zobject; + zval _zobject; + zval *zserver; + zval _zserver; + zval *zheader; + zval _zheader; + zval *zget; + zval _zget; + zval *zpost; + zval _zpost; + zval *zcookie; + zval _zcookie; + zval *zfiles; + zval _zfiles; + zval *ztmpfiles; + zval _ztmpfiles; +}; + +struct Response { + enum swoole_http_method method; + int version; + int status; + char *reason; + + // Notice: Do not change the order + zval *zobject; + zval _zobject; + zval *zheader; + zval _zheader; + zval *zcookie; + zval _zcookie; + zval *ztrailer; + zval _ztrailer; +}; + +struct Context { + SessionId fd; + uchar completed : 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 keepalive : 1; + uchar websocket : 1; +#ifdef SW_HAVE_ZLIB + uchar websocket_compression : 1; +#endif + 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; + + http2::Stream *stream; + 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; + Response response; + + swoole_http_parser parser; + multipart_parser *mt_parser; + + uint16_t input_var_num; + const char *current_header_name; + size_t current_header_name_len; + char *current_input_name; + size_t current_input_name_len; + char *current_form_data_name; + size_t current_form_data_name_len; + zval *current_multipart_header; + const char *tmp_content_type; + size_t tmp_content_type_len; + String *form_data_buffer; + + std::string upload_tmp_dir; + + void *private_data; + void *private_data_2; + bool (*send)(Context *ctx, const char *data, size_t length); + bool (*sendfile)(Context *ctx, const char *file, uint32_t l_file, off_t offset, size_t length); + bool (*close)(Context *ctx); + bool (*onBeforeRequest)(Context *ctx); + void (*onAfterResponse)(Context *ctx); + + void init(Server *server); + void init(coroutine::Socket *socket); + void bind(Server *server); + void bind(coroutine::Socket *socket); + void copy(Context *ctx); + bool 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 + +namespace http2 { +class Stream { + public: + http::Context *ctx; + // uint8_t priority; // useless now + uint32_t id; + // flow control + uint32_t remote_window_size; + uint32_t local_window_size; + Coroutine *waiting_coroutine = nullptr; + + Stream(Session *client, uint32_t _id); + ~Stream(); + + 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); +}; + +class Session { + public: + int fd; + std::unordered_map streams; + + nghttp2_hd_inflater *inflater = nullptr; + nghttp2_hd_deflater *deflater = nullptr; + + 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; + + http::Context *default_ctx = nullptr; + void *private_data = nullptr; + + void (*handle)(Session *, Stream *) = nullptr; + + Session(SessionId _fd); + ~Session(); +}; +} // namespace http2 + +} // 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 zend_class_entry *swoole_http_cookie_ce; + +swoole::http::Context *swoole_http_context_new(swoole::SessionId fd); +swoole::http::Context *php_swoole_http_request_get_and_check_context(zval *zobject); +swoole::http::Context *php_swoole_http_response_get_and_check_context(zval *zobject); +swoole::http::Cookie *php_swoole_http_get_cooke_safety(zval *zobject); + +/** + * These class properties cannot be modified by the user before assignment, such as Swoole\\Http\\Request. + * So we can use this function to init property. + */ +static sw_inline zval *swoole_http_init_and_read_property( + zend_class_entry *ce, zval *zobject, zval **zproperty_store_pp, zend_string *name, int size = HT_MIN_SIZE) { + if (UNEXPECTED(!*zproperty_store_pp)) { + zval *zv = zend_hash_find(&ce->properties_info, name); + zend_property_info *property_info = (zend_property_info *) Z_PTR_P(zv); + zval *property = OBJ_PROP(SW_Z8_OBJ_P(zobject), property_info->offset); + array_init_size(property, size); + *zproperty_store_pp = (zval *) (zproperty_store_pp + 1); + **zproperty_store_pp = *property; + } + return *zproperty_store_pp; +} + +static sw_inline zval *swoole_http_init_and_read_property( + zend_class_entry *ce, zval *zobject, zval **zproperty_store_pp, const char *name, size_t name_len) { + if (UNEXPECTED(!*zproperty_store_pp)) { + // 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 = *property; + } + return *zproperty_store_pp; +} + +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); + +#ifdef SW_HAVE_ZLIB +voidpf php_zlib_alloc(voidpf opaque, uInt items, uInt size); +void php_zlib_free(voidpf opaque, voidpf address); +#endif + +#ifdef SW_HAVE_BROTLI +void *php_brotli_alloc(void *opaque, size_t size); +void php_brotli_free(void *opaque, void *address); +#endif + +static sw_inline nghttp2_mem *php_nghttp2_mem() { + static nghttp2_mem mem = {nullptr, + [](size_t size, void *mem_user_data) { return emalloc(size); }, + [](void *ptr, void *mem_user_data) { return efree(ptr); }, + [](size_t nmemb, size_t size, void *mem_user_data) { return ecalloc(nmemb, size); }, + [](void *ptr, size_t size, void *mem_user_data) { return erealloc(ptr, size); }}; + return &mem; +} + +namespace swoole { +namespace http2 { +//-----------------------------------namespace begin-------------------------------------------- +class HeaderSet { + public: + HeaderSet(size_t size) : size(size), index(0) { + nvs = (nghttp2_nv *) ecalloc(size, sizeof(nghttp2_nv)); + } + + inline nghttp2_nv *get() { + return nvs; + } + + inline size_t len() { + return index; + } + + void reserve_one() { + index++; + } + + inline void add(size_t index, + const char *name, + size_t name_len, + const char *value, + size_t value_len, + 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); + 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; + 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); + } + } + + inline void add(const char *name, + size_t name_len, + const char *value, + size_t value_len, + const uint8_t flags = NGHTTP2_NV_FLAG_NONE) { + add(index++, name, name_len, value, value_len, flags); + } + + ~HeaderSet() { + for (size_t i = 0; i < size; ++i) { + if (sw_likely(nvs[i].name /* && nvs[i].value */)) { + efree((void *) nvs[i].name); + efree((void *) nvs[i].value); + } + } + efree(nvs); + } + + private: + nghttp2_nv *nvs; + size_t size; + size_t index; +}; +//-----------------------------------namespace end-------------------------------------------- +} // namespace http2 +} // namespace swoole diff --git a/ext-src/php_swoole_http_server.h b/ext-src/php_swoole_http_server.h new file mode 100644 index 00000000000..22855ccfcbc --- /dev/null +++ b/ext-src/php_swoole_http_server.h @@ -0,0 +1,87 @@ +/* + +----------------------------------------------------------------------+ + | 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: Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#pragma once + +#include "php_swoole_server.h" +#include "php_swoole_http.h" + +#include "swoole_http.h" +#include "swoole_websocket.h" +#include "swoole_mime_type.h" +#include "swoole_http2.h" + +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); + +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); + +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); +} + +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 new file mode 100644 index 00000000000..78cc7d011d0 --- /dev/null +++ b/ext-src/php_swoole_library.h @@ -0,0 +1,10179 @@ +/** + * ----------------------------------------------------------------------- + * 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: 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" + "/**\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" + "define('SWOOLE_LIBRARY', true);\n" + "\n" + "!defined('CURLOPT_HEADEROPT') && define('CURLOPT_HEADEROPT', 229);\n" + "!defined('CURLOPT_PROXYHEADER') && define('CURLOPT_PROXYHEADER', 10228);\n" + "!defined('CURLOPT_RESOLVE') && define('CURLOPT_RESOLVE', 10203);\n" + "!defined('CURLOPT_UNIX_SOCKET_PATH') && define('CURLOPT_UNIX_SOCKET_PATH', 10231);\n"; + +static const char* swoole_library_source_std_exec = + "\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" + "use Swoole\\Coroutine\\System;\n" + "\n" + "function swoole_exec(string $command, &$output = null, &$returnVar = null)\n" + "{\n" + " $result = System::exec($command);\n" + " if ($result) {\n" + " $outputList = explode(PHP_EOL, $result['output']);\n" + " foreach ($outputList as &$value) {\n" + " $value = rtrim($value);\n" + " }\n" + " if (($endLine = end($outputList)) === '') {\n" + " array_pop($outputList);\n" + " $endLine = end($outputList);\n" + " }\n" + " if ($output) {\n" + " $output = array_merge($output, $outputList);\n" + " } else {\n" + " $output = $outputList;\n" + " }\n" + " $returnVar = $result['code'];\n" + " return $endLine;\n" + " }\n" + " return false;\n" + "}\n" + "\n" + "function swoole_shell_exec(string $cmd)\n" + "{\n" + " $result = System::exec($cmd);\n" + " if ($result && $result['output'] !== '') {\n" + " return $result['output'];\n" + " }\n" + " return null;\n" + "}\n"; + +static const char* swoole_library_source_core_constant = + "\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" + "class Constant\n" + "{\n" + " /* {{{ EVENT */\n" + " public const EVENT_START = 'start';\n" + "\n" + " public const EVENT_BEFORE_SHUTDOWN = 'beforeShutdown';\n" + "\n" + " public const EVENT_SHUTDOWN = 'shutdown';\n" + "\n" + " public const EVENT_WORKER_START = 'workerStart';\n" + "\n" + " public const EVENT_WORKER_STOP = 'workerStop';\n" + "\n" + " public const EVENT_BEFORE_RELOAD = 'beforeReload';\n" + "\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_WORKER_EXIT = 'workerExit';\n" + "\n" + " public const EVENT_WORKER_ERROR = 'workerError';\n" + "\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_CONNECT = 'connect';\n" + "\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" + " /* {{{ OPTION */\n" + " public const OPTION_DEBUG_MODE = 'debug_mode';\n" + "\n" + " public const OPTION_TRACE_FLAGS = 'trace_flags';\n" + "\n" + " public const OPTION_LOG_FILE = 'log_file';\n" + "\n" + " public const OPTION_LOG_LEVEL = 'log_level';\n" + "\n" + " public const OPTION_LOG_DATE_FORMAT = 'log_date_format';\n" + "\n" + " public const OPTION_LOG_DATE_WITH_MICROSECONDS = 'log_date_with_microseconds';\n" + "\n" + " public const OPTION_LOG_ROTATION = 'log_rotation';\n" + "\n" + " public const OPTION_DISPLAY_ERRORS = 'display_errors';\n" + "\n" + " public const OPTION_DNS_SERVER = 'dns_server';\n" + "\n" + " public const OPTION_SOCKET_DNS_TIMEOUT = 'socket_dns_timeout';\n" + "\n" + " public const OPTION_SOCKET_CONNECT_TIMEOUT = 'socket_connect_timeout';\n" + "\n" + " public const OPTION_SOCKET_WRITE_TIMEOUT = 'socket_write_timeout';\n" + "\n" + " public const OPTION_SOCKET_SEND_TIMEOUT = 'socket_send_timeout';\n" + "\n" + " public const OPTION_SOCKET_READ_TIMEOUT = 'socket_read_timeout';\n" + "\n" + " public const OPTION_SOCKET_RECV_TIMEOUT = 'socket_recv_timeout';\n" + "\n" + " public const OPTION_SOCKET_BUFFER_SIZE = 'socket_buffer_size';\n" + "\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" + "\n" + " public const OPTION_DNS_CACHE_REFRESH_TIME = 'dns_cache_refresh_time';\n" + "\n" + " public const OPTION_THREAD_NUM = 'thread_num';\n" + "\n" + " public const OPTION_MIN_THREAD_NUM = 'min_thread_num';\n" + "\n" + " public const OPTION_MAX_THREAD_NUM = 'max_thread_num';\n" + "\n" + " public const OPTION_SOCKET_DONTWAIT = 'socket_dontwait';\n" + "\n" + " public const OPTION_DNS_LOOKUP_RANDOM = 'dns_lookup_random';\n" + "\n" + " public const OPTION_USE_ASYNC_RESOLVER = 'use_async_resolver';\n" + "\n" + " public const OPTION_ENABLE_COROUTINE = 'enable_coroutine';\n" + "\n" + " public const OPTION_SSL_PROTOCOLS = 'ssl_protocols';\n" + "\n" + " public const OPTION_SSL_COMPRESS = 'ssl_compress';\n" + "\n" + " public const OPTION_SSL_CERT_FILE = 'ssl_cert_file';\n" + "\n" + " public const OPTION_SSL_KEY_FILE = 'ssl_key_file';\n" + "\n" + " public const OPTION_SSL_PASSPHRASE = 'ssl_passphrase';\n" + "\n" + " public const OPTION_SSL_HOST_NAME = 'ssl_host_name';\n" + "\n" + " public const OPTION_SSL_VERIFY_PEER = 'ssl_verify_peer';\n" + "\n" + " public const OPTION_SSL_ALLOW_SELF_SIGNED = 'ssl_allow_self_signed';\n" + "\n" + " public const OPTION_SSL_CAFILE = 'ssl_cafile';\n" + "\n" + " public const OPTION_SSL_CAPATH = 'ssl_capath';\n" + "\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" + "\n" + " public const OPTION_PACKAGE_EOF = 'package_eof';\n" + "\n" + " public const OPTION_OPEN_MQTT_PROTOCOL = 'open_mqtt_protocol';\n" + "\n" + " public const OPTION_OPEN_LENGTH_CHECK = 'open_length_check';\n" + "\n" + " public const OPTION_PACKAGE_LENGTH_TYPE = 'package_length_type';\n" + "\n" + " public const OPTION_PACKAGE_LENGTH_OFFSET = 'package_length_offset';\n" + "\n" + " public const OPTION_PACKAGE_BODY_OFFSET = 'package_body_offset';\n" + "\n" + " public const OPTION_PACKAGE_LENGTH_FUNC = 'package_length_func';\n" + "\n" + " public const OPTION_PACKAGE_MAX_LENGTH = 'package_max_length';\n" + "\n" + " public const OPTION_BUFFER_HIGH_WATERMARK = 'buffer_high_watermark';\n" + "\n" + " public const OPTION_BUFFER_LOW_WATERMARK = 'buffer_low_watermark';\n" + "\n" + " public const OPTION_BIND_PORT = 'bind_port';\n" + "\n" + " public const OPTION_BIND_ADDRESS = 'bind_address';\n" + "\n" + " public const OPTION_OPEN_TCP_NODELAY = 'open_tcp_nodelay';\n" + "\n" + " public const OPTION_SOCKS5_HOST = 'socks5_host';\n" + "\n" + " public const OPTION_SOCKS5_PORT = 'socks5_port';\n" + "\n" + " public const OPTION_SOCKS5_USERNAME = 'socks5_username';\n" + "\n" + " public const OPTION_SOCKS5_PASSWORD = 'socks5_password';\n" + "\n" + " public const OPTION_HTTP_PROXY_HOST = 'http_proxy_host';\n" + "\n" + " public const OPTION_HTTP_PROXY_PORT = 'http_proxy_port';\n" + "\n" + " public const OPTION_HTTP_PROXY_USERNAME = 'http_proxy_username';\n" + "\n" + " public const OPTION_HTTP_PROXY_USER = 'http_proxy_user';\n" + "\n" + " public const OPTION_HTTP_PROXY_PASSWORD = 'http_proxy_password';\n" + "\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" + "\n" + " public const OPTION_C_STACK_SIZE = 'c_stack_size';\n" + "\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_CONNECT_TIMEOUT = 'connect_timeout';\n" + "\n" + " public const OPTION_TIMEOUT = 'timeout';\n" + "\n" + " public const OPTION_MAX_RETRIES = 'max_retries';\n" + "\n" + " public const OPTION_DEFER = 'defer';\n" + "\n" + " public const OPTION_LOWERCASE_HEADER = 'lowercase_header';\n" + "\n" + " public const OPTION_KEEP_ALIVE = 'keep_alive';\n" + "\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_LEVEL = 'http_compression_level';\n" + "\n" + " public const OPTION_COMPRESSION_LEVEL = 'compression_level';\n" + "\n" + " public const OPTION_HTTP_GZIP_LEVEL = 'http_gzip_level';\n" + "\n" + " public const OPTION_HTTP_COMPRESSION_MIN_LENGTH = 'http_compression_min_length';\n" + "\n" + " public const OPTION_COMPRESSION_MIN_LENGTH = 'compression_min_length';\n" + "\n" + " public const OPTION_HTTP_COMPRESSION_TYPES = 'http_compression_types';\n" + "\n" + " public const OPTION_COMPRESSION_TYPES = 'compression_types';\n" + "\n" + " public const OPTION_UPLOAD_TMP_DIR = 'upload_tmp_dir';\n" + "\n" + " public const OPTION_ENABLE_MESSAGE_BUS = 'enable_message_bus';\n" + "\n" + " public const OPTION_MAX_PACKAGE_SIZE = 'max_package_size';\n" + "\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" + "\n" + " public const OPTION_PID_FILE = 'pid_file';\n" + "\n" + " public const OPTION_REACTOR_NUM = 'reactor_num';\n" + "\n" + " public const OPTION_SINGLE_THREAD = 'single_thread';\n" + "\n" + " public const OPTION_WORKER_NUM = 'worker_num';\n" + "\n" + " public const OPTION_MAX_WAIT_TIME = 'max_wait_time';\n" + "\n" + " public const OPTION_MAX_QUEUED_BYTES = 'max_queued_bytes';\n" + "\n" + " public const OPTION_MAX_CONCURRENCY = 'max_concurrency';\n" + "\n" + " public const OPTION_WORKER_MAX_CONCURRENCY = 'worker_max_concurrency';\n" + "\n" + " public const OPTION_SEND_TIMEOUT = 'send_timeout';\n" + "\n" + " public const OPTION_DISPATCH_MODE = 'dispatch_mode';\n" + "\n" + " public const OPTION_SEND_YIELD = 'send_yield';\n" + "\n" + " public const OPTION_DISPATCH_FUNC = 'dispatch_func';\n" + "\n" + " public const OPTION_DISCARD_TIMEOUT_REQUEST = 'discard_timeout_request';\n" + "\n" + " public const OPTION_ENABLE_UNSAFE_EVENT = 'enable_unsafe_event';\n" + "\n" + " public const OPTION_ENABLE_DELAY_RECEIVE = 'enable_delay_receive';\n" + "\n" + " public const OPTION_ENABLE_REUSE_PORT = 'enable_reuse_port';\n" + "\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" + "\n" + " public const OPTION_TASK_IPC_MODE = 'task_ipc_mode';\n" + "\n" + " public const OPTION_TASK_TMPDIR = 'task_tmpdir';\n" + "\n" + " public const OPTION_TASK_MAX_REQUEST = 'task_max_request';\n" + "\n" + " public const OPTION_TASK_MAX_REQUEST_GRACE = 'task_max_request_grace';\n" + "\n" + " public const OPTION_MAX_CONNECTION = 'max_connection';\n" + "\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" + "\n" + " public const OPTION_MAX_REQUEST = 'max_request';\n" + "\n" + " public const OPTION_MAX_REQUEST_GRACE = 'max_request_grace';\n" + "\n" + " public const OPTION_RELOAD_ASYNC = 'reload_async';\n" + "\n" + " public const OPTION_OPEN_CPU_AFFINITY = 'open_cpu_affinity';\n" + "\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" + "\n" + " public const OPTION_HTTP_AUTOINDEX = 'http_autoindex';\n" + "\n" + " public const OPTION_HTTP_INDEX_FILES = 'http_index_files';\n" + "\n" + " public const OPTION_STATIC_HANDLER_LOCATIONS = 'static_handler_locations';\n" + "\n" + " public const OPTION_INPUT_BUFFER_SIZE = 'input_buffer_size';\n" + "\n" + " public const OPTION_BUFFER_INPUT_SIZE = 'buffer_input_size';\n" + "\n" + " public const OPTION_OUTPUT_BUFFER_SIZE = 'output_buffer_size';\n" + "\n" + " public const OPTION_BUFFER_OUTPUT_SIZE = 'buffer_output_size';\n" + "\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" + "\n" + " public const OPTION_KERNEL_SOCKET_SEND_BUFFER_SIZE = 'kernel_socket_send_buffer_size';\n" + "\n" + " public const OPTION_TCP_DEFER_ACCEPT = 'tcp_defer_accept';\n" + "\n" + " public const OPTION_OPEN_TCP_KEEPALIVE = 'open_tcp_keepalive';\n" + "\n" + " public const OPTION_OPEN_HTTP_PROTOCOL = 'open_http_protocol';\n" + "\n" + " public const OPTION_OPEN_WEBSOCKET_PROTOCOL = 'open_websocket_protocol';\n" + "\n" + " public const OPTION_WEBSOCKET_SUBPROTOCOL = 'websocket_subprotocol';\n" + "\n" + " public const OPTION_OPEN_WEBSOCKET_CLOSE_FRAME = 'open_websocket_close_frame';\n" + "\n" + " public const OPTION_OPEN_WEBSOCKET_PING_FRAME = 'open_websocket_ping_frame';\n" + "\n" + " public const OPTION_OPEN_WEBSOCKET_PONG_FRAME = 'open_websocket_pong_frame';\n" + "\n" + " public const OPTION_OPEN_HTTP2_PROTOCOL = 'open_http2_protocol';\n" + "\n" + " public const OPTION_OPEN_REDIS_PROTOCOL = 'open_redis_protocol';\n" + "\n" + " public const OPTION_MAX_IDLE_TIME = 'max_idle_time';\n" + "\n" + " public const OPTION_TCP_KEEPIDLE = 'tcp_keepidle';\n" + "\n" + " public const OPTION_TCP_KEEPINTERVAL = 'tcp_keepinterval';\n" + "\n" + " public const OPTION_TCP_KEEPCOUNT = 'tcp_keepcount';\n" + "\n" + " public const OPTION_TCP_USER_TIMEOUT = 'tcp_user_timeout';\n" + "\n" + " public const OPTION_TCP_FASTOPEN = 'tcp_fastopen';\n" + "\n" + " public const OPTION_PACKAGE_BODY_START = 'package_body_start';\n" + "\n" + " public const OPTION_SSL_CLIENT_CERT_FILE = 'ssl_client_cert_file';\n" + "\n" + " public const OPTION_SSL_PREFER_SERVER_CIPHERS = 'ssl_prefer_server_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 = + "\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" + "class StringObject implements \\Stringable\n" + "{\n" + " /**\n" + " * StringObject constructor.\n" + " */\n" + " public function __construct(protected string $string = '')\n" + " {\n" + " }\n" + "\n" + " public function __toString(): string\n" + " {\n" + " 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" + " public function indexOf(string $needle, int $offset = 0): false|int\n" + " {\n" + " return strpos($this->string, $needle, $offset);\n" + " }\n" + "\n" + " public function lastIndexOf(string $needle, int $offset = 0): false|int\n" + " {\n" + " return strrpos($this->string, $needle, $offset);\n" + " }\n" + "\n" + " public function pos(string $needle, int $offset = 0): false|int\n" + " {\n" + " return strpos($this->string, $needle, $offset);\n" + " }\n" + "\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 new static(strrev($this->string)); // @phpstan-ignore new.static\n" + " }\n" + "\n" + " /**\n" + " * @return false|int\n" + " */\n" + " public function ipos(string $needle)\n" + " {\n" + " return stripos($this->string, $needle);\n" + " }\n" + "\n" + " public function lower(): static\n" + " {\n" + " return new static(strtolower($this->string)); // @phpstan-ignore new.static\n" + " }\n" + "\n" + " public function upper(): static\n" + " {\n" + " return new static(strtoupper($this->string)); // @phpstan-ignore new.static\n" + " }\n" + "\n" + " public function trim(string $characters = ''): static\n" + " {\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" + " * @return static\n" + " */\n" + " public function ltrim(): self\n" + " {\n" + " return new static(ltrim($this->string)); // @phpstan-ignore new.static\n" + " }\n" + "\n" + " /**\n" + " * @return static\n" + " */\n" + " public function rtrim(): self\n" + " {\n" + " return new static(rtrim($this->string)); // @phpstan-ignore new.static\n" + " }\n" + "\n" + " /**\n" + " * @return static\n" + " */\n" + " public function substr(int $offset, ?int $length = null)\n" + " {\n" + " return new static(substr($this->string, $offset, $length)); // @phpstan-ignore new.static\n" + " }\n" + "\n" + " public function repeat(int $times): static\n" + " {\n" + " return new static(str_repeat($this->string, $times)); // @phpstan-ignore new.static\n" + " }\n" + "\n" + " public function append(mixed $str): static\n" + " {\n" + " return new static($this->string .= $str); // @phpstan-ignore new.static\n" + " }\n" + "\n" + " /**\n" + " * @param int|null $count\n" + " */\n" + " public function replace(string $search, string $replace, &$count = null): static\n" + " {\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 str_starts_with($this->string, $needle);\n" + " }\n" + "\n" + " public function endsWith(string $needle): bool\n" + " {\n" + " return strrpos($this->string, $needle) === (strlen($this->string) - strlen($needle));\n" + " }\n" + "\n" + " public function equals($str, bool $strict = false): bool\n" + " {\n" + " if ($str instanceof StringObject) {\n" + " $str = strval($str);\n" + " }\n" + " if ($strict) {\n" + " return $this->string === $str;\n" + " }\n" + " return $this->string == $str;\n" + " }\n" + "\n" + " public function contains(string $subString): bool\n" + " {\n" + " return str_contains($this->string, $subString);\n" + " }\n" + "\n" + " public function split(string $delimiter, int $limit = PHP_INT_MAX): ArrayObject\n" + " {\n" + " return static::detectArrayType(explode($delimiter, $this->string, $limit));\n" + " }\n" + "\n" + " public function char(int $index): string\n" + " {\n" + " if ($index > strlen($this->string)) {\n" + " return '';\n" + " }\n" + " return $this->string[$index];\n" + " }\n" + "\n" + " /**\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 $length = 76, string $separator = \"\\r\\n\"): static\n" + " {\n" + " return new static(chunk_split($this->string, $length, $separator)); // @phpstan-ignore new.static\n" + " }\n" + "\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, $length));\n" + " }\n" + "\n" + " public function toString(): string\n" + " {\n" + " return $this->string;\n" + " }\n" + "\n" + " protected static function detectArrayType(array $value): ArrayObject\n" + " {\n" + " return new ArrayObject($value);\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_multibyte_string_object = + "\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" + "class MultibyteStringObject extends StringObject\n" + "{\n" + " public function length(): int\n" + " {\n" + " return mb_strlen($this->string);\n" + " }\n" + "\n" + " public function indexOf(string $needle, int $offset = 0, ?string $encoding = null): false|int\n" + " {\n" + " return mb_strpos($this->string, $needle, $offset, $encoding);\n" + " }\n" + "\n" + " public function lastIndexOf(string $needle, int $offset = 0, ?string $encoding = null): false|int\n" + " {\n" + " return mb_strrpos($this->string, $needle, $offset, $encoding);\n" + " }\n" + "\n" + " public function pos(string $needle, int $offset = 0, ?string $encoding = null): false|int\n" + " {\n" + " return mb_strpos($this->string, $needle, $offset, $encoding);\n" + " }\n" + "\n" + " public function rpos(string $needle, int $offset = 0, ?string $encoding = null): false|int\n" + " {\n" + " return mb_strrpos($this->string, $needle, $offset, $encoding);\n" + " }\n" + "\n" + " public function ipos(string $needle, int $offset = 0, ?string $encoding = null): int|false\n" + " {\n" + " return mb_stripos($this->string, $needle, $offset, $encoding);\n" + " }\n" + "\n" + " /**\n" + " * @see https://www.php.net/mb_substr\n" + " */\n" + " public function substr(int $start, ?int $length = null, ?string $encoding = null): static\n" + " {\n" + " return new static(mb_substr($this->string, $start, $length, $encoding)); // @phpstan-ignore new.static\n" + " }\n" + "\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_str_split($this->string, $length));\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_exception_array_key_not_exists = + "\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\\Exception;\n" + "\n" + "class ArrayKeyNotExists extends \\RuntimeException\n" + "{\n" + "}\n"; + +static const char* swoole_library_source_core_array_object = + "\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\\Exception\\ArrayKeyNotExists;\n" + "\n" + "class ArrayObject implements \\ArrayAccess, \\Serializable, \\Countable, \\Iterator\n" + "{\n" + " /**\n" + " * @var array\n" + " */\n" + " protected $array;\n" + "\n" + " /**\n" + " * ArrayObject constructor.\n" + " */\n" + " public function __construct(array $array = [])\n" + " {\n" + " $this->array = $array;\n" + " }\n" + "\n" + " public function __toArray(): array\n" + " {\n" + " 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" + " }\n" + "\n" + " public function isEmpty(): bool\n" + " {\n" + " return empty($this->array);\n" + " }\n" + "\n" + " public function count(): int\n" + " {\n" + " return count($this->array);\n" + " }\n" + "\n" + " /**\n" + " * @return mixed\n" + " */\n" + " #[\\ReturnTypeWillChange]\n" + " public function current()\n" + " {\n" + " return current($this->array);\n" + " }\n" + "\n" + " /**\n" + " * @return mixed\n" + " */\n" + " #[\\ReturnTypeWillChange]\n" + " public function key()\n" + " {\n" + " return key($this->array);\n" + " }\n" + "\n" + " public function valid(): bool\n" + " {\n" + " return array_key_exists($this->key(), $this->array);\n" + " }\n" + "\n" + " /**\n" + " * @return mixed\n" + " */\n" + " #[\\ReturnTypeWillChange]\n" + " public function rewind()\n" + " {\n" + " return reset($this->array);\n" + " }\n" + "\n" + " /**\n" + " * @return mixed\n" + " */\n" + " #[\\ReturnTypeWillChange]\n" + " public function next()\n" + " {\n" + " return next($this->array);\n" + " }\n" + "\n" + " /**\n" + " * @return ArrayObject|StringObject\n" + " */\n" + " public function get(mixed $key)\n" + " {\n" + " if (!$this->exists($key)) {\n" + " throw new ArrayKeyNotExists($key);\n" + " }\n" + " return static::detectType($this->array[$key]);\n" + " }\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" + " {\n" + " $key = array_key_last($this->array);\n" + " if ($key === null) {\n" + " return null;\n" + " }\n" + " return $this->get($key);\n" + " }\n" + "\n" + " /**\n" + " * @return int|string|null\n" + " */\n" + " public function firstKey()\n" + " {\n" + " return array_key_first($this->array);\n" + " }\n" + "\n" + " /**\n" + " * @return int|string|null\n" + " */\n" + " public function lastKey()\n" + " {\n" + " return array_key_last($this->array);\n" + " }\n" + "\n" + " /**\n" + " * @return mixed\n" + " */\n" + " public function first()\n" + " {\n" + " $key = array_key_first($this->array);\n" + " if ($key === null) {\n" + " return null;\n" + " }\n" + " return $this->get($key);\n" + " }\n" + "\n" + " /**\n" + " * @return $this\n" + " */\n" + " public function set(mixed $key, mixed $value): self\n" + " {\n" + " $this->array[$key] = $value;\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * @return $this\n" + " */\n" + " public function delete(mixed $key): self\n" + " {\n" + " unset($this->array[$key]);\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * @return $this\n" + " */\n" + " public function remove(mixed $value, bool $strict = true, bool $loop = false): self\n" + " {\n" + " do {\n" + " $key = $this->search($value, $strict);\n" + " if ($key === false) {\n" + " break;\n" + " }\n" + " unset($this->array[$key]);\n" + " } while ($loop);\n" + "\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * @return $this\n" + " */\n" + " public function clear(): self\n" + " {\n" + " $this->array = [];\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * @return mixed|null\n" + " */\n" + " #[\\ReturnTypeWillChange]\n" + " public function offsetGet(mixed $key)\n" + " {\n" + " if (!array_key_exists($key, $this->array)) {\n" + " return null;\n" + " }\n" + " return $this->array[$key];\n" + " }\n" + "\n" + " public function offsetSet(mixed $key, mixed $value): void\n" + " {\n" + " $this->array[$key] = $value;\n" + " }\n" + "\n" + " public function offsetUnset(mixed $key): void\n" + " {\n" + " unset($this->array[$key]);\n" + " }\n" + "\n" + " /**\n" + " * @return bool\n" + " */\n" + " #[\\ReturnTypeWillChange]\n" + " public function offsetExists(mixed $key)\n" + " {\n" + " return isset($this->array[$key]);\n" + " }\n" + "\n" + " public function exists(mixed $key): bool\n" + " {\n" + " return array_key_exists($key, $this->array);\n" + " }\n" + "\n" + " public function contains(mixed $value, bool $strict = true): bool\n" + " {\n" + " return in_array($value, $this->array, $strict);\n" + " }\n" + "\n" + " /**\n" + " * @return mixed\n" + " */\n" + " public function indexOf(mixed $value, bool $strict = true)\n" + " {\n" + " return $this->search($value, $strict);\n" + " }\n" + "\n" + " /**\n" + " * @return mixed\n" + " */\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" + " $currentValue = current($array);\n" + " if ($currentValue == $value) {\n" + " if ($strict && $currentValue !== $value) {\n" + " continue;\n" + " }\n" + " break;\n" + " }\n" + " }\n" + " return $currentKey;\n" + " }\n" + "\n" + " /**\n" + " * @return mixed\n" + " */\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 self::detectStringType(implode($glue, $this->array));\n" + " }\n" + "\n" + " public function serialize(): string\n" + " {\n" + " return serialize($this->array);\n" + " }\n" + "\n" + " public function unserialize(string|\\Stringable|StringObject $string): self\n" + " {\n" + " $this->array = (array) unserialize((string) $string);\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * @return float|int\n" + " */\n" + " public function sum()\n" + " {\n" + " return array_sum($this->array);\n" + " }\n" + "\n" + " /**\n" + " * @return float|int\n" + " */\n" + " public function product()\n" + " {\n" + " return array_product($this->array);\n" + " }\n" + "\n" + " /**\n" + " * @return int\n" + " */\n" + " public function push(mixed $value)\n" + " {\n" + " return $this->pushBack($value);\n" + " }\n" + "\n" + " /**\n" + " * @return int\n" + " */\n" + " public function pushFront(mixed $value)\n" + " {\n" + " return array_unshift($this->array, $value);\n" + " }\n" + "\n" + " public function append(...$values): ArrayObject\n" + " {\n" + " array_push($this->array, ...$values);\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * @return int\n" + " */\n" + " public function pushBack(mixed $value)\n" + " {\n" + " return array_push($this->array, $value);\n" + " }\n" + "\n" + " /**\n" + " * @return $this\n" + " */\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" + " }\n" + " array_splice($this->array, $offset, 0, $value);\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * @return mixed\n" + " */\n" + " public function pop()\n" + " {\n" + " return $this->popBack();\n" + " }\n" + "\n" + " /**\n" + " * @return mixed\n" + " */\n" + " public function popFront()\n" + " {\n" + " return array_shift($this->array);\n" + " }\n" + "\n" + " /**\n" + " * @return mixed\n" + " */\n" + " public function popBack()\n" + " {\n" + " return array_pop($this->array);\n" + " }\n" + "\n" + " public function slice(int $offset, ?int $length = null, bool $preserve_keys = false): static\n" + " {\n" + " return new static(array_slice($this->array, $offset, $length, $preserve_keys)); // @phpstan-ignore new.static\n" + " }\n" + "\n" + " /**\n" + " * @return ArrayObject|mixed|StringObject\n" + " */\n" + " public function randomGet()\n" + " {\n" + " return static::detectType($this->array[array_rand($this->array, 1)]);\n" + " }\n" + "\n" + " public function each(callable $fn): self\n" + " {\n" + " array_walk($this->array, $fn);\n" + "\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * @param array $args\n" + " */\n" + " public function map(callable $fn, ...$args): static\n" + " {\n" + " return new static(array_map($fn, $this->array, ...$args)); // @phpstan-ignore new.static\n" + " }\n" + "\n" + " /**\n" + " * @param null $initial\n" + " * @return mixed\n" + " */\n" + " public function reduce(callable $fn, $initial = null)\n" + " {\n" + " return array_reduce($this->array, $fn, $initial);\n" + " }\n" + "\n" + " /**\n" + " * @param array $args\n" + " */\n" + " public function keys(...$args): static\n" + " {\n" + " return new static(array_keys($this->array, ...$args)); // @phpstan-ignore new.static\n" + " }\n" + "\n" + " public function values(): static\n" + " {\n" + " return new static(array_values($this->array)); // @phpstan-ignore new.static\n" + " }\n" + "\n" + " public function column(mixed $column_key, mixed $index = null): static\n" + " {\n" + " return new static(array_column($this->array, $column_key, $index)); // @phpstan-ignore new.static\n" + " }\n" + "\n" + " public function unique(int $sort_flags = SORT_STRING): static\n" + " {\n" + " return new static(array_unique($this->array, $sort_flags)); // @phpstan-ignore new.static\n" + " }\n" + "\n" + " public function reverse(bool $preserve_keys = false): static\n" + " {\n" + " return new static(array_reverse($this->array, $preserve_keys)); // @phpstan-ignore new.static\n" + " }\n" + "\n" + " public function chunk(int $size, bool $preserve_keys = false): static\n" + " {\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" + " */\n" + " public function flip(): static\n" + " {\n" + " return new static(array_flip($this->array)); // @phpstan-ignore new.static\n" + " }\n" + "\n" + " public function filter(callable $fn, int $flag = 0): static\n" + " {\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" + " * | asort() | value | yes | low to high | arsort() |\n" + " * | arsort() | value | yes | high to low | asort() |\n" + " * | krsort() | key | yes | high to low | ksort() |\n" + " * | ksort() | key | yes | low to high | asort() |\n" + " * | 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" + " * | 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" + " */\n" + "\n" + " /**\n" + " * @return $this\n" + " */\n" + " public function asort(int $sort_flags = SORT_REGULAR): self\n" + " {\n" + " asort($this->array, $sort_flags);\n" + "\n" + " return $this;\n" + " }\n" + "\n" + " public function arsort(int $sort_flags = SORT_REGULAR): self\n" + " {\n" + " arsort($this->array, $sort_flags);\n" + "\n" + " return $this;\n" + " }\n" + "\n" + " public function krsort(int $sort_flags = SORT_REGULAR): self\n" + " {\n" + " krsort($this->array, $sort_flags);\n" + "\n" + " return $this;\n" + " }\n" + "\n" + " public function ksort(int $sort_flags = SORT_REGULAR): self\n" + " {\n" + " ksort($this->array, $sort_flags);\n" + "\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * @return $this\n" + " */\n" + " public function natcasesort(): self\n" + " {\n" + " if (natcasesort($this->array) !== true) { // @phpstan-ignore notIdentical.alwaysFalse\n" + " throw new \\RuntimeException('natcasesort() failed');\n" + " }\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * @return $this\n" + " */\n" + " public function natsort(): self\n" + " {\n" + " if (natsort($this->array) !== true) { // @phpstan-ignore notIdentical.alwaysFalse\n" + " throw new \\RuntimeException('natsort() failed');\n" + " }\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * @return $this\n" + " */\n" + " public function rsort(int $sort_flags = SORT_REGULAR): self\n" + " {\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" + " public function shuffle(): self\n" + " {\n" + " shuffle($this->array);\n" + "\n" + " return $this;\n" + " }\n" + "\n" + " public function sort(int $sort_flags = SORT_REGULAR): self\n" + " {\n" + " sort($this->array, $sort_flags);\n" + "\n" + " return $this;\n" + " }\n" + "\n" + " public function uasort(callable $value_compare_func): self\n" + " {\n" + " uasort($this->array, $value_compare_func);\n" + "\n" + " return $this;\n" + " }\n" + "\n" + " public function uksort(callable $value_compare_func): self\n" + " {\n" + " uksort($this->array, $value_compare_func);\n" + "\n" + " return $this;\n" + " }\n" + "\n" + " public function usort(callable $value_compare_func): self\n" + " {\n" + " usort($this->array, $value_compare_func);\n" + "\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * @return ArrayObject|mixed|StringObject\n" + " */\n" + " protected static function detectType(mixed $value)\n" + " {\n" + " if (is_string($value)) {\n" + " return static::detectStringType($value);\n" + " }\n" + " if (is_array($value)) {\n" + " return static::detectArrayType($value);\n" + " }\n" + " return $value;\n" + " }\n" + "\n" + " protected static function detectStringType(string $value): StringObject\n" + " {\n" + " return new StringObject($value);\n" + " }\n" + "\n" + " protected static function detectArrayType(array $value): static\n" + " {\n" + " return new static($value); // @phpstan-ignore new.static\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_object_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;\n" + "\n" + "class ObjectProxy\n" + "{\n" + " /** @var object */\n" + " protected $__object;\n" + "\n" + " public function __construct(object $object)\n" + " {\n" + " $this->__object = $object;\n" + " }\n" + "\n" + " public function __getObject()\n" + " {\n" + " return $this->__object;\n" + " }\n" + "\n" + " public function __get(string $name)\n" + " {\n" + " return $this->__object->{$name};\n" + " }\n" + "\n" + " public function __set(string $name, $value): void\n" + " {\n" + " $this->__object->{$name} = $value;\n" + " }\n" + "\n" + " public function __isset($name)\n" + " {\n" + " return isset($this->__object->{$name});\n" + " }\n" + "\n" + " public function __unset(string $name): void\n" + " {\n" + " unset($this->__object->{$name});\n" + " }\n" + "\n" + " public function __call(string $name, array $arguments)\n" + " {\n" + " return $this->__object->{$name}(...$arguments);\n" + " }\n" + "\n" + " public function __invoke(...$arguments)\n" + " {\n" + " /** @var mixed $object */\n" + " $object = $this->__object;\n" + " return $object(...$arguments);\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_coroutine_wait_group = + "\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" + "class WaitGroup\n" + "{\n" + " protected Channel $chan;\n" + "\n" + " protected int $count = 0;\n" + "\n" + " protected bool $waiting = false;\n" + "\n" + " public function __construct(int $delta = 0)\n" + " {\n" + " $this->chan = new Channel(1);\n" + " if ($delta > 0) {\n" + " $this->add($delta);\n" + " }\n" + " }\n" + "\n" + " public function add(int $delta = 1): void\n" + " {\n" + " if ($this->waiting) {\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" + " }\n" + " $this->count = $count;\n" + " }\n" + "\n" + " public function done(): void\n" + " {\n" + " $count = $this->count - 1;\n" + " if ($count < 0) {\n" + " throw new \\BadMethodCallException('WaitGroup misuse: negative counter');\n" + " }\n" + " $this->count = $count;\n" + " if ($count === 0 && $this->waiting) {\n" + " $this->chan->push(true);\n" + " }\n" + " }\n" + "\n" + " 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" + " }\n" + " if ($this->count > 0) {\n" + " $this->waiting = true;\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 = + "\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\\Constant;\n" + "use Swoole\\Coroutine;\n" + "use Swoole\\Coroutine\\Server\\Connection;\n" + "use Swoole\\Exception;\n" + "\n" + "class Server\n" + "{\n" + " /** @var string */\n" + " public $host = '';\n" + "\n" + " /** @var int */\n" + " public $port = 0;\n" + "\n" + " /** @var int */\n" + " public $type = AF_INET;\n" + "\n" + " /** @var int */\n" + " public $fd = -1;\n" + "\n" + " /** @var int */\n" + " public $errCode = 0;\n" + "\n" + " /** @var array */\n" + " public $setting = [];\n" + "\n" + " /** @var bool */\n" + " protected $running = false;\n" + "\n" + " /** @var callable|null */\n" + " protected $fn;\n" + "\n" + " /** @var Socket */\n" + " protected $socket;\n" + "\n" + " /**\n" + " * Server constructor.\n" + " * @throws Exception\n" + " */\n" + " public function __construct(string $host, int $port = 0, bool $ssl = false, bool $reuse_port = false)\n" + " {\n" + " $_host = swoole_string($host);\n" + " if ($_host->contains('::')) {\n" + " $this->type = AF_INET6;\n" + " } elseif ($_host->startsWith('unix:/')) {\n" + " $host = $_host->substr(5)->__toString();\n" + " $this->type = AF_UNIX;\n" + " } else {\n" + " $this->type = AF_INET;\n" + " }\n" + " $this->host = $host;\n" + "\n" + " $socket = new Socket($this->type, SOCK_STREAM, 0);\n" + " if ($reuse_port and defined('SO_REUSEPORT')) {\n" + " $socket->setOption(SOL_SOCKET, SO_REUSEPORT, true);\n" + " }\n" + " if (!$socket->bind($this->host, $port)) {\n" + " throw new Exception(\"bind({$this->host}:{$port}) failed\", $socket->errCode);\n" + " }\n" + " 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->setting['open_ssl'] = $ssl;\n" + " }\n" + "\n" + " public function set(array $setting): void\n" + " {\n" + " $this->setting = array_merge($this->setting, $setting);\n" + " }\n" + "\n" + " public function handle(callable $fn): void\n" + " {\n" + " $this->fn = $fn;\n" + " }\n" + "\n" + " public function shutdown(): bool\n" + " {\n" + " $this->running = false;\n" + " return $this->socket->cancel();\n" + " }\n" + "\n" + " public function start(): bool\n" + " {\n" + " $this->running = true;\n" + " if ($this->fn === null) {\n" + " $this->errCode = SOCKET_EINVAL;\n" + " return false;\n" + " }\n" + " $socket = $this->socket;\n" + " if (!$socket->setProtocol($this->setting)) {\n" + " $this->errCode = SOCKET_EINVAL;\n" + " return false;\n" + " }\n" + "\n" + " while ($this->running) { // @phpstan-ignore while.alwaysTrue\n" + " $conn = null;\n" + " /** @var Socket $conn */\n" + " $conn = $socket->accept();\n" + " if ($conn) { // @phpstan-ignore if.alwaysTrue\n" + " $conn->setProtocol($this->setting);\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" + " return;\n" + " }\n" + " $fn($connection);\n" + " };\n" + " $arguments = [$this->fn, new Connection($conn)];\n" + " } else {\n" + " $fn = $this->fn;\n" + " $arguments = [new Connection($conn)];\n" + " }\n" + " if (Coroutine::create($fn, ...$arguments) < 0) {\n" + " goto _wait;\n" + " }\n" + " } else {\n" + " if ($socket->errCode == SOCKET_EMFILE or $socket->errCode == SOCKET_ENFILE) {\n" + " _wait:\n" + " Coroutine::sleep(1);\n" + " continue;\n" + " }\n" + " if ($socket->errCode == SOCKET_ETIMEDOUT) {\n" + " continue;\n" + " }\n" + " if ($socket->errCode == SOCKET_ECANCELED) {\n" + " break;\n" + " }\n" + " trigger_error(\"accept failed, Error: {$socket->errMsg}[{$socket->errCode}]\", E_USER_WARNING);\n" + " break;\n" + " }\n" + " }\n" + "\n" + " return true; // @phpstan-ignore deadCode.unreachable\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_coroutine_server_connection = + "\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\\Server;\n" + "\n" + "use Swoole\\Coroutine\\Socket;\n" + "\n" + "class Connection\n" + "{\n" + " protected $socket;\n" + "\n" + " public function __construct(Socket $conn)\n" + " {\n" + " $this->socket = $conn;\n" + " }\n" + "\n" + " public function recv(float $timeout = 0)\n" + " {\n" + " return $this->socket->recvPacket($timeout);\n" + " }\n" + "\n" + " public function send(string $data)\n" + " {\n" + " return $this->socket->sendAll($data);\n" + " }\n" + "\n" + " public function close(): bool\n" + " {\n" + " return $this->socket->close();\n" + " }\n" + "\n" + " public function exportSocket(): Socket\n" + " {\n" + " return $this->socket;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_coroutine_barrier = + "\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" + "use Swoole\\Exception;\n" + "use Swoole\\Timer;\n" + "\n" + "class Barrier\n" + "{\n" + " private int $cid = -1;\n" + "\n" + " private $timer = -1;\n" + "\n" + " private static array $cancel_list = [];\n" + "\n" + " public function __destruct()\n" + " {\n" + " if ($this->timer !== -1) {\n" + " Timer::clear($this->timer);\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" + " Coroutine::resume($this->cid);\n" + " } else {\n" + " self::$cancel_list[$this->cid] = true;\n" + " }\n" + " }\n" + "\n" + " public static function make(): self\n" + " {\n" + " return new self();\n" + " }\n" + "\n" + " /**\n" + " * @param-out null $barrier\n" + " */\n" + " public static function wait(Barrier &$barrier, float $timeout = -1): void\n" + " {\n" + " if ($barrier->cid !== -1) {\n" + " throw new Exception('The barrier is waiting, cannot wait again.');\n" + " }\n" + " $cid = Coroutine::getCid();\n" + " $barrier->cid = $cid;\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" + " });\n" + " }\n" + " $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 = + "\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\\Channel;\n" + "\n" + "class ConnectionPool\n" + "{\n" + " public const DEFAULT_SIZE = 64;\n" + "\n" + " protected ?Channel $pool;\n" + "\n" + " /** @var callable */\n" + " protected $constructor;\n" + "\n" + " protected int $size;\n" + "\n" + " protected int $num = 0;\n" + "\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->constructor = $constructor;\n" + " }\n" + "\n" + " public function fill(): void\n" + " {\n" + " while ($this->size > $this->num) {\n" + " $this->make();\n" + " }\n" + " }\n" + "\n" + " /**\n" + " * Get a connection from the pool.\n" + " *\n" + " * @param float $timeout > 0 means waiting for the specified number of seconds. other means no waiting.\n" + " * @return mixed|false Returns a connection object from the pool, or false if the pool is full and the timeout is reached.\n" + " */\n" + " public function get(float $timeout = -1)\n" + " {\n" + " if ($this->pool === null) {\n" + " throw new \\RuntimeException('Pool has been closed');\n" + " }\n" + " if ($this->pool->isEmpty() && $this->num < $this->size) {\n" + " $this->make();\n" + " }\n" + " return $this->pool->pop($timeout);\n" + " }\n" + "\n" + " public function put($connection): void\n" + " {\n" + " if ($this->pool === null) {\n" + " return;\n" + " }\n" + " if ($connection !== null) {\n" + " $this->pool->push($connection);\n" + " } else {\n" + " /* connection broken */\n" + " $this->num -= 1;\n" + " $this->make();\n" + " }\n" + " }\n" + "\n" + " public function close(): void\n" + " {\n" + " $this->pool->close();\n" + " $this->pool = null;\n" + " $this->num = 0;\n" + " }\n" + "\n" + " protected function make(): void\n" + " {\n" + " $this->num++;\n" + " try {\n" + " if ($this->proxy) {\n" + " $connection = new $this->proxy($this->constructor);\n" + " } else {\n" + " $constructor = $this->constructor;\n" + " $connection = $constructor();\n" + " }\n" + " } catch (\\Throwable $throwable) {\n" + " $this->num--;\n" + " throw $throwable;\n" + " }\n" + " $this->put($connection);\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_database_object_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" + "class ObjectProxy extends \\Swoole\\ObjectProxy\n" + "{\n" + " final public function __clone(): void\n" + " {\n" + " throw new \\Error('Trying to clone an uncloneable database proxy object');\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_database_mysqli_config = + "\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 MysqliConfig\n" + "{\n" + " protected string $host = '127.0.0.1';\n" + "\n" + " protected int $port = 3306;\n" + "\n" + " protected ?string $unixSocket;\n" + "\n" + " protected string $dbname = 'test';\n" + "\n" + " protected string $charset = 'utf8mb4';\n" + "\n" + " protected string $username = 'root';\n" + "\n" + " protected string $password = 'root';\n" + "\n" + " protected array $options = [];\n" + "\n" + " public function getHost(): string\n" + " {\n" + " return $this->host;\n" + " }\n" + "\n" + " public function withHost(string $host): self\n" + " {\n" + " $this->host = $host;\n" + " return $this;\n" + " }\n" + "\n" + " public function getPort(): int\n" + " {\n" + " return $this->port;\n" + " }\n" + "\n" + " public function getUnixSocket(): ?string\n" + " {\n" + " return $this->unixSocket ?? null;\n" + " }\n" + "\n" + " public function withUnixSocket(?string $unixSocket): self\n" + " {\n" + " $this->unixSocket = $unixSocket;\n" + " return $this;\n" + " }\n" + "\n" + " public function withPort(int $port): self\n" + " {\n" + " $this->port = $port;\n" + " return $this;\n" + " }\n" + "\n" + " public function getDbname(): string\n" + " {\n" + " return $this->dbname;\n" + " }\n" + "\n" + " public function withDbname(string $dbname): self\n" + " {\n" + " $this->dbname = $dbname;\n" + " return $this;\n" + " }\n" + "\n" + " public function getCharset(): string\n" + " {\n" + " return $this->charset;\n" + " }\n" + "\n" + " public function withCharset(string $charset): self\n" + " {\n" + " $this->charset = $charset;\n" + " return $this;\n" + " }\n" + "\n" + " public function getUsername(): string\n" + " {\n" + " return $this->username;\n" + " }\n" + "\n" + " public function withUsername(string $username): self\n" + " {\n" + " $this->username = $username;\n" + " return $this;\n" + " }\n" + "\n" + " public function getPassword(): string\n" + " {\n" + " return $this->password;\n" + " }\n" + "\n" + " public function withPassword(string $password): self\n" + " {\n" + " $this->password = $password;\n" + " return $this;\n" + " }\n" + "\n" + " public function getOptions(): array\n" + " {\n" + " return $this->options;\n" + " }\n" + "\n" + " public function withOptions(array $options): self\n" + " {\n" + " $this->options = $options;\n" + " return $this;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_database_mysqli_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" + "namespace Swoole\\Database;\n" + "\n" + "class MysqliException extends \\Exception\n" + "{\n" + "}\n"; + +static const char* swoole_library_source_core_database_mysqli_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\\Database;\n" + "\n" + "use mysqli;\n" + "use Swoole\\ConnectionPool;\n" + "\n" + "/**\n" + " * @method \\mysqli|MysqliProxy get()\n" + " * @method void put(mysqli|MysqliProxy $connection)\n" + " */\n" + "class MysqliPool extends ConnectionPool\n" + "{\n" + " public function __construct(protected MysqliConfig $config, int $size = self::DEFAULT_SIZE)\n" + " {\n" + " parent::__construct(function () {\n" + " $mysqli = new \\mysqli();\n" + " foreach ($this->config->getOptions() as $option => $value) {\n" + " $mysqli->set_opt($option, $value);\n" + " }\n" + " $mysqli->real_connect(\n" + " $this->config->getHost(),\n" + " $this->config->getUsername(),\n" + " $this->config->getPassword(),\n" + " $this->config->getDbname(),\n" + " $this->config->getPort(),\n" + " $this->config->getUnixSocket()\n" + " );\n" + " 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" + "}\n"; + +static const char* swoole_library_source_core_database_mysqli_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" + "/**\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" + "\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 \\mysqli */\n" + " protected $__object;\n" + "\n" + " protected string $charsetContext;\n" + "\n" + " protected array $setOptContext = [];\n" + "\n" + " protected array $changeUserContext;\n" + "\n" + " /** @var callable */\n" + " protected $constructor;\n" + "\n" + " protected int $round = 0;\n" + "\n" + " public function __construct(callable $constructor)\n" + " {\n" + " parent::__construct($constructor());\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" + " /* no more chances or non-IO failures */\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" + " continue;\n" + " }\n" + " if (strcasecmp($name, 'prepare') === 0) {\n" + " $ret = new MysqliStatementProxy($ret, $arguments[0], $this);\n" + " } elseif (strcasecmp($name, 'stmt_init') === 0) {\n" + " $ret = new MysqliStatementProxy($ret, null, $this);\n" + " }\n" + " break;\n" + " }\n" + " /* @noinspection PhpUndefinedVariableInspection */\n" + " return $ret;\n" + " }\n" + "\n" + " public function getRound(): int\n" + " {\n" + " return $this->round;\n" + " }\n" + "\n" + " public function reconnect(): void\n" + " {\n" + " $constructor = $this->constructor;\n" + " parent::__construct($constructor());\n" + " $this->round++;\n" + " /* restore context */\n" + " if (!empty($this->charsetContext)) {\n" + " $this->__object->set_charset($this->charsetContext);\n" + " }\n" + " foreach ($this->setOptContext as $opt => $val) {\n" + " $this->__object->set_opt($opt, $val);\n" + " }\n" + " if (!empty($this->changeUserContext)) {\n" + " $this->__object->change_user(...$this->changeUserContext);\n" + " }\n" + " }\n" + "\n" + " public function options(int $option, $value): bool\n" + " {\n" + " $this->setOptContext[$option] = $value;\n" + " return $this->__object->options($option, $value);\n" + " }\n" + "\n" + " public function set_opt(int $option, $value): bool\n" + " {\n" + " return $this->options($option, $value);\n" + " }\n" + "\n" + " public function set_charset(string $charset): bool\n" + " {\n" + " $this->charsetContext = $charset;\n" + " return $this->__object->set_charset($charset);\n" + " }\n" + "\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" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_database_mysqli_statement_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" + "class MysqliStatementProxy extends ObjectProxy\n" + "{\n" + " public const IO_METHOD_REGEX = '/^close|execute|fetch|prepare$/i';\n" + "\n" + " /** @var \\mysqli_stmt */\n" + " protected $__object;\n" + "\n" + " protected ?string $queryString;\n" + "\n" + " protected array $attrSetContext = [];\n" + "\n" + " protected array $bindParamContext;\n" + "\n" + " protected array $bindResultContext;\n" + "\n" + " protected MysqliProxy $parent;\n" + "\n" + " protected int $parentRound;\n" + "\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->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" + " /* non-IO method */\n" + " if (!preg_match(static::IO_METHOD_REGEX, $name)) {\n" + " break;\n" + " }\n" + " /* no more chances or non-IO failures or in transaction */\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" + " $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 (!empty($this->bindParamContext)) {\n" + " $this->__object->bind_param($this->bindParamContext[0], ...$this->bindParamContext[1]);\n" + " }\n" + " if (!empty($this->bindResultContext)) {\n" + " $this->__object->bind_result($this->bindResultContext);\n" + " }\n" + " foreach ($this->attrSetContext as $attr => $value) {\n" + " $this->__object->attr_set($attr, $value);\n" + " }\n" + " continue;\n" + " }\n" + " if (strcasecmp($name, 'prepare') === 0) {\n" + " $this->queryString = $arguments[0];\n" + " }\n" + " break;\n" + " }\n" + " /* @noinspection PhpUndefinedVariableInspection */\n" + " return $ret;\n" + " }\n" + "\n" + " public function attr_set($attr, $mode): bool\n" + " {\n" + " $this->attrSetContext[$attr] = $mode;\n" + " return $this->__object->attr_set($attr, $mode);\n" + " }\n" + "\n" + " public function bind_param($types, &...$arguments): bool\n" + " {\n" + " $this->bindParamContext = [$types, $arguments];\n" + " return $this->__object->bind_param($types, ...$arguments);\n" + " }\n" + "\n" + " public function bind_result(&...$arguments): bool\n" + " {\n" + " $this->bindResultContext = $arguments;\n" + " return $this->__object->bind_result(...$arguments);\n" + " }\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" + " * 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 PDOConfig\n" + "{\n" + " public const DRIVER_MYSQL = 'mysql';\n" + "\n" + " protected string $driver = self::DRIVER_MYSQL;\n" + "\n" + " protected string $host = '127.0.0.1';\n" + "\n" + " protected int $port = 3306;\n" + "\n" + " protected ?string $unixSocket;\n" + "\n" + " protected string $dbname = 'test';\n" + "\n" + " protected string $charset = 'utf8mb4';\n" + "\n" + " protected string $username = 'root';\n" + "\n" + " protected string $password = 'root';\n" + "\n" + " protected array $options = [];\n" + "\n" + " public function getDriver(): string\n" + " {\n" + " return $this->driver;\n" + " }\n" + "\n" + " public function withDriver(string $driver): self\n" + " {\n" + " $this->driver = $driver;\n" + " return $this;\n" + " }\n" + "\n" + " public function getHost(): string\n" + " {\n" + " return $this->host;\n" + " }\n" + "\n" + " public function withHost(string $host): self\n" + " {\n" + " $this->host = $host;\n" + " return $this;\n" + " }\n" + "\n" + " public function getPort(): int\n" + " {\n" + " return $this->port;\n" + " }\n" + "\n" + " public function hasUnixSocket(): bool\n" + " {\n" + " return !empty($this->unixSocket);\n" + " }\n" + "\n" + " public function getUnixSocket(): ?string\n" + " {\n" + " return $this->unixSocket ?? null;\n" + " }\n" + "\n" + " public function withUnixSocket(?string $unixSocket): self\n" + " {\n" + " $this->unixSocket = $unixSocket;\n" + " return $this;\n" + " }\n" + "\n" + " public function withPort(int $port): self\n" + " {\n" + " $this->port = $port;\n" + " return $this;\n" + " }\n" + "\n" + " public function getDbname(): string\n" + " {\n" + " return $this->dbname;\n" + " }\n" + "\n" + " public function withDbname(string $dbname): self\n" + " {\n" + " $this->dbname = $dbname;\n" + " return $this;\n" + " }\n" + "\n" + " public function getCharset(): string\n" + " {\n" + " return $this->charset;\n" + " }\n" + "\n" + " public function withCharset(string $charset): self\n" + " {\n" + " $this->charset = $charset;\n" + " return $this;\n" + " }\n" + "\n" + " public function getUsername(): string\n" + " {\n" + " return $this->username;\n" + " }\n" + "\n" + " public function withUsername(string $username): self\n" + " {\n" + " $this->username = $username;\n" + " return $this;\n" + " }\n" + "\n" + " public function getPassword(): string\n" + " {\n" + " return $this->password;\n" + " }\n" + "\n" + " public function withPassword(string $password): self\n" + " {\n" + " $this->password = $password;\n" + " return $this;\n" + " }\n" + "\n" + " public function getOptions(): array\n" + " {\n" + " return $this->options;\n" + " }\n" + "\n" + " public function withOptions(array $options): self\n" + " {\n" + " $this->options = $options;\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * Returns the list of available drivers\n" + " *\n" + " * @return string[]\n" + " */\n" + " public static function getAvailableDrivers(): array\n" + " {\n" + " return [\n" + " self::DRIVER_MYSQL,\n" + " ];\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_database_pdo_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\\Database;\n" + "\n" + "use PDO;\n" + "use Swoole\\ConnectionPool;\n" + "\n" + "/**\n" + " * @method void put(PDO|PDOProxy $connection)\n" + " */\n" + "class PDOPool extends ConnectionPool\n" + "{\n" + " public function __construct(protected PDOConfig $config, int $size = self::DEFAULT_SIZE)\n" + " {\n" + " parent::__construct(function () {\n" + " $driver = $this->config->getDriver();\n" + " if ($driver === 'sqlite') {\n" + " return new \\PDO($this->createDSN('sqlite'));\n" + " }\n" + "\n" + " return new \\PDO($this->createDSN($driver), $this->config->getUsername(), $this->config->getPassword(), $this->config->getOptions());\n" + " }, $size, PDOProxy::class);\n" + " }\n" + "\n" + " /**\n" + " * Get a PDO connection from the pool. The PDO connection (a PDO object) is wrapped in a PDOProxy object returned.\n" + " *\n" + " * @param float $timeout > 0 means waiting for the specified number of seconds. other means no waiting.\n" + " * @return PDOProxy|false Returns a PDOProxy object from the pool, or false if the pool is full and the timeout is reached.\n" + " * {@inheritDoc}\n" + " */\n" + " public function get(float $timeout = -1)\n" + " {\n" + " /* @var \\Swoole\\Database\\PDOProxy|false $pdo */\n" + " $pdo = parent::get($timeout);\n" + " if ($pdo === false) {\n" + " return false;\n" + " }\n" + "\n" + " $pdo->reset();\n" + "\n" + " return $pdo;\n" + " }\n" + "\n" + " /**\n" + " * @purpose create DSN\n" + " * @throws \\Exception\n" + " */\n" + " private function createDSN(string $driver): string\n" + " {\n" + " switch ($driver) {\n" + " case 'mysql':\n" + " if ($this->config->hasUnixSocket()) {\n" + " $dsn = \"mysql:unix_socket={$this->config->getUnixSocket()};dbname={$this->config->getDbname()};charset={$this->config->getCharset()}\";\n" + " } else {\n" + " $dsn = \"mysql:host={$this->config->getHost()};port={$this->config->getPort()};dbname={$this->config->getDbname()};charset={$this->config->getCharset()}\";\n" + " }\n" + " break;\n" + " case 'pgsql':\n" + " $dsn = 'pgsql:host=' . ($this->config->hasUnixSocket() ? $this->config->getUnixSocket() : $this->config->getHost()) . \";port={$this->config->getPort()};dbname={$this->config->getDbname()}\";\n" + " break;\n" + " case 'oci':\n" + " $dsn = 'oci:dbname=' . ($this->config->hasUnixSocket() ? $this->config->getUnixSocket() : $this->config->getHost()) . ':' . $this->config->getPort() . '/' . $this->config->getDbname() . ';charset=' . $this->config->getCharset();\n" + " break;\n" + " case 'sqlite':\n" + " // There are three types of SQLite databases: databases on disk, databases in memory, and temporary\n" + " // databases (which are deleted when the connections are closed). It doesn't make sense to use\n" + " // connection pool for the latter two types of databases, because each connection connects to a\n" + " //different in-memory or temporary SQLite database.\n" + " if ($this->config->getDbname() === '') {\n" + " throw new \\Exception('Connection pool in Swoole does not support temporary SQLite databases.');\n" + " }\n" + " if ($this->config->getDbname() === ':memory:') {\n" + " throw new \\Exception('Connection pool in Swoole does not support creating SQLite databases in memory.');\n" + " }\n" + " $dsn = 'sqlite:' . $this->config->getDbname();\n" + " break;\n" + " default:\n" + " throw new \\Exception('Unsupported Database Driver:' . $driver);\n" + " }\n" + " return $dsn;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_database_pdo_proxy = + "\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" + "/**\n" + " * @method \\PDO __getObject()\n" + " */\n" + "class PDOProxy extends ObjectProxy\n" + "{\n" + " /** @var \\PDO */\n" + " protected $__object;\n" + "\n" + " protected array $setAttributeContext = [];\n" + "\n" + " /** @var callable */\n" + " protected $constructor;\n" + "\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_EXCEPTION);\n" + " $this->constructor = $constructor;\n" + " }\n" + "\n" + " public function __call(string $name, array $arguments)\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" + " $ret = $this->__object->{$name}(...$arguments);\n" + " } else {\n" + " throw $e;\n" + " }\n" + " }\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" + " public function getRound(): int\n" + " {\n" + " return $this->round;\n" + " }\n" + "\n" + " public function reconnect(): void\n" + " {\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" + " foreach ($this->setAttributeContext as $attribute => $value) {\n" + " $this->__object->setAttribute($attribute, $value);\n" + " }\n" + " }\n" + "\n" + " public function setAttribute(int $attribute, $value): bool\n" + " {\n" + " $this->setAttributeContext[$attribute] = $value;\n" + " return $this->__object->setAttribute($attribute, $value);\n" + " }\n" + "\n" + " public function inTransaction(): bool\n" + " {\n" + " return $this->inTransaction > 0;\n" + " }\n" + "\n" + " public function reset(): void\n" + " {\n" + " $this->inTransaction = 0;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_database_pdo_statement_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" + "/**\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" + " protected $__object;\n" + "\n" + " protected array $setAttributeContext = [];\n" + "\n" + " protected array $setFetchModeContext;\n" + "\n" + " protected array $bindParamContext = [];\n" + "\n" + " protected array $bindColumnContext = [];\n" + "\n" + " protected array $bindValueContext = [];\n" + "\n" + " protected PDOProxy $parent;\n" + "\n" + " /** @var int */\n" + " protected $parentRound;\n" + "\n" + " public function __construct(\\PDOStatement $object, PDOProxy $parent)\n" + " {\n" + " parent::__construct($object);\n" + " $this->parent = $parent;\n" + " $this->parentRound = $parent->getRound();\n" + " }\n" + "\n" + " public function __call(string $name, array $arguments)\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" + " $this->__object = $parent->prepare($this->__object->queryString);\n" + "\n" + " foreach ($this->setAttributeContext as $attribute => $value) {\n" + " $this->__object->setAttribute($attribute, $value);\n" + " }\n" + " if (!empty($this->setFetchModeContext)) {\n" + " $this->__object->setFetchMode(...$this->setFetchModeContext);\n" + " }\n" + " foreach ($this->bindParamContext as $param => $item) {\n" + " $this->__object->bindParam($param, ...$item);\n" + " }\n" + " foreach ($this->bindColumnContext as $column => $item) {\n" + " $this->__object->bindColumn($column, ...$item);\n" + " }\n" + " foreach ($this->bindValueContext as $value => $item) {\n" + " $this->__object->bindParam($value, ...$item);\n" + " }\n" + " $ret = $this->__object->{$name}(...$arguments);\n" + " } else {\n" + " throw $e;\n" + " }\n" + " }\n" + "\n" + " return $ret;\n" + " }\n" + "\n" + " public function setAttribute(int $attribute, $value): bool\n" + " {\n" + " $this->setAttributeContext[$attribute] = $value;\n" + " return $this->__object->setAttribute($attribute, $value);\n" + " }\n" + "\n" + " /**\n" + " * Set the default fetch mode for this statement.\n" + " *\n" + " * @see https://www.php.net/manual/en/pdostatement.setfetchmode.php\n" + " */\n" + " public function setFetchMode(int $mode, ...$params): bool\n" + " {\n" + " $this->setFetchModeContext = func_get_args();\n" + " return $this->__object->setFetchMode(...$this->setFetchModeContext);\n" + " }\n" + "\n" + " public function bindParam($parameter, &$variable, $data_type = \\PDO::PARAM_STR, $length = 0, $driver_options = null): bool\n" + " {\n" + " $this->bindParamContext[$parameter] = [$variable, $data_type, $length, $driver_options];\n" + " return $this->__object->bindParam($parameter, $variable, $data_type, $length, $driver_options);\n" + " }\n" + "\n" + " public function bindColumn($column, &$param, $type = null, $maxlen = null, $driverdata = null): bool\n" + " {\n" + " $this->bindColumnContext[$column] = [$param, $type, $maxlen, $driverdata];\n" + " return $this->__object->bindColumn($column, $param, $type, $maxlen, $driverdata);\n" + " }\n" + "\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" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_database_redis_config = + "\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 RedisConfig\n" + "{\n" + " protected string $host = '127.0.0.1';\n" + "\n" + " protected int $port = 6379;\n" + "\n" + " protected float $timeout = 0.0;\n" + "\n" + " protected string $reserved = '';\n" + "\n" + " protected int $retry_interval = 0;\n" + "\n" + " protected float $read_timeout = 0.0;\n" + "\n" + " protected string $auth = '';\n" + "\n" + " protected int $dbIndex = 0;\n" + "\n" + " /**\n" + " * @var array\n" + " */\n" + " protected array $options = [];\n" + "\n" + " public function getHost(): string\n" + " {\n" + " return $this->host;\n" + " }\n" + "\n" + " public function withHost(string $host): self\n" + " {\n" + " $this->host = $host;\n" + " return $this;\n" + " }\n" + "\n" + " public function getPort(): int\n" + " {\n" + " return $this->port;\n" + " }\n" + "\n" + " public function withPort(int $port): self\n" + " {\n" + " $this->port = $port;\n" + " return $this;\n" + " }\n" + "\n" + " public function getTimeout(): float\n" + " {\n" + " return $this->timeout;\n" + " }\n" + "\n" + " public function withTimeout(float $timeout): self\n" + " {\n" + " $this->timeout = $timeout;\n" + " return $this;\n" + " }\n" + "\n" + " public function getReserved(): string\n" + " {\n" + " return $this->reserved;\n" + " }\n" + "\n" + " public function withReserved(string $reserved): self\n" + " {\n" + " $this->reserved = $reserved;\n" + " return $this;\n" + " }\n" + "\n" + " public function getRetryInterval(): int\n" + " {\n" + " return $this->retry_interval;\n" + " }\n" + "\n" + " public function withRetryInterval(int $retry_interval): self\n" + " {\n" + " $this->retry_interval = $retry_interval;\n" + " return $this;\n" + " }\n" + "\n" + " public function getReadTimeout(): float\n" + " {\n" + " return $this->read_timeout;\n" + " }\n" + "\n" + " public function withReadTimeout(float $read_timeout): self\n" + " {\n" + " $this->read_timeout = $read_timeout;\n" + " return $this;\n" + " }\n" + "\n" + " public function getAuth(): string\n" + " {\n" + " return $this->auth;\n" + " }\n" + "\n" + " public function withAuth(string $auth): self\n" + " {\n" + " $this->auth = $auth;\n" + " return $this;\n" + " }\n" + "\n" + " public function getDbIndex(): int\n" + " {\n" + " return $this->dbIndex;\n" + " }\n" + "\n" + " public function withDbIndex(int $dbIndex): self\n" + " {\n" + " $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 = + "\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 Redis;\n" + "use Swoole\\ConnectionPool;\n" + "\n" + "/**\n" + " * @method \\Redis get()\n" + " * @method void put(Redis $connection)\n" + " */\n" + "class RedisPool extends ConnectionPool\n" + "{\n" + " public function __construct(protected RedisConfig $config, int $size = self::DEFAULT_SIZE)\n" + " {\n" + " parent::__construct(function () {\n" + " $redis = new \\Redis();\n" + " /* Compatible with different versions of Redis extension as much as possible */\n" + " $arguments = [\n" + " $this->config->getHost(),\n" + " $this->config->getPort(),\n" + " ];\n" + " if ($this->config->getTimeout() !== 0.0) {\n" + " $arguments[] = $this->config->getTimeout();\n" + " }\n" + " if ($this->config->getRetryInterval() !== 0) {\n" + " /* reserved should always be NULL */\n" + " $arguments[] = null;\n" + " $arguments[] = $this->config->getRetryInterval();\n" + " }\n" + " if ($this->config->getReadTimeout() !== 0.0) {\n" + " $arguments[] = $this->config->getReadTimeout();\n" + " }\n" + " $redis->connect(...$arguments);\n" + " if ($this->config->getAuth()) {\n" + " $redis->auth($this->config->getAuth());\n" + " }\n" + " 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" + "}\n"; + +static const char* swoole_library_source_core_http_status = + "\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\\Http;\n" + "\n" + "abstract class Status\n" + "{\n" + " public const CONTINUE = 100;\n" + "\n" + " public const SWITCHING_PROTOCOLS = 101;\n" + "\n" + " public const PROCESSING = 102;\n" + "\n" + " public const OK = 200;\n" + "\n" + " public const CREATED = 201;\n" + "\n" + " public const ACCEPTED = 202;\n" + "\n" + " public const NON_AUTHORITATIVE_INFORMATION = 203;\n" + "\n" + " public const NO_CONTENT = 204;\n" + "\n" + " public const RESET_CONTENT = 205;\n" + "\n" + " public const PARTIAL_CONTENT = 206;\n" + "\n" + " public const MULTI_STATUS = 207;\n" + "\n" + " public const ALREADY_REPORTED = 208;\n" + "\n" + " public const IM_USED = 226;\n" + "\n" + " public const MULTIPLE_CHOICES = 300;\n" + "\n" + " public const MOVED_PERMANENTLY = 301;\n" + "\n" + " public const FOUND = 302;\n" + "\n" + " public const SEE_OTHER = 303;\n" + "\n" + " public const NOT_MODIFIED = 304;\n" + "\n" + " public const USE_PROXY = 305;\n" + "\n" + " public const SWITCH_PROXY = 306;\n" + "\n" + " public const TEMPORARY_REDIRECT = 307;\n" + "\n" + " public const PERMANENT_REDIRECT = 308;\n" + "\n" + " public const BAD_REQUEST = 400;\n" + "\n" + " public const UNAUTHORIZED = 401;\n" + "\n" + " public const PAYMENT_REQUIRED = 402;\n" + "\n" + " public const FORBIDDEN = 403;\n" + "\n" + " public const NOT_FOUND = 404;\n" + "\n" + " public const METHOD_NOT_ALLOWED = 405;\n" + "\n" + " public const NOT_ACCEPTABLE = 406;\n" + "\n" + " public const PROXY_AUTHENTICATION_REQUIRED = 407;\n" + "\n" + " public const REQUEST_TIME_OUT = 408;\n" + "\n" + " public const CONFLICT = 409;\n" + "\n" + " public const GONE = 410;\n" + "\n" + " public const LENGTH_REQUIRED = 411;\n" + "\n" + " public const PRECONDITION_FAILED = 412;\n" + "\n" + " public const REQUEST_ENTITY_TOO_LARGE = 413;\n" + "\n" + " public const REQUEST_URI_TOO_LARGE = 414;\n" + "\n" + " public const UNSUPPORTED_MEDIA_TYPE = 415;\n" + "\n" + " public const REQUESTED_RANGE_NOT_SATISFIABLE = 416;\n" + "\n" + " public const EXPECTATION_FAILED = 417;\n" + "\n" + " public const MISDIRECTED_REQUEST = 421;\n" + "\n" + " public const UNPROCESSABLE_ENTITY = 422;\n" + "\n" + " public const LOCKED = 423;\n" + "\n" + " public const FAILED_DEPENDENCY = 424;\n" + "\n" + " public const UNORDERED_COLLECTION = 425;\n" + "\n" + " public const UPGRADE_REQUIRED = 426;\n" + "\n" + " public const PRECONDITION_REQUIRED = 428;\n" + "\n" + " public const TOO_MANY_REQUESTS = 429;\n" + "\n" + " public const REQUEST_HEADER_FIELDS_TOO_LARGE = 431;\n" + "\n" + " public const UNAVAILABLE_FOR_LEGAL_REASONS = 451;\n" + "\n" + " public const INTERNAL_SERVER_ERROR = 500;\n" + "\n" + " public const NOT_IMPLEMENTED = 501;\n" + "\n" + " public const BAD_GATEWAY = 502;\n" + "\n" + " public const SERVICE_UNAVAILABLE = 503;\n" + "\n" + " public const GATEWAY_TIME_OUT = 504;\n" + "\n" + " public const HTTP_VERSION_NOT_SUPPORTED = 505;\n" + "\n" + " public const VARIANT_ALSO_NEGOTIATES = 506;\n" + "\n" + " public const INSUFFICIENT_STORAGE = 507;\n" + "\n" + " public const LOOP_DETECTED = 508;\n" + "\n" + " public const NOT_EXTENDED = 510;\n" + "\n" + " 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::REQUESTED_RANGE_NOT_SATISFIABLE => 'Requested range not satisfiable',\n" + " self::EXPECTATION_FAILED => 'Expectation Failed',\n" + " self::MISDIRECTED_REQUEST => 'Misdirected Request',\n" + " self::UNPROCESSABLE_ENTITY => 'Unprocessable Entity',\n" + " self::LOCKED => 'Locked',\n" + " self::FAILED_DEPENDENCY => 'Failed Dependency',\n" + " self::UNORDERED_COLLECTION => 'Unordered Collection',\n" + " self::UPGRADE_REQUIRED => 'Upgrade Required',\n" + " self::PRECONDITION_REQUIRED => 'Precondition Required',\n" + " self::TOO_MANY_REQUESTS => 'Too Many Requests',\n" + " self::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::NETWORK_AUTHENTICATION_REQUIRED => 'Network Authentication Required',\n" + " ];\n" + "\n" + " public static function getReasonPhrases(): array\n" + " {\n" + " return static::$reasonPhrases;\n" + " }\n" + "\n" + " public static function getReasonPhrase(int $value): string\n" + " {\n" + " return static::$reasonPhrases[$value] ?? 'Unknown';\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_curl_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" + "namespace Swoole\\Curl;\n" + "\n" + "use Swoole;\n" + "\n" + "class Exception extends Swoole\\Exception\n" + "{\n" + "}\n"; + +static const char* swoole_library_source_core_curl_handler = + "\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, PhpDuplicateSwitchCaseBodyInspection, PhpInconsistentReturnPointsInspection */\n" + "\n" + "declare(strict_types=1);\n" + "\n" + "namespace Swoole\\Curl;\n" + "\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 implements \\Stringable\n" + "{\n" + " /**\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" + " 'download_content_length' => -1.0,\n" + " 'upload_content_length' => -1.0,\n" + " 'starttransfer_time' => 0.0,\n" + " 'redirect_time' => 0.0,\n" + " 'redirect_url' => '',\n" + " 'primary_ip' => '',\n" + " 'certinfo' => [],\n" + " 'primary_port' => 0,\n" + " 'local_ip' => '',\n" + " 'local_port' => 0,\n" + " 'http_version' => 0,\n" + " 'protocol' => 0,\n" + " 'ssl_verifyresult' => 0,\n" + " 'scheme' => '',\n" + " 'private' => '',\n" + " ];\n" + "\n" + " private $withHeaderOut = false;\n" + "\n" + " private $withFileTime = false;\n" + "\n" + " private $urlInfo;\n" + "\n" + " private $postData;\n" + "\n" + " private $infile;\n" + "\n" + " private $infileSize = PHP_INT_MAX;\n" + "\n" + " private $outputStream;\n" + "\n" + " private $proxyType;\n" + "\n" + " private $proxy;\n" + "\n" + " private $proxyPort = 1080;\n" + "\n" + " private $proxyUsername;\n" + "\n" + " private $proxyPassword;\n" + "\n" + " private $clientOptions = [];\n" + "\n" + " private $followLocation = false;\n" + "\n" + " private $autoReferer = false;\n" + "\n" + " private $maxRedirects;\n" + "\n" + " private $withHeader = false;\n" + "\n" + " private $nobody = false;\n" + "\n" + " /** @var callable|null */\n" + " private $headerFunction;\n" + "\n" + " /** @var callable|null */\n" + " private $readFunction;\n" + "\n" + " /** @var callable|null */\n" + " private $writeFunction;\n" + "\n" + " private $noProgress = true;\n" + "\n" + " /** @var callable */\n" + " private $progressFunction;\n" + "\n" + " private $returnTransfer = false;\n" + "\n" + " private $method = '';\n" + "\n" + " private $headers = [];\n" + "\n" + " private $headerMap = [];\n" + "\n" + " private $transfer;\n" + "\n" + " private $errCode = 0;\n" + "\n" + " private $errMsg = '';\n" + "\n" + " private $failOnError = false;\n" + "\n" + " private $closed = false;\n" + "\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" + " $this->setUrl($url);\n" + " }\n" + " }\n" + "\n" + " public function __toString(): string\n" + " {\n" + " $id = spl_object_id($this);\n" + " return \"Object({$id}) of type (curl)\";\n" + " }\n" + "\n" + " /* ====== Public APIs ====== */\n" + "\n" + " public function isAvailable(): bool\n" + " {\n" + " if ($this->closed) {\n" + " trigger_error('supplied resource is not a valid cURL handle resource', E_USER_WARNING);\n" + " return false;\n" + " }\n" + " return true;\n" + " }\n" + "\n" + " public function setOpt(int $opt, $value): bool\n" + " {\n" + " return $this->isAvailable() and $this->setOption($opt, $value);\n" + " }\n" + "\n" + " public function exec()\n" + " {\n" + " if (!$this->isAvailable()) {\n" + " return false;\n" + " }\n" + " return $this->execute();\n" + " }\n" + "\n" + " public function getInfo()\n" + " {\n" + " return $this->isAvailable() ? $this->info : false;\n" + " }\n" + "\n" + " public function errno(): int\n" + " {\n" + " return $this->isAvailable() ? $this->errCode : 0;\n" + " }\n" + "\n" + " public function error(): string\n" + " {\n" + " return $this->isAvailable() ? $this->errMsg : '';\n" + " }\n" + "\n" + " public function reset()\n" + " {\n" + " if (!$this->isAvailable()) {\n" + " return false;\n" + " }\n" + " foreach ((new \\ReflectionClass(self::class))->getDefaultProperties() as $name => $value) {\n" + " $this->{$name} = $value;\n" + " }\n" + " }\n" + "\n" + " public function getContent()\n" + " {\n" + " if (!$this->isAvailable()) {\n" + " return false;\n" + " }\n" + " return $this->transfer;\n" + " }\n" + "\n" + " public function close(): void\n" + " {\n" + " if (!$this->isAvailable()) {\n" + " return;\n" + " }\n" + " foreach ($this as &$property) { // @phpstan-ignore foreach.nonIterable\n" + " $property = null;\n" + " }\n" + " $this->closed = true;\n" + " }\n" + "\n" + " private function create(?array $urlInfo = null): void\n" + " {\n" + " if ($urlInfo === null) {\n" + " $urlInfo = $this->urlInfo;\n" + " }\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" + " {\n" + " if (empty($this->urlInfo['path'])) {\n" + " $url = '/';\n" + " } else {\n" + " $url = $this->urlInfo['path'];\n" + " }\n" + " if (!empty($this->urlInfo['query'])) {\n" + " $url .= '?' . $this->urlInfo['query'];\n" + " }\n" + " if (!empty($this->urlInfo['fragment'])) {\n" + " $url .= '#' . $this->urlInfo['fragment'];\n" + " }\n" + " return $url;\n" + " }\n" + "\n" + " private function setUrl(string $url, bool $setInfo = true): bool\n" + " {\n" + " if (strlen($url) === 0) {\n" + " $this->setError(CURLE_URL_MALFORMAT, 'No URL set!');\n" + " return false;\n" + " }\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" + " }\n" + " if (!$this->setUrlInfo($urlInfo)) {\n" + " return false;\n" + " }\n" + " }\n" + " $this->info['url'] = $url;\n" + " return true;\n" + " }\n" + "\n" + " private function setUrlInfo(array $urlInfo): bool\n" + " {\n" + " if (empty($urlInfo['scheme'])) {\n" + " $urlInfo['scheme'] = 'http';\n" + " }\n" + " $scheme = $urlInfo['scheme'];\n" + " if ($scheme !== 'http' and $scheme !== 'https') {\n" + " $this->setError(CURLE_UNSUPPORTED_PROTOCOL, \"Protocol \\\"{$scheme}\\\" not supported or disabled in libcurl\");\n" + " return false;\n" + " }\n" + " $host = $urlInfo['host'];\n" + " if ($this->info['primary_port'] !== 0) {\n" + " /* keep same with cURL, primary_port has the highest priority */\n" + " $urlInfo['port'] = $this->info['primary_port'];\n" + " } elseif (empty($urlInfo['port'])) {\n" + " $urlInfo['port'] = $scheme === 'https' ? 443 : 80;\n" + " } else {\n" + " $urlInfo['port'] = intval($urlInfo['port']);\n" + " }\n" + " $port = $urlInfo['port'];\n" + " if (isset($this->client)) {\n" + " $oldUrlInfo = $this->urlInfo;\n" + " if (($host !== $oldUrlInfo['host']) || ($port !== $oldUrlInfo['port']) || ($scheme !== $oldUrlInfo['scheme'])) {\n" + " /* target changed */\n" + " $this->create($urlInfo);\n" + " }\n" + " }\n" + " $this->urlInfo = $urlInfo;\n" + " return true;\n" + " }\n" + "\n" + " private function setPort(int $port): void\n" + " {\n" + " $this->info['primary_port'] = $port;\n" + " if (!isset($this->urlInfo['port']) || $this->urlInfo['port'] !== $port) {\n" + " $this->urlInfo['port'] = $port;\n" + " if (isset($this->client)) {\n" + " /* target changed */\n" + " $this->create();\n" + " }\n" + " }\n" + " }\n" + "\n" + " private function setError($code, $msg = ''): void\n" + " {\n" + " $this->errCode = $code;\n" + " $this->errMsg = $msg ?: curl_strerror($code);\n" + " }\n" + "\n" + " private function hasHeader(string $headerName): bool\n" + " {\n" + " return isset($this->headerMap[strtolower($headerName)]);\n" + " }\n" + "\n" + " private function setHeader(string $headerName, string $value): void\n" + " {\n" + " $lowerCaseHeaderName = strtolower($headerName);\n" + "\n" + " if (isset($this->headerMap[$lowerCaseHeaderName])) {\n" + " unset($this->headers[$this->headerMap[$lowerCaseHeaderName]]);\n" + " }\n" + "\n" + " if ($value !== '') {\n" + " $this->headers[$headerName] = $value;\n" + " $this->headerMap[$lowerCaseHeaderName] = $headerName;\n" + " } else {\n" + " // remove empty headers (keep same with raw cURL)\n" + " unset($this->headerMap[$lowerCaseHeaderName]);\n" + " }\n" + " }\n" + "\n" + " /**\n" + " * @throws Exception\n" + " */\n" + " private function setOption(int $opt, mixed $value): bool\n" + " {\n" + " switch ($opt) {\n" + " // case CURLOPT_STDERR:\n" + " // case CURLOPT_WRITEHEADER:\n" + " case CURLOPT_FILE:\n" + " case CURLOPT_INFILE:\n" + " if (!is_resource($value)) {\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" + " }\n" + "\n" + " switch ($opt) {\n" + " /*\n" + " * Basic\n" + " */\n" + " case CURLOPT_URL:\n" + " return $this->setUrl((string) $value);\n" + " case CURLOPT_PORT:\n" + " $this->setPort((int) $value);\n" + " break;\n" + " case CURLOPT_FORBID_REUSE:\n" + " $this->clientOptions[Constant::OPTION_KEEP_ALIVE] = !$value;\n" + " break;\n" + " case CURLOPT_RETURNTRANSFER:\n" + " $this->returnTransfer = (bool) $value;\n" + " $this->transfer = '';\n" + " break;\n" + " case CURLOPT_ENCODING:\n" + " if (empty($value)) {\n" + " if (defined('SWOOLE_HAVE_ZLIB')) {\n" + " $value = 'gzip, deflate';\n" + " }\n" + " if (defined('SWOOLE_HAVE_BROTLI')) {\n" + " if (!empty($value)) {\n" + " $value = 'br, ' . $value;\n" + " } else {\n" + " $value = 'br';\n" + " }\n" + " }\n" + " if (empty($value)) {\n" + " break;\n" + " }\n" + " }\n" + " $this->setHeader('Accept-Encoding', $value);\n" + " break;\n" + " case CURLOPT_PROXYTYPE:\n" + " if ($value !== CURLPROXY_HTTP and $value !== CURLPROXY_SOCKS5) {\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" + " case CURLOPT_PROXY:\n" + " $this->proxy = $value;\n" + " break;\n" + " case CURLOPT_PROXYPORT:\n" + " $this->proxyPort = $value;\n" + " break;\n" + " case CURLOPT_PROXYUSERNAME:\n" + " $this->proxyUsername = $value;\n" + " break;\n" + " case CURLOPT_PROXYPASSWORD:\n" + " $this->proxyPassword = $value;\n" + " break;\n" + " case CURLOPT_PROXYUSERPWD:\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 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" + " case CURLOPT_PRIVATE:\n" + " $this->info['private'] = $value;\n" + " break;\n" + " /*\n" + " * Ignore options\n" + " */\n" + " case CURLOPT_VERBOSE:\n" + " // trigger_error('swoole_curl_setopt(): CURLOPT_VERBOSE is not supported', E_USER_WARNING);\n" + " case CURLOPT_SSLVERSION:\n" + " case CURLOPT_NOSIGNAL:\n" + " case CURLOPT_FRESH_CONNECT:\n" + " case CURLOPT_DNS_USE_GLOBAL_CACHE:\n" + " case CURLOPT_DNS_CACHE_TIMEOUT:\n" + " case CURLOPT_STDERR:\n" + " case CURLOPT_WRITEHEADER:\n" + " case CURLOPT_BUFFERSIZE:\n" + " case CURLOPT_SSLCERTTYPE:\n" + " case CURLOPT_SSLKEYTYPE:\n" + " case CURLOPT_NOPROXY:\n" + " case CURLOPT_CERTINFO:\n" + " case CURLOPT_HEADEROPT:\n" + " case CURLOPT_PROXYHEADER:\n" + " case CURLOPT_HTTPPROXYTUNNEL:\n" + " break;\n" + " /*\n" + " * SSL\n" + " */\n" + " case CURLOPT_SSL_VERIFYHOST:\n" + " break;\n" + " case CURLOPT_SSL_VERIFYPEER:\n" + " $this->clientOptions[Constant::OPTION_SSL_VERIFY_PEER] = $value;\n" + " break;\n" + " case CURLOPT_SSLCERT:\n" + " $this->clientOptions[Constant::OPTION_SSL_CERT_FILE] = $value;\n" + " break;\n" + " case CURLOPT_SSLKEY:\n" + " $this->clientOptions[Constant::OPTION_SSL_KEY_FILE] = $value;\n" + " break;\n" + " case CURLOPT_CAINFO:\n" + " $this->clientOptions[Constant::OPTION_SSL_CAFILE] = $value;\n" + " break;\n" + " case CURLOPT_CAPATH:\n" + " $this->clientOptions[Constant::OPTION_SSL_CAPATH] = $value;\n" + " break;\n" + " case CURLOPT_KEYPASSWD:\n" + " case CURLOPT_SSLCERTPASSWD:\n" + " case CURLOPT_SSLKEYPASSWD:\n" + " $this->clientOptions[Constant::OPTION_SSL_PASSPHRASE] = $value;\n" + " break;\n" + " /*\n" + " * Http POST\n" + " */\n" + " case CURLOPT_POST:\n" + " $this->method = 'POST';\n" + " break;\n" + " case CURLOPT_POSTFIELDS:\n" + " $this->postData = $value;\n" + " if (!$this->method) {\n" + " $this->method = 'POST';\n" + " }\n" + " break;\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" + " 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" + " $headerValue = trim($header[1] ?? '');\n" + " $this->setHeader($headerName, $headerValue);\n" + " }\n" + " break;\n" + " case CURLOPT_REFERER:\n" + " $this->setHeader('Referer', $value);\n" + " break;\n" + " case CURLINFO_HEADER_OUT:\n" + " $this->withHeaderOut = boolval($value);\n" + " break;\n" + " case CURLOPT_FILETIME:\n" + " $this->withFileTime = boolval($value);\n" + " break;\n" + " case CURLOPT_USERAGENT:\n" + " $this->setHeader('User-Agent', $value);\n" + " break;\n" + " case CURLOPT_CUSTOMREQUEST:\n" + " $this->method = (string) $value;\n" + " break;\n" + " case CURLOPT_PROTOCOLS:\n" + " if (($value & ~(CURLPROTO_HTTP | CURLPROTO_HTTPS)) != 0) {\n" + " throw new CurlException(\"swoole_curl_setopt(): CURLOPT_PROTOCOLS[{$value}] is not supported\");\n" + " }\n" + " break;\n" + " case CURLOPT_REDIR_PROTOCOLS:\n" + " if (($value & ~(CURLPROTO_HTTP | CURLPROTO_HTTPS)) != 0) {\n" + " throw new CurlException(\"swoole_curl_setopt(): CURLOPT_REDIR_PROTOCOLS[{$value}] is not supported\");\n" + " }\n" + " break;\n" + " case CURLOPT_HTTP_VERSION:\n" + " if ($value != CURL_HTTP_VERSION_1_1) {\n" + " trigger_error(\"swoole_curl_setopt(): CURLOPT_HTTP_VERSION[{$value}] is not supported\", E_USER_WARNING);\n" + " return false;\n" + " }\n" + " break;\n" + " case CURLOPT_FAILONERROR:\n" + " $this->failOnError = $value;\n" + " break;\n" + " /*\n" + " * Http Cookie\n" + " */\n" + " case CURLOPT_COOKIE:\n" + " $this->setHeader('Cookie', $value);\n" + " break;\n" + " case CURLOPT_COOKIEJAR:\n" + " $this->cookieJar = (string) $value;\n" + " break;\n" + " case CURLOPT_COOKIEFILE:\n" + " if (is_file((string) $value)) {\n" + " $this->setHeader('Cookie', file_get_contents($value));\n" + " }\n" + " break;\n" + " case CURLOPT_CONNECTTIMEOUT:\n" + " $this->clientOptions[Constant::OPTION_CONNECT_TIMEOUT] = $value;\n" + " break;\n" + " case CURLOPT_CONNECTTIMEOUT_MS:\n" + " $this->clientOptions[Constant::OPTION_CONNECT_TIMEOUT] = $value / 1000;\n" + " break;\n" + " case CURLOPT_TIMEOUT:\n" + " $this->clientOptions[Constant::OPTION_TIMEOUT] = $value;\n" + " break;\n" + " case CURLOPT_TIMEOUT_MS:\n" + " $this->clientOptions[Constant::OPTION_TIMEOUT] = $value / 1000;\n" + " break;\n" + " case CURLOPT_FILE:\n" + " $this->outputStream = $value;\n" + " break;\n" + " case CURLOPT_HEADER:\n" + " $this->withHeader = $value;\n" + " break;\n" + " case CURLOPT_HEADERFUNCTION:\n" + " $this->headerFunction = $value;\n" + " break;\n" + " case CURLOPT_READFUNCTION:\n" + " $this->readFunction = $value;\n" + " break;\n" + " 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" + " case CURLOPT_HTTPAUTH:\n" + " if (!($value & CURLAUTH_BASIC)) {\n" + " trigger_error(\"swoole_curl_setopt(): CURLOPT_HTTPAUTH[{$value}] is not supported\", E_USER_WARNING);\n" + " return false;\n" + " }\n" + " break;\n" + " case CURLOPT_USERPWD:\n" + " $this->setHeader('Authorization', 'Basic ' . base64_encode($value));\n" + " break;\n" + " case CURLOPT_FOLLOWLOCATION:\n" + " $this->followLocation = $value;\n" + " break;\n" + " case CURLOPT_AUTOREFERER:\n" + " $this->autoReferer = $value;\n" + " break;\n" + " case CURLOPT_MAXREDIRS:\n" + " $this->maxRedirects = $value;\n" + " break;\n" + " case CURLOPT_PUT:\n" + " case CURLOPT_UPLOAD:\n" + " /* after libcurl 7.12, CURLOPT_PUT is replaced by CURLOPT_UPLOAD */\n" + " $this->method = 'PUT';\n" + " break;\n" + " case CURLOPT_INFILE:\n" + " $this->infile = $value;\n" + " break;\n" + " case CURLOPT_INFILESIZE:\n" + " $this->infileSize = $value;\n" + " break;\n" + " case CURLOPT_HTTPGET:\n" + " /* Since GET is the default, this is only necessary if the request method has been changed. */\n" + " $this->method = 'GET';\n" + " break;\n" + " default:\n" + " throw new CurlException(\"swoole_curl_setopt(): option[{$opt}] is not supported\");\n" + " }\n" + " return true;\n" + " }\n" + "\n" + " private function execute()\n" + " {\n" + " $this->info['redirect_count'] = $this->info['starttransfer_time'] = 0;\n" + " $this->info['redirect_url'] = '';\n" + " $timeBegin = microtime(true);\n" + " /*\n" + " * Socket\n" + " */\n" + " if (!$this->urlInfo) {\n" + " $this->setError(CURLE_URL_MALFORMAT, 'No URL set or URL using bad/illegal format');\n" + " return false;\n" + " }\n" + " if (!isset($this->client)) {\n" + " $this->create();\n" + " }\n" + " while (true) {\n" + " $client = $this->client;\n" + " /*\n" + " * Http Proxy\n" + " */\n" + " if ($this->proxy) {\n" + " $parse = parse_url($this->proxy);\n" + " $proxy = $parse['host'] ?? $parse['path'];\n" + " $proxyPort = $parse['port'] ?? $this->proxyPort;\n" + " $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 = 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" + " $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" + " */\n" + " $client->set(\n" + " $this->clientOptions +\n" + " ($proxyOptions ?? [])\n" + " );\n" + " /*\n" + " * Method\n" + " */\n" + " if ($this->method) {\n" + " $client->setMethod($this->method);\n" + " }\n" + " /*\n" + " * Data\n" + " */\n" + " if ($this->infile) {\n" + " // Infile\n" + " // Notice: we make its priority higher than postData but raw cURL will send both of them\n" + " $data = '';\n" + " while (true) {\n" + " $nLength = $this->infileSize - strlen($data);\n" + " if ($nLength === 0) {\n" + " break;\n" + " }\n" + " if (feof($this->infile)) {\n" + " break;\n" + " }\n" + " $data .= fread($this->infile, $nLength);\n" + " }\n" + " $client->setData($data);\n" + " // Notice: although we reset it, raw cURL never do this\n" + " $this->infile = null;\n" + " $this->infileSize = PHP_INT_MAX;\n" + " } else {\n" + " // POST data\n" + " if ($this->postData) {\n" + " if (is_string($this->postData)) {\n" + " if (!$this->hasHeader('content-type')) {\n" + " $this->setHeader('Content-Type', 'application/x-www-form-urlencoded');\n" + " }\n" + " } elseif (is_array($this->postData)) {\n" + " foreach ($this->postData as $k => $v) {\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" + " }\n" + " /*\n" + " * Headers\n" + " */\n" + " // Notice: setHeaders must be placed last, because headers may be changed by other parts\n" + " // As much as possible to ensure that Host is the first header.\n" + " // See: http://tools.ietf.org/html/rfc7230#section-5.4\n" + " $client->setHeaders($this->headers);\n" + " /**\n" + " * Execute.\n" + " */\n" + " $executeResult = $client->execute($this->getUrl());\n" + " if (!$executeResult) {\n" + " $errCode = $client->errCode;\n" + " if ($errCode == SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED or $errCode == SWOOLE_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT) {\n" + " $this->setError(CURLE_COULDNT_RESOLVE_HOST, 'Could not resolve host: ' . $client->host);\n" + " } else {\n" + " $this->setError($errCode, $client->errMsg);\n" + " }\n" + " $this->info['total_time'] = microtime(true) - $timeBegin;\n" + " return false;\n" + " }\n" + " if ($client->statusCode >= 300 and $client->statusCode < 400 and isset($client->headers['location'])) {\n" + " $redirectParsedUrl = $this->getRedirectUrl($client->headers['location']);\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" + " }\n" + " // force GET\n" + " if (in_array($client->statusCode, [Status::MOVED_PERMANENTLY, Status::FOUND, Status::SEE_OTHER])) {\n" + " $this->method = 'GET';\n" + " }\n" + " if ($this->autoReferer) {\n" + " $this->setHeader('Referer', $this->info['url']);\n" + " }\n" + " $this->setUrl($redirectUrl, false);\n" + " $this->setUrlInfo($redirectParsedUrl);\n" + " $this->info['redirect_count']++;\n" + " } else {\n" + " $this->info['redirect_url'] = $redirectUrl;\n" + " break;\n" + " }\n" + " } elseif ($this->failOnError && $client->statusCode >= 400) {\n" + " $this->setError(CURLE_HTTP_RETURNED_ERROR, \"The requested URL returned error: {$client->statusCode} \" . Status::getReasonPhrase($client->statusCode));\n" + " return false;\n" + " } else {\n" + " break;\n" + " }\n" + " }\n" + " $this->info['total_time'] = microtime(true) - $timeBegin;\n" + " $this->info['http_code'] = $client->statusCode;\n" + " $this->info['content_type'] = $client->headers['content-type'] ?? '';\n" + " $this->info['size_download'] = $this->info['download_content_length'] = strlen($client->body);\n" + " $this->info['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" + " if ($client->statusCode > 0) {\n" + " $row = \"HTTP/1.1 {$client->statusCode} \" . Status::getReasonPhrase($client->statusCode) . \"\\r\\n\";\n" + " if ($cb) {\n" + " $cb($this, $row);\n" + " }\n" + " $headerContent .= $row;\n" + " }\n" + " foreach ($client->headers as $k => $v) {\n" + " $row = \"{$k}: {$v}\\r\\n\";\n" + " if ($cb) {\n" + " $cb($this, $row);\n" + " }\n" + " $headerContent .= $row;\n" + " }\n" + " $headerContent .= \"\\r\\n\";\n" + " $this->info['header_size'] = strlen($headerContent);\n" + " if ($cb) {\n" + " $cb($this, '');\n" + " }\n" + " } else {\n" + " $this->info['header_size'] = 0;\n" + " }\n" + "\n" + " if ($client->body and $this->readFunction) {\n" + " $cb = $this->readFunction;\n" + " $cb($this, $this->outputStream, strlen($client->body));\n" + " }\n" + "\n" + " if ($this->withHeader) {\n" + " $transfer = $headerContent . $client->body;\n" + " } else {\n" + " $transfer = $client->body;\n" + " }\n" + "\n" + " if ($this->withHeaderOut) {\n" + " $headerOutContent = $client->getHeaderOut();\n" + " $this->info['request_header'] = $headerOutContent ? $headerOutContent . \"\\r\\n\\r\\n\" : '';\n" + " }\n" + " if ($this->withFileTime) {\n" + " if (isset($client->headers['last-modified'])) {\n" + " $this->info['filetime'] = strtotime($client->headers['last-modified']);\n" + " } else {\n" + " $this->info['filetime'] = -1;\n" + " }\n" + " }\n" + "\n" + " if (!empty($this->cookieJar)) {\n" + " if ($this->cookieJar === '-') {\n" + " foreach ((array) $client->set_cookie_headers as $cookie) {\n" + " echo $cookie . PHP_EOL;\n" + " }\n" + " } else {\n" + " $cookies = '';\n" + " foreach ((array) $client->set_cookie_headers as $cookie) {\n" + " $cookies .= \"{$cookie};\";\n" + " }\n" + " file_put_contents($this->cookieJar, $cookies);\n" + " }\n" + " }\n" + "\n" + " if ($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" + " }\n" + " call_user_func($this->writeFunction, $this, $transfer);\n" + " return true;\n" + " }\n" + "\n" + " if ($this->returnTransfer) {\n" + " return $this->transfer = $transfer;\n" + " }\n" + " if ($this->outputStream) {\n" + " return fwrite($this->outputStream, $transfer) === strlen($transfer);\n" + " }\n" + " echo $transfer;\n" + "\n" + " return true;\n" + " }\n" + "\n" + " /* ====== Redirect helper ====== */\n" + "\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" + " $fragment = isset($parsedUrl['fragment']) ? '#' . $parsedUrl['fragment'] : '';\n" + " return $scheme . $user . $pass . $host . $port . $path . $query . $fragment;\n" + " }\n" + "\n" + " private function getRedirectUrl(string $location): array\n" + " {\n" + " $uri = parse_url($location);\n" + " if (isset($uri['host'])) {\n" + " $redirectUri = $uri;\n" + " } else {\n" + " if (!isset($location[0])) {\n" + " return [];\n" + " }\n" + " $redirectUri = $this->urlInfo;\n" + " $redirectUri['query'] = '';\n" + " if ($location[0] === '/') {\n" + " $redirectUri['path'] = $location;\n" + " } else {\n" + " $path = dirname($redirectUri['path'] ?? '');\n" + " if ($path === '.') {\n" + " $path = '/';\n" + " }\n" + " if (isset($location[1]) and str_starts_with($location, './')) {\n" + " $location = substr($location, 2);\n" + " }\n" + " $redirectUri['path'] = $path . $location;\n" + " }\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" + " return $redirectUri;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_fast_cgi = + "\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" + "/**\n" + " * FastCGI constants.\n" + " */\n" + "class FastCGI\n" + "{\n" + " /**\n" + " * Number of bytes in a FCGI_Header. Future versions of the protocol\n" + " * will not reduce this number.\n" + " */\n" + " public const HEADER_LEN = 8;\n" + "\n" + " /**\n" + " * Format of FCGI_HEADER for unpacking in PHP\n" + " */\n" + " public const HEADER_FORMAT = 'Cversion/Ctype/nrequestId/ncontentLength/CpaddingLength/Creserved';\n" + "\n" + " /**\n" + " * Max content length of a record\n" + " */\n" + " public const MAX_CONTENT_LENGTH = 65535;\n" + "\n" + " /**\n" + " * Value for version component of FCGI_Header\n" + " */\n" + " public const VERSION_1 = 1;\n" + "\n" + " /**\n" + " * Values for type component of FCGI_Header\n" + " */\n" + " public const BEGIN_REQUEST = 1;\n" + "\n" + " public const ABORT_REQUEST = 2;\n" + "\n" + " public const END_REQUEST = 3;\n" + "\n" + " public const PARAMS = 4;\n" + "\n" + " public const STDIN = 5;\n" + "\n" + " public const STDOUT = 6;\n" + "\n" + " public const STDERR = 7;\n" + "\n" + " public const DATA = 8;\n" + "\n" + " public const GET_VALUES = 9;\n" + "\n" + " public const GET_VALUES_RESULT = 10;\n" + "\n" + " public const UNKNOWN_TYPE = 11;\n" + "\n" + " /**\n" + " * Value for requestId component of FCGI_Header\n" + " */\n" + " public const DEFAULT_REQUEST_ID = 1;\n" + "\n" + " /**\n" + " * Mask for flags component of FCGI_BeginRequestBody\n" + " */\n" + " public const KEEP_CONN = 1;\n" + "\n" + " /**\n" + " * Values for role component of FCGI_BeginRequestBody\n" + " */\n" + " public const RESPONDER = 1;\n" + "\n" + " public const AUTHORIZER = 2;\n" + "\n" + " public const FILTER = 3;\n" + "\n" + " /**\n" + " * Values for protocolStatus component of FCGI_EndRequestBody\n" + " */\n" + " public const REQUEST_COMPLETE = 0;\n" + "\n" + " public const CANT_MPX_CONN = 1;\n" + "\n" + " public const OVERLOADED = 2;\n" + "\n" + " public const UNKNOWN_ROLE = 3;\n" + "}\n"; + +static const char* swoole_library_source_core_fast_cgi_record = + "\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\\FastCGI;\n" + "\n" + "use Swoole\\FastCGI;\n" + "\n" + "/**\n" + " * FastCGI record.\n" + " */\n" + "class Record implements \\Stringable\n" + "{\n" + " /**\n" + " * Identifies the FastCGI protocol version.\n" + " */\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" + " protected int $type = FastCGI::UNKNOWN_TYPE;\n" + "\n" + " /**\n" + " * Identifies the FastCGI request to which the record belongs.\n" + " */\n" + " protected int $requestId = FastCGI::DEFAULT_REQUEST_ID;\n" + "\n" + " /**\n" + " * Reserved byte for future proposes\n" + " */\n" + " protected int $reserved = 0;\n" + "\n" + " /**\n" + " * The number of bytes in the contentData component of the record.\n" + " */\n" + " private int $contentLength = 0;\n" + "\n" + " /**\n" + " * The number of bytes in the paddingData component of the record.\n" + " */\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" + " private string $contentData = '';\n" + "\n" + " /**\n" + " * Padding data, between 0 and 255 bytes of data, which are ignored.\n" + " */\n" + " private string $paddingData = '';\n" + "\n" + " /**\n" + " * Returns the binary message representation of record\n" + " */\n" + " final public function __toString(): string\n" + " {\n" + " $headerPacket = pack(\n" + " 'CCnnCC',\n" + " $this->version,\n" + " $this->type,\n" + " $this->requestId,\n" + " $this->contentLength,\n" + " $this->paddingLength,\n" + " $this->reserved\n" + " );\n" + "\n" + " $payloadPacket = $this->packPayload();\n" + " $paddingPacket = pack(\"a{$this->paddingLength}\", $this->paddingData);\n" + "\n" + " return $headerPacket . $payloadPacket . $paddingPacket;\n" + " }\n" + "\n" + " /**\n" + " * Unpacks the message from the binary data buffer\n" + " */\n" + " final public static function unpack(string $binaryData): static\n" + " {\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($packet);\n" + "\n" + " $payload = substr($binaryData, FastCGI::HEADER_LEN);\n" + " self::unpackPayload($self, $payload);\n" + " if (static::class !== self::class && $self->contentLength > 0) {\n" + " static::unpackPayload($self, $payload);\n" + " }\n" + "\n" + " return $self;\n" + " }\n" + "\n" + " /**\n" + " * Sets the content data and adjusts the length fields\n" + " *\n" + " * @return static\n" + " */\n" + " public function setContentData(string $data): self\n" + " {\n" + " $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" + " } else {\n" + " $this->contentData = $data;\n" + " }\n" + " $extraLength = $this->contentLength % 8;\n" + " $this->paddingLength = $extraLength ? (8 - $extraLength) : 0;\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * Returns the context data from the record\n" + " */\n" + " public function getContentData(): string\n" + " {\n" + " return $this->contentData;\n" + " }\n" + "\n" + " /**\n" + " * Returns the version of record\n" + " */\n" + " public function getVersion(): int\n" + " {\n" + " return $this->version;\n" + " }\n" + "\n" + " /**\n" + " * Returns record type\n" + " */\n" + " public function getType(): int\n" + " {\n" + " return $this->type;\n" + " }\n" + "\n" + " /**\n" + " * Returns request ID\n" + " */\n" + " public function getRequestId(): int\n" + " {\n" + " return $this->requestId;\n" + " }\n" + "\n" + " /**\n" + " * Sets request ID\n" + " *\n" + " * There should be only one unique ID for all active requests,\n" + " * use random number or preferably resetting auto-increment.\n" + " *\n" + " * @return static\n" + " */\n" + " public function setRequestId(int $requestId): self\n" + " {\n" + " $this->requestId = $requestId;\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * Returns the size of content length\n" + " */\n" + " final public function getContentLength(): int\n" + " {\n" + " return $this->contentLength;\n" + " }\n" + "\n" + " /**\n" + " * Returns the size of padding length\n" + " */\n" + " final public function getPaddingLength(): int\n" + " {\n" + " return $this->paddingLength;\n" + " }\n" + "\n" + " /**\n" + " * Method to unpack the payload for the record.\n" + " *\n" + " * NB: Default implementation will be always called\n" + " */\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($payload);\n" + " }\n" + "\n" + " /**\n" + " * Implementation of packing the payload\n" + " */\n" + " protected function packPayload(): string\n" + " {\n" + " return pack(\"a{$this->contentLength}\", $this->contentData);\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_fast_cgi_record_params = + "\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\\FastCGI\\Record;\n" + "\n" + "use Swoole\\FastCGI;\n" + "use Swoole\\FastCGI\\Record;\n" + "\n" + "/**\n" + " * Params request record\n" + " */\n" + "class Params extends Record\n" + "{\n" + " /**\n" + " * List of params\n" + " *\n" + " * @var string[]\n" + " * @phpstan-var array\n" + " */\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" + " {\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" + " return $this->values;\n" + " }\n" + "\n" + " /**\n" + " * {@inheritdoc}\n" + " * @param static $self\n" + " */\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" + " /** @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" + "\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" + " /** @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" + " $binaryData = substr($binaryData, $keyValueLength);\n" + " $currentOffset += $keyValueLength;\n" + " } while ($currentOffset < $self->getContentLength());\n" + " }\n" + "\n" + " /**\n" + " * {@inheritdoc}\n" + " */\n" + " protected function packPayload(): string\n" + " {\n" + " $payload = '';\n" + " foreach ($this->values as $nameData => $valueData) {\n" + " if ($valueData === null) { // @phpstan-ignore identical.alwaysFalse\n" + " continue;\n" + " }\n" + " $nameLength = strlen($nameData);\n" + " $valueLength = strlen((string) $valueData);\n" + " $isLongName = $nameLength > 127;\n" + " $isLongValue = $valueLength > 127;\n" + " $formatParts = [\n" + " $isLongName ? 'N' : 'C',\n" + " $isLongValue ? 'N' : 'C',\n" + " \"a{$nameLength}\",\n" + " \"a{$valueLength}\",\n" + " ];\n" + "\n" + " $format = join('', $formatParts);\n" + "\n" + " $payload .= pack(\n" + " $format,\n" + " $isLongName ? ($nameLength | 0x80000000) : $nameLength,\n" + " $isLongValue ? ($valueLength | 0x80000000) : $valueLength,\n" + " $nameData,\n" + " $valueData\n" + " );\n" + " }\n" + "\n" + " return $payload;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_fast_cgi_record_abort_request = + "\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\\FastCGI\\Record;\n" + "\n" + "use Swoole\\FastCGI;\n" + "use Swoole\\FastCGI\\Record;\n" + "\n" + "/**\n" + " * The Web server sends a FCGI_ABORT_REQUEST record to abort a request\n" + " */\n" + "class AbortRequest extends Record\n" + "{\n" + " public function __construct(int $requestId)\n" + " {\n" + " $this->type = FastCGI::ABORT_REQUEST;\n" + " $this->setRequestId($requestId);\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_fast_cgi_record_begin_request = + "\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\\FastCGI\\Record;\n" + "\n" + "use Swoole\\FastCGI;\n" + "use Swoole\\FastCGI\\Record;\n" + "\n" + "/**\n" + " * The Web server sends a FCGI_BEGIN_REQUEST record to start a request.\n" + " */\n" + "class BeginRequest extends Record\n" + "{\n" + " /**\n" + " * The role component sets the role the Web server expects the application to play.\n" + " * The currently-defined roles are:\n" + " * FCGI_RESPONDER\n" + " * FCGI_AUTHORIZER\n" + " * FCGI_FILTER\n" + " */\n" + " protected int $role = FastCGI::UNKNOWN_ROLE;\n" + "\n" + " /**\n" + " * The flags component contains a bit that controls connection shutdown.\n" + " *\n" + " * flags & FCGI_KEEP_CONN:\n" + " * 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" + " protected int $flags;\n" + "\n" + " /**\n" + " * Reserved data, 5 bytes maximum\n" + " */\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->reserved1 = $reserved;\n" + " $this->setContentData($this->packPayload());\n" + " }\n" + "\n" + " /**\n" + " * Returns the role\n" + " *\n" + " * The role component sets the role the Web server expects the application to play.\n" + " * The currently-defined roles are:\n" + " * FCGI_RESPONDER\n" + " * FCGI_AUTHORIZER\n" + " * FCGI_FILTER\n" + " */\n" + " public function getRole(): int\n" + " {\n" + " return $this->role;\n" + " }\n" + "\n" + " /**\n" + " * Returns the flags\n" + " *\n" + " * The flags component contains a bit that controls connection shutdown.\n" + " *\n" + " * flags & FCGI_KEEP_CONN:\n" + " * 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" + " public function getFlags(): int\n" + " {\n" + " return $this->flags;\n" + " }\n" + "\n" + " /**\n" + " * {@inheritdoc}\n" + " * @param static $self\n" + " */\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($payload);\n" + " }\n" + "\n" + " /** {@inheritdoc} */\n" + " protected function packPayload(): string\n" + " {\n" + " return pack(\n" + " 'nCa5',\n" + " $this->role,\n" + " $this->flags,\n" + " $this->reserved1\n" + " );\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_fast_cgi_record_data = + "\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\\FastCGI\\Record;\n" + "\n" + "use Swoole\\FastCGI;\n" + "use Swoole\\FastCGI\\Record;\n" + "\n" + "/**\n" + " * Data binary stream\n" + " *\n" + " * FCGI_DATA is a second stream record type used to send additional data to the application.\n" + " */\n" + "class Data extends Record\n" + "{\n" + " public function __construct(string $contentData)\n" + " {\n" + " $this->type = FastCGI::DATA;\n" + " $this->setContentData($contentData);\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_fast_cgi_record_end_request = + "\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\\FastCGI\\Record;\n" + "\n" + "use Swoole\\FastCGI;\n" + "use Swoole\\FastCGI\\Record;\n" + "\n" + "/**\n" + " * The application sends a FCGI_END_REQUEST record to terminate a request, either because the application\n" + " * has processed the request or because the application has rejected the request.\n" + " */\n" + "class EndRequest extends Record\n" + "{\n" + " /**\n" + " * The appStatus component is an application-level status code. Each role documents its usage of appStatus.\n" + " */\n" + " protected int $appStatus = 0;\n" + "\n" + " /**\n" + " * The protocolStatus component is a protocol-level status code.\n" + " *\n" + " * The possible protocolStatus values are:\n" + " * FCGI_REQUEST_COMPLETE: normal end of request.\n" + " * FCGI_CANT_MPX_CONN: rejecting a new request.\n" + " * This happens when a Web server sends concurrent requests over one connection to an application that is\n" + " * designed to process one request at a time per connection.\n" + " * FCGI_OVERLOADED: rejecting a new request.\n" + " * 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" + " protected int $protocolStatus = FastCGI::REQUEST_COMPLETE;\n" + "\n" + " /**\n" + " * Reserved data, 3 bytes maximum\n" + " */\n" + " protected string $reserved1;\n" + "\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->setContentData($this->packPayload());\n" + " }\n" + "\n" + " /**\n" + " * Returns app status\n" + " *\n" + " * The appStatus component is an application-level status code. Each role documents its usage of appStatus.\n" + " */\n" + " public function getAppStatus(): int\n" + " {\n" + " return $this->appStatus;\n" + " }\n" + "\n" + " /**\n" + " * Returns the protocol status\n" + " *\n" + " * The possible protocolStatus values are:\n" + " * FCGI_REQUEST_COMPLETE: normal end of request.\n" + " * FCGI_CANT_MPX_CONN: rejecting a new request.\n" + " * This happens when a Web server sends concurrent requests over one connection to an application that is\n" + " * designed to process one request at a time per connection.\n" + " * FCGI_OVERLOADED: rejecting a new request.\n" + " * 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" + " public function getProtocolStatus(): int\n" + " {\n" + " return $this->protocolStatus;\n" + " }\n" + "\n" + " /**\n" + " * {@inheritdoc}\n" + " * @param static $self\n" + " */\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($payload);\n" + " }\n" + "\n" + " /** {@inheritdoc} */\n" + " protected function packPayload(): string\n" + " {\n" + " return pack(\n" + " 'NCa3',\n" + " $this->appStatus,\n" + " $this->protocolStatus,\n" + " $this->reserved1\n" + " );\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_fast_cgi_record_get_values = + "\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\\FastCGI\\Record;\n" + "\n" + "use Swoole\\FastCGI;\n" + "\n" + "/**\n" + " * GetValues API\n" + " *\n" + " * The Web server can query specific variables within the application.\n" + " * The server will typically perform a query on application startup in order to to automate certain aspects of\n" + " * system configuration.\n" + " *\n" + " * The application responds by sending a record {FCGI_GET_VALUES_RESULT, 0, ...} with the values supplied.\n" + " * If the application doesn't understand a variable name that was included in the query, it omits that name from\n" + " * the response.\n" + " *\n" + " * FCGI_GET_VALUES is designed to allow an open-ended set of variables.\n" + " *\n" + " * The initial set provides information to help the server perform application and connection management:\n" + " * FCGI_MAX_CONNS: The maximum number of concurrent transport connections this application will accept,\n" + " * e.g. \"1\" or \"10\".\n" + " * FCGI_MAX_REQS: The maximum number of concurrent requests this application will accept, e.g. \"1\" or \"50\".\n" + " * FCGI_MPXS_CONNS: \"0\" if this application does not multiplex connections (i.e. handle concurrent requests\n" + " * over each connection), \"1\" otherwise.\n" + " */\n" + "class GetValues extends Params\n" + "{\n" + " /**\n" + " * 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" + " {\n" + " parent::__construct(array_fill_keys($keys, ''));\n" + " $this->type = FastCGI::GET_VALUES;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_fast_cgi_record_get_values_result = + "\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\\FastCGI\\Record;\n" + "\n" + "use Swoole\\FastCGI;\n" + "\n" + "/**\n" + " * GetValues API\n" + " *\n" + " * The Web server can query specific variables within the application.\n" + " * The server will typically perform a query on application startup in order to to automate certain aspects of\n" + " * system configuration.\n" + " *\n" + " * The application responds by sending a record {FCGI_GET_VALUES_RESULT, 0, ...} with the values supplied.\n" + " * If the application doesn't understand a variable name that was included in the query, it omits that name from\n" + " * the response.\n" + " *\n" + " * FCGI_GET_VALUES is designed to allow an open-ended set of variables.\n" + " *\n" + " * The initial set provides information to help the server perform application and connection management:\n" + " * FCGI_MAX_CONNS: The maximum number of concurrent transport connections this application will accept,\n" + " * e.g. \"1\" or \"10\".\n" + " * FCGI_MAX_REQS: The maximum number of concurrent requests this application will accept, e.g. \"1\" or \"50\".\n" + " * FCGI_MPXS_CONNS: \"0\" if this application does not multiplex connections (i.e. handle concurrent requests\n" + " * over each connection), \"1\" otherwise.\n" + " */\n" + "class GetValuesResult extends Params\n" + "{\n" + " /**\n" + " * Constructs a param request\n" + " *\n" + " * @phpstan-param array $values\n" + " */\n" + " public function __construct(array $values)\n" + " {\n" + " parent::__construct($values);\n" + " $this->type = FastCGI::GET_VALUES_RESULT;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_fast_cgi_record_stdin = + "\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\\FastCGI\\Record;\n" + "\n" + "use Swoole\\FastCGI;\n" + "use Swoole\\FastCGI\\Record;\n" + "\n" + "/**\n" + " * Stdin binary stream\n" + " *\n" + " * FCGI_STDIN is a stream record type used in sending arbitrary data from the Web server to the application\n" + " */\n" + "class Stdin extends Record\n" + "{\n" + " public function __construct(string $contentData)\n" + " {\n" + " $this->type = FastCGI::STDIN;\n" + " $this->setContentData($contentData);\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_fast_cgi_record_stdout = + "\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\\FastCGI\\Record;\n" + "\n" + "use Swoole\\FastCGI;\n" + "use Swoole\\FastCGI\\Record;\n" + "\n" + "/**\n" + " * Stdout binary stream\n" + " *\n" + " * FCGI_STDOUT is a stream record for sending arbitrary data from the application to the Web server\n" + " */\n" + "class Stdout extends Record\n" + "{\n" + " public function __construct(string $contentData)\n" + " {\n" + " $this->type = FastCGI::STDOUT;\n" + " $this->setContentData($contentData);\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_fast_cgi_record_stderr = + "\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\\FastCGI\\Record;\n" + "\n" + "use Swoole\\FastCGI;\n" + "use Swoole\\FastCGI\\Record;\n" + "\n" + "/**\n" + " * Stderr binary stream\n" + " *\n" + " * FCGI_STDERR is a stream record for sending arbitrary data from the application to the Web server\n" + " */\n" + "class Stderr extends Record\n" + "{\n" + " public function __construct(string $contentData)\n" + " {\n" + " $this->type = FastCGI::STDERR;\n" + " $this->setContentData($contentData);\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_fast_cgi_record_unknown_type = + "\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\\FastCGI\\Record;\n" + "\n" + "use Swoole\\FastCGI;\n" + "use Swoole\\FastCGI\\Record;\n" + "\n" + "/**\n" + " * Record for unknown queries\n" + " *\n" + " * The set of management record types is likely to grow in future versions of this protocol.\n" + " * To provide for this evolution, the protocol includes the FCGI_UNKNOWN_TYPE management record.\n" + " * When an application receives a management record whose type T it does not understand, the application responds\n" + " * with {FCGI_UNKNOWN_TYPE, 0, {T}}.\n" + " */\n" + "class UnknownType extends Record\n" + "{\n" + " /**\n" + " * Type of the unrecognized management record.\n" + " */\n" + " protected int $type1;\n" + "\n" + " /**\n" + " * Reserved data, 7 bytes maximum\n" + " */\n" + " protected string $reserved1;\n" + "\n" + " public function __construct(int $type, string $reserved = '')\n" + " {\n" + " $this->type = FastCGI::UNKNOWN_TYPE;\n" + " $this->type1 = $type;\n" + " $this->reserved1 = $reserved;\n" + " $this->setContentData($this->packPayload());\n" + " }\n" + "\n" + " /**\n" + " * Returns the unrecognized type\n" + " */\n" + " public function getUnrecognizedType(): int\n" + " {\n" + " return $this->type1;\n" + " }\n" + "\n" + " /**\n" + " * {@inheritdoc}\n" + " * @param static $self\n" + " */\n" + " public 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{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" + " /**\n" + " * {@inheritdoc}\n" + " */\n" + " protected function packPayload(): string\n" + " {\n" + " return pack(\n" + " 'Ca7',\n" + " $this->type1,\n" + " $this->reserved1\n" + " );\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_fast_cgi_frame_parser = + "\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\\FastCGI;\n" + "\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" + " */\n" + "class FrameParser\n" + "{\n" + " /**\n" + " * Mapping of constants to the classes\n" + " *\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" + " public static function hasFrame(string $binaryBuffer): bool\n" + " {\n" + " $bufferLength = strlen($binaryBuffer);\n" + " if ($bufferLength < FastCGI::HEADER_LEN) {\n" + " return false;\n" + " }\n" + "\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" + "\n" + " return true;\n" + " }\n" + "\n" + " /**\n" + " * Parses a frame from the binary buffer\n" + " *\n" + " * @return Record One of the corresponding FastCGI record\n" + " */\n" + " public static function parseFrame(string &$binaryBuffer): Record\n" + " {\n" + " $bufferLength = strlen($binaryBuffer);\n" + " if ($bufferLength < FastCGI::HEADER_LEN) {\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" + " $recordType = $recordHeader['type'];\n" + " if (!isset(self::$classMapping[$recordType])) {\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($binaryBuffer);\n" + "\n" + " $offset = FastCGI::HEADER_LEN + $record->getContentLength() + $record->getPaddingLength();\n" + " $binaryBuffer = substr($binaryBuffer, $offset);\n" + "\n" + " return $record;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_fast_cgi_message = + "\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\\FastCGI;\n" + "\n" + "class Message\n" + "{\n" + " protected array $params = [];\n" + "\n" + " protected string $body = '';\n" + "\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): static\n" + " {\n" + " $this->params[$name] = $value;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutParam(string $name): static\n" + " {\n" + " unset($this->params[$name]);\n" + " return $this;\n" + " }\n" + "\n" + " public function getParams(): array\n" + " {\n" + " return $this->params;\n" + " }\n" + "\n" + " public function withParams(array $params): static\n" + " {\n" + " $this->params = $params;\n" + " return $this;\n" + " }\n" + "\n" + " public function withAddedParams(array $params): static\n" + " {\n" + " $this->params = $params + $this->params;\n" + " return $this;\n" + " }\n" + "\n" + " public function getBody(): string\n" + " {\n" + " return $this->body;\n" + " }\n" + "\n" + " public function withBody(string|\\Stringable $body): self\n" + " {\n" + " $this->body = (string) $body;\n" + " return $this;\n" + " }\n" + "\n" + " public function getError(): string\n" + " {\n" + " return $this->error;\n" + " }\n" + "\n" + " public function withError(string $error): static\n" + " {\n" + " $this->error = $error;\n" + " return $this;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_fast_cgi_request = + "\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\\FastCGI;\n" + "\n" + "use Swoole\\FastCGI;\n" + "use Swoole\\FastCGI\\Record\\BeginRequest;\n" + "use Swoole\\FastCGI\\Record\\Params;\n" + "use Swoole\\FastCGI\\Record\\Stdin;\n" + "\n" + "class Request extends Message implements \\Stringable\n" + "{\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" + " if (empty($body)) {\n" + " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}\";\n" + " } else {\n" + " $stdinList = [];\n" + " while (true) {\n" + " $stdinList[] = $stdin = new Stdin($body);\n" + " $stdinLength = $stdin->getContentLength();\n" + " if ($stdinLength === strlen($body)) {\n" + " break;\n" + " }\n" + " $body = substr($body, $stdinLength);\n" + " }\n" + " $stdinList[] = new Stdin('');\n" + " $stdin = implode('', $stdinList);\n" + " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}{$stdin}\";\n" + " }\n" + " return $message;\n" + " }\n" + "\n" + " public function getKeepConn(): bool\n" + " {\n" + " return $this->keepConn;\n" + " }\n" + "\n" + " public function withKeepConn(bool $keepConn): self\n" + " {\n" + " $this->keepConn = $keepConn;\n" + " return $this;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_fast_cgi_response = + "\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\\FastCGI;\n" + "\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" + " /**\n" + " * @param array $records\n" + " */\n" + " public function __construct(array $records)\n" + " {\n" + " if (!static::verify($records)) {\n" + " throw new \\InvalidArgumentException('Bad records');\n" + " }\n" + "\n" + " $body = $error = '';\n" + " foreach ($records as $record) {\n" + " if ($record instanceof Stdout) {\n" + " if ($record->getContentLength() > 0) {\n" + " $body .= $record->getContentData();\n" + " }\n" + " } elseif ($record instanceof Stderr) {\n" + " if ($record->getContentLength() > 0) {\n" + " $error .= $record->getContentData();\n" + " }\n" + " }\n" + " }\n" + " $this->withBody($body)->withError($error);\n" + " }\n" + "\n" + " /**\n" + " * @param array $records\n" + " */\n" + " protected static function verify(array $records): bool\n" + " {\n" + " return !empty($records) && $records[array_key_last($records)] instanceof EndRequest;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_fast_cgi_http_request = + "\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\\FastCGI;\n" + "\n" + "class HttpRequest extends Request\n" + "{\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" + " ];\n" + "\n" + " public function getScheme(): ?string\n" + " {\n" + " return $this->params['REQUEST_SCHEME'] ?? null;\n" + " }\n" + "\n" + " public function withScheme(string $scheme): self\n" + " {\n" + " $this->params['REQUEST_SCHEME'] = $scheme;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutScheme(): void\n" + " {\n" + " unset($this->params['REQUEST_SCHEME']);\n" + " }\n" + "\n" + " public function getMethod(): ?string\n" + " {\n" + " return $this->params['REQUEST_METHOD'] ?? null;\n" + " }\n" + "\n" + " public function withMethod(string $method): self\n" + " {\n" + " $this->params['REQUEST_METHOD'] = $method;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutMethod(): void\n" + " {\n" + " unset($this->params['REQUEST_METHOD']);\n" + " }\n" + "\n" + " public function getDocumentRoot(): ?string\n" + " {\n" + " return $this->params['DOCUMENT_ROOT'] ?? null;\n" + " }\n" + "\n" + " public function withDocumentRoot(string $documentRoot): self\n" + " {\n" + " $this->params['DOCUMENT_ROOT'] = $documentRoot;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutDocumentRoot(): void\n" + " {\n" + " unset($this->params['DOCUMENT_ROOT']);\n" + " }\n" + "\n" + " public function getScriptFilename(): ?string\n" + " {\n" + " return $this->params['SCRIPT_FILENAME'] ?? null;\n" + " }\n" + "\n" + " public function withScriptFilename(string $scriptFilename): self\n" + " {\n" + " $this->params['SCRIPT_FILENAME'] = $scriptFilename;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutScriptFilename(): void\n" + " {\n" + " unset($this->params['SCRIPT_FILENAME']);\n" + " }\n" + "\n" + " public function getScriptName(): ?string\n" + " {\n" + " return $this->params['SCRIPT_NAME'] ?? null;\n" + " }\n" + "\n" + " public function withScriptName(string $scriptName): self\n" + " {\n" + " $this->params['SCRIPT_NAME'] = $scriptName;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutScriptName(): void\n" + " {\n" + " unset($this->params['SCRIPT_NAME']);\n" + " }\n" + "\n" + " public function withUri(string $uri): self\n" + " {\n" + " $info = parse_url($uri);\n" + " return $this->withRequestUri($uri)\n" + " ->withDocumentUri($info['path'] ?? '')\n" + " ->withQueryString($info['query'] ?? '')\n" + " ;\n" + " }\n" + "\n" + " public function getDocumentUri(): ?string\n" + " {\n" + " return $this->params['DOCUMENT_URI'] ?? null;\n" + " }\n" + "\n" + " public function withDocumentUri(string $documentUri): self\n" + " {\n" + " $this->params['DOCUMENT_URI'] = $documentUri;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutDocumentUri(): void\n" + " {\n" + " unset($this->params['DOCUMENT_URI']);\n" + " }\n" + "\n" + " public function getRequestUri(): ?string\n" + " {\n" + " return $this->params['REQUEST_URI'] ?? null;\n" + " }\n" + "\n" + " public function withRequestUri(string $requestUri): self\n" + " {\n" + " $this->params['REQUEST_URI'] = $requestUri;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutRequestUri(): void\n" + " {\n" + " unset($this->params['REQUEST_URI']);\n" + " }\n" + "\n" + " public function withQuery($query): self\n" + " {\n" + " if (is_array($query)) {\n" + " $query = http_build_query($query);\n" + " }\n" + " return $this->withQueryString($query);\n" + " }\n" + "\n" + " public function getQueryString(): ?string\n" + " {\n" + " return $this->params['QUERY_STRING'] ?? null;\n" + " }\n" + "\n" + " public function withQueryString(string $queryString): self\n" + " {\n" + " $this->params['QUERY_STRING'] = $queryString;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutQueryString(): void\n" + " {\n" + " unset($this->params['QUERY_STRING']);\n" + " }\n" + "\n" + " public function getContentType(): ?string\n" + " {\n" + " return $this->params['CONTENT_TYPE'] ?? null;\n" + " }\n" + "\n" + " public function withContentType(string $contentType): self\n" + " {\n" + " $this->params['CONTENT_TYPE'] = $contentType;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutContentType(): void\n" + " {\n" + " unset($this->params['CONTENT_TYPE']);\n" + " }\n" + "\n" + " public function getContentLength(): ?int\n" + " {\n" + " return isset($this->params['CONTENT_LENGTH']) ? (int) $this->params['CONTENT_LENGTH'] : null;\n" + " }\n" + "\n" + " public function withContentLength(int $contentLength): self\n" + " {\n" + " $this->params['CONTENT_LENGTH'] = (string) $contentLength;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutContentLength(): void\n" + " {\n" + " unset($this->params['CONTENT_LENGTH']);\n" + " }\n" + "\n" + " public function getGatewayInterface(): ?string\n" + " {\n" + " return $this->params['GATEWAY_INTERFACE'] ?? null;\n" + " }\n" + "\n" + " public function withGatewayInterface(string $gatewayInterface): self\n" + " {\n" + " $this->params['GATEWAY_INTERFACE'] = $gatewayInterface;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutGatewayInterface(): void\n" + " {\n" + " unset($this->params['GATEWAY_INTERFACE']);\n" + " }\n" + "\n" + " public function getServerProtocol(): ?string\n" + " {\n" + " return $this->params['SERVER_PROTOCOL'] ?? null;\n" + " }\n" + "\n" + " public function withServerProtocol(string $serverProtocol): self\n" + " {\n" + " $this->params['SERVER_PROTOCOL'] = $serverProtocol;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutServerProtocol(): void\n" + " {\n" + " unset($this->params['SERVER_PROTOCOL']);\n" + " }\n" + "\n" + " public function withProtocolVersion(string $protocolVersion): self\n" + " {\n" + " if (!is_numeric($protocolVersion)) {\n" + " throw new \\InvalidArgumentException('Protocol version must be numeric');\n" + " }\n" + " $this->params['SERVER_PROTOCOL'] = \"HTTP/{$protocolVersion}\";\n" + " return $this;\n" + " }\n" + "\n" + " public function getServerSoftware(): ?string\n" + " {\n" + " return $this->params['SERVER_SOFTWARE'] ?? null;\n" + " }\n" + "\n" + " public function withServerSoftware(string $serverSoftware): self\n" + " {\n" + " $this->params['SERVER_SOFTWARE'] = $serverSoftware;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutServerSoftware(): void\n" + " {\n" + " unset($this->params['SERVER_SOFTWARE']);\n" + " }\n" + "\n" + " public function getRemoteAddr(): ?string\n" + " {\n" + " return $this->params['REMOTE_ADDR'] ?? null;\n" + " }\n" + "\n" + " public function withRemoteAddr(string $remoteAddr): self\n" + " {\n" + " $this->params['REMOTE_ADDR'] = $remoteAddr;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutRemoteAddr(): void\n" + " {\n" + " unset($this->params['REMOTE_ADDR']);\n" + " }\n" + "\n" + " public function getRemotePort(): ?int\n" + " {\n" + " return isset($this->params['REMOTE_PORT']) ? (int) $this->params['REMOTE_PORT'] : null;\n" + " }\n" + "\n" + " public function withRemotePort(int $remotePort): self\n" + " {\n" + " $this->params['REMOTE_PORT'] = (string) $remotePort;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutRemotePort(): void\n" + " {\n" + " unset($this->params['REMOTE_PORT']);\n" + " }\n" + "\n" + " public function getServerAddr(): ?string\n" + " {\n" + " return $this->params['SERVER_ADDR'] ?? null;\n" + " }\n" + "\n" + " public function withServerAddr(string $serverAddr): self\n" + " {\n" + " $this->params['SERVER_ADDR'] = $serverAddr;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutServerAddr(): void\n" + " {\n" + " unset($this->params['SERVER_ADDR']);\n" + " }\n" + "\n" + " public function getServerPort(): ?int\n" + " {\n" + " return isset($this->params['SERVER_PORT']) ? (int) $this->params['SERVER_PORT'] : null;\n" + " }\n" + "\n" + " public function withServerPort(int $serverPort): self\n" + " {\n" + " $this->params['SERVER_PORT'] = (string) $serverPort;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutServerPort(): void\n" + " {\n" + " unset($this->params['SERVER_PORT']);\n" + " }\n" + "\n" + " public function getServerName(): ?string\n" + " {\n" + " return $this->params['SERVER_NAME'] ?? null;\n" + " }\n" + "\n" + " public function withServerName(string $serverName): self\n" + " {\n" + " $this->params['SERVER_NAME'] = $serverName;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutServerName(): void\n" + " {\n" + " unset($this->params['SERVER_NAME']);\n" + " }\n" + "\n" + " public function getRedirectStatus(): ?string\n" + " {\n" + " return $this->params['REDIRECT_STATUS'] ?? null;\n" + " }\n" + "\n" + " public function withRedirectStatus(string $redirectStatus): self\n" + " {\n" + " $this->params['REDIRECT_STATUS'] = $redirectStatus;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutRedirectStatus(): void\n" + " {\n" + " unset($this->params['REDIRECT_STATUS']);\n" + " }\n" + "\n" + " public function getHeader(string $name): ?string\n" + " {\n" + " return $this->params[static::convertHeaderNameToParamName($name)] ?? null;\n" + " }\n" + "\n" + " public function withHeader(string $name, string $value): self\n" + " {\n" + " $this->params[static::convertHeaderNameToParamName($name)] = $value;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutHeader(string $name): void\n" + " {\n" + " unset($this->params[static::convertHeaderNameToParamName($name)]);\n" + " }\n" + "\n" + " public function getHeaders(): array\n" + " {\n" + " $headers = [];\n" + " foreach ($this->params as $name => $value) {\n" + " if (str_starts_with($name, 'HTTP_')) {\n" + " $headers[static::convertParamNameToHeaderName($name)] = $value;\n" + " }\n" + " }\n" + " return $headers;\n" + " }\n" + "\n" + " public function withHeaders(array $headers): self\n" + " {\n" + " foreach ($headers as $name => $value) {\n" + " $this->withHeader($name, $value);\n" + " }\n" + " return $this;\n" + " }\n" + "\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" + " protected static function convertHeaderNameToParamName(string $name)\n" + " {\n" + " return 'HTTP_' . str_replace('-', '_', strtoupper($name));\n" + " }\n" + "\n" + " protected static function convertParamNameToHeaderName(string $name)\n" + " {\n" + " return ucwords(str_replace('_', '-', substr($name, strlen('HTTP_'))), '-');\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_fast_cgi_http_response = + "\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\\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" + "{\n" + " /** @var int */\n" + " protected $statusCode;\n" + "\n" + " /** @var string */\n" + " protected $reasonPhrase;\n" + "\n" + " /**\n" + " * @var array\n" + " */\n" + " protected array $headers = [];\n" + "\n" + " /**\n" + " * @var array\n" + " */\n" + " protected array $headersMap = [];\n" + "\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 = $this->getBody();\n" + " if (strlen($body) === 0) {\n" + " return;\n" + " }\n" + " $array = explode(\"\\r\\n\\r\\n\", $body, 2); // An array that contains the HTTP headers and the body.\n" + " if (count($array) != 2) {\n" + " $this->withStatusCode(Status::BAD_GATEWAY)->withReasonPhrase('Invalid FastCGI Response')->withError($body);\n" + " return;\n" + " }\n" + " $headers = explode(\"\\r\\n\", $array[0]);\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" + " $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" + " $reasonPhrase = $array[1] ?? null;\n" + " } elseif (strcasecmp($name, 'Set-Cookie') === 0) {\n" + " $this->withSetCookieHeaderLine($value);\n" + " } else {\n" + " $this->withHeader($name, $value);\n" + " }\n" + " }\n" + " $statusCode = (int) ($statusCode ?? Status::OK);\n" + " $reasonPhrase = $reasonPhrase ?? Status::getReasonPhrase($statusCode);\n" + " $this->withStatusCode($statusCode)->withReasonPhrase($reasonPhrase);\n" + " $this->withBody($body);\n" + " }\n" + "\n" + " public function getStatusCode(): int\n" + " {\n" + " return $this->statusCode;\n" + " }\n" + "\n" + " public function withStatusCode(int $statusCode): self\n" + " {\n" + " $this->statusCode = $statusCode;\n" + " return $this;\n" + " }\n" + "\n" + " public function getReasonPhrase(): string\n" + " {\n" + " return $this->reasonPhrase;\n" + " }\n" + "\n" + " public function withReasonPhrase(string $reasonPhrase): self\n" + " {\n" + " $this->reasonPhrase = $reasonPhrase;\n" + " return $this;\n" + " }\n" + "\n" + " public function getHeader(string $name): ?string\n" + " {\n" + " $name = $this->headersMap[strtolower($name)] ?? null;\n" + " return $name ? $this->headers[$name] : null;\n" + " }\n" + "\n" + " /**\n" + " * @return array\n" + " */\n" + " public function getHeaders(): array\n" + " {\n" + " return $this->headers;\n" + " }\n" + "\n" + " public function withHeader(string $name, string $value): self\n" + " {\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" + " $this->withHeader($name, $value);\n" + " }\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * @return array\n" + " */\n" + " public function getSetCookieHeaderLines(): array\n" + " {\n" + " return $this->setCookieHeaderLines;\n" + " }\n" + "\n" + " public function withSetCookieHeaderLine(string $value): self\n" + " {\n" + " $this->setCookieHeaderLines[] = $value;\n" + " return $this;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_coroutine_fast_cgi_client = + "\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\\FastCGI;\n" + "\n" + "use Swoole\\Constant;\n" + "use Swoole\\Coroutine\\FastCGI\\Client\\Exception;\n" + "use Swoole\\Coroutine\\Socket;\n" + "use Swoole\\FastCGI\\FrameParser;\n" + "use Swoole\\FastCGI\\HttpRequest;\n" + "use Swoole\\FastCGI\\HttpResponse;\n" + "use Swoole\\FastCGI\\Record\\EndRequest;\n" + "use Swoole\\FastCGI\\Request;\n" + "use Swoole\\FastCGI\\Response;\n" + "\n" + "class Client\n" + "{\n" + " protected int $af;\n" + "\n" + " protected string $host;\n" + "\n" + " protected int $port;\n" + "\n" + " protected bool $ssl;\n" + "\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 (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" + " }\n" + "\n" + " /**\n" + " * @return ($request is HttpRequest ? HttpResponse : Response)\n" + " * @throws Exception\n" + " */\n" + " public function execute(Request $request, float $timeout = -1): Response\n" + " {\n" + " if (!isset($this->socket)) {\n" + " $this->socket = $socket = new Socket($this->af, SOCK_STREAM, IPPROTO_IP);\n" + " $socket->setProtocol([\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" + " }\n" + " } else {\n" + " $socket = $this->socket;\n" + " }\n" + " $sendData = (string) $request;\n" + " if ($socket->sendAll($sendData) !== strlen($sendData)) {\n" + " $this->ioException();\n" + " }\n" + " $records = [];\n" + " while (true) {\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" + " if ($record instanceof EndRequest) {\n" + " if (!$request->getKeepConn()) {\n" + " $this->socket->close();\n" + " $this->socket = null;\n" + " }\n" + " // @phpstan-ignore argument.type,argument.type\n" + " return ($request instanceof HttpRequest) ? new HttpResponse($records) : new Response($records);\n" + " }\n" + " }\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" + " $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" + " }\n" + " $host = \"unix:/{$host}\";\n" + " }\n" + " return [$host, $port];\n" + " }\n" + "\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" + " $documentUri = $scriptName;\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" + " ->withDocumentUri($documentUri)\n" + " ->withRequestUri($requestUri)\n" + " ->withQueryString($query)\n" + " ->withBody($data)\n" + " ->withMethod($request->getContentLength() === 0 ? 'GET' : 'POST')\n" + " ;\n" + " $response = $client->execute($request, $timeout);\n" + " return $response->getBody();\n" + " }\n" + "\n" + " protected function ioException(?int $errno = null): void\n" + " {\n" + " $socket = $this->socket;\n" + " if ($errno !== null) {\n" + " $socket->errCode = $errno;\n" + " $socket->errMsg = swoole_strerror($errno);\n" + " }\n" + " $socket->close();\n" + " $this->socket = null;\n" + " throw new Exception($socket->errMsg, $socket->errCode);\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_coroutine_fast_cgi_client_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" + "namespace Swoole\\Coroutine\\FastCGI\\Client;\n" + "\n" + "class Exception extends \\Swoole\\Exception\n" + "{\n" + "}\n"; + +static const char* swoole_library_source_core_coroutine_fast_cgi_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\\FastCGI;\n" + "\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" + " /* @var string */\n" + " protected $host;\n" + "\n" + " /* @var int */\n" + " protected $port;\n" + "\n" + " /* @var float */\n" + " protected $timeout = -1;\n" + "\n" + " /* @var string */\n" + " protected $documentRoot;\n" + "\n" + " /* @var bool */\n" + " protected $https = false;\n" + "\n" + " /* @var string */\n" + " protected $index = 'index.php';\n" + "\n" + " /* @var array */\n" + " protected $params = [];\n" + "\n" + " /* @var null|callable */\n" + " protected $staticFileFilter;\n" + "\n" + " 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" + " }\n" + "\n" + " public function withTimeout(float $timeout): self\n" + " {\n" + " $this->timeout = $timeout;\n" + " return $this;\n" + " }\n" + "\n" + " public function withHttps(bool $https): self\n" + " {\n" + " $this->https = $https;\n" + " return $this;\n" + " }\n" + "\n" + " public function withIndex(string $index): self\n" + " {\n" + " $this->index = $index;\n" + " return $this;\n" + " }\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" + " {\n" + " $this->params[$name] = $value;\n" + " return $this;\n" + " }\n" + "\n" + " public function withoutParam(string $name): self\n" + " {\n" + " unset($this->params[$name]);\n" + " return $this;\n" + " }\n" + "\n" + " public function getParams(): array\n" + " {\n" + " return $this->params;\n" + " }\n" + "\n" + " public function withParams(array $params): self\n" + " {\n" + " $this->params = $params;\n" + " return $this;\n" + " }\n" + "\n" + " public function withAddedParams(array $params): self\n" + " {\n" + " $this->params = $params + $this->params;\n" + " return $this;\n" + " }\n" + "\n" + " public function withStaticFileFilter(?callable $filter): self\n" + " {\n" + " $this->staticFileFilter = $filter;\n" + " return $this;\n" + " }\n" + "\n" + " public function translateRequest(SwooleHttpRequest $userRequest): HttpRequest\n" + " {\n" + " $server = $userRequest->server;\n" + " $headers = $userRequest->header;\n" + " $pathInfo = $userRequest->server['path_info'];\n" + " $pathInfo = '/' . ltrim($pathInfo, '/');\n" + " if (strlen($this->index) !== 0) {\n" + " $extension = pathinfo($pathInfo, PATHINFO_EXTENSION);\n" + " if (empty($extension)) {\n" + " $pathInfo = rtrim($pathInfo, '/') . '/' . $this->index;\n" + " }\n" + " }\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, SwooleHttpResponse $userResponse): void\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(SwooleHttpRequest|HttpRequest $userRequest, SwooleHttpResponse $userResponse): void\n" + " {\n" + " if (!$userRequest instanceof HttpRequest) {\n" + " $request = $this->translateRequest($userRequest);\n" + " } else {\n" + " $request = $userRequest;\n" + " }\n" + " unset($userRequest);\n" + " if ($this->staticFileFilter) {\n" + " $filter = $this->staticFileFilter;\n" + " if ($filter($request, $userResponse)) {\n" + " return;\n" + " }\n" + " }\n" + " $response = (new Client($this->host, $this->port))->execute($request, $this->timeout);\n" + " $this->translateResponse($response, $userResponse);\n" + " }\n" + "\n" + " /**\n" + " * Send content of a static file to the client, if the file is accessible and is not a PHP file.\n" + " *\n" + " * @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" + " $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 true;\n" + " }\n" + " return false;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_process_manager = + "\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\\Process;\n" + "\n" + "use Swoole\\Constant;\n" + "\n" + "use function Swoole\\Coroutine\\run;\n" + "\n" + "class Manager\n" + "{\n" + " /**\n" + " * @var Pool\n" + " */\n" + " protected $pool;\n" + "\n" + " /**\n" + " * @var int\n" + " */\n" + " protected $ipcType = SWOOLE_IPC_NONE;\n" + "\n" + " /**\n" + " * @var int\n" + " */\n" + " protected $msgQueueKey = 0;\n" + "\n" + " /**\n" + " * @var array\n" + " */\n" + " protected $startFuncMap = [];\n" + "\n" + " public function __construct(int $ipcType = SWOOLE_IPC_NONE, int $msgQueueKey = 0)\n" + " {\n" + " $this->setIPCType($ipcType)->setMsgQueueKey($msgQueueKey);\n" + " }\n" + "\n" + " public function add(callable $func, bool $enableCoroutine = false): self\n" + " {\n" + " $this->addBatch(1, $func, $enableCoroutine);\n" + " return $this;\n" + " }\n" + "\n" + " public function addBatch(int $workerNum, callable $func, bool $enableCoroutine = false): self\n" + " {\n" + " for ($i = 0; $i < $workerNum; $i++) {\n" + " $this->startFuncMap[] = [$func, $enableCoroutine];\n" + " }\n" + " return $this;\n" + " }\n" + "\n" + " public function start(): void\n" + " {\n" + " $this->pool = new Pool(count($this->startFuncMap), $this->ipcType, $this->msgQueueKey, false);\n" + "\n" + " $this->pool->on(Constant::EVENT_WORKER_START, function (Pool $pool, int $workerId) {\n" + " [$func, $enableCoroutine] = $this->startFuncMap[$workerId];\n" + " if ($enableCoroutine) {\n" + " run($func, $pool, $workerId);\n" + " } else {\n" + " $func($pool, $workerId);\n" + " }\n" + " });\n" + "\n" + " $this->pool->start();\n" + " }\n" + "\n" + " public function setIPCType(int $ipcType): self\n" + " {\n" + " $this->ipcType = $ipcType;\n" + " return $this;\n" + " }\n" + "\n" + " public function getIPCType(): int\n" + " {\n" + " return $this->ipcType;\n" + " }\n" + "\n" + " public function setMsgQueueKey(int $msgQueueKey): self\n" + " {\n" + " $this->msgQueueKey = $msgQueueKey;\n" + " return $this;\n" + " }\n" + "\n" + " public function getMsgQueueKey(): int\n" + " {\n" + " return $this->msgQueueKey;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_server_admin = + "\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\\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 Admin\n" + "{\n" + " /**\n" + " * gdb php\n" + " * (gdb) p sizeof(zval)\n" + " * $2 = 16\n" + " * (gdb) p sizeof(zend_array)\n" + " * $1 = 56\n" + " * (gdb) p sizeof(zend_string)\n" + " * $3 = 32\n" + " * (gdb) p sizeof(zend_object)\n" + " * $4 = 56\n" + " */\n" + " public const SIZE_OF_ZVAL = 16;\n" + "\n" + " public const SIZE_OF_ZEND_STRING = 32;\n" + "\n" + " public const SIZE_OF_ZEND_OBJECT = 56;\n" + "\n" + " 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" + " private static array $allList = [\n" + " 'all',\n" + " 'all_reactor',\n" + " 'all_reactor_thread',\n" + " 'all_worker',\n" + " 'all_event_worker',\n" + " 'all_task',\n" + " 'all_task_worker',\n" + " 'specific',\n" + " ];\n" + "\n" + " private static array $postMethodList = [\n" + " 'server_reload',\n" + " 'server_shutdown',\n" + " 'close_session',\n" + " ];\n" + "\n" + " private static string $accessToken = '';\n" + "\n" + " public static function init(Server $server): void\n" + " {\n" + " $accepted_process_types = SWOOLE_SERVER_COMMAND_MASTER |\n" + " SWOOLE_SERVER_COMMAND_MANAGER |\n" + " SWOOLE_SERVER_COMMAND_EVENT_WORKER |\n" + " SWOOLE_SERVER_COMMAND_TASK_WORKER;\n" + "\n" + " $server->addCommand(\n" + " 'server_reload',\n" + " $accepted_process_types,\n" + " function (Server $server, string $msg) {\n" + " $server->reload();\n" + " return self::json('Operation succeeded');\n" + " }\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'server_shutdown',\n" + " $accepted_process_types,\n" + " function (Server $server, string $msg): void {\n" + " $server->shutdown();\n" + " }\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'coroutine_stats',\n" + " $accepted_process_types,\n" + " fn (Server $server, string $msg) => self::json(Coroutine::stats())\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'coroutine_list',\n" + " $accepted_process_types,\n" + " fn (Server $server, string $msg) => self::json(iterator_to_array(Coroutine::list()))\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'coroutine_bt',\n" + " $accepted_process_types,\n" + " function (Server $server, string $msg) {\n" + " $json = json_decode($msg, null, 512, JSON_THROW_ON_ERROR);\n" + " $cid = empty($json->cid) ? 0 : intval($json->cid);\n" + " $bt = Coroutine::getBackTrace($cid);\n" + " if ($bt === false) {\n" + " return self::json(\"Coroutine#{$cid} not exists\", 4004);\n" + " }\n" + " return self::json($bt);\n" + " }\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'server_stats',\n" + " $accepted_process_types,\n" + " fn (Server $server, string $msg) => self::json($server->stats())\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'server_setting',\n" + " $accepted_process_types,\n" + " function (Server $server, string $msg) {\n" + " $setting = $server->setting;\n" + " $setting['mode'] = $server->mode;\n" + " $setting['host'] = $server->host;\n" + " $setting['port'] = $server->port;\n" + " $setting['master_pid'] = $server->master_pid;\n" + " $setting['manager_pid'] = $server->manager_pid;\n" + " return self::json($setting);\n" + " }\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'get_client_info',\n" + " $accepted_process_types,\n" + " function (Server $server, string $msg) {\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" + " if (empty($json['session_id'])) {\n" + " return self::json('require session_id', 4003);\n" + " }\n" + " return self::json($server->getClientInfo(intval($json['session_id'])));\n" + " }\n" + " );\n" + "\n" + " $server->addCommand('close_session', $accepted_process_types, [self::class, 'handlerCloseSession']);\n" + " $server->addCommand('get_version_info', $accepted_process_types, [self::class, 'handlerGetVersionInfo']);\n" + " $server->addCommand('get_worker_info', $accepted_process_types, [self::class, 'handlerGetWorkerInfo']);\n" + " $server->addCommand('get_timer_list', $accepted_process_types, [self::class, 'handlerGetTimerList']);\n" + " $server->addCommand('get_coroutine_list', $accepted_process_types, [self::class, 'handlerGetCoroutineList']);\n" + " $server->addCommand('get_objects', $accepted_process_types, [self::class, 'handlerGetObjects']);\n" + " $server->addCommand('get_class_info', $accepted_process_types, [self::class, 'handlerGetClassInfo']);\n" + " $server->addCommand('get_function_info', $accepted_process_types, [self::class, 'handlerGetFunctionInfo']);\n" + " $server->addCommand('get_object_by_handle', $accepted_process_types, [self::class, 'handlerGetObjectByHandle']);\n" + " $server->addCommand('get_server_cpu_usage', $accepted_process_types, [self::class, 'handlerGetServerCpuUsage']);\n" + " $server->addCommand(\n" + " 'get_server_memory_usage',\n" + " $accepted_process_types,\n" + " [self::class, 'handlerGetServerMemoryUsage']\n" + " );\n" + " $server->addCommand(\n" + " 'get_static_property_value',\n" + " $accepted_process_types,\n" + " [self::class, 'handlerGetStaticPropertyValue']\n" + " );\n" + " $server->addCommand(\n" + " 'get_defined_functions',\n" + " $accepted_process_types,\n" + " [self::class, 'handlerGetDefinedFunctions']\n" + " );\n" + " $server->addCommand('get_declared_classes', $accepted_process_types, [self::class, 'handlerGetDeclaredClasses']);\n" + "\n" + " $server->addCommand(\n" + " 'gc_status',\n" + " $accepted_process_types,\n" + " function (Server $server, string $msg) {\n" + " return self::json(gc_status());\n" + " }\n" + " );\n" + "\n" + " if (extension_loaded('opcache')) {\n" + " $server->addCommand(\n" + " 'opcache_status',\n" + " $accepted_process_types,\n" + " fn (Server $server, string $msg) => self::json(opcache_get_status(true))\n" + " );\n" + " }\n" + "\n" + " $server->addCommand(\n" + " 'getpid',\n" + " $accepted_process_types,\n" + " fn (Server $server, string $msg) => self::json(['pid' => posix_getpid()])\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'memory_usage',\n" + " $accepted_process_types,\n" + " fn (Server $server, string $msg) => self::json([\n" + " 'usage' => memory_get_usage(),\n" + " 'real_usage' => memory_get_usage(true),\n" + " ])\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'get_included_files',\n" + " $accepted_process_types,\n" + " fn (Server $server, string $msg) => self::json(['files' => get_included_files()])\n" + " );\n" + "\n" + " $server->addCommand('get_resources', $accepted_process_types, [self::class, 'handlerGetResources']);\n" + "\n" + " $server->addCommand(\n" + " 'get_defined_constants',\n" + " $accepted_process_types,\n" + " function (Server $server, string $msg) {\n" + " $constants = get_defined_constants();\n" + " foreach ($constants as $k => $c) {\n" + " if (is_resource($c)) {\n" + " unset($constants[$k]);\n" + " }\n" + " }\n" + " unset($constants['NULL'], $constants['NAN'], $constants['INF']);\n" + " return self::json($constants);\n" + " }\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'get_loaded_extensions',\n" + " $accepted_process_types,\n" + " function (Server $server, string $msg) {\n" + " $extensions = get_loaded_extensions();\n" + " $list = [];\n" + " foreach ($extensions as $key => $extension) {\n" + " $ext = new \\ReflectionExtension($extension);\n" + " $list[$key] = [\n" + " 'id' => ++$key,\n" + " 'name' => $extension,\n" + " 'version' => (string) $ext->getVersion(),\n" + " ];\n" + " }\n" + " return self::json($list);\n" + " }\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'get_declared_interfaces',\n" + " $accepted_process_types,\n" + " fn (Server $server, string $msg) => self::json(get_declared_interfaces())\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'get_declared_traits',\n" + " $accepted_process_types,\n" + " fn (Server $server, string $msg) => self::json(get_declared_traits())\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'get_included_file_contents',\n" + " $accepted_process_types,\n" + " function (Server $server, string $msg) {\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" + " if (empty($json['filename'])) {\n" + " return self::json('require filename', 4003);\n" + " }\n" + "\n" + " if (!file_exists($json['filename'])) {\n" + " return self::json(\"{$json['filename']} not exist\", 4004);\n" + " }\n" + "\n" + " if (!in_array($json['filename'], get_included_files())) {\n" + " return self::json('no permission', 4003);\n" + " }\n" + "\n" + " return self::json(file_get_contents($json['filename']));\n" + " }\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'get_globals',\n" + " $accepted_process_types,\n" + " function (Server $server, string $msg) {\n" + " $globals = [];\n" + " foreach ($GLOBALS as $key => $item) {\n" + " if ($key === 'GLOBALS') {\n" + " continue;\n" + " }\n" + " $type = gettype($item);\n" + " $other = [];\n" + " if ($type === 'object') {\n" + " $other = [\n" + " 'class_name' => $item::class,\n" + " 'object_id' => spl_object_id($item),\n" + " 'object_hash' => spl_object_hash($item),\n" + " ];\n" + " }\n" + " if ($type == 'resource' || $type == 'resource (closed)') {\n" + " $item = '';\n" + " }\n" + " $globals[] = [\n" + " 'key' => $key,\n" + " 'value' => $item,\n" + " 'type' => $type,\n" + " 'other' => $other,\n" + " ];\n" + " }\n" + " return self::json($globals);\n" + " }\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'get_extension_info',\n" + " $accepted_process_types,\n" + " function (Server $server, string $msg) {\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" + "\n" + " if (empty($json['extension_name']) || !extension_loaded($json['extension_name'])) {\n" + " return self::json('require extension_name', 4004);\n" + " }\n" + "\n" + " $ext = new \\ReflectionExtension($json['extension_name']);\n" + "\n" + " ob_start();\n" + " $ext->info();\n" + " $info = ob_get_clean();\n" + "\n" + " $constants = $ext->getConstants();\n" + " foreach ($constants as $k => $c) {\n" + " if (is_resource($c)) {\n" + " unset($constants[$k]);\n" + " }\n" + " }\n" + "\n" + " unset($constants['NULL'], $constants['NAN'], $constants['INF']);\n" + "\n" + " return self::json([\n" + " 'classes' => $ext->getClassNames(),\n" + " 'version' => $ext->getVersion(),\n" + " 'constants' => $constants,\n" + " 'ini_entries' => $ext->getINIEntries(),\n" + " 'dependencies' => $ext->getDependencies(),\n" + " 'functions' => array_keys($ext->getFunctions()),\n" + " 'info' => trim($info),\n" + " ]);\n" + " }\n" + " );\n" + "\n" + " $server->addCommand(\n" + " 'get_composer_packages',\n" + " $accepted_process_types,\n" + " function (Server $server, string $msg) {\n" + " if (!class_exists(\\Composer\\InstalledVersions::class)) {\n" + " return self::json('require composer 2.0', 4003);\n" + " }\n" + "\n" + " $list = [];\n" + " foreach (['getAllRawData', 'getRawData'] as $method) {\n" + " if (!method_exists(\\Composer\\InstalledVersions::class, $method)) {\n" + " continue;\n" + " }\n" + "\n" + " $raw_data[] = \\Composer\\InstalledVersions::$method();\n" + " if ($method === 'getAllRawData') {\n" + " $raw_data = \\Composer\\InstalledVersions::$method();\n" + " array_shift($raw_data);\n" + " }\n" + "\n" + " foreach ($raw_data as $key => $package) {\n" + " $key_name = $package['root']['name'];\n" + " if ($package['root']['name'] === '__root__' && isset($list['__root__'])) {\n" + " $key_name = \"__root__{$key}\";\n" + " }\n" + " $package['root']['install_path'] = !empty($package['root']['install_path']) ? realpath($package['root']['install_path']) : '';\n" + " $list[$key_name] = $package;\n" + " }\n" + " break;\n" + " }\n" + " return self::json($list);\n" + " }\n" + " );\n" + " }\n" + "\n" + " public static function getAccessToken(): string\n" + " {\n" + " return self::$accessToken;\n" + " }\n" + "\n" + " public static function start(Server $server): void\n" + " {\n" + " $admin_server_uri = swoole_string($server->setting['admin_server']);\n" + " if ($admin_server_uri->startsWith('unix:/')) {\n" + " swoole_error_log(SWOOLE_LOG_ERROR, \"admin_server[{$server->setting['admin_server']}] is not supported\");\n" + " return;\n" + " }\n" + "\n" + " if ($admin_server_uri->contains('@')) {\n" + " [$access_name, $access_secret] = $admin_server_uri->split('@', 2)->get(0)->split(':', 2)->toArray();\n" + " self::$accessToken = sha1($access_name . $access_secret);\n" + " [$host, $port] = $admin_server_uri->split('@', 2)->get(1)->split(':', 2)->toArray();\n" + " } else {\n" + " [$host, $port] = $admin_server_uri->split(':', 2)->toArray();\n" + " }\n" + "\n" + " $admin_server = new Coroutine\\Http\\Server($host, intval($port));\n" + "\n" + " $admin_server->handle('/api', function (Request $req, Response $resp) use ($server) {\n" + " $path_array = swoole_string($req->server['request_uri'])->trim('/')->split('/');\n" + " if ($path_array->count() < 2 or $path_array->count() > 3) {\n" + " $resp->status(403);\n" + " $resp->end(self::json('Bad API path', 4003));\n" + " return;\n" + " }\n" + "\n" + " $resp->header('Server', 'swoole-admin-server');\n" + " $resp->header('Access-Control-Allow-Origin', '*');\n" + " $resp->header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');\n" + " $resp->header('Access-Control-Allow-Headers', 'X-ACCESS-TOKEN, X-ADMIN-SERVER-ACCESS-TOKEN');\n" + "\n" + " $method = $req->getMethod();\n" + "\n" + " if ($method === 'OPTIONS') {\n" + " $resp->end();\n" + " return;\n" + " }\n" + "\n" + " $token = self::getAccessToken();\n" + " if (!empty($token)) {\n" + " $token_header = $req->header['x-admin-server-access-token'] ?? '';\n" + " if ($token_header !== $token) {\n" + " $resp->status(403);\n" + " $resp->end(self::json('Bad access token', 4003));\n" + " return;\n" + " }\n" + " }\n" + "\n" + " $cmd = $path_array->get(1)->toString();\n" + "\n" + " if (in_array($cmd, self::$postMethodList) && $method != 'POST') {\n" + " $resp->status(403);\n" + " $resp->end(self::json('Bad request method', 4003));\n" + " return;\n" + " }\n" + "\n" + " if ($method == 'GET') {\n" + " $data = $req->get;\n" + " } else {\n" + " $data = $req->post;\n" + " }\n" + "\n" + " if ($cmd === 'multi') {\n" + " $body = json_decode($req->getContent(), true, 512, JSON_THROW_ON_ERROR);\n" + " if (empty($body) || !is_array($body) || $method != 'POST') {\n" + " goto _bad_process;\n" + " }\n" + "\n" + " $result = self::handlerMulti($server, $body);\n" + " $resp->end(json_encode($result, JSON_INVALID_UTF8_IGNORE));\n" + " return;\n" + " }\n" + "\n" + " if ($path_array->count() == 2) {\n" + " $process = swoole_string('master');\n" + " } else {\n" + " $process = $path_array->get(2);\n" + " }\n" + "\n" + " if ($process->startsWith('master')) {\n" + " $process_type = SWOOLE_SERVER_COMMAND_MASTER;\n" + " $process_id = 0;\n" + " } elseif ($process->startsWith('manager')) {\n" + " $process_type = SWOOLE_SERVER_COMMAND_MANAGER;\n" + " $process_id = 0;\n" + " } elseif ($process->startsWith('all') || $process->equals('specific')) {\n" + " if (!in_array($process->toString(), self::$allList)) {\n" + " goto _bad_process;\n" + " }\n" + "\n" + " $result = self::handlerGetAll($server, $process, $cmd, $data);\n" + "\n" + " $resp->end(self::json($result));\n" + " return;\n" + " } else {\n" + " $array = $process->split('-');\n" + " if ($array->count() != 2) {\n" + " _bad_process:\n" + " $resp->status(403);\n" + " $resp->end(self::json('Bad process', 4003));\n" + " return;\n" + " }\n" + "\n" + " if (!isset(self::$map[$array->get(0)->toString()])) {\n" + " goto _bad_process;\n" + " }\n" + "\n" + " $process_type = self::$map[$array->get(0)->toString()];\n" + " $process_id = intval($array->get(1)->toString());\n" + " }\n" + "\n" + " $result = $server->command($cmd, $process_id, intval($process_type), $data, false);\n" + " if (!$result) {\n" + " $resp->end(json_encode([\n" + " 'code' => swoole_last_error(),\n" + " 'data' => swoole_strerror(swoole_last_error()),\n" + " ], JSON_THROW_ON_ERROR));\n" + " } else {\n" + " $resp->end($result);\n" + " }\n" + " });\n" + " $admin_server->handle('/', function (Request $req, Response $resp): void {\n" + " $resp->status(404);\n" + " });\n" + " $server->admin_server = $admin_server;\n" + " $admin_server->start();\n" + " }\n" + "\n" + " /**\n" + " * @return false|string\n" + " */\n" + " public static function handlerGetResources(Server $server, string $msg)\n" + " {\n" + " $resources = get_resources();\n" + " $list = [];\n" + " foreach ($resources as $r) {\n" + " $info = [\n" + " 'id' => get_resource_id($r),\n" + " 'type' => get_resource_type($r),\n" + " ];\n" + " if ($info['type'] == 'stream') {\n" + " $info['info'] = stream_get_meta_data($r);\n" + " }\n" + " $list[] = $info;\n" + " }\n" + " return self::json($list);\n" + " }\n" + "\n" + " /**\n" + " * @return false|string\n" + " */\n" + " public static function handlerGetWorkerInfo(Server $server, string $msg)\n" + " {\n" + " $info = [\n" + " 'id' => $server->getWorkerId(),\n" + " 'pid' => $server->getWorkerPid(),\n" + " 'gc_status' => gc_status(),\n" + " 'memory_usage' => memory_get_usage(),\n" + " 'memory_real_usage' => memory_get_usage(true),\n" + " 'process_status' => self::getProcessStatus(),\n" + " 'coroutine_stats' => Coroutine::stats(),\n" + " 'timer_stats' => Timer::stats(),\n" + " 'vm_status' => swoole_get_vm_status(),\n" + " ];\n" + " return self::json($info);\n" + " }\n" + "\n" + " /**\n" + " * @return false|string\n" + " */\n" + " public static function handlerCloseSession(Server $server, string $msg)\n" + " {\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" + " if (empty($json['session_id'])) {\n" + " return self::json('require session_id', 4003);\n" + " }\n" + " if ($server->close(intval($json['session_id']), !empty($json['force']))) {\n" + " return self::json([]);\n" + " }\n" + " return self::json(['error' => swoole_last_error()], 4004);\n" + " }\n" + "\n" + " /**\n" + " * @return false|string\n" + " */\n" + " public static function handlerGetTimerList(Server $server, string $msg)\n" + " {\n" + " $list = [];\n" + " foreach (Timer::list() as $timer_id) {\n" + " $list[] = [\n" + " 'id' => $timer_id,\n" + " 'info' => Timer::info($timer_id),\n" + " ];\n" + " }\n" + "\n" + " return self::json($list);\n" + " }\n" + "\n" + " /**\n" + " * @return false|string\n" + " */\n" + " public static function handlerGetCoroutineList(Server $server, string $msg)\n" + " {\n" + " $list = [];\n" + " foreach (Coroutine::list() as $cid) {\n" + " $list[] = [\n" + " 'id' => $cid,\n" + " 'elapsed' => Coroutine::getElapsed($cid),\n" + " 'stack_usage' => Coroutine::getStackUsage($cid),\n" + " 'backTrace' => Coroutine::getBackTrace($cid, DEBUG_BACKTRACE_IGNORE_ARGS, 1),\n" + " ];\n" + " }\n" + "\n" + " return self::json($list);\n" + " }\n" + "\n" + " public static function handlerGetObjects(Server $server, string $msg)\n" + " {\n" + " $list = [];\n" + " $objects = swoole_get_objects();\n" + " foreach ($objects as $o) {\n" + " $class_name = $o::class;\n" + " $class = new \\ReflectionClass($class_name);\n" + " $filename = $class->getFileName();\n" + " $line = $class->getStartLine();\n" + " $list[] = [\n" + " 'id' => spl_object_id($o),\n" + " 'hash' => spl_object_hash($o),\n" + " 'class' => $class_name,\n" + " 'filename' => $filename ?: '',\n" + " 'line' => $line ?: '',\n" + " 'memory_size' => self::getObjectMemorySize($o),\n" + " ];\n" + " }\n" + "\n" + " return self::json($list);\n" + " }\n" + "\n" + " public static function handlerGetClassInfo(Server $server, string $msg)\n" + " {\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" + " if (!empty($json['class_name'])) {\n" + " if (!class_exists($json['class_name'], false) && !interface_exists($json['class_name'], false)) {\n" + " return self::json(\"{$json['class_name']} not exists\", 4003);\n" + " }\n" + " $name = $json['class_name'];\n" + " } elseif (!empty($json['interface_name'])) {\n" + " if (!interface_exists($json['interface_name'], false)) {\n" + " return self::json(\"{$json['interface_name']} not exists\", 4003);\n" + " }\n" + " $name = $json['interface_name'];\n" + " } else {\n" + " return self::json(['error' => 'require class_name or interface_name'], 4004);\n" + " }\n" + "\n" + " $class = new \\ReflectionClass($name);\n" + "\n" + " $filename = $class->getFileName();\n" + "\n" + " $getTmpConstants = function ($data) {\n" + " $tmp = [];\n" + " foreach ($data as $k => $v) {\n" + " $tmp[] = [\n" + " 'name' => $k,\n" + " 'value' => is_array($v) ? var_export($v, true) : $v,\n" + " 'type' => is_array($v) ? 'detail' : 'default',\n" + " ];\n" + " }\n" + " return $tmp;\n" + " };\n" + "\n" + " $tmpConstants = $class->getConstants();\n" + " $constants = $tmpConstants ? $getTmpConstants($tmpConstants) : [];\n" + "\n" + " $staticProperties = [];\n" + " $properties = [];\n" + " $tmpProperties = $class->getProperties();\n" + "\n" + " $getTmpProperties = function ($class, $data) {\n" + " $static = [];\n" + " $noStatic = [];\n" + " $defaultProperties = $class->getDefaultProperties();\n" + " foreach ($data as $k => $v) {\n" + " $name = $v->getName();\n" + " $modifiers = \\Reflection::getModifierNames($v->getModifiers());\n" + " if ($v->isStatic()) {\n" + " $static[] = [\n" + " 'name' => $name,\n" + " 'value' => $defaultProperties[$name],\n" + " 'modifiers' => implode(' ', $modifiers),\n" + " ];\n" + " } else {\n" + " $noStatic[] = [\n" + " 'name' => $name,\n" + " 'value' => $defaultProperties[$name],\n" + " 'modifiers' => implode(' ', $modifiers),\n" + " ];\n" + " }\n" + " }\n" + " return ['static' => $static, 'no_static' => $noStatic];\n" + " };\n" + "\n" + " if ($tmpProperties) {\n" + " $tmpProperties = $getTmpProperties($class, $tmpProperties);\n" + " $staticProperties = $tmpProperties['static'];\n" + " $properties = $tmpProperties['no_static'];\n" + " }\n" + "\n" + " $staticMethods = [];\n" + " $methods = [];\n" + " $tmpStaticMethods = $class->getMethods();\n" + "\n" + " $getTmpMethods = function ($data) {\n" + " $static = [];\n" + " $noStatic = [];\n" + " foreach ($data as $k => $v) {\n" + " $name = $v->getName();\n" + " $line = $v->getStartLine();\n" + " $modifiers = \\Reflection::getModifierNames($v->getModifiers());\n" + " if ($v->isStatic()) {\n" + " $static[] = [\n" + " 'name' => $name,\n" + " 'line' => $line ?: '',\n" + " 'modifiers' => implode(' ', $modifiers),\n" + " ];\n" + " } else {\n" + " $noStatic[] = [\n" + " 'name' => $name,\n" + " 'line' => $line ?: '',\n" + " 'modifiers' => implode(' ', $modifiers),\n" + " ];\n" + " }\n" + " }\n" + " return ['static' => $static, 'no_static' => $noStatic];\n" + " };\n" + "\n" + " if ($tmpStaticMethods) {\n" + " $tmpStaticMethods = $getTmpMethods($tmpStaticMethods);\n" + " $staticMethods = $tmpStaticMethods['static'];\n" + " $methods = $tmpStaticMethods['no_static'];\n" + " }\n" + "\n" + " $tmpParentClass = $class->getParentClass();\n" + " $parentClass = $tmpParentClass ? $tmpParentClass->getName() : '';\n" + "\n" + " $data = [\n" + " 'filename' => $filename,\n" + " 'constants' => $constants,\n" + " 'staticProperties' => $staticProperties,\n" + " 'properties' => $properties,\n" + " 'staticMethods' => $staticMethods,\n" + " 'methods' => $methods,\n" + " 'parentClass' => $parentClass,\n" + " 'interface' => $class->getInterfaceNames(),\n" + " ];\n" + " return self::json($data);\n" + " }\n" + "\n" + " public static function handlerGetFunctionInfo(Server $server, string $msg)\n" + " {\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" + "\n" + " $className = $json['class_name'] ?? '';\n" + " $functionName = $json['function_name'] ?? '';\n" + "\n" + " if (empty($json) || empty($functionName)) {\n" + " return self::json('require function_name', 4004);\n" + " }\n" + "\n" + " $isStatic = false;\n" + " if (!empty($className)) {\n" + " if (!class_exists($className) && !interface_exists($className)) {\n" + " return self::json(\"{$className} not exists\", 4004);\n" + " }\n" + " if (!method_exists($className, $functionName)) {\n" + " return self::json(\"{$className}->{$functionName} not exists\", 4004);\n" + " }\n" + " $ref = new \\ReflectionMethod($className, $functionName);\n" + " $isStatic = $ref->isStatic();\n" + " } else {\n" + " if (!function_exists($functionName)) {\n" + " return self::json(\"{$functionName} not exists\", 4004);\n" + " }\n" + " $ref = new \\ReflectionFunction($functionName);\n" + " }\n" + "\n" + " $result = [\n" + " 'filename' => $ref->getFileName(),\n" + " 'line' => $ref->getStartLine() ?: '',\n" + " 'num' => $ref->getNumberOfParameters(),\n" + " 'user_defined' => $ref->isUserDefined(),\n" + " 'extension' => $ref->getExtensionName(),\n" + " 'is_static' => $isStatic,\n" + " ];\n" + "\n" + " $params = $ref->getParameters();\n" + "\n" + " $list = [];\n" + " foreach ($params as $param) {\n" + " $type = $optional = $default = '';\n" + "\n" + " $paramName = $param->getName();\n" + "\n" + " if ($param->hasType()) {\n" + " /** @var \\ReflectionNamedType|\\ReflectionUnionType $reflection */\n" + " $reflection = $param->getType();\n" + " if ($reflection instanceof \\ReflectionUnionType) {\n" + " $unionType = [];\n" + " foreach ($reflection->getTypes() as $objType) {\n" + " $unionType[] = $objType->getName();\n" + " }\n" + " $type = implode('|', $unionType);\n" + " } else {\n" + " $type = $reflection->getName();\n" + " }\n" + " }\n" + "\n" + " if ($param->isOptional() && !$param->isVariadic()) {\n" + " $optional = '?';\n" + " if ($param->isDefaultValueAvailable()) {\n" + " $value = $param->getDefaultValue();\n" + " if (in_array($value, [true, false, null, ''])) {\n" + " if ($value === null) {\n" + " $value = 'null';\n" + " }\n" + " if ($value === true) {\n" + " $value = 'true';\n" + " }\n" + " if ($value === false) {\n" + " $value = 'false';\n" + " }\n" + " if ($value === '') {\n" + " $value = \"''\";\n" + " }\n" + " }\n" + " $default = \" = {$value}\";\n" + " }\n" + " }\n" + "\n" + " $isPassedByReference = $param->isPassedByReference() ? '&' : '';\n" + " $isVariadic = $param->isVariadic() ? '...' : '';\n" + "\n" + " $option = \"{$optional}{$type} {$isPassedByReference}{$isVariadic}\";\n" + " $param = \"\\${$paramName}{$default}\";\n" + "\n" + " $list[] = [\n" + " 'optional' => $optional,\n" + " 'type' => $type,\n" + " 'is_passed_by_reference' => $isPassedByReference,\n" + " 'is_variadic' => $isVariadic,\n" + " 'name' => $paramName,\n" + " 'default' => $default,\n" + " 'full' => $option !== ' ' ? \"{$option}{$param}\" : $param,\n" + " ];\n" + " }\n" + " $result['params'] = $list;\n" + "\n" + " return self::json($result);\n" + " }\n" + "\n" + " public static function handlerGetObjectByHandle(Server $server, string $msg)\n" + " {\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" + " if (empty($json) || empty($json['object_id']) || empty($json['object_hash'])) {\n" + " return self::json(['error' => 'Params Error!'], 4004);\n" + " }\n" + "\n" + " $object = swoole_get_object_by_handle((int) $json['object_id']);\n" + " if (!$object) {\n" + " return self::json(['error' => 'Object destroyed!'], 4004);\n" + " }\n" + "\n" + " $object_hash = spl_object_hash($object); // @phpstan-ignore argument.type\n" + " if ($object_hash != $json['object_hash']) {\n" + " return self::json(['error' => 'Object destroyed!'], 4004);\n" + " }\n" + "\n" + " return self::json(var_export($object, true));\n" + " }\n" + "\n" + " public static function handlerGetVersionInfo(Server $server, string $msg)\n" + " {\n" + " $ip_arr = swoole_get_local_ip();\n" + " $host = [];\n" + " $local = [];\n" + " foreach ($ip_arr as $k => $ip) {\n" + " if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {\n" + " $host[] = $ip;\n" + " } else {\n" + " $local[] = $ip;\n" + " }\n" + " }\n" + " $data = [\n" + " 'os' => php_uname('s') . '-' . php_uname('r'),\n" + " 'swoole' => swoole_version(),\n" + " 'php' => phpversion(),\n" + " 'ip' => $host ? $host[0] : $local[0],\n" + " ];\n" + " return self::json($data);\n" + " }\n" + "\n" + " public static function handlerGetDefinedFunctions(Server $server, string $msg)\n" + " {\n" + " $functions = get_defined_functions();\n" + " $arr = [\n" + " 'internal' => $functions['internal'],\n" + " ];\n" + " if (!empty($functions['user'])) {\n" + " foreach ($functions['user'] as $function_name) {\n" + " $function = new \\ReflectionFunction($function_name);\n" + " $filename = $function->getFileName();\n" + " $line = $function->getStartLine();\n" + " $arr['user'][] = [\n" + " 'function' => $function_name,\n" + " 'filename' => $filename,\n" + " 'line' => $line,\n" + " ];\n" + " }\n" + " }\n" + " return self::json($arr);\n" + " }\n" + "\n" + " public static function handlerGetDeclaredClasses(Server $server, string $msg)\n" + " {\n" + " $classes = get_declared_classes();\n" + " $arr = [];\n" + " if ($classes) {\n" + " foreach ($classes as $classes_name) {\n" + " $function = new \\ReflectionClass($classes_name);\n" + " $filename = $function->getFileName();\n" + " $line = $function->getStartLine();\n" + " $arr[] = [\n" + " 'class' => $classes_name,\n" + " 'filename' => $filename ?: '',\n" + " 'line' => $line ?: '',\n" + " ];\n" + " }\n" + " }\n" + " return self::json($arr);\n" + " }\n" + "\n" + " public static function handlerGetServerMemoryUsage(Server $server, string $msg)\n" + " {\n" + " $total = 0;\n" + "\n" + " $result['master'] = $result['manager'] = 0;\n" + " if (self::haveMasterProcess($server)) {\n" + " $result['master'] = self::getProcessMemoryRealUsage($server->master_pid);\n" + " }\n" + " $total += $result['master'];\n" + "\n" + " if (self::haveManagerProcess($server)) {\n" + " $result['manager'] = self::getProcessMemoryRealUsage($server->manager_pid);\n" + " }\n" + " $total += $result['manager'];\n" + "\n" + " $n = $server->setting['worker_num'] + $server->setting['task_worker_num'];\n" + " /** @var int $n */\n" + " for ($i = 0; $i < $n; $i++) {\n" + " $key = 'worker-' . $i;\n" + " $result[$key] = self::getProcessMemoryRealUsage($server->getWorkerPid($i));\n" + " $total += $result[$key];\n" + " }\n" + "\n" + " $result['total'] = $total;\n" + "\n" + " $result['memory_size'] = 0;\n" + " // TODO: Support other OS\n" + " if (PHP_OS_FAMILY === 'Linux') {\n" + " preg_match('#MemTotal:\\s+(\\d+) kB#i', file_get_contents('/proc/meminfo'), $match);\n" + " $result['memory_size'] = intval($match[1]) * 1024;\n" + " }\n" + "\n" + " return self::json($result);\n" + " }\n" + "\n" + " public static function handlerGetServerCpuUsage(Server $server, string $msg)\n" + " {\n" + " $total = 0;\n" + "\n" + " $result['master'] = $result['manager'] = 0;\n" + " if (self::haveMasterProcess($server)) {\n" + " $result['master'] = self::getProcessCpuUsage($server->master_pid);\n" + " }\n" + "\n" + " $total += $result['master'][1] ?? 0;\n" + "\n" + " if (self::haveManagerProcess($server)) {\n" + " $result['manager'] = self::getProcessCpuUsage($server->manager_pid);\n" + " }\n" + "\n" + " $total += $result['manager'][1] ?? 0;\n" + "\n" + " $n = $server->setting['worker_num'] + $server->setting['task_worker_num'];\n" + " /** @var int $n */\n" + " for ($i = 0; $i < $n; $i++) {\n" + " $key = 'worker-' . $i;\n" + " $result[$key] = self::getProcessCpuUsage($server->getWorkerPid($i))[1] ?? 0;\n" + " $total += $result[$key];\n" + " }\n" + "\n" + " $result['total'] = $total;\n" + " $result['cpu_num'] = swoole_cpu_num();\n" + "\n" + " return self::json($result);\n" + " }\n" + "\n" + " public static function handlerGetStaticPropertyValue(Server $server, string $msg)\n" + " {\n" + " $json = json_decode($msg, true, 512, JSON_THROW_ON_ERROR);\n" + " if (empty($json['class_name'])) {\n" + " return self::json(['error' => 'require class_name!'], 4004);\n" + " }\n" + " if (empty($json['property_name'])) {\n" + " return self::json(['error' => 'require property_name!'], 4004);\n" + " }\n" + "\n" + " $className = $json['class_name'];\n" + " $propertyName = $json['property_name'];\n" + "\n" + " if (!class_exists($className)) {\n" + " return self::json(\"class[{$className}] not exists\", 4004);\n" + " }\n" + "\n" + " $reflection = new \\ReflectionClass($className);\n" + " $value = $reflection->getStaticPropertyValue($propertyName, []);\n" + "\n" + " $result = [\n" + " 'value' => var_export($value, true),\n" + " ];\n" + " return self::json($result);\n" + " }\n" + "\n" + " private static function handlerMulti(Server $server, array $list)\n" + " {\n" + " $return_list = [];\n" + " foreach ($list as $key => $content) {\n" + " $path_array = swoole_string($content['path'])->trim('/')->split('/');\n" + " $cmd = $path_array->get(1)->toString();\n" + "\n" + " if ($path_array->count() == 2) {\n" + " $process = swoole_string('master');\n" + " } else {\n" + " $process = $path_array->get(2);\n" + " }\n" + "\n" + " $data = [];\n" + " $url_query = parse_url($process->toString(), PHP_URL_QUERY) ?? [];\n" + " if (!empty($url_query)) {\n" + " parse_str($url_query, $data);\n" + " }\n" + " $data = array_merge($data, $content['post'] ?? []);\n" + "\n" + " if ($process->startsWith('master')) {\n" + " $process_type = SWOOLE_SERVER_COMMAND_MASTER;\n" + " $process_id = 0;\n" + " } elseif ($process->startsWith('manager')) {\n" + " $process_type = SWOOLE_SERVER_COMMAND_MANAGER;\n" + " $process_id = 0;\n" + " } elseif ($process->startsWith('all') || $process->startsWith('specific')) {\n" + " if (!in_array($process->toString(), self::$allList) && !$process->startsWith('specific')) {\n" + " $return_list[$key] = json_decode('{}');\n" + " continue;\n" + " }\n" + "\n" + " $result = self::handlerGetAll($server, $process, $cmd, $data);\n" + "\n" + " $return_list[$key] = ['code' => 0, 'data' => $result];\n" + " continue;\n" + " } else {\n" + " $array = $process->split('-');\n" + "\n" + " if ($array->count() != 2 || !isset(self::$map[$array->get(0)->toString()])) {\n" + " $return_list[$key] = json_decode('{}');\n" + " continue;\n" + " }\n" + "\n" + " $process_type = self::$map[$array->get(0)->toString()];\n" + " $process_id = intval($array->get(1)->toString());\n" + " }\n" + "\n" + " $return_list[$key] = $server->command($cmd, $process_id, intval($process_type), $data, true);\n" + " }\n" + "\n" + " return $return_list;\n" + " }\n" + "\n" + " private static function handlerGetAll(Server $server, StringObject $process, $cmd, $data, bool $json_decode = true)\n" + " {\n" + " if ($process->equals('all')) {\n" + " $result = self::handlerGetMaster($cmd, $data, $server, $json_decode) +\n" + " self::handlerGetManager($cmd, $data, $server, $json_decode) +\n" + " self::handlerGetAllWorker($cmd, $data, $server, $json_decode) +\n" + " self::handlerGetAllTaskWorker($cmd, $data, $server, $json_decode);\n" + " } elseif ($process->startsWith('all_reactor')) {\n" + " $result = self::handlerGetAllReactor($cmd, $data, $server, $json_decode);\n" + " } elseif ($process->equals('all_worker') || $process->equals('all_event_worker')) {\n" + " $result = self::handlerGetAllWorker($cmd, $data, $server, $json_decode);\n" + " } elseif ($process->startsWith('all_task')) {\n" + " $result = self::handlerGetAllTaskWorker($cmd, $data, $server, $json_decode);\n" + " } else {\n" + " // specific\n" + " $result = [];\n" + " if (!empty($data['workers']) && is_array($data['workers'])) {\n" + " foreach ($data['workers'] as $name) {\n" + " $process = swoole_string($name);\n" + " if ($process->startsWith('master')) {\n" + " $result += self::handlerGetMaster($cmd, $data, $server, $json_decode);\n" + " } elseif ($process->startsWith('manager')) {\n" + " $result += self::handlerGetManager($cmd, $data, $server, $json_decode);\n" + " } else {\n" + " $array = $process->split('-');\n" + " if ($array->count() != 2 || !isset(self::$map[$array->get(0)->toString()])) {\n" + " $result[$name] = $json_decode ? json_decode('{}') : $json_decode;\n" + " } else {\n" + " $process_type = self::$map[$array->get(0)->toString()];\n" + " $process_id = intval($array->get(1)->toString());\n" + " $result[$name] = $server->command($cmd, $process_id, $process_type, $data, $json_decode);\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + "\n" + " return $result;\n" + " }\n" + "\n" + " private static function handlerGetMaster($cmd, $data, Server $server, bool $json_decode = false)\n" + " {\n" + " $list['master'] = $server->command($cmd, 0, SWOOLE_SERVER_COMMAND_MASTER, $data, $json_decode);\n" + " return $list;\n" + " }\n" + "\n" + " private static function handlerGetManager($cmd, $data, Server $server, bool $json_decode = false)\n" + " {\n" + " $list['manager'] = $server->command($cmd, 0, SWOOLE_SERVER_COMMAND_MANAGER, $data, $json_decode);\n" + " return $list;\n" + " }\n" + "\n" + " private static function handlerGetAllReactor($cmd, $data, Server $server, bool $json_decode = false)\n" + " {\n" + " $list = [];\n" + " if ($server->mode === SWOOLE_BASE) {\n" + " return $list;\n" + " }\n" + " $process_type = SWOOLE_SERVER_COMMAND_REACTOR_THREAD;\n" + " if (empty($server->setting['reactor_num'])) {\n" + " $reactor_num = $server->setting['worker_num'];\n" + " } else {\n" + " $reactor_num = $server->setting['reactor_num'];\n" + " }\n" + " for ($process_id = 0; $process_id < $reactor_num; $process_id++) {\n" + " $list[\"reactor-{$process_id}\"] = $server->command($cmd, $process_id, $process_type, $data, $json_decode);\n" + " }\n" + " return $list;\n" + " }\n" + "\n" + " private static function handlerGetAllWorker($cmd, $data, Server $server, bool $json_decode = false)\n" + " {\n" + " $process_type = SWOOLE_SERVER_COMMAND_EVENT_WORKER;\n" + " $worker_num = $server->setting['worker_num'];\n" + " $list = [];\n" + " for ($process_id = 0; $process_id < $worker_num; $process_id++) {\n" + " $list[\"worker-{$process_id}\"] = $server->command($cmd, $process_id, $process_type, $data, $json_decode);\n" + " }\n" + " return $list;\n" + " }\n" + "\n" + " private static function handlerGetAllTaskWorker($cmd, $data, Server $server, bool $json_decode = false)\n" + " {\n" + " $process_type = SWOOLE_SERVER_COMMAND_TASK_WORKER;\n" + " $list = [];\n" + " if (empty($server->setting['task_worker_num'])) {\n" + " return $list;\n" + " }\n" + " $task_worker_num = $server->setting['task_worker_num'];\n" + " for ($process_id = 0; $process_id < $task_worker_num; $process_id++) {\n" + " $list[\"task_worker-{$process_id}\"] = $server->command($cmd, $process_id, $process_type, $data, $json_decode);\n" + " }\n" + " return $list;\n" + " }\n" + "\n" + " private static function getProcessCpuUsage($pid)\n" + " {\n" + " // TODO: Support other OS\n" + " if (PHP_OS_FAMILY !== 'Linux' || !file_exists(\"/proc/{$pid}/stat\")) {\n" + " return [0];\n" + " }\n" + "\n" + " $statAll = file_get_contents('/proc/stat');\n" + " $statProc = file_get_contents(\"/proc/{$pid}/stat\");\n" + "\n" + " $dataAll = preg_split(\"/[ \\t]+/\", $statAll, 6);\n" + " assert($dataAll[0] === 'cpu', '/proc/stat malformed');\n" + " $dataProc = preg_split(\"/[ \\t]+/\", $statProc, 15);\n" + "\n" + " if (isset($dataProc[13]) and isset($dataProc[14])) {\n" + " return [\n" + " (int) $dataAll[1] + (int) $dataAll[2] + (int) $dataAll[3] + (int) $dataAll[4],\n" + " (int) $dataProc[13] + (int) $dataProc[14],\n" + " ];\n" + " }\n" + " return [(int) $dataAll[1] + (int) $dataAll[2] + (int) $dataAll[3] + (int) $dataAll[4]];\n" + " }\n" + "\n" + " private static function getProcessMemoryRealUsage($pid = 'self')\n" + " {\n" + " $status = self::getProcessStatus($pid);\n" + " if (!is_array($status) || !isset($status['VmRSS'])) {\n" + " return 0;\n" + " }\n" + " return intval($status['VmRSS']) * 1024;\n" + " }\n" + "\n" + " private static function getProcessStatus($pid = 'self')\n" + " {\n" + " $array = [];\n" + " // TODO: Support other OS\n" + " if (PHP_OS_FAMILY !== 'Linux' || !file_exists(\"/proc/{$pid}/status\")) {\n" + " return $array;\n" + " }\n" + " $status = swoole_string(trim(file_get_contents(\"/proc/{$pid}/status\")));\n" + " $lines = $status->split(\"\\n\");\n" + " foreach ($lines as $l) {\n" + " if (empty($l)) {\n" + " continue;\n" + " }\n" + " [$k, $v] = swoole_string($l)->split(':');\n" + " $array[$k] = trim($v);\n" + " }\n" + " return $array;\n" + " }\n" + "\n" + " private static function getArrayMemorySize(array $a): int\n" + " {\n" + " $size = self::SIZE_OF_ZVAL + self::SIZE_OF_ZEND_ARRAY;\n" + " foreach ($a as $k => $v) {\n" + " if (is_string($k)) {\n" + " $size += self::getStringMemorySize($k);\n" + " } else {\n" + " $size += self::SIZE_OF_ZVAL;\n" + " }\n" + " if (is_string($v)) {\n" + " $size += self::getStringMemorySize($v);\n" + " } elseif (is_array($v)) {\n" + " $size += self::getArrayMemorySize($v);\n" + " } else {\n" + " $size += self::SIZE_OF_ZVAL;\n" + " }\n" + " }\n" + " return $size;\n" + " }\n" + "\n" + " private static function getStringMemorySize(string $s): int\n" + " {\n" + " return self::SIZE_OF_ZVAL + self::SIZE_OF_ZEND_STRING + strlen($s);\n" + " }\n" + "\n" + " private static function getObjectMemorySize(object $o): int\n" + " {\n" + " $vars = get_object_vars($o);\n" + " $size = self::SIZE_OF_ZEND_OBJECT;\n" + "\n" + " foreach ($vars as $v) {\n" + " if (is_array($v)) {\n" + " $size += self::getArrayMemorySize($v);\n" + " } elseif (is_string($v)) {\n" + " $size += self::getStringMemorySize($v);\n" + " } else {\n" + " $size += self::SIZE_OF_ZVAL;\n" + " }\n" + " }\n" + "\n" + " return $size;\n" + " }\n" + "\n" + " private static function haveMasterProcess(Server $server): bool\n" + " {\n" + " if ($server->mode === SWOOLE_BASE) {\n" + " return false;\n" + " }\n" + "\n" + " return true;\n" + " }\n" + "\n" + " private static function haveManagerProcess(Server $server): bool\n" + " {\n" + " if ($server->mode === SWOOLE_BASE && $server->getManagerPid() === 0) {\n" + " return false;\n" + " }\n" + "\n" + " return true;\n" + " }\n" + "\n" + " private static function json($data, $code = 0)\n" + " {\n" + " $result = json_encode(['code' => $code, 'data' => $data], JSON_INVALID_UTF8_IGNORE);\n" + " if (empty($result)) {\n" + " return json_encode([\n" + " 'code' => 5010,\n" + " 'data' => ['message' => json_last_error_msg(), 'code' => json_last_error()],\n" + " ]);\n" + " }\n" + " return $result;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_server_helper = + "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" + "\n" + "declare(strict_types=1);\n" + "\n" + "namespace Swoole\\Server;\n" + "\n" + "use Swoole\\Constant;\n" + "use Swoole\\Coroutine;\n" + "use Swoole\\Server;\n" + "use Swoole\\Timer;\n" + "\n" + "class Helper\n" + "{\n" + " public const STATS_TIMER_INTERVAL_TIME = 1000;\n" + "\n" + " public const GLOBAL_OPTIONS = [\n" + " 'debug_mode' => true,\n" + " 'trace_flags' => true,\n" + " 'log_file' => true,\n" + " 'log_level' => true,\n" + " 'log_date_format' => true,\n" + " 'log_date_with_microseconds' => true,\n" + " 'log_rotation' => true,\n" + " 'display_errors' => true,\n" + " 'dns_server' => true,\n" + " 'socket_dns_timeout' => true,\n" + " 'socket_connect_timeout' => true,\n" + " 'socket_write_timeout' => true,\n" + " 'socket_send_timeout' => true,\n" + " 'socket_read_timeout' => true,\n" + " 'socket_recv_timeout' => true,\n" + " 'socket_buffer_size' => true,\n" + " 'socket_timeout' => true,\n" + " 'http2_header_table_size' => true,\n" + " 'http2_enable_push' => true,\n" + " 'http2_max_concurrent_streams' => true,\n" + " 'http2_init_window_size' => true,\n" + " 'http2_max_frame_size' => true,\n" + " 'http2_max_header_list_size' => true,\n" + " ];\n" + "\n" + " public const SERVER_OPTIONS = [\n" + " 'chroot' => true,\n" + " 'user' => true,\n" + " 'group' => true,\n" + " 'daemonize' => true,\n" + " 'pid_file' => true,\n" + " 'reactor_num' => true,\n" + " 'single_thread' => true,\n" + " 'worker_num' => true,\n" + " 'max_wait_time' => true,\n" + " 'max_queued_bytes' => true,\n" + " 'max_concurrency' => true,\n" + " 'worker_max_concurrency' => true,\n" + " 'enable_coroutine' => true,\n" + " 'send_timeout' => true,\n" + " 'dispatch_mode' => true,\n" + " 'send_yield' => true,\n" + " 'dispatch_func' => true,\n" + " 'discard_timeout_request' => true,\n" + " 'enable_unsafe_event' => true,\n" + " 'enable_delay_receive' => true,\n" + " 'enable_reuse_port' => true,\n" + " 'task_use_object' => true,\n" + " 'task_object' => true,\n" + " 'event_object' => true,\n" + " 'task_enable_coroutine' => true,\n" + " 'task_worker_num' => true,\n" + " 'task_ipc_mode' => true,\n" + " 'task_tmpdir' => true,\n" + " 'task_max_request' => true,\n" + " 'task_max_request_grace' => true,\n" + " 'max_connection' => true,\n" + " 'max_conn' => true,\n" + " 'start_session_id' => true,\n" + " 'heartbeat_check_interval' => true,\n" + " 'heartbeat_idle_time' => true,\n" + " 'max_request' => true,\n" + " 'max_request_grace' => true,\n" + " 'reload_async' => true,\n" + " 'open_cpu_affinity' => true,\n" + " 'cpu_affinity_ignore' => true,\n" + " 'http_parse_cookie' => true,\n" + " 'http_parse_post' => true,\n" + " 'http_parse_files' => true,\n" + " 'http_compression' => true,\n" + " 'http_compression_level' => true,\n" + " 'compression_level' => true,\n" + " 'http_gzip_level' => true,\n" + " 'http_compression_min_length' => true,\n" + " 'compression_min_length' => true,\n" + " 'websocket_compression' => true,\n" + " 'upload_tmp_dir' => true,\n" + " 'upload_max_filesize' => true,\n" + " 'enable_static_handler' => true,\n" + " 'document_root' => true,\n" + " 'http_autoindex' => true,\n" + " 'http_index_files' => true,\n" + " 'http_compression_types' => true,\n" + " 'compression_types' => true,\n" + " 'static_handler_locations' => true,\n" + " 'input_buffer_size' => true,\n" + " 'buffer_input_size' => true,\n" + " 'output_buffer_size' => true,\n" + " 'buffer_output_size' => true,\n" + " 'message_queue_key' => true,\n" + " 'init_arguments' => true,\n" + " 'bootstrap' => true,\n" + " ];\n" + "\n" + " public const PORT_OPTIONS = [\n" + " 'ssl_cert_file' => true,\n" + " 'ssl_key_file' => true,\n" + " 'backlog' => true,\n" + " 'socket_buffer_size' => true,\n" + " 'kernel_socket_recv_buffer_size' => true,\n" + " 'kernel_socket_send_buffer_size' => true,\n" + " 'heartbeat_idle_time' => true,\n" + " 'buffer_high_watermark' => true,\n" + " 'buffer_low_watermark' => true,\n" + " 'open_tcp_nodelay' => true,\n" + " 'tcp_defer_accept' => true,\n" + " 'open_tcp_keepalive' => true,\n" + " 'open_eof_check' => true,\n" + " 'open_eof_split' => true,\n" + " 'package_eof' => true,\n" + " 'open_http_protocol' => true,\n" + " 'open_websocket_protocol' => true,\n" + " 'websocket_subprotocol' => true,\n" + " 'open_websocket_close_frame' => true,\n" + " 'open_websocket_ping_frame' => true,\n" + " 'open_websocket_pong_frame' => true,\n" + " 'open_http2_protocol' => true,\n" + " 'open_mqtt_protocol' => true,\n" + " 'open_redis_protocol' => true,\n" + " 'max_idle_time' => true,\n" + " 'tcp_keepidle' => true,\n" + " 'tcp_keepinterval' => true,\n" + " 'tcp_keepcount' => true,\n" + " 'tcp_user_timeout' => true,\n" + " 'tcp_fastopen' => true,\n" + " 'open_length_check' => true,\n" + " 'package_length_type' => true,\n" + " 'package_length_offset' => true,\n" + " 'package_body_offset' => true,\n" + " 'package_body_start' => true,\n" + " 'package_length_func' => true,\n" + " 'package_max_length' => true,\n" + " 'ssl_compress' => true,\n" + " 'ssl_protocols' => true,\n" + " 'ssl_verify_peer' => true,\n" + " 'ssl_allow_self_signed' => true,\n" + " 'ssl_client_cert_file' => true,\n" + " 'ssl_verify_depth' => true,\n" + " 'ssl_prefer_server_ciphers' => true,\n" + " 'ssl_ciphers' => true,\n" + " 'ssl_ecdh_curve' => true,\n" + " 'ssl_dhparam' => true,\n" + " 'ssl_sni_certs' => true,\n" + " ];\n" + "\n" + " public const AIO_OPTIONS = [\n" + " 'aio_core_worker_num' => true,\n" + " 'aio_worker_num' => true,\n" + " 'aio_max_wait_time' => true,\n" + " 'aio_max_idle_time' => true,\n" + " 'enable_signalfd' => true,\n" + " 'wait_signal' => true,\n" + " 'dns_cache_refresh_time' => true,\n" + " 'thread_num' => true,\n" + " 'min_thread_num' => true,\n" + " 'max_thread_num' => true,\n" + " 'socket_dontwait' => true,\n" + " 'dns_lookup_random' => true,\n" + " 'use_async_resolver' => true,\n" + " 'enable_coroutine' => true,\n" + " '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" + "{\n" + " return $obj->setOpt($opt, $value);\n" + "}\n" + "\n" + "function swoole_curl_setopt_array(Swoole\\Curl\\Handler $obj, $array): bool\n" + "{\n" + " foreach ($array as $k => $v) {\n" + " if ($obj->setOpt($k, $v) !== true) {\n" + " return false;\n" + " }\n" + " }\n" + " return true;\n" + "}\n" + "\n" + "function swoole_curl_exec(Swoole\\Curl\\Handler $obj)\n" + "{\n" + " return $obj->exec();\n" + "}\n" + "\n" + "function swoole_curl_getinfo(Swoole\\Curl\\Handler $obj, int $opt = 0)\n" + "{\n" + " $info = $obj->getInfo();\n" + " if (is_array($info) and $opt) {\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): int\n" + "{\n" + " return $obj->errno();\n" + "}\n" + "\n" + "function swoole_curl_error(Swoole\\Curl\\Handler $obj): string\n" + "{\n" + " return $obj->error();\n" + "}\n" + "\n" + "function swoole_curl_reset(Swoole\\Curl\\Handler $obj)\n" + "{\n" + " return $obj->reset();\n" + "}\n" + "\n" + "function swoole_curl_close(Swoole\\Curl\\Handler $obj): void\n" + "{\n" + " $obj->close();\n" + "}\n" + "\n" + "function swoole_curl_multi_getcontent(Swoole\\Curl\\Handler $obj)\n" + "{\n" + " return $obj->getContent();\n" + "}\n"; + +static const char* swoole_library_source_ext_sockets = + "\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" + "use Swoole\\Coroutine\\Socket;\n" + "\n" + "function swoole_socket_create(int $domain, int $type, int $protocol)\n" + "{\n" + " return new Socket($domain, $type, $protocol);\n" + "}\n" + "\n" + "function swoole_socket_connect(Socket $socket, string $address, int $port = 0)\n" + "{\n" + " return $socket->connect($address, $port);\n" + "}\n" + "\n" + "function swoole_socket_read(Socket $socket, int $length, int $type = PHP_BINARY_READ)\n" + "{\n" + " if ($type != PHP_BINARY_READ) {\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)\n" + "{\n" + " if ($length > 0 and $length < strlen($buffer)) {\n" + " $buffer = substr($buffer, 0, $length);\n" + " }\n" + " return $socket->send($buffer);\n" + "}\n" + "\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" + " }\n" + " return swoole_socket_write($socket, $buffer, $length);\n" + "}\n" + "\n" + "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" + " }\n" + " if ($flags & MSG_PEEK) {\n" + " $buffer = $socket->peek($length);\n" + " }\n" + " $timeout = $flags & MSG_DONTWAIT ? 0.001 : 0;\n" + " if ($flags & MSG_WAITALL) {\n" + " $buffer = $socket->recvAll($length, $timeout);\n" + " } else {\n" + " $buffer = $socket->recv($length, $timeout);\n" + " }\n" + " if ($buffer === false) {\n" + " return false;\n" + " }\n" + " return strlen($buffer);\n" + "}\n" + "\n" + "function swoole_socket_sendto(Socket $socket, string $buffer, int $length, int $flags, string $addr, int $port = 0)\n" + "{\n" + " if ($flags != 0) {\n" + " throw new RuntimeException(\"\\$flags[{$flags}] is not supported\");\n" + " }\n" + " if ($socket->type != SOCK_DGRAM) {\n" + " throw new RuntimeException('only supports dgram type socket');\n" + " }\n" + " if ($length > 0 and $length < strlen($buffer)) {\n" + " $buffer = substr($buffer, 0, $length);\n" + " }\n" + " return $socket->sendto($addr, $port, $buffer);\n" + "}\n" + "\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" + " $data = $socket->recvfrom($peer);\n" + " if ($data === false) {\n" + " return false;\n" + " }\n" + " $name = $peer['address'];\n" + " if (func_num_args() == 6) {\n" + " $port = $peer['port'];\n" + " }\n" + " if ($length < strlen($data)) {\n" + " $buffer = substr($data, 0, $length);\n" + " } else {\n" + " $buffer = $data;\n" + " }\n" + " return strlen($buffer);\n" + "}\n" + "\n" + "function swoole_socket_bind(Socket $socket, string $address, int $port = 0): bool\n" + "{\n" + " return $socket->bind($address, $port);\n" + "}\n" + "\n" + "function swoole_socket_listen(Socket $socket, int $backlog = 0): bool\n" + "{\n" + " return $socket->listen($backlog);\n" + "}\n" + "\n" + "function swoole_socket_create_listen(int $port, int $backlog = 128)\n" + "{\n" + " $socket = new Socket(AF_INET, SOCK_STREAM, SOL_TCP);\n" + " if (!$socket->bind('0.0.0.0', $port)) {\n" + " return false;\n" + " }\n" + " if (!$socket->listen($backlog)) {\n" + " return false;\n" + " }\n" + " return $socket;\n" + "}\n" + "\n" + "function swoole_socket_accept(Socket $socket)\n" + "{\n" + " return $socket->accept();\n" + "}\n" + "\n" + "function swoole_socket_getpeername(Socket $socket, &$address, &$port = null)\n" + "{\n" + " $info = $socket->getpeername();\n" + " if (!$info) {\n" + " return false;\n" + " }\n" + " $address = $info['address'];\n" + " if (func_num_args() == 3) {\n" + " $port = $info['port'];\n" + " }\n" + " return true;\n" + "}\n" + "\n" + "function swoole_socket_getsockname(Socket $socket, &$address, &$port = null)\n" + "{\n" + " $info = $socket->getsockname();\n" + " if (!$info) {\n" + " return false;\n" + " }\n" + " $address = $info['address'];\n" + " if (func_num_args() == 3) {\n" + " $port = $info['port'];\n" + " }\n" + " return true;\n" + "}\n" + "\n" + "function swoole_socket_set_option(Socket $socket, int $level, int $optname, $optval): bool\n" + "{\n" + " return $socket->setOption($level, $optname, $optval);\n" + "}\n" + "\n" + "function swoole_socket_setopt(Socket $socket, int $level, int $optname, $optval): bool\n" + "{\n" + " return $socket->setOption($level, $optname, $optval);\n" + "}\n" + "\n" + "function swoole_socket_get_option(Socket $socket, int $level, int $optname)\n" + "{\n" + " return $socket->getOption($level, $optname);\n" + "}\n" + "\n" + "function swoole_socket_getopt(Socket $socket, int $level, int $optname)\n" + "{\n" + " return $socket->getOption($level, $optname);\n" + "}\n" + "\n" + "function swoole_socket_shutdown(Socket $socket, int $how = 2): bool\n" + "{\n" + " return $socket->shutdown($how);\n" + "}\n" + "\n" + "function swoole_socket_close(Socket $socket)\n" + "{\n" + " $socket->close();\n" + "}\n" + "\n" + "function swoole_socket_clear_error(?Socket $socket = null)\n" + "{\n" + " if ($socket) {\n" + " $socket->errCode = 0;\n" + " }\n" + " swoole_clear_error();\n" + "}\n" + "\n" + "function swoole_socket_last_error(?Socket $socket = null): int\n" + "{\n" + " if ($socket) {\n" + " return $socket->errCode;\n" + " }\n" + " return swoole_last_error();\n" + "}\n" + "\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); // @phpstan-ignore property.notFound\n" + " }\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; // @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 = + "\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" + "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) { // @phpstan-ignore if.alwaysTrue\n" + " function _string(string $string = ''): Swoole\\StringObject\n" + " {\n" + " return new Swoole\\StringObject($string);\n" + " }\n" + "\n" + " function _mbstring(string $string = ''): Swoole\\MultibyteStringObject\n" + " {\n" + " return new Swoole\\MultibyteStringObject($string);\n" + " }\n" + "\n" + " function _array(array $array = []): Swoole\\ArrayObject\n" + " {\n" + " return new Swoole\\ArrayObject($array);\n" + " }\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" + "}\n" + "\n" + "function swoole_mbstring(string $string = ''): Swoole\\MultibyteStringObject\n" + "{\n" + " return new Swoole\\MultibyteStringObject($string);\n" + "}\n" + "\n" + "function swoole_array(array $array = []): Swoole\\ArrayObject\n" + "{\n" + " return new Swoole\\ArrayObject($array);\n" + "}\n" + "\n" + "function swoole_table(int $size, string $fields): Swoole\\Table\n" + "{\n" + " $_fields = swoole_string($fields)->trim()->split(',');\n" + "\n" + " $table = new Swoole\\Table($size, 0.25);\n" + "\n" + " foreach ($_fields as $f) {\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 = (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" + " if (!$table->create()) {\n" + " throw new RuntimeException('failed to create table');\n" + " }\n" + "\n" + " return $table;\n" + "}\n" + "\n" + "function swoole_array_list(...$arrray): Swoole\\ArrayObject\n" + "{\n" + " return new Swoole\\ArrayObject($arrray);\n" + "}\n" + "\n" + "function swoole_array_default_value(array $array, $key, $default_value = null)\n" + "{\n" + " return array_key_exists($key, $array) ? $array[$key] : $default_value;\n" + "}\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" + "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 = + "\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" + "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" + " class_alias(Swoole\\Coroutine\\FastCGI\\Client::class, Co\\FastCGI\\Client::class, true);\n" + " class_alias(Swoole\\Coroutine\\FastCGI\\Client\\Exception::class, Co\\FastCGI\\Client\\Exception::class, true);\n" + " class_alias(Swoole\\Coroutine\\FastCGI\\Proxy::class, Co\\FastCGI\\Proxy::class, true);\n" + "}\n" + "\n" + "class_alias(Swoole\\Process\\Manager::class, Swoole\\Process\\ProcessManager::class, true);\n"; + +static const char* swoole_library_source_alias_ns = + "\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 Co;\n" + "\n" + "use Swoole\\Coroutine;\n" + "\n" + "if (SWOOLE_USE_SHORTNAME) { // @phpstan-ignore if.alwaysTrue\n" + " function run(callable $fn, ...$args)\n" + " {\n" + " return \\Swoole\\Coroutine\\run($fn, ...$args);\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"; + +void php_swoole_load_library() +{ + _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_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 new file mode 100644 index 00000000000..7717ca7ead6 --- /dev/null +++ b/ext-src/php_swoole_process.h @@ -0,0 +1,28 @@ +/* + +----------------------------------------------------------------------+ + | 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_process_pool.h" + +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 new file mode 100644 index 00000000000..0fe4904a0e0 --- /dev/null +++ b/ext-src/php_swoole_server.h @@ -0,0 +1,161 @@ +/* + +----------------------------------------------------------------------+ + | 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: Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#pragma once + +#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_onBeforeShutdown, // master + SW_SERVER_CB_onShutdown, // master + SW_SERVER_CB_onWorkerStart, // worker(event & task) + SW_SERVER_CB_onWorkerStop, // worker(event & task) + SW_SERVER_CB_onBeforeReload, // manager + SW_SERVER_CB_onAfterReload, // manager + SW_SERVER_CB_onTask, // worker(task) + SW_SERVER_CB_onFinish, // worker(event & task) + SW_SERVER_CB_onWorkerExit, // worker(event) + SW_SERVER_CB_onWorkerError, // manager + SW_SERVER_CB_onManagerStart, // manager + SW_SERVER_CB_onManagerStop, // manager + SW_SERVER_CB_onPipeMessage, // worker(event & task) +}; +//-------------------------------------------------------- +enum php_swoole_server_port_callback_type { + SW_SERVER_CB_onConnect, // stream, worker(event) + SW_SERVER_CB_onReceive, // stream, worker(event) + SW_SERVER_CB_onClose, // stream, worker(event) + SW_SERVER_CB_onPacket, // dgram, worker(event) + SW_SERVER_CB_onRequest, // http, worker(event) + SW_SERVER_CB_onHandshake, // websocket, worker(event) + SW_SERVER_CB_onBeforeHandshakeResponse, // websocket, worker(event) + SW_SERVER_CB_onOpen, // websocket, worker(event) + SW_SERVER_CB_onMessage, // websocket, worker(event) + SW_SERVER_CB_onDisconnect, // websocket (non websocket connection), worker(event) + SW_SERVER_CB_onBufferFull, // worker(event) + SW_SERVER_CB_onBufferEmpty, // worker(event) +}; + +#define PHP_SWOOLE_SERVER_CALLBACK_NUM (SW_SERVER_CB_onPipeMessage + 1) +#define PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM (SW_SERVER_CB_onBufferEmpty + 1) + +namespace swoole { +struct ServerPortProperty; +struct TaskCo; +}; // namespace swoole + +zval *php_swoole_server_zval_ptr(swoole::Server *serv); +swoole::ServerPortProperty *php_swoole_server_get_port_property(swoole::ListenPort *port); +void php_swoole_server_set_port_property(swoole::ListenPort *port, swoole::ServerPortProperty *property); + +namespace swoole { + +struct ServerPortProperty { + 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::Callable *php_swoole_server_get_callback(swServer *serv, int server_fd, int event_type); +int php_swoole_create_dir(const char *path, size_t length); +void php_swoole_server_before_start(swServer *serv, zval *zobject); +bool php_swoole_server_isset_callback(swServer *serv, swListenPort *port, int event_type); +void php_swoole_server_send_yield(swServer *serv, swoole::SessionId sesion_id, zval *zdata, zval *return_value); +void php_swoole_get_recv_data(swServer *serv, zval *zdata, swRecvData *req); +void php_swoole_server_onConnect(swServer *, swDataHead *); +int php_swoole_server_onReceive(swServer *, swRecvData *); +int php_swoole_http_server_onReceive(swServer *, swRecvData *); +void php_swoole_http_server_onClose(swServer *serv, swDataHead *info); +int php_swoole_redis_server_onReceive(swServer *serv, swRecvData *req); +int php_swoole_server_onPacket(swServer *, swRecvData *); +void php_swoole_server_onClose(swServer *, swDataHead *); +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 new file mode 100644 index 00000000000..896728b4cde --- /dev/null +++ b/ext-src/swoole_async_coro.cc @@ -0,0 +1,193 @@ +/* + +----------------------------------------------------------------------+ + | 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 "swoole_socket.h" + +#include +#include +#include + +using std::string; +using std::vector; +using swoole::Coroutine; +using swoole::PHPCoroutine; +using swoole::Timer; +using swoole::coroutine::Socket; + +struct DNSCacheEntity { + char address[INET6_ADDRSTRLEN]; + time_t update_time; +}; + +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++) { + efree(i->second); + } +} + +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); + } +#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; + } + + zval *zset = nullptr; + HashTable *vht; + zval *ztmp; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(zset) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + vht = Z_ARRVAL_P(zset); + + php_swoole_set_global_option(vht); + php_swoole_set_aio_option(vht); + + if (php_swoole_array_get_value(vht, "enable_signalfd", ztmp)) { + SwooleG.enable_signalfd = zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "wait_signal", ztmp)) { + SwooleG.wait_signal = zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "dns_cache_refresh_time", ztmp)) { + SwooleG.dns_cache_refresh_time = zval_get_double(ztmp); + } + if (php_swoole_array_get_value(vht, "thread_num", ztmp) || + php_swoole_array_get_value(vht, "min_thread_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, "max_thread_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, "socket_dontwait", ztmp)) { + SwooleG.socket_dontwait = zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "dns_lookup_random", ztmp)) { + SwooleG.dns_lookup_random = zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "use_async_resolver", ztmp)) { + SwooleG.use_async_resolver = zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "enable_coroutine", 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; + + 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"); + RETURN_FALSE; + } + + if (Z_STRLEN_P(domain) == 0) { + php_swoole_fatal_error(E_WARNING, "domain name empty"); + RETURN_FALSE; + } + + // find cache + std::string key(Z_STRVAL_P(domain), Z_STRLEN_P(domain)); + DNSCacheEntity *cache; + + if (request_cache_map.find(key) != request_cache_map.end()) { + cache = request_cache_map[key]; + if (cache->update_time > Timer::get_absolute_msec()) { + RETURN_STRING(cache->address); + } + } + + php_swoole_check_reactor(); + + 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; + } + + if (SwooleG.dns_lookup_random) { + RETVAL_STRING(result[rand() % result.size()].c_str()); + } else { + RETVAL_STRING(result[0].c_str()); + } + + auto cache_iterator = request_cache_map.find(key); + if (cache_iterator == request_cache_map.end()) { + cache = (DNSCacheEntity *) emalloc(sizeof(DNSCacheEntity)); + request_cache_map[key] = cache; + } else { + cache = cache_iterator->second; + } + 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); +} diff --git a/ext-src/swoole_atomic.cc b/ext-src/swoole_atomic.cc new file mode 100644 index 00000000000..90dc7f4fe81 --- /dev/null +++ b/ext-src/swoole_atomic.cc @@ -0,0 +1,325 @@ +/* + +----------------------------------------------------------------------+ + | 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" + +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; + +zend_class_entry *swoole_atomic_long_ce; +static zend_object_handlers swoole_atomic_long_handlers; + +struct AtomicObject { + sw_atomic_t *ptr; + zend_object std; +}; + +static sw_inline AtomicObject *php_swoole_atomic_fetch_object(zend_object *obj) { + return (AtomicObject *) ((char *) obj - swoole_atomic_handlers.offset); +} + +static sw_atomic_t *php_swoole_atomic_get_ptr(zval *zobject) { + return php_swoole_atomic_fetch_object(Z_OBJ_P(zobject))->ptr; +} + +void php_swoole_atomic_set_ptr(zval *zobject, sw_atomic_t *ptr) { + php_swoole_atomic_fetch_object(Z_OBJ_P(zobject))->ptr = ptr; +} + +static void php_swoole_atomic_free_object(zend_object *object) { + sw_mem_pool()->free((void *) php_swoole_atomic_fetch_object(object)->ptr); + zend_object_std_dtor(object); +} + +static zend_object *php_swoole_atomic_create_object(zend_class_entry *ce) { + AtomicObject *atomic = (AtomicObject *) zend_object_alloc(sizeof(AtomicObject), ce); + if (atomic == nullptr) { + zend_throw_exception(swoole_exception_ce, "global memory allocation failure", SW_ERROR_MALLOC_FAIL); + } + + zend_object_std_init(&atomic->std, ce); + object_properties_init(&atomic->std, ce); + atomic->std.handlers = &swoole_atomic_handlers; + atomic->ptr = (sw_atomic_t *) sw_mem_pool()->alloc(sizeof(sw_atomic_t)); + if (atomic->ptr == nullptr) { + zend_throw_exception(swoole_exception_ce, "global memory allocation failure", SW_ERROR_MALLOC_FAIL); + } + + return &atomic->std; +} + +struct AtomicLongObject { + sw_atomic_long_t *ptr; + zend_object std; +}; + +static sw_inline AtomicLongObject *php_swoole_atomic_long_fetch_object(zend_object *obj) { + return (AtomicLongObject *) ((char *) obj - swoole_atomic_long_handlers.offset); +} + +static sw_atomic_long_t *php_swoole_atomic_long_get_ptr(zval *zobject) { + return php_swoole_atomic_long_fetch_object(Z_OBJ_P(zobject))->ptr; +} + +void php_swoole_atomic_long_set_ptr(zval *zobject, sw_atomic_long_t *ptr) { + php_swoole_atomic_long_fetch_object(Z_OBJ_P(zobject))->ptr = ptr; +} + +static void php_swoole_atomic_long_free_object(zend_object *object) { + sw_mem_pool()->free((void *) php_swoole_atomic_long_fetch_object(object)->ptr); + zend_object_std_dtor(object); +} + +static zend_object *php_swoole_atomic_long_create_object(zend_class_entry *ce) { + AtomicLongObject *atomic_long = (AtomicLongObject *) zend_object_alloc(sizeof(AtomicLongObject), ce); + if (atomic_long == nullptr) { + zend_throw_exception(swoole_exception_ce, "global memory allocation failure", SW_ERROR_MALLOC_FAIL); + } + + zend_object_std_init(&atomic_long->std, ce); + object_properties_init(&atomic_long->std, ce); + atomic_long->std.handlers = &swoole_atomic_long_handlers; + + atomic_long->ptr = (sw_atomic_long_t *) sw_mem_pool()->alloc(sizeof(sw_atomic_long_t)); + if (atomic_long->ptr == nullptr) { + zend_throw_exception(swoole_exception_ce, "global memory allocation failure", SW_ERROR_MALLOC_FAIL); + } + + return &atomic_long->std; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_atomic, __construct); +static PHP_METHOD(swoole_atomic, add); +static PHP_METHOD(swoole_atomic, sub); +static PHP_METHOD(swoole_atomic, get); +static PHP_METHOD(swoole_atomic, set); +static PHP_METHOD(swoole_atomic, cmpset); +static PHP_METHOD(swoole_atomic, wait); +static PHP_METHOD(swoole_atomic, wakeup); + +static PHP_METHOD(swoole_atomic_long, __construct); +static PHP_METHOD(swoole_atomic_long, add); +static PHP_METHOD(swoole_atomic_long, sub); +static PHP_METHOD(swoole_atomic_long, get); +static PHP_METHOD(swoole_atomic_long, set); +static PHP_METHOD(swoole_atomic_long, cmpset); +SW_EXTERN_C_END + +// clang-format off + +static const zend_function_entry swoole_atomic_methods[] = +{ + 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_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", 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", 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, + php_swoole_atomic_long_create_object, + php_swoole_atomic_long_free_object, + AtomicLongObject, + std); +} + +PHP_METHOD(swoole_atomic, __construct) { + sw_atomic_t *atomic = php_swoole_atomic_get_ptr(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); + + *atomic = (sw_atomic_t) value; +} + +PHP_METHOD(swoole_atomic, add) { + sw_atomic_t *atomic = php_swoole_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_atomic, sub) { + sw_atomic_t *atomic = php_swoole_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_atomic, get) { + sw_atomic_t *atomic = php_swoole_atomic_get_ptr(ZEND_THIS); + RETURN_LONG(*atomic); +} + +PHP_METHOD(swoole_atomic, set) { + sw_atomic_t *atomic = php_swoole_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_atomic, cmpset) { + sw_atomic_t *atomic = php_swoole_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_atomic, wait) { + sw_atomic_t *atomic = php_swoole_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_atomic, wakeup) { + sw_atomic_t *atomic = php_swoole_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_atomic_long, __construct) { + sw_atomic_long_t *atomic_long = php_swoole_atomic_long_get_ptr(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); + + *atomic_long = (sw_atomic_long_t) value; + RETURN_TRUE; +} + +PHP_METHOD(swoole_atomic_long, add) { + sw_atomic_long_t *atomic_long = php_swoole_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_atomic_long, sub) { + sw_atomic_long_t *atomic_long = php_swoole_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_atomic_long, get) { + sw_atomic_long_t *atomic_long = php_swoole_atomic_long_get_ptr(ZEND_THIS); + RETURN_LONG(*atomic_long); +} + +PHP_METHOD(swoole_atomic_long, set) { + sw_atomic_long_t *atomic_long = php_swoole_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_atomic_long, cmpset) { + sw_atomic_long_t *atomic_long = php_swoole_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)); +} diff --git a/ext-src/swoole_channel_coro.cc b/ext-src/swoole_channel_coro.cc new file mode 100644 index 00000000000..a265ce45acc --- /dev/null +++ b/ext-src/swoole_channel_coro.cc @@ -0,0 +1,223 @@ +/* + +----------------------------------------------------------------------+ + | 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: Xinyu Zhu | + | Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#include "php_swoole_cxx.h" + +#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; +static zend_object_handlers swoole_channel_coro_handlers; + +struct ChannelObject { + Channel *chan; + zend_object std; +}; + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_channel_coro, __construct); +static PHP_METHOD(swoole_channel_coro, push); +static PHP_METHOD(swoole_channel_coro, pop); +static PHP_METHOD(swoole_channel_coro, close); +static PHP_METHOD(swoole_channel_coro, stats); +static PHP_METHOD(swoole_channel_coro, length); +static PHP_METHOD(swoole_channel_coro, isEmpty); +static PHP_METHOD(swoole_channel_coro, isFull); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_channel_coro_methods[] = +{ + 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 + +static sw_inline ChannelObject *php_swoole_channel_coro_fetch_object(zend_object *obj) { + return (ChannelObject *) ((char *) obj - swoole_channel_coro_handlers.offset); +} + +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)) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); + } + return chan; +} + +static void php_swoole_channel_coro_dtor_object(zend_object *object) { + zend_objects_destroy_object(object); + + ChannelObject *chan_object = php_swoole_channel_coro_fetch_object(object); + Channel *chan = chan_object->chan; + if (chan) { + zval *data; + while ((data = (zval *) chan->pop_data())) { + sw_zval_free(data); + } + delete chan; + chan_object->chan = nullptr; + } +} + +static void php_swoole_channel_coro_free_object(zend_object *object) { + ChannelObject *chan_object = php_swoole_channel_coro_fetch_object(object); + Channel *chan = chan_object->chan; + if (chan) { + delete chan; + } + zend_object_std_dtor(object); +} + +static zend_object *php_swoole_channel_coro_create_object(zend_class_entry *ce) { + ChannelObject *chan_object = (ChannelObject *) zend_object_alloc(sizeof(ChannelObject), ce); + zend_object_std_init(&chan_object->std, ce); + object_properties_init(&chan_object->std, ce); + chan_object->std.handlers = &swoole_channel_coro_handlers; + return &chan_object->std; +} + +void php_swoole_channel_coro_minit(int module_number) { + 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, + php_swoole_channel_coro_create_object, + php_swoole_channel_coro_free_object, + ChannelObject, + std); + SW_SET_CLASS_DTOR(swoole_channel_coro, php_swoole_channel_coro_dtor_object); + if (SWOOLE_G(use_shortname)) { + SW_CLASS_ALIAS("Chan", swoole_channel_coro); + } + + 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", 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) { + zend_long capacity = 1; + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(capacity) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (capacity <= 0) { + capacity = 1; + } + + ChannelObject *chan_t = php_swoole_channel_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); + chan_t->chan = new Channel(capacity); + zend_update_property_long(swoole_channel_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("capacity"), capacity); +} + +static PHP_METHOD(swoole_channel_coro, push) { + Channel *chan = php_swoole_get_channel(ZEND_THIS); + zval *zdata; + double timeout = -1; + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 2) + Z_PARAM_ZVAL(zdata) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + 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"), Channel::ERROR_OK); + RETURN_TRUE; + } else { + 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; + } +} + +static PHP_METHOD(swoole_channel_coro, pop) { + Channel *chan = php_swoole_get_channel(ZEND_THIS); + double timeout = -1; + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + zval *zdata = (zval *) chan->pop(timeout); + 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"), Channel::ERROR_OK); + } else { + zend_update_property_long( + swoole_channel_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), chan->get_error()); + RETURN_FALSE; + } +} + +static PHP_METHOD(swoole_channel_coro, close) { + Channel *chan = php_swoole_get_channel(ZEND_THIS); + RETURN_BOOL(chan->close()); +} + +static PHP_METHOD(swoole_channel_coro, length) { + Channel *chan = php_swoole_get_channel(ZEND_THIS); + RETURN_LONG(chan->length()); +} + +static PHP_METHOD(swoole_channel_coro, isEmpty) { + Channel *chan = php_swoole_get_channel(ZEND_THIS); + RETURN_BOOL(chan->is_empty()); +} + +static PHP_METHOD(swoole_channel_coro, isFull) { + Channel *chan = php_swoole_get_channel(ZEND_THIS); + RETURN_BOOL(chan->is_full()); +} + +static PHP_METHOD(swoole_channel_coro, stats) { + Channel *chan = php_swoole_get_channel(ZEND_THIS); + array_init(return_value); + add_assoc_long_ex(return_value, ZEND_STRL("consumer_num"), chan->consumer_num()); + 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 new file mode 100644 index 00000000000..1db29c46030 --- /dev/null +++ b/ext-src/swoole_client.cc @@ -0,0 +1,1562 @@ +/* + +----------------------------------------------------------------------+ + | 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" + +#include +#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::Socks5Proxy; +using swoole::String; +using swoole::network::Client; +using swoole::network::Socket; + +static std::unordered_map *> long_connections; + +zend_class_entry *swoole_client_ce; +zend_object_handlers swoole_client_handlers; + +static zend_class_entry *swoole_client_exception_ce; +static zend_object_handlers swoole_client_exception_handlers; + +static Client *php_swoole_client_new(zval *zobject, char *host, int host_len, int port); + +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 *client_get_zsocket(zval *zobject) { + return php_swoole_client_fetch_object(Z_OBJ_P(zobject))->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 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 *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; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_client, __construct); +static PHP_METHOD(swoole_client, __destruct); +static PHP_METHOD(swoole_client, set); +static PHP_METHOD(swoole_client, connect); +static PHP_METHOD(swoole_client, recv); +static PHP_METHOD(swoole_client, send); +static PHP_METHOD(swoole_client, sendfile); +static PHP_METHOD(swoole_client, sendto); +#ifdef SW_USE_OPENSSL +static PHP_METHOD(swoole_client, enableSSL); +static PHP_METHOD(swoole_client, getPeerCert); +static PHP_METHOD(swoole_client, verifyPeerCert); +#endif +static PHP_METHOD(swoole_client, isConnected); +static PHP_METHOD(swoole_client, getsockname); +static PHP_METHOD(swoole_client, getpeername); +static PHP_METHOD(swoole_client, close); +static PHP_METHOD(swoole_client, shutdown); + +#ifdef SWOOLE_SOCKETS_SUPPORT +static PHP_METHOD(swoole_client, getSocket); +#endif +SW_EXTERN_C_END + +#ifdef PHP_SWOOLE_CLIENT_USE_POLL +static uint32_t client_poll_add(zval *sock_array, uint32_t index, struct pollfd *fds, int maxevents, int event); +static int client_poll_wait(zval *sock_array, struct pollfd *fds, int maxevents, int n_event, int revent); +#else +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 + +Client *php_swoole_client_get_cli_safe(zval *zobject) { + Client *cli = php_swoole_client_get_cli(zobject); + 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 +static const zend_function_entry swoole_client_methods[] = +{ + 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_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_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_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", 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, client_create_object, client_free_object, ClientObject, std); + + 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); + zend_declare_property_bool(swoole_client_ce, ZEND_STRL("reuse"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_client_ce, ZEND_STRL("reuseCount"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_client_ce, ZEND_STRL("type"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_client_ce, ZEND_STRL("id"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_client_ce, ZEND_STRL("setting"), ZEND_ACC_PUBLIC); + + zend_declare_class_constant_long(swoole_client_ce, ZEND_STRL("MSG_OOB"), MSG_OOB); + zend_declare_class_constant_long(swoole_client_ce, ZEND_STRL("MSG_PEEK"), MSG_PEEK); + zend_declare_class_constant_long(swoole_client_ce, ZEND_STRL("MSG_DONTWAIT"), MSG_DONTWAIT); + zend_declare_class_constant_long(swoole_client_ce, ZEND_STRL("MSG_WAITALL"), MSG_WAITALL); + + zend_declare_class_constant_long(swoole_client_ce, ZEND_STRL("SHUT_RDWR"), SHUT_RDWR); + zend_declare_class_constant_long(swoole_client_ce, ZEND_STRL("SHUT_RD"), SHUT_RD); + zend_declare_class_constant_long(swoole_client_ce, ZEND_STRL("SHUT_WR"), SHUT_WR); +} + +#ifdef SW_USE_OPENSSL +void php_swoole_client_check_ssl_setting(Client *cli, zval *zset) { + HashTable *vht = Z_ARRVAL_P(zset); + zval *ztmp; + + if (php_swoole_array_get_value(vht, "ssl_protocols", ztmp)) { + cli->ssl_context->set_protocols(zval_get_long(ztmp)); + } + if (php_swoole_array_get_value(vht, "ssl_compress", ztmp)) { + cli->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 (!cli->ssl_context->set_cert_file(str_v.to_std_string())) { + php_swoole_fatal_error(E_ERROR, "ssl cert file[%s] not found", str_v.val()); + return; + } + } + if (php_swoole_array_get_value(vht, "ssl_key_file", ztmp)) { + zend::String str_v(ztmp); + if (!cli->ssl_context->set_key_file(str_v.to_std_string())) { + php_swoole_fatal_error(E_ERROR, "ssl key file[%s] not found", str_v.val()); + return; + } + } + if (php_swoole_array_get_value(vht, "ssl_passphrase", ztmp)) { + zend::String str_v(ztmp); + cli->ssl_context->passphrase = str_v.to_std_string(); + } +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + if (php_swoole_array_get_value(vht, "ssl_host_name", ztmp)) { + zend::String str_v(ztmp); + cli->ssl_context->tls_host_name = str_v.to_std_string(); + } +#endif + if (php_swoole_array_get_value(vht, "ssl_verify_peer", ztmp)) { + cli->ssl_context->verify_peer = zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "ssl_allow_self_signed", ztmp)) { + cli->ssl_context->allow_self_signed = zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "ssl_cafile", ztmp)) { + zend::String str_v(ztmp); + cli->ssl_context->cafile = str_v.to_std_string(); + } + if (php_swoole_array_get_value(vht, "ssl_capath", ztmp)) { + zend::String str_v(ztmp); + cli->ssl_context->capath = str_v.to_std_string(); + } + if (php_swoole_array_get_value(vht, "ssl_verify_depth", ztmp)) { + 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; + } +} +#endif + +bool php_swoole_client_check_setting(Client *cli, zval *zset) { + HashTable *vht; + zval *ztmp; + int value = 1; + + vht = Z_ARRVAL_P(zset); + + // buffer: eof check + if (php_swoole_array_get_value(vht, "open_eof_check", ztmp)) { + cli->open_eof_check = zval_is_true(ztmp); + } + // buffer: split package with eof + if (php_swoole_array_get_value(vht, "open_eof_split", ztmp)) { + cli->protocol.split_by_eof = zval_is_true(ztmp); + if (cli->protocol.split_by_eof) { + cli->open_eof_check = 1; + } + } + // package eof + if (php_swoole_array_get_value(vht, "package_eof", ztmp)) { + zend::String str_v(ztmp); + cli->protocol.package_eof_len = str_v.len(); + if (cli->protocol.package_eof_len == 0) { + php_swoole_fatal_error(E_ERROR, "package_eof cannot be an empty string"); + return false; + } else if (cli->protocol.package_eof_len > SW_DATA_EOF_MAXLEN) { + php_swoole_fatal_error(E_ERROR, "package_eof max length is %d", SW_DATA_EOF_MAXLEN); + return false; + } + memcpy(cli->protocol.package_eof, str_v.val(), str_v.len()); + } + // open mqtt protocol + if (php_swoole_array_get_value(vht, "open_mqtt_protocol", ztmp)) { + cli->open_length_check = zval_is_true(ztmp); + if (zval_is_true(ztmp)) { + swoole::mqtt::set_protocol(&cli->protocol); + } + } + // open length check + if (php_swoole_array_get_value(vht, "open_length_check", ztmp)) { + cli->open_length_check = zval_is_true(ztmp); + cli->protocol.get_package_length = Protocol::default_length_func; + } + // package length size + if (php_swoole_array_get_value(vht, "package_length_type", ztmp)) { + zend::String str_v(ztmp); + cli->protocol.package_length_type = str_v.val()[0]; + cli->protocol.package_length_size = swoole_type_size(cli->protocol.package_length_type); + + if (cli->protocol.package_length_size == 0) { + php_swoole_fatal_error(E_ERROR, + "Unknown package_length_type name '%c', see pack(). Link: http://php.net/pack", + cli->protocol.package_length_type); + return false; + } + } + // package length offset + if (php_swoole_array_get_value(vht, "package_length_offset", ztmp)) { + zend_long v = zval_get_long(ztmp); + cli->protocol.package_length_offset = SW_MAX(0, SW_MIN(v, UINT16_MAX)); + } + // package body start + if (php_swoole_array_get_value(vht, "package_body_offset", ztmp)) { + zend_long v = zval_get_long(ztmp); + cli->protocol.package_body_offset = SW_MAX(0, SW_MIN(v, UINT16_MAX)); + } + // length function + if (php_swoole_array_get_value(vht, "package_length_func", ztmp)) { + 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; + } + /** + * package max length + */ + if (php_swoole_array_get_value(vht, "package_max_length", 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; + } + /** + * socket send/recv buffer size + */ + if (php_swoole_array_get_value(vht, "socket_buffer_size", 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 = 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 = php_swoole_parse_to_size(ztmp); + value = SW_MAX(0, SW_MIN(v, UINT32_MAX)); + cli->buffer_low_watermark = value; + } + /** + * bind port + */ + 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)); + } + /** + * bind address + */ + if (php_swoole_array_get_value(vht, "bind_address", ztmp)) { + bind_address = zend::String(ztmp).to_std_string(); + } + if (bind_address.length() > 0 && cli->socket->bind(bind_address, &bind_port) < 0) { + php_swoole_error(E_WARNING, "bind address or port error in set method"); + swoole_set_last_error(errno); + return false; + } + /** + * client: tcp_nodelay + */ + if (php_swoole_array_get_value(vht, "open_tcp_nodelay", ztmp)) { + if (zval_is_true(ztmp)) { + goto _open_tcp_nodelay; + } + } else { + _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) { + swoole_sys_warning("setsockopt(%d, TCP_NODELAY) failed", cli->socket->fd); + } + } + } + /** + * 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"); + // because we do not set last errcode, return true + return true; + } + } + } else { + php_swoole_fatal_error(E_WARNING, "socks5_port should not be null"); + // because we do not set last errcode, return true + return true; + } + } + /** + * 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"); + // because we do not set last errcode, return true + return true; + } + } + } else { + php_swoole_fatal_error(E_WARNING, "http_proxy_port should not be null"); + // because we do not set last errcode, return true + return true; + } + } + /** + * ssl + */ +#ifdef SW_USE_OPENSSL + if (cli->open_ssl) { + php_swoole_client_check_ssl_setting(cli, zset); + } +#endif + return true; +} + +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_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) { + std::string conn_key = std::string(cli->server_str, cli->server_strlen); + auto i = long_connections.find(conn_key); + if (i != long_connections.end()) { + std::queue *q = i->second; + if (q->empty()) { + delete q; + long_connections.erase(std::string(cli->server_str, cli->server_strlen)); + } + } + } + + delete cli; + +#ifdef SWOOLE_SOCKETS_SUPPORT + zval *zsocket = client_get_zsocket(zobject); + if (zsocket) { + sw_zval_free(zsocket); + client_set_zsocket(zobject, nullptr); + } +#endif + // unset object + php_swoole_client_set_cli(zobject, nullptr); +} + +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, 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); + zval_ptr_dtor(&retval); + } + zval_ptr_dtor(&zdata); + + return ret; +} + +static Client *php_swoole_client_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; + std::string conn_key; + zval *zconnection_id = + sw_zend_read_property_not_null_ex(Z_OBJCE_P(zobject), zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_ID), 1); + + if (zconnection_id && Z_TYPE_P(zconnection_id) == IS_STRING && Z_STRLEN_P(zconnection_id) > 0) { + conn_key = std::string(Z_STRVAL_P(zconnection_id), Z_STRLEN_P(zconnection_id)); + } else { + size_t size = sw_snprintf(sw_tg_buffer()->str, sw_tg_buffer()->size, "%s:%d", host, port); + conn_key = std::string(sw_tg_buffer()->str, size); + } + + // keep the tcp connection + if (type & SW_FLAG_KEEP) { + auto i = long_connections.find(conn_key); + if (i == long_connections.end() || i->second->empty()) { + goto _create_socket; + } else { + std::queue *q = i->second; + cli = q->front(); + q->pop(); + if (!cli->socket->check_liveness()) { + cli->close(); + php_swoole_client_free(zobject, cli); + goto _create_socket; + } + cli->reuse_count++; + zend_update_property_long( + Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("reuseCount"), cli->reuse_count); + } + } else { + _create_socket: + 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); + delete cli; + return nullptr; + } + + // don't forget free it + cli->server_str = sw_strndup(conn_key.c_str(), conn_key.length()); + cli->server_strlen = conn_key.length(); + } + + zend_update_property_long(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("sock"), cli->socket->fd); + + if (type & SW_FLAG_KEEP) { + cli->keep = 1; + } + +#ifdef SW_USE_OPENSSL + if (type & SW_SOCK_SSL) { + cli->enable_ssl_encrypt(); + } +#endif + + return cli; +} + +static PHP_METHOD(swoole_client, __construct) { + zend_long type = 0; + zend_bool async = 0; + char *id = nullptr; + size_t len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|bs", &type, &async, &id, &len) == FAILURE) { + zend_throw_error(NULL, "socket type param is required"); + RETURN_FALSE; + } + + if (async) { + zend_throw_error(NULL, "The $async parameter is not supported"); + 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; + } + + zend_update_property_long(swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("type"), type); + if (id) { + zend_update_property_stringl(swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("id"), id, len); + } + RETURN_TRUE; +} + +static PHP_METHOD(swoole_client, __destruct) { + SW_PREVENT_USER_DESTRUCT(); + + Client *cli = php_swoole_client_get_cli(ZEND_THIS); + // no keep connection + if (cli) { + sw_zend_call_method_with_0_params(ZEND_THIS, swoole_client_ce, nullptr, "close", nullptr); + } +} + +static PHP_METHOD(swoole_client, set) { + zval *zset; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zset) == FAILURE) { + RETURN_FALSE; + } + if (!ZVAL_IS_ARRAY(zset)) { + RETURN_FALSE; + } + + zval *zsetting = sw_zend_read_and_convert_property_array(swoole_client_ce, ZEND_THIS, ZEND_STRL("setting"), 0); + php_array_merge(Z_ARRVAL_P(zsetting), Z_ARRVAL_P(zset)); + + RETURN_TRUE; +} + +static PHP_METHOD(swoole_client, 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; + } + + Client *cli = php_swoole_client_get_cli(ZEND_THIS); + if (cli) { + php_swoole_fatal_error(E_WARNING, "connection to the server has already been established"); + RETURN_FALSE; + } + + cli = php_swoole_client_new(ZEND_THIS, host, host_len, port); + if (cli == nullptr) { + RETURN_FALSE; + } + php_swoole_client_set_cli(ZEND_THIS, cli); + + if (cli->keep && cli->active) { + zend_update_property_bool(swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("reuse"), 1); + RETURN_TRUE; + } else if (cli->active == 1) { + php_swoole_fatal_error(E_WARNING, "connection to the server has already been established"); + RETURN_FALSE; + } + + zval *zset = sw_zend_read_property_ex(swoole_client_ce, ZEND_THIS, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0); + if (zset && ZVAL_IS_ARRAY(zset)) { + swoole_set_last_error(0); + if (!php_swoole_client_check_setting(cli, zset)) { + zend_update_property_long( + swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), swoole_get_last_error()); + RETURN_FALSE; + } + } + + if (cli->connect(cli, host, port, timeout, sock_flag) < 0) { + 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; + } + RETURN_TRUE; +} + +static PHP_METHOD(swoole_client, send) { + char *data; + size_t data_len; + zend_long flags = 0; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STRING(data, data_len) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (data_len == 0) { + php_swoole_fatal_error(E_WARNING, "data to send is empty"); + RETURN_FALSE; + } + + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); + if (!cli) { + RETURN_FALSE; + } + + // clear errno + swoole_set_last_error(0); + ssize_t ret = cli->send(cli, data, data_len, flags); + if (ret < 0) { + php_swoole_sys_error(E_WARNING, "failed to send(%d) %zu bytes", cli->socket->fd, data_len); + zend_update_property_long( + swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), swoole_get_last_error()); + RETVAL_FALSE; + } else { + RETURN_LONG(ret); + } +} + +static PHP_METHOD(swoole_client, sendto) { + char *host; + size_t host_len; + long port; + char *data; + size_t len; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sls", &host, &host_len, &port, &data, &len) == FAILURE) { + RETURN_FALSE; + } + + if (len == 0) { + php_swoole_error(E_WARNING, "data to send is empty"); + RETURN_FALSE; + } + + Client *cli = php_swoole_client_get_cli(ZEND_THIS); + if (!cli) { + cli = php_swoole_client_new(ZEND_THIS, host, host_len, port); + if (cli == nullptr) { + RETURN_FALSE; + } + cli->active = 1; + php_swoole_client_set_cli(ZEND_THIS, cli); + } + + 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 + */ + if (cli->socket->socket_type != SW_SOCK_UNIX_DGRAM) { + if (swoole::network::gethostbyname(cli->_sock_domain, host, addr) < 0) { + swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); + php_swoole_error(E_WARNING, + "sendto 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()); + RETURN_FALSE; + } + + if (!inet_ntop(cli->_sock_domain, addr, ip, sizeof(ip))) { + php_swoole_error(E_WARNING, "ip[%s] is invalid", ip); + zend_update_property_long(swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), errno); + RETURN_FALSE; + } + } + + double ori_timeout = Socket::default_write_timeout; + Socket::default_write_timeout = cli->timeout; + + ssize_t ret = -1; + if (cli->socket->is_dgram()) { + ret = cli->socket->sendto(ip, port, data, len); + } else { + php_swoole_fatal_error(E_WARNING, "only supports SWOOLE_SOCK_(UDP/UDP6/UNIX_DGRAM)"); + } + Socket::default_write_timeout = ori_timeout; + SW_CHECK_RETURN(ret); +} + +static PHP_METHOD(swoole_client, sendfile) { + char *file; + size_t file_len; + 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; + } + if (file_len == 0) { + php_swoole_fatal_error(E_WARNING, "file to send is empty"); + RETURN_FALSE; + } + + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); + if (!cli) { + RETURN_FALSE; + } + // only stream socket can sendfile + if (!(cli->socket->is_stream())) { + php_swoole_error(E_WARNING, "dgram socket cannot use sendfile"); + RETURN_FALSE; + } + // clear errno + swoole_set_last_error(0); + int ret = cli->sendfile(cli, file, offset, length); + if (ret < 0) { + swoole_set_last_error(errno); + php_swoole_fatal_error(E_WARNING, + "sendfile() failed. Error: %s [%d]", + 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()); + RETVAL_FALSE; + } else { + RETVAL_TRUE; + } +} + +static PHP_METHOD(swoole_client, recv) { + zend_long buf_len = SW_PHP_CLIENT_BUFFER_SIZE; + zend_long flags = 0; + int ret; + zend_string *strbuf = nullptr; + + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(buf_len) + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + // waitall + if (flags == 1) { + flags = MSG_WAITALL; + } + + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); + if (!cli) { + RETURN_FALSE; + } + + Protocol *protocol = &cli->protocol; + + if (cli->open_eof_check) { + if (cli->buffer == nullptr) { + cli->buffer = swoole::make_string(SW_BUFFER_SIZE_BIG, sw_zend_string_allocator()); + } + + String *buffer = cli->buffer; + ssize_t eof = -1; + char *buf = nullptr; + + if (buffer->length > 0) { + goto _find_eof; + } + + while (1) { + buf = buffer->str + buffer->length; + buf_len = buffer->size - buffer->length; + + if (buf_len > SW_BUFFER_SIZE_BIG) { + buf_len = SW_BUFFER_SIZE_BIG; + } + + ret = cli->recv(cli, buf, buf_len, 0); + if (ret < 0) { + 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()); + buffer->length = 0; + RETURN_FALSE; + } else if (ret == 0) { + buffer->length = 0; + RETURN_EMPTY_STRING(); + } + + buffer->length += ret; + + if (buffer->length < protocol->package_eof_len) { + continue; + } + + _find_eof: + eof = swoole_strnpos(buffer->str, buffer->length, protocol->package_eof, protocol->package_eof_len); + if (eof >= 0) { + eof += protocol->package_eof_len; + + if ((ssize_t) buffer->length > eof) { + cli->buffer = swoole::make_string(SW_BUFFER_SIZE_BIG, sw_zend_string_allocator()); + cli->buffer->length = buffer->length - eof; + memcpy(cli->buffer->str, buffer->str + eof, cli->buffer->length); + } else { + cli->buffer = nullptr; + buffer->length = 0; + } + + zend::assign_zend_string_by_val(return_value, buffer->str, eof); + buffer->str = nullptr; + delete buffer; + + return; + } else { + if (buffer->length == protocol->package_max_length) { + php_swoole_error(E_WARNING, "no package eof"); + buffer->length = 0; + RETURN_FALSE; + } else if (buffer->length == buffer->size) { + if (buffer->size < protocol->package_max_length) { + uint32_t new_size = buffer->size * 2; + if (new_size > protocol->package_max_length) { + new_size = protocol->package_max_length; + } + if (!buffer->extend(new_size)) { + buffer->length = 0; + RETURN_FALSE; + } + } + } + } + } + buffer->length = 0; + RETURN_FALSE; + } else if (cli->open_length_check) { + if (cli->buffer == nullptr) { + cli->buffer = new String(SW_BUFFER_SIZE_STD); + } else { + cli->buffer->clear(); + } + String *buffer = cli->buffer; + + uint32_t header_len = protocol->package_length_offset + protocol->package_length_size; + + while (1) { + int retval = cli->recv(cli, buffer->str + buffer->length, header_len - buffer->length, 0); + if (retval <= 0) { + break; + } + buffer->length += retval; + 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) { + break; + } else { + break; + } + } + + // error package + if (buf_len < 0) { + RETURN_EMPTY_STRING(); + } + // empty package + else if (buf_len == header_len) { + RETURN_STRINGL(buffer->str, header_len); + } else if (buf_len > protocol->package_max_length) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, + "Package is too big. package_length=%d", + (int) buf_len); + RETURN_EMPTY_STRING(); + } else if (buf_len == (zend_long) buffer->length) { + RETURN_STRINGL(buffer->str, buffer->length); + } else if (buf_len < (zend_long) buffer->length) { + RETVAL_STRINGL(buffer->str, buf_len); + memmove(buffer->str, buffer->str + buf_len, buffer->length - buf_len); + buffer->length -= buf_len; + return; + } + + strbuf = zend_string_alloc(buf_len, 0); + memcpy(strbuf->val, buffer->str, buffer->length); + swoole_set_last_error(0); + ret = cli->recv(cli, strbuf->val + header_len, buf_len - buffer->length, MSG_WAITALL); + if (ret > 0) { + ret += header_len; + if (ret != buf_len) { + ret = 0; + } + } + } else { + if (!(flags & MSG_WAITALL) && buf_len > SW_PHP_CLIENT_BUFFER_SIZE) { + buf_len = SW_PHP_CLIENT_BUFFER_SIZE; + } + strbuf = zend_string_alloc(buf_len, 0); + swoole_set_last_error(0); + ret = cli->recv(cli, strbuf->val, buf_len, flags); + } + + if (ret < 0) { + 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()); + if (strbuf) { + zend_string_free(strbuf); + } + RETURN_FALSE; + } else { + if (ret == 0) { + if (strbuf) { + zend_string_free(strbuf); + } + RETURN_EMPTY_STRING(); + } else { + strbuf->len = ret; + strbuf->val[ret] = 0; + RETVAL_STR(strbuf); + } + } +} + +static PHP_METHOD(swoole_client, isConnected) { + Client *cli = 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, getsockname) { + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); + if (!cli) { + RETURN_FALSE; + } + + if (cli->socket->is_local()) { + php_swoole_fatal_error(E_WARNING, "getsockname() only support AF_INET family socket"); + RETURN_FALSE; + } + + if (cli->socket->get_name(&cli->socket->info) < 0) { + php_swoole_sys_error(E_WARNING, "getsockname() failed"); + RETURN_FALSE; + } + + array_init(return_value); + if (cli->socket->is_inet6()) { + add_assoc_long(return_value, "port", ntohs(cli->socket->info.addr.inet_v6.sin6_port)); + char tmp[INET6_ADDRSTRLEN]; + if (inet_ntop(AF_INET6, &cli->socket->info.addr.inet_v6.sin6_addr, tmp, sizeof(tmp))) { + add_assoc_string(return_value, "host", tmp); + } else { + php_swoole_fatal_error(E_WARNING, "inet_ntop() failed"); + } + } else { + add_assoc_long(return_value, "port", ntohs(cli->socket->info.addr.inet_v4.sin_port)); + 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 = client_get_zsocket(ZEND_THIS); + if (zsocket) { + RETURN_ZVAL(zsocket, 1, 0); + } + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); + if (!cli) { + RETURN_FALSE; + } + if (cli->keep) { + php_swoole_fatal_error(E_WARNING, "the 'getSocket' method can't be used on persistent connection"); + RETURN_FALSE; + } + php_socket *socket_object = php_swoole_convert_to_socket(cli->socket->fd); + if (!socket_object) { + RETURN_FALSE; + } + SW_ZVAL_SOCKET(return_value, socket_object); + zsocket = sw_zval_dup(return_value); + Z_TRY_ADDREF_P(zsocket); + client_set_zsocket(ZEND_THIS, zsocket); +} +#endif + +static PHP_METHOD(swoole_client, getpeername) { + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); + if (!cli) { + RETURN_FALSE; + } + + 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)); + 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)); + char tmp[INET6_ADDRSTRLEN]; + + if (inet_ntop(AF_INET6, &cli->remote_addr.addr.inet_v6.sin6_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_UNIX_DGRAM) { + add_assoc_string(return_value, "host", cli->remote_addr.addr.un.sun_path); + } else { + php_swoole_fatal_error(E_WARNING, "only supports SWOOLE_SOCK_(UDP/UDP6/UNIX_DGRAM)"); + RETURN_FALSE; + } +} + +static PHP_METHOD(swoole_client, close) { + int ret = 1; + zend_bool force = 0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(force) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + Client *cli = 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; + } + // Connection error, or short tcp connection. + // No keep connection + if (force || !cli->keep || cli->socket->catch_error(swoole_get_last_error()) == SW_CLOSE) { + ret = cli->close(); + php_swoole_client_free(ZEND_THIS, cli); + } else { + if (cli->keep) { + std::string conn_key(cli->server_str, cli->server_strlen); + std::queue *q; + auto i = long_connections.find(conn_key); + if (i == long_connections.end()) { + q = new std::queue; + long_connections[conn_key] = q; + } else { + q = i->second; + } + q->push(cli); + } + // unset object + php_swoole_client_set_cli(ZEND_THIS, nullptr); + } + SW_CHECK_RETURN(ret); +} + +#ifdef SW_USE_OPENSSL +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; + } + if (cli->socket->ssl) { + php_swoole_fatal_error(E_WARNING, "SSL has been enabled"); + return false; + } + 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); + } + return cli->enable_ssl_encrypt() == SW_OK; +} + +static PHP_METHOD(swoole_client, enableSSL) { + zval *zcallback = nullptr; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(zcallback) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (zcallback) { + zend_throw_exception( + swoole_exception_ce, "sync client does not support `onSslReady` callback", SW_ERROR_INVALID_PARAMS); + RETURN_FALSE; + } + + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); + if (!cli) { + RETURN_FALSE; + } + if (!php_swoole_client_enable_ssl_encryption(cli, ZEND_THIS)) { + RETURN_FALSE; + } + RETURN_BOOL(cli->ssl_handshake() == SW_OK); +} + +static PHP_METHOD(swoole_client, getPeerCert) { + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); + if (!cli) { + RETURN_FALSE; + } + if (!cli->socket->ssl) { + php_swoole_fatal_error(E_WARNING, "SSL is not ready"); + RETURN_FALSE; + } + if (!cli->socket->ssl_get_peer_certificate(sw_tg_buffer())) { + RETURN_FALSE; + } + RETURN_SW_STRING(sw_tg_buffer()); +} + +static PHP_METHOD(swoole_client, verifyPeerCert) { + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); + if (!cli) { + RETURN_FALSE; + } + if (!cli->socket->ssl) { + php_swoole_fatal_error(E_WARNING, "SSL is not ready"); + RETURN_FALSE; + } + zend_bool allow_self_signed = 0; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &allow_self_signed) == FAILURE) { + RETURN_FALSE; + } + SW_CHECK_RETURN(cli->ssl_verify(allow_self_signed)); +} +#endif + +static PHP_METHOD(swoole_client, shutdown) { + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); + if (!cli) { + RETURN_FALSE; + } + long __how; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &__how) == FAILURE) { + RETURN_FALSE; + } + SW_CHECK_RETURN(cli->shutdown(__how)); +} + +PHP_FUNCTION(swoole_client_select) { +#ifdef PHP_SWOOLE_CLIENT_USE_POLL + zval *r_array, *w_array, *e_array; + int retval; + uint32_t index = 0; + double timeout = SW_CLIENT_CONNECT_TIMEOUT; + + 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)); + struct pollfd *fds = (struct pollfd *) ecalloc(maxevents, sizeof(struct pollfd)); + + if (r_array != nullptr && php_swoole_array_length(r_array) > 0) { + index = client_poll_add(r_array, index, fds, maxevents, POLLIN); + } + if (w_array != nullptr && php_swoole_array_length(w_array) > 0) { + index = client_poll_add(w_array, index, fds, maxevents, POLLOUT); + } + if (e_array != nullptr && php_swoole_array_length(e_array) > 0) { + index = client_poll_add(e_array, index, fds, maxevents, POLLHUP); + } + if (index == 0) { + efree(fds); + php_swoole_fatal_error(E_WARNING, "no resource arrays were passed to select"); + RETURN_FALSE; + } + + do { + retval = poll(fds, maxevents, (int) (timeout * 1000)); + } while (retval < 0 && errno == EINTR); + + if (retval == -1) { + efree(fds); + php_swoole_sys_error(E_WARNING, "unable to poll()"); + RETURN_FALSE; + } + + if (r_array != nullptr && php_swoole_array_length(r_array) > 0) { + client_poll_wait(r_array, fds, maxevents, retval, POLLIN); + } + if (w_array != nullptr && php_swoole_array_length(w_array) > 0) { + client_poll_wait(w_array, fds, maxevents, retval, POLLOUT); + } + if (e_array != nullptr && php_swoole_array_length(e_array) > 0) { + client_poll_wait(e_array, fds, maxevents, retval, POLLHUP); + } + efree(fds); + RETURN_LONG(retval); +#else + zval *r_array, *w_array, *e_array; + fd_set rfds, wfds, efds; + + int max_fd = 0; + int retval, sets = 0; + double timeout = SW_CLIENT_CONNECT_TIMEOUT; + struct timeval timeo; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a!a!a!|d", &r_array, &w_array, &e_array, &timeout) == FAILURE) { + RETURN_FALSE; + } + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&efds); + + if (r_array != nullptr) sets += client_select_add(r_array, &rfds, &max_fd); + if (w_array != nullptr) sets += client_select_add(w_array, &wfds, &max_fd); + if (e_array != nullptr) sets += client_select_add(e_array, &efds, &max_fd); + + if (!sets) { + php_swoole_fatal_error(E_WARNING, "no resource arrays were passed to select"); + RETURN_FALSE; + } + + if (max_fd >= FD_SETSIZE) { + php_swoole_fatal_error(E_WARNING, "select max_fd > FD_SETSIZE[%d]", FD_SETSIZE); + RETURN_FALSE; + } + timeo.tv_sec = (int) timeout; + timeo.tv_usec = (int) ((timeout - timeo.tv_sec) * 1000 * 1000); + + retval = select(max_fd + 1, &rfds, &wfds, &efds, &timeo); + if (retval == -1) { + php_swoole_sys_error(E_WARNING, "unable to select"); + RETURN_FALSE; + } + if (r_array != nullptr) { + client_select_wait(r_array, &rfds); + } + if (w_array != nullptr) { + client_select_wait(w_array, &wfds); + } + if (e_array != nullptr) { + client_select_wait(e_array, &efds); + } + RETURN_LONG(retval); +#endif +} + +#ifdef PHP_SWOOLE_CLIENT_USE_POLL +static inline int client_poll_get(struct pollfd *fds, int maxevents, int fd) { + int i; + for (i = 0; i < maxevents; i++) { + if (fds[i].fd == fd) { + return i; + } + } + return -1; +} + +static int client_poll_wait(zval *sock_array, struct pollfd *fds, int maxevents, int n_event, int revent) { + zval *element = nullptr; + int sock; + + ulong_t num = 0; + if (!ZVAL_IS_ARRAY(sock_array)) { + return 0; + } + + zval new_array; + array_init(&new_array); + zend_ulong num_key; + zend_string *key; + zval *dest_element; + int poll_key; + + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(sock_array), num_key, key, element) { + sock = php_swoole_convert_to_fd(element); + if (sock < 0) { + continue; + } + poll_key = client_poll_get(fds, maxevents, sock); + if (poll_key == -1) { + php_swoole_fatal_error(E_WARNING, "bad fd[%d]", sock); + continue; + } + if (!(fds[poll_key].revents & revent)) { + continue; + } + if (key) { + dest_element = zend_hash_add(Z_ARRVAL(new_array), key, element); + } else { + dest_element = zend_hash_index_update(Z_ARRVAL(new_array), num_key, element); + } + if (dest_element) { + Z_ADDREF_P(dest_element); + } + num++; + } + ZEND_HASH_FOREACH_END(); + + zval_ptr_dtor(sock_array); + ZVAL_COPY_VALUE(sock_array, &new_array); + return num; +} + +static uint32_t client_poll_add(zval *sock_array, uint32_t index, struct pollfd *fds, int maxevents, int event) { + zval *element = nullptr; + if (!ZVAL_IS_ARRAY(sock_array)) { + return 0; + } + + int sock; + int key = -1; + + SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(sock_array), element) + sock = php_swoole_convert_to_fd(element); + if (sock < 0) { + continue; + } + if (event != POLLIN) { + key = client_poll_get(fds, maxevents, sock); + } + if (key < 0) { + fds[index].fd = sock; + fds[index].events = event; + index++; + } else { + fds[key].fd = sock; + fds[key].events |= event; + } + SW_HASHTABLE_FOREACH_END(); + + return index; +} +#else +static int client_select_wait(zval *sock_array, fd_set *fds) { + zval *element = nullptr; + int sock; + + ulong_t num = 0; + if (!ZVAL_IS_ARRAY(sock_array)) { + return 0; + } + + zval new_array; + array_init(&new_array); + zend_ulong num_key; + zend_string *key; + zval *dest_element; + + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(sock_array), num_key, key, element) { + sock = php_swoole_convert_to_fd(element); + if (sock < 0) { + continue; + } + if ((sock < FD_SETSIZE) && FD_ISSET(sock, fds)) { + if (key) { + dest_element = zend_hash_add(Z_ARRVAL(new_array), key, element); + } else { + dest_element = zend_hash_index_update(Z_ARRVAL(new_array), num_key, element); + } + if (dest_element) { + Z_ADDREF_P(dest_element); + } + } + num++; + } + ZEND_HASH_FOREACH_END(); + + zval_ptr_dtor(sock_array); + ZVAL_COPY_VALUE(sock_array, &new_array); + return num ? 1 : 0; +} + +static int client_select_add(zval *sock_array, fd_set *fds, int *max_fd) { + zval *element = nullptr; + if (!ZVAL_IS_ARRAY(sock_array)) { + return 0; + } + + int sock; + int num = 0; + + SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(sock_array), element) + sock = php_swoole_convert_to_fd(element); + if (sock < 0) { + continue; + } + if (sock < FD_SETSIZE) { + FD_SET(sock, fds); + } else { + php_swoole_fatal_error(E_WARNING, "socket[%d] > FD_SETSIZE[%d]", sock, FD_SETSIZE); + continue; + } + if (sock > *max_fd) { + *max_fd = sock; + } + num++; + SW_HASHTABLE_FOREACH_END(); + + return num ? 1 : 0; +} +#endif 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 new file mode 100644 index 00000000000..4db4778e1ff --- /dev/null +++ b/ext-src/swoole_client_coro.cc @@ -0,0 +1,632 @@ +/* + +----------------------------------------------------------------------+ + | 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 "swoole_string.h" +#include "swoole_socket.h" +#include "swoole_protocol.h" +#include "swoole_proxy.h" + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_client_coro_arginfo.h" +END_EXTERN_C() + +using swoole::String; +using swoole::coroutine::Socket; +using swoole::network::Address; +#ifdef SW_USE_OPENSSL +using swoole::SSLContext; +#endif + +static zend_class_entry *swoole_client_coro_ce; +static zend_object_handlers swoole_client_coro_handlers; + +struct ClientCoroObject { + Socket *socket; + zval zsocket; + /* safety zval */ + zval zobject; + zend_object std; +}; + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_client_coro, __construct); +static PHP_METHOD(swoole_client_coro, __destruct); +static PHP_METHOD(swoole_client_coro, set); +static PHP_METHOD(swoole_client_coro, connect); +static PHP_METHOD(swoole_client_coro, recv); +static PHP_METHOD(swoole_client_coro, peek); +static PHP_METHOD(swoole_client_coro, send); +static PHP_METHOD(swoole_client_coro, sendfile); +static PHP_METHOD(swoole_client_coro, sendto); +static PHP_METHOD(swoole_client_coro, recvfrom); +#ifdef SW_USE_OPENSSL +static PHP_METHOD(swoole_client_coro, enableSSL); +static PHP_METHOD(swoole_client_coro, getPeerCert); +static PHP_METHOD(swoole_client_coro, verifyPeerCert); +#endif +static PHP_METHOD(swoole_client_coro, exportSocket); +static PHP_METHOD(swoole_client_coro, isConnected); +static PHP_METHOD(swoole_client_coro, getsockname); +static PHP_METHOD(swoole_client_coro, getpeername); +static PHP_METHOD(swoole_client_coro, close); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_client_coro_methods[] = +{ + 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_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_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 *client_coro_fetch_object(zend_object *obj) { + return (ClientCoroObject *) ((char *) obj - swoole_client_coro_handlers.offset); +} + +static sw_inline ClientCoroObject *client_coro_get_client(zval *zobject) { + return client_coro_fetch_object(Z_OBJ_P(zobject)); +} + +static sw_inline Socket *client_coro_get_socket(zval *zobject) { + return client_coro_get_client(zobject)->socket; +} + +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); +} + +#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", "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, 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_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); + + zend_declare_class_constant_long(swoole_client_coro_ce, ZEND_STRL("MSG_OOB"), MSG_OOB); + zend_declare_class_constant_long(swoole_client_coro_ce, ZEND_STRL("MSG_PEEK"), MSG_PEEK); + zend_declare_class_constant_long(swoole_client_coro_ce, ZEND_STRL("MSG_DONTWAIT"), MSG_DONTWAIT); + zend_declare_class_constant_long(swoole_client_coro_ce, ZEND_STRL("MSG_WAITALL"), MSG_WAITALL); +} + +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; + } + + 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; + } + + auto sock = client_coro_create_socket(zobject, zval_get_long(ztype)); + if (!sock) { + return nullptr; + } + 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 sock; +} + +static PHP_METHOD(swoole_client_coro, __construct) { + 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); + + 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, + space, + get_active_function_name(), + 1, + 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) { + zval *zset, *zsetting; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(zset) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (php_swoole_array_length(zset) == 0) { + RETURN_FALSE; + } 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_socket_set(cli, zset)); + } + RETURN_TRUE; + } +} + +static PHP_METHOD(swoole_client_coro, connect) { + char *host; + size_t host_len; + zend_long port = 0; + double timeout = 0; + 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; + } + + Socket *socket = client_coro_get_socket_for_connect(ZEND_THIS, port); + if (!socket) { + RETURN_FALSE; + } + 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; + } + 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; +} + +static PHP_METHOD(swoole_client_coro, send) { + char *data; + size_t data_len; + double timeout = 0; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STRING(data, data_len) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (data_len == 0) { + php_swoole_fatal_error(E_WARNING, "data to send is empty"); + 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) { + 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; + zend_long port; + char *data; + size_t len; + + 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; + } + + 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; + } + + 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 = socket->sendto(std::string(host, host_len), port, data, len); + if (ret < 0) { + php_swoole_socket_set_error_properties(ZEND_THIS, socket); + RETURN_FALSE; + } + RETURN_TRUE; +} + +static PHP_METHOD(swoole_client_coro, recvfrom) { + zend_long length; + zval *address, *port; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz/|z/", &length, &address, &port) == FAILURE) { + RETURN_FALSE; + } + + if (length <= 0) { + 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 = socket->recvfrom(ZSTR_VAL(retval), length); + if (n_bytes < 0) { + zend_string_free(retval); + php_swoole_socket_set_error_properties(ZEND_THIS, socket); + RETURN_FALSE; + } else { + zval_ptr_dtor(address); + ZVAL_STRING(address, socket->get_ip()); + if (port) { + zval_ptr_dtor(port); + ZVAL_LONG(port, socket->get_port()); + } + + ZSTR_LEN(retval) = n_bytes; + ZSTR_VAL(retval)[ZSTR_LEN(retval)] = '\0'; + RETURN_STR(retval); + } +} + +static PHP_METHOD(swoole_client_coro, sendfile) { + char *file; + size_t file_len; + 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; + } + if (file_len == 0) { + php_swoole_fatal_error(E_WARNING, "file to send is empty"); + 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)) { + zend_update_property_long(swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), EINVAL); + zend_update_property_string( + swoole_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), "dgram socket cannot use sendfile"); + RETURN_FALSE; + } + if (!cli->sendfile(file, offset, length)) { + php_swoole_socket_set_error_properties(ZEND_THIS, cli); + RETVAL_FALSE; + } else { + RETVAL_TRUE; + } +} + +static PHP_METHOD(swoole_client_coro, recv) { + double timeout = 0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + CLIENT_CORO_GET_SOCKET_SAFE(cli); + + ssize_t retval; + zend_string *result = nullptr; + + if (cli->open_length_check || cli->open_eof_check) { + retval = cli->recv_packet(timeout); + if (retval > 0) { + auto strval = cli->pop_packet(); + if (strval == nullptr) { + retval = -1; + cli->set_err(ENOMEM); + } else { + result = zend::fetch_zend_string_by_val(strval); + } + } + } else { + result = zend_string_alloc(SW_PHP_CLIENT_BUFFER_SIZE - sizeof(zend_string), 0); + Socket::TimeoutSetter ts(cli, timeout, Socket::TIMEOUT_READ); + retval = cli->recv(ZSTR_VAL(result), SW_PHP_CLIENT_BUFFER_SIZE - sizeof(zend_string)); + if (retval <= 0) { + zend_string_free(result); + } + } + if (retval < 0) { + php_swoole_socket_set_error_properties(ZEND_THIS, cli); + RETURN_FALSE; + } else if (retval == 0) { + RETURN_EMPTY_STRING(); + } else { + ZSTR_VAL(result)[retval] = '\0'; + ZSTR_LEN(result) = retval; + RETURN_STR(result); + } +} + +static PHP_METHOD(swoole_client_coro, peek) { + zend_long buf_len = SW_PHP_CLIENT_BUFFER_SIZE; + int ret; + char *buf = nullptr; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(buf_len) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + CLIENT_CORO_GET_SOCKET_SAFE(cli); + + buf = (char *) emalloc((size_t)buf_len + 1); + ret = cli->peek(buf, buf_len); + if (ret < 0) { + php_swoole_socket_set_error_properties(ZEND_THIS, cli); + efree(buf); + RETURN_FALSE; + } else { + buf[ret] = 0; + RETVAL_STRINGL(buf, ret); + efree(buf); + } +} + +static PHP_METHOD(swoole_client_coro, isConnected) { + Socket *cli = client_coro_get_socket(ZEND_THIS); + if (cli && cli->is_connected()) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} + +static PHP_METHOD(swoole_client_coro, getsockname) { + CLIENT_CORO_GET_SOCKET_SAFE(cli); + + Address sa; + if (!cli->getsockname(&sa)) { + php_swoole_socket_set_error_properties(ZEND_THIS, cli); + RETURN_FALSE; + } + + array_init(return_value); + zval zaddress; + ZVAL_STRING(&zaddress, sa.get_ip()); + add_assoc_zval(return_value, "host", &zaddress); /* backward compatibility */ + zend::array_set(return_value, SW_STRL("address"), &zaddress); + add_assoc_long(return_value, "port", sa.get_port()); +} + +/** + * export Swoole\Coroutine\Socket object + */ +static PHP_METHOD(swoole_client_coro, exportSocket) { + auto cli = client_coro_get_client(ZEND_THIS); + RETURN_ZVAL(&cli->zsocket, 1, 0); +} + +static PHP_METHOD(swoole_client_coro, getpeername) { + CLIENT_CORO_GET_SOCKET_SAFE(cli); + + Address sa; + if (!cli->getpeername(&sa)) { + php_swoole_socket_set_error_properties(ZEND_THIS, cli); + RETURN_FALSE; + } + + array_init(return_value); + 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); + add_assoc_long(return_value, "port", sa.get_port()); +} + +static PHP_METHOD(swoole_client_coro, close) { + 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) { + CLIENT_CORO_GET_SOCKET_SAFE(cli); + if (cli->get_type() != SW_SOCK_TCP && cli->get_type() != SW_SOCK_TCP6) { + php_swoole_socket_set_error_properties(ZEND_THIS, ESOCKTNOSUPPORT); + RETURN_FALSE; + } + if (cli->get_ssl()) { + 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); + } + if (!cli->ssl_handshake()) { + php_swoole_socket_set_error_properties(ZEND_THIS, cli); + RETURN_FALSE; + } + RETURN_TRUE; +} + +static PHP_METHOD(swoole_client_coro, getPeerCert) { + CLIENT_CORO_GET_SOCKET_SAFE(cli); + if (!cli->get_ssl()) { + php_swoole_socket_set_error_properties(ZEND_THIS, EISCONN); + RETURN_FALSE; + } + if (!cli->get_socket()->ssl_get_peer_certificate(sw_tg_buffer())) { + RETURN_FALSE; + } + RETURN_SW_STRING(sw_tg_buffer()); +} + +static PHP_METHOD(swoole_client_coro, verifyPeerCert) { + CLIENT_CORO_GET_SOCKET_SAFE(cli); + if (!cli->get_ssl()) { + php_swoole_socket_set_error_properties(ZEND_THIS, ENOTCONN); + RETURN_FALSE; + } + zend_bool allow_self_signed = 0; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &allow_self_signed) == FAILURE) { + RETURN_FALSE; + } + RETURN_BOOL(cli->ssl_verify(allow_self_signed)); +} +#endif diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc new file mode 100644 index 00000000000..02ad505c131 --- /dev/null +++ b/ext-src/swoole_coroutine.cc @@ -0,0 +1,1475 @@ +/* + +----------------------------------------------------------------------+ + | 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: Xinyu Zhu | + | shiguangqi | + | Twosee | + | Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#include "php_swoole_cxx.h" +#include "php_swoole_thread.h" +#include "php_swoole_coroutine_system.h" + +#include "swoole_server.h" +#include "swoole_signal.h" + +#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; +using swoole::PHPCoroutine; +using swoole::coroutine::Socket; +using swoole::coroutine::System; + +#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 }; + +SW_THREAD_LOCAL bool PHPCoroutine::activated = false; +SW_THREAD_LOCAL zend_array *PHPCoroutine::options = nullptr; + +SW_THREAD_LOCAL PHPCoroutine::Config PHPCoroutine::config{ + SW_DEFAULT_MAX_CORO_NUM, + 0, + false, + true, +}; + +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_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; + +static void (*orig_interrupt_function)(zend_execute_data *execute_data) = nullptr; + +static zend_class_entry *swoole_coroutine_util_ce; +static zend_class_entry *swoole_exit_exception_ce; +static zend_object_handlers swoole_exit_exception_handlers; +static zend_class_entry *swoole_coroutine_iterator_ce; +static zend_class_entry *swoole_coroutine_context_ce; + +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); +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 +static const zend_function_entry swoole_coroutine_methods[] = +{ + /** + * Coroutine Core API + */ + 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_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 + +/** + * Exit Exception + */ +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_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; + 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; + } + if (flags) { + const zend_op *opline = EX(opline); + zval _exit_status{}; + zval *exit_status = nullptr; + + if (opline->op1_type != IS_UNUSED) { + if (opline->op1_type == IS_CONST) { + // see: https://github.com/php/php-src/commit/e70618aff6f447a298605d07648f2ce9e5a284f5 +#ifdef EX_CONSTANT + exit_status = EX_CONSTANT(opline->op1); +#else + exit_status = RT_CONSTANT(opline, opline->op1); +#endif + } else { + exit_status = EX_VAR(opline->op1.var); + } + if (Z_ISREF_P(exit_status)) { + exit_status = Z_REFVAL_P(exit_status); + } + ZVAL_DUP(&_exit_status, exit_status); + exit_status = &_exit_status; + } else { + exit_status = &_exit_status; + ZVAL_NULL(exit_status); + } + obj = zend_throw_exception(swoole_exit_exception_ce, "swoole exit", 0); + ZVAL_OBJ(&ex, obj); + zend_update_property_long(swoole_exit_exception_ce, SW_Z8_OBJ_P(&ex), ZEND_STRL("flags"), flags); + Z_TRY_ADDREF_P(exit_status); + zend_update_property(swoole_exit_exception_ce, SW_Z8_OBJ_P(&ex), ZEND_STRL("status"), exit_status); + } + + 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(); + task->in_silence = true; + task->ori_error_reporting = EG(error_reporting); + return ZEND_USER_OPCODE_DISPATCH; +} + +static int coro_end_silence_handler(zend_execute_data *execute_data) { + PHPContext *task = PHPCoroutine::get_context(); + task->in_silence = false; + return ZEND_USER_OPCODE_DISPATCH; +} + +static void coro_interrupt_resume(void *data) { + Coroutine *co = (Coroutine *) data; + if (co && !co->is_end()) { + swoole_trace_log(SW_TRACE_COROUTINE, "interrupt_callback cid=%ld ", co->get_cid()); + co->resume(); + } +} + +static void coro_interrupt_function(zend_execute_data *execute_data) { + PHPContext *task = PHPCoroutine::get_context(); + if (task && task->co && PHPCoroutine::is_schedulable(task)) { + swoole_event_defer(coro_interrupt_resume, (void *) task->co); + task->co->yield(); + } + if (orig_interrupt_function) { + orig_interrupt_function(execute_data); + } +} + +PHPContext *PHPCoroutine::create_context(Args *args) { + PHPContext *ctx = (PHPContext *) emalloc(sizeof(PHPContext)); + ctx->output_ptr = nullptr; +#if PHP_VERSION_ID < 80100 + ctx->array_walk_fci = nullptr; +#endif + ctx->in_silence = false; + + ctx->co = Coroutine::get_current(); + ctx->co->set_task((void *) ctx); + ctx->defer_tasks = nullptr; + ctx->pcid = ctx->co->get_origin_cid(); + ctx->context = nullptr; + ctx->on_yield = nullptr; + ctx->on_resume = nullptr; + ctx->on_close = nullptr; + ctx->enable_scheduler = true; + +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + fiber_context_try_init(ctx); + ctx->fiber_init_notified = false; +#endif + + EG(vm_stack) = zend_vm_stack_new_page(SW_DEFAULT_PHP_STACK_PAGE_SIZE, nullptr); + EG(vm_stack_top) = EG(vm_stack)->top + ZEND_CALL_FRAME_SLOT; + EG(vm_stack_end) = EG(vm_stack)->end; + EG(vm_stack_page_size) = SW_DEFAULT_PHP_STACK_PAGE_SIZE; + + zend_function *func = EG(current_execute_data)->func; + zend_execute_data *call = (zend_execute_data *) (EG(vm_stack_top)); + EG(current_execute_data) = call; + memset(EG(current_execute_data), 0, sizeof(zend_execute_data)); + + EG(error_handling) = EH_NORMAL; + EG(exception_class) = nullptr; + EG(exception) = nullptr; + EG(jit_trace_num) = 0; + + call->func = func; + EG(vm_stack_top) += ZEND_CALL_FRAME_SLOT; + +#ifdef ZEND_CHECK_STACK_LIMIT + EG(stack_base) = stack_base(ctx); + EG(stack_limit) = stack_limit(ctx); +#endif + + save_vm_stack(ctx); + record_last_msec(ctx); + + ctx->fci_cache = *args->fci_cache; + ctx->fci.size = sizeof(ctx->fci); + ctx->fci.object = NULL; + ctx->fci.param_count = args->argc; + ctx->fci.params = args->argv; + ctx->fci.named_params = NULL; + ctx->return_value = {}; + ctx->fci.retval = &ctx->return_value; + + if (args->callable) { + ctx->fci.function_name = *args->callable; + Z_TRY_ADDREF(ctx->fci.function_name); + } else { + ZVAL_UNDEF(&ctx->fci.function_name); + } + sw_zend_fci_cache_persist(&ctx->fci_cache); + + return ctx; +} + +void PHPCoroutine::bailout() { + Coroutine::bailout([]() { + if (sw_reactor()) { + sw_reactor()->running = false; + sw_reactor()->bailout = true; + } + zend_bailout(); + }); +} + +bool PHPCoroutine::catch_exception() { + if (UNEXPECTED(EG(exception))) { + // the exception error messages MUST be output on the current coroutine stack + zend_exception_error(EG(exception), E_ERROR); + return true; + } + return false; +} + +void PHPCoroutine::activate() { + if (sw_unlikely(activated)) { + return; + } + + 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 */ + php_swoole_check_reactor(); + + /* replace interrupt function */ + orig_interrupt_function = zend_interrupt_function; + zend_interrupt_function = coro_interrupt_function; + + 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(); + } + + if (config.hook_flags) { + enable_hook(config.hook_flags); + } + + 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(); + /** + * reset runtime hook + */ + disable_hook(); + + Coroutine::set_on_yield(nullptr); + Coroutine::set_on_resume(nullptr); + Coroutine::set_on_close(nullptr); + + zend_interrupt_function = orig_interrupt_function; + + if (config.enable_deadlock_check) { + deadlock_check(); + } + + enable_unsafe_function(); + Coroutine::deactivate(); +} + +void PHPCoroutine::shutdown() { + 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() || (sw_reactor() && sw_reactor()->bailout)) { + return; + } + if (SWOOLE_G(enable_library)) { + zend::function::call("\\Swoole\\Coroutine\\deadlock_check", 0, nullptr); + } else { + printf("\n===================================================================" + "\n [FATAL ERROR]: all coroutines (count: %lu) are asleep - deadlock!" + "\n===================================================================\n", + Coroutine::count()); + } +} + +void PHPCoroutine::interrupt_thread_stop() { + if (!interrupt_thread_running) { + return; + } + interrupt_thread_running = false; + interrupt_thread.join(); +} + +void PHPCoroutine::interrupt_thread_start() { + if (interrupt_thread_running) { + return; + } + zend_vm_interrupt = &EG(vm_interrupt); + interrupt_thread_running = true; + interrupt_thread = std::thread([]() { + swoole_signal_block_all(); + while (interrupt_thread_running) { + zend_atomic_bool_store(zend_vm_interrupt, 1); + std::this_thread::sleep_for(std::chrono::milliseconds(MAX_EXEC_MSEC / 2)); + } + }); +} + +/** + * The meaning of the task argument in coro switch functions + * + * create: origin_task + * yield: current_task + * resume: target_task + * close: current_task + * + */ +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 (!ctx->array_walk_fci) { + ctx->array_walk_fci = (zend::Function *) emalloc(sizeof(*ctx->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(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 *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(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(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 *ctx) { + if (OG(handlers).elements) { + ctx->output_ptr = (zend_output_globals *) emalloc(sizeof(zend_output_globals)); + memcpy(ctx->output_ptr, SWOG, sizeof(zend_output_globals)); + php_output_activate(); + } else { + ctx->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; + } +} + +void PHPCoroutine::set_hook_flags(uint32_t flags) { + zval options; + array_init(&options); + add_assoc_long(&options, "hook_flags", flags); + + if (PHPCoroutine::options) { + zend_hash_merge(PHPCoroutine::options, Z_ARRVAL(options), nullptr, true); + zval_ptr_dtor(&options); + } else { + PHPCoroutine::options = Z_ARRVAL(options); + } + + config.hook_flags = flags; +} + +void PHPCoroutine::save_context(PHPContext *ctx) { + save_vm_stack(ctx); + save_og(ctx); +} + +void PHPCoroutine::restore_context(PHPContext *ctx) { + restore_vm_stack(ctx); + restore_og(ctx); +} + +void PHPCoroutine::on_yield(void *arg) { + 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 *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 *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 = ctx->co->get_cid(); + long origin_cid = ctx->co->get_origin_cid(); +#endif + + if (swoole_isset_hook(SW_GLOBAL_HOOK_ON_CORO_STOP)) { + swoole_call_hook(SW_GLOBAL_HOOK_ON_CORO_STOP, ctx); + } + + if (OG(handlers).elements) { + zend_bool no_headers = SG(request_info).no_headers; + /* Do not send headers by SAPI */ + SG(request_info).no_headers = 1; + if (OG(active)) { + php_output_end_all(); + } + php_output_deactivate(); + php_output_activate(); + SG(request_info).no_headers = no_headers; + } +#if PHP_VERSION_ID < 80100 + if (ctx->array_walk_fci) { + efree(ctx->array_walk_fci); + } +#endif + + 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; + } + + // Release resources + if (ctx->context) { + zend_object *context = ctx->context; + ctx->context = (zend_object *) ~0; + OBJ_RELEASE(context); + } + + Z_TRY_DELREF(ctx->fci.function_name); + ZVAL_UNDEF(&ctx->fci.function_name); + sw_zend_fci_cache_discard(&ctx->fci_cache); + + Z_TRY_DELREF(ctx->return_value); + +#ifdef SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT + fiber_context_switch_try_notify(ctx, origin_ctx); + fiber_context_try_destroy(ctx); +#endif + + swoole_trace_log(SW_TRACE_COROUTINE, + "coro close cid=%ld and resume to %ld, %zu remained. usage size: %zu. malloc size: %zu", + cid, + origin_cid, + (uintmax_t) Coroutine::count() - 1, + (uintmax_t) zend_memory_usage(0), + (uintmax_t) zend_memory_usage(1)); + + zend_vm_stack_destroy(); + restore_context(origin_ctx); +} + +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); + } + +#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(); + + // The defer tasks still need to be executed after an exception occurs + if (ctx->defer_tasks) { + std::stack *tasks = ctx->defer_tasks; + while (!tasks->empty()) { + zend::Function *defer_fci = tasks->top(); + tasks->pop(); + if (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 ctx->defer_tasks; + ctx->defer_tasks = nullptr; + } + } + zend_catch { + // zend_bailout is executed in the c function + catch_exception(); + exception_caught = true; + } + zend_end_try(); + destroy_context(ctx); + if (exception_caught) { + bailout(); + } +} + +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; + } + if (sw_unlikely(!fci_cache || !fci_cache->function_handler)) { + php_swoole_fatal_error(E_ERROR, "invalid function call info cache"); + return Coroutine::ERR_INVALID; + } + zend_uchar type = fci_cache->function_handler->type; + if (sw_unlikely(type != ZEND_USER_FUNCTION && type != ZEND_INTERNAL_FUNCTION)) { + php_swoole_fatal_error(E_ERROR, "invalid function type %u", fci_cache->function_handler->type); + return Coroutine::ERR_INVALID; + } + + if (sw_unlikely(!activated)) { + activate(); + } + + Args _args; + _args.fci_cache = fci_cache; + _args.argv = argv; + _args.argc = argc; + _args.callable = callable; + save_context(get_context()); + + return Coroutine::create(main_func, (void *) &_args); +} + +void PHPCoroutine::defer(zend::Function *fci) { + PHPContext *ctx = get_context(); + if (ctx->defer_tasks == nullptr) { + ctx->defer_tasks = new std::stack; + } + ctx->defer_tasks->push(fci); +} + +#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::fiber_context_try_init(PHPContext *ctx) { + if (EXPECTED(!SWOOLE_G(enable_fiber_mock))) { + return; + } + fiber_context_init(ctx); +} + +void PHPCoroutine::fiber_context_destroy(PHPContext *ctx) { + zend_observer_fiber_destroy_notify(ctx->fiber_context); + + if (ctx->fiber_context != NULL) { + efree(ctx->fiber_context); + } +} + +void PHPCoroutine::fiber_context_try_destroy(PHPContext *ctx) { + if (EXPECTED(!SWOOLE_G(enable_fiber_mock))) { + return; + } + fiber_context_destroy(ctx); +} + +zend_fiber_status PHPCoroutine::get_fiber_status(PHPContext *ctx) { + switch (ctx->co->get_state()) { + case Coroutine::STATE_INIT: + return ZEND_FIBER_STATUS_INIT; + case Coroutine::STATE_WAITING: + return ZEND_FIBER_STATUS_SUSPENDED; + case Coroutine::STATE_RUNNING: + return ZEND_FIBER_STATUS_RUNNING; + case Coroutine::STATE_END: + return ZEND_FIBER_STATUS_DEAD; + default: + php_swoole_fatal_error(E_ERROR, "Unexpected state when get fiber status"); + return ZEND_FIBER_STATUS_DEAD; + } +} + +void PHPCoroutine::fiber_context_switch_notify(PHPContext *from, PHPContext *to) { + zend_fiber_context *from_context = from->fiber_context; + zend_fiber_context *to_context = to->fiber_context; + + from_context->status = get_fiber_status(from); + to_context->status = get_fiber_status(to); + + if (!to->fiber_init_notified) { + to_context->status = ZEND_FIBER_STATUS_INIT; + zend_observer_fiber_switch_notify(from_context, to_context); + to_context->status = get_fiber_status(to); + to->fiber_init_notified = true; + } else { + zend_observer_fiber_switch_notify(from_context, to_context); + } +} + +void PHPCoroutine::fiber_context_switch_try_notify(PHPContext *from, PHPContext *to) { + if (EXPECTED(!SWOOLE_G(enable_fiber_mock))) { + return; + } + fiber_context_switch_notify(from, to); +} +#endif /* SWOOLE_COROUTINE_MOCK_FIBER_CONTEXT */ + +#ifdef ZEND_CHECK_STACK_LIMIT +void *PHPCoroutine::stack_limit(PHPContext *ctx) { +#ifdef SW_USE_THREAD_CONTEXT + return nullptr; +#else + zend_ulong reserve = EG(reserved_stack_size); + +#ifdef __APPLE__ + /* On Apple Clang, the stack probing function ___chkstk_darwin incorrectly + * probes a location that is twice the entered function's stack usage away + * from the stack pointer, when using an alternative stack. + * https://openradar.appspot.com/radar?id=5497722702397440 + */ + reserve = reserve * 2; +#endif + + if (!ctx->co) { + return nullptr; + } + + /* stack->pointer is the end of the stack */ + return (int8_t *) ctx->co->get_ctx().get_stack() + reserve; +#endif +} +void *PHPCoroutine::stack_base(PHPContext *ctx) { +#ifdef SW_USE_THREAD_CONTEXT + return nullptr; +#else + if (!ctx->co) { + return nullptr; + } + + return (void *) ((uintptr_t) ctx->co->get_ctx().get_stack() + ctx->co->get_ctx().get_stack_size()); +#endif +} +#endif /* ZEND_CHECK_STACK_LIMIT */ + +/* hook autoload */ + +static zend_class_entry *(*original_zend_autoload)(zend_string *name, zend_string *lc_name); + +struct AutoloadContext { + Coroutine *coroutine; + zend_class_entry *ce; +}; + +struct AutoloadQueue { + Coroutine *coroutine; + std::queue *queue; +}; + +static zend_class_entry *swoole_coroutine_autoload(zend_string *name, zend_string *lc_name) { + auto current = Coroutine::get_current(); + if (!current) { + return original_zend_autoload(name, lc_name); + } + + ZEND_ASSERT(EG(in_autoload) != nullptr); + zend_hash_del(EG(in_autoload), lc_name); + + if (UNEXPECTED(SWOOLE_G(in_autoload) == nullptr)) { + ALLOC_HASHTABLE(SWOOLE_G(in_autoload)); + zend_hash_init(SWOOLE_G(in_autoload), 8, nullptr, nullptr, 0); + } + zval *z_queue = zend_hash_find(SWOOLE_G(in_autoload), lc_name); + if (z_queue != nullptr) { + auto queue = (AutoloadQueue *) Z_PTR_P(z_queue); + if (queue->coroutine == current) { + return nullptr; + } + AutoloadContext context; + context.coroutine = current; + context.ce = nullptr; + queue->queue->push(&context); + current->yield(); + return context.ce; + } + AutoloadQueue queue; + queue.coroutine = current; + std::queue queue_object; + queue.queue = &queue_object; + + zend_hash_add_ptr(SWOOLE_G(in_autoload), lc_name, &queue); + zend_class_entry *ce = original_zend_autoload(name, lc_name); + zend_hash_del(SWOOLE_G(in_autoload), lc_name); + + AutoloadContext *pending_context = nullptr; + while (!queue_object.empty()) { + pending_context = queue_object.front(); + queue_object.pop(); + pending_context->ce = ce; + pending_context->coroutine->resume(); + } + return ce; +} + +void php_swoole_coroutine_minit(int module_number) { + SW_INIT_CLASS_ENTRY_BASE(swoole_coroutine_util, "Swoole\\Coroutine", "Co", swoole_coroutine_methods, nullptr); + SW_SET_CLASS_CREATE(swoole_coroutine_util, sw_zend_create_object_deny); + + SW_INIT_CLASS_ENTRY_BASE( + swoole_coroutine_iterator, "Swoole\\Coroutine\\Iterator", "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); + SW_REGISTER_LONG_CONSTANT("SWOOLE_CORO_INIT", Coroutine::STATE_INIT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_CORO_WAITING", Coroutine::STATE_WAITING); + SW_REGISTER_LONG_CONSTANT("SWOOLE_CORO_RUNNING", Coroutine::STATE_RUNNING); + 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, 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); + + 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(); +} + +static PHP_METHOD(swoole_exit_exception, getFlags) { + SW_RETURN_PROPERTY("flags"); +} + +static PHP_METHOD(swoole_exit_exception, getStatus) { + SW_RETURN_PROPERTY("status"); +} + +PHP_FUNCTION(swoole_coroutine_create) { + zend_fcall_info fci; + zend_fcall_info_cache fci_cache; + + ZEND_PARSE_PARAMETERS_START(1, -1) + Z_PARAM_FUNC(fci, fci_cache) + Z_PARAM_VARIADIC('*', fci.params, fci.param_count) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (sw_unlikely(SWOOLE_G(req_status) == PHP_SWOOLE_CALL_USER_SHUTDOWNFUNC_BEGIN)) { + zend_function *func = (zend_function *) EG(current_execute_data)->prev_execute_data->func; + if (func->common.function_name && + sw_unlikely(memcmp(ZSTR_VAL(func->common.function_name), ZEND_STRS("__destruct")) == 0)) { + php_swoole_fatal_error(E_ERROR, "can not use coroutine in __destruct after php_request_shutdown"); + RETURN_FALSE; + } + } + + long cid = PHPCoroutine::create(&fci_cache, fci.param_count, fci.params, &fci.function_name); + if (sw_likely(cid > 0)) { + RETURN_LONG(cid); + } else { + RETURN_FALSE; + } +} + +PHP_FUNCTION(swoole_coroutine_defer) { + zend_fcall_info fci; + zend_fcall_info_cache fci_cache; + zend::Function *defer_fci; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_FUNC(fci, fci_cache) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + Coroutine::get_current_safe(); + defer_fci = (zend::Function *) emalloc(sizeof(zend::Function)); + defer_fci->fci = fci; + defer_fci->fci_cache = fci_cache; + sw_zend_fci_cache_persist(&defer_fci->fci_cache); + PHPCoroutine::defer(defer_fci); +} + +static PHP_METHOD(swoole_coroutine, stats) { + array_init(return_value); + add_assoc_long_ex(return_value, ZEND_STRL("event_num"), sw_reactor() ? sw_reactor()->get_event_num() : 0); + add_assoc_long_ex( + return_value, ZEND_STRL("signal_listener_num"), SwooleTG.signal_listener_num + SwooleTG.co_signal_listener_num); + + if (SwooleTG.async_threads) { + add_assoc_long_ex(return_value, ZEND_STRL("aio_task_num"), SwooleTG.async_threads->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()); + add_assoc_long_ex(return_value, ZEND_STRL("coroutine_last_cid"), Coroutine::get_last_cid()); +} + +PHP_METHOD(swoole_coroutine, getCid) { + RETURN_LONG(PHPCoroutine::get_cid()); +} + +PHP_METHOD(swoole_coroutine, getPcid) { + zend_long cid = 0; + zend_long ret; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(cid) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + ret = PHPCoroutine::get_pcid(cid); + if (ret == 0) { + RETURN_FALSE; + } + + RETURN_LONG(ret); +} + +static PHP_METHOD(swoole_coroutine, getContext) { + zend_long cid = 0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(cid) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + PHPContext *ctx = + (PHPContext *) (EXPECTED(cid == 0) ? Coroutine::get_current_task() : Coroutine::get_task_by_cid(cid)); + if (UNEXPECTED(!ctx)) { + swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS); + RETURN_NULL(); + } + 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(!ctx->context)) { + object_init_ex(return_value, swoole_coroutine_context_ce); + ctx->context = Z_OBJ_P(return_value); + } + GC_ADDREF(ctx->context); + RETURN_OBJ(ctx->context); +} + +static PHP_METHOD(swoole_coroutine, getElapsed) { + zend_long cid = 0; + zend_long ret; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(cid) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + ret = PHPCoroutine::get_elapsed(cid); + RETURN_LONG(ret); +} + +static PHP_METHOD(swoole_coroutine, getStackUsage) { + zend_long cid = 0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(cid) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + ssize_t usage = PHPCoroutine::get_stack_usage(cid); + if (usage < 0) { + RETURN_FALSE; + } + RETURN_LONG(usage); +} + +static PHP_METHOD(swoole_coroutine, exists) { + zend_long cid; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(cid) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_BOOL(Coroutine::get_by_cid(cid) != nullptr); +} + +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, "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; +} + +static PHP_METHOD(swoole_coroutine, yield) { + Coroutine *co = Coroutine::get_current_safe(); + user_yield_coros[co->get_cid()] = co; + + 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; +} + +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; + + 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); + + if (!cid || cid == PHPCoroutine::get_cid()) { + zend_fetch_debug_backtrace(return_value, 0, options, limit); + } 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); + EG(current_execute_data) = task->execute_data; + zend_fetch_debug_backtrace(return_value, 0, options, limit); + EG(current_execute_data) = ex_backup; + } +} + +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); + + if (!cid || cid == PHPCoroutine::get_cid()) { + zend::function::call("debug_print_backtrace", 2, argv); + } else { + 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) = ctx->execute_data; + zend::function::call("debug_print_backtrace", 2, argv); + EG(current_execute_data) = ex_backup; + } +} + +static PHP_METHOD(swoole_coroutine, printBackTrace) { + zend_long cid; + zend_long options = 0; + zend_long limit = 0; + + ZEND_PARSE_PARAMETERS_START(0, 3) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(cid) + Z_PARAM_LONG(options) + Z_PARAM_LONG(limit) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + sw_php_print_backtrace(cid, options, limit, return_value); +} + +static PHP_METHOD(swoole_coroutine, list) { + zval zlist; + array_init(&zlist); + for (auto &co : Coroutine::coroutines) { + add_next_index_long(&zlist, co.second->get_cid()); + } + object_init_ex(return_value, swoole_coroutine_iterator_ce); + sw_zend_call_method_with_1_params(return_value, + swoole_coroutine_iterator_ce, + &swoole_coroutine_iterator_ce->constructor, + "__construct", + nullptr, + &zlist); + 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()); +} + +PHP_METHOD(swoole_coroutine, disableScheduler) { + RETURN_BOOL(PHPCoroutine::disable_scheduler()); +} + +/** + * for gdb + */ +zend_executor_globals *php_swoole_get_executor_globals() { + return (zend_executor_globals *) &EG(uninitialized_zval); +} 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 new file mode 100644 index 00000000000..ad20b1e65e0 --- /dev/null +++ b/ext-src/swoole_coroutine_scheduler.cc @@ -0,0 +1,311 @@ +/* + +----------------------------------------------------------------------+ + | 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: Xinyu Zhu | + | shiguangqi | + | Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#include "php_swoole_cxx.h" + +#include + +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; + +struct SchedulerTask { + zend_long count; + zend_fcall_info fci; + zend_fcall_info_cache fci_cache; +}; + +struct SchedulerObject { + std::queue *list; + bool started; + zend_object std; +}; + +static zend_class_entry *swoole_coroutine_scheduler_ce; +static zend_object_handlers swoole_coroutine_scheduler_handlers; + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_coroutine_scheduler, add); +static PHP_METHOD(swoole_coroutine_scheduler, parallel); +static PHP_METHOD(swoole_coroutine_scheduler, start); +SW_EXTERN_C_END + +static sw_inline SchedulerObject *scheduler_get_object(zend_object *obj) { + return (SchedulerObject *) ((char *) obj - swoole_coroutine_scheduler_handlers.offset); +} + +static zend_object *scheduler_create_object(zend_class_entry *ce) { + SchedulerObject *s = (SchedulerObject *) zend_object_alloc(sizeof(SchedulerObject), ce); + zend_object_std_init(&s->std, ce); + object_properties_init(&s->std, ce); + s->std.handlers = &swoole_coroutine_scheduler_handlers; + return &s->std; +} + +static void scheduler_free_object(zend_object *object) { + SchedulerObject *s = scheduler_get_object(object); + if (s->list) { + while (!s->list->empty()) { + SchedulerTask *task = s->list->front(); + s->list->pop(); + sw_zend_fci_cache_discard(&task->fci_cache); + sw_zend_fci_params_discard(&task->fci); + efree(task); + } + delete s->list; + s->list = nullptr; + } + zend_object_std_dtor(&s->std); +} + +// clang-format off +static const zend_function_entry swoole_coroutine_scheduler_methods[] = { + 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 +}; + +// clang-format on + +void php_swoole_coroutine_scheduler_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_coroutine_scheduler, + "Swoole\\Coroutine\\Scheduler", + "Co\\Scheduler", + swoole_coroutine_scheduler_methods); + 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); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_coroutine_scheduler, scheduler_create_object, scheduler_free_object, SchedulerObject, std); + swoole_coroutine_scheduler_ce->ce_flags |= ZEND_ACC_FINAL; +} + +static zend::Callable *exit_condition_fci_cache = nullptr; + +static bool php_swoole_coroutine_reactor_can_exit(Reactor *reactor, size_t &event_num) { + zval retval; + int success; + + SW_ASSERT(exit_condition_fci_cache); + ZVAL_NULL(&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"); + } + if (UNEXPECTED(EG(exception))) { + zend_exception_error(EG(exception), E_ERROR); + } + 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)) { + zend_long max_num = zval_get_long(ztmp); + PHPCoroutine::set_max_num(max_num <= 0 ? SW_DEFAULT_MAX_CORO_NUM : max_num); + } + if (php_swoole_array_get_value(vht, "enable_deadlock_check", ztmp)) { + PHPCoroutine::set_deadlock_check(zval_is_true(ztmp)); + } + if (php_swoole_array_get_value(vht, "hook_flags", ztmp)) { + PHPCoroutine::set_hook_flags(zval_get_long(ztmp)); + } + if (php_swoole_array_get_value(vht, "enable_preemptive_scheduler", ztmp)) { + 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(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); + } else { + PHPCoroutine::options = zend_array_dup(vht); + } +} + +PHP_METHOD(swoole_coroutine_scheduler, set) { + zval *zset = nullptr; + HashTable *vht = nullptr; + zval *ztmp; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(zset) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + vht = Z_ARRVAL_P(zset); + php_swoole_set_global_option(vht); + php_swoole_set_coroutine_option(vht); + + if (php_swoole_array_get_value(vht, "dns_cache_expire", ztmp)) { + System::set_dns_cache_expire((time_t) zval_get_long(ztmp)); + } + if (php_swoole_array_get_value(vht, "dns_cache_capacity", ztmp)) { + System::set_dns_cache_capacity((size_t) zval_get_long(ztmp)); + } + /* Reactor can exit */ + if ((ztmp = zend_hash_str_find(vht, ZEND_STRL("exit_condition")))) { + if (exit_condition_fci_cache) { + sw_callable_free(exit_condition_fci_cache); + } + + 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()) { + sw_reactor()->remove_exit_condition(Reactor::EXIT_CONDITION_USER_AFTER_DEFAULT); + SwooleG.user_exit_condition = nullptr; + } + } + } +} + +PHP_METHOD(swoole_coroutine_scheduler, getOptions) { + if (!PHPCoroutine::options) { + return; + } + RETURN_ARR(zend_array_dup(PHPCoroutine::options)); +} + +static void scheduler_add_task(SchedulerObject *s, SchedulerTask *task) { + if (!s->list) { + s->list = new std::queue; + } + sw_zend_fci_cache_persist(&task->fci_cache); + sw_zend_fci_params_persist(&task->fci); + s->list->push(task); +} + +static PHP_METHOD(swoole_coroutine_scheduler, add) { + SchedulerObject *s = scheduler_get_object(Z_OBJ_P(ZEND_THIS)); + if (s->started) { + php_swoole_fatal_error( + E_WARNING, "scheduler is running, unable to execute %s->add", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } + + SchedulerTask *task = (SchedulerTask *) ecalloc(1, sizeof(SchedulerTask)); + + ZEND_PARSE_PARAMETERS_START(1, -1) + Z_PARAM_FUNC(task->fci, task->fci_cache) + Z_PARAM_VARIADIC('*', task->fci.params, task->fci.param_count) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + task->count = 1; + scheduler_add_task(s, task); +} + +static PHP_METHOD(swoole_coroutine_scheduler, parallel) { + SchedulerObject *s = scheduler_get_object(Z_OBJ_P(ZEND_THIS)); + if (s->started) { + php_swoole_fatal_error( + E_WARNING, "scheduler is running, unable to execute %s->parallel", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } + + SchedulerTask *task = (SchedulerTask *) ecalloc(1, sizeof(SchedulerTask)); + zend_long count; + + ZEND_PARSE_PARAMETERS_START(2, -1) + Z_PARAM_LONG(count) + Z_PARAM_FUNC(task->fci, task->fci_cache) + Z_PARAM_VARIADIC('*', task->fci.params, task->fci.param_count) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + task->count = count; + scheduler_add_task(s, task); +} + +static PHP_METHOD(swoole_coroutine_scheduler, start) { + SchedulerObject *s = scheduler_get_object(Z_OBJ_P(ZEND_THIS)); + + 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)); + RETURN_FALSE; + } + if (php_swoole_reactor_init() < 0) { + RETURN_FALSE; + } + + s->started = true; + + if (!s->list) { + php_swoole_fatal_error(E_WARNING, "no coroutine task"); + RETURN_FALSE; + } + + while (!s->list->empty()) { + 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, &task->fci.function_name); + } + sw_zend_fci_cache_discard(&task->fci_cache); + sw_zend_fci_params_discard(&task->fci); + efree(task); + } + php_swoole_event_wait(); + delete s->list; + s->list = nullptr; + s->started = false; + RETURN_TRUE; +} diff --git a/ext-src/swoole_coroutine_system.cc b/ext-src/swoole_coroutine_system.cc new file mode 100644 index 00000000000..45646bf11a0 --- /dev/null +++ b/ext-src/swoole_coroutine_system.cc @@ -0,0 +1,367 @@ +/* ++----------------------------------------------------------------------+ +| 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" +#include + +#include + +using swoole::Coroutine; +using swoole::Event; +using swoole::PHPCoroutine; +using swoole::Reactor; +using swoole::String; +using swoole::TimerNode; +using swoole::coroutine::Socket; +using swoole::coroutine::System; + +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_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", "Co\\System", swoole_coroutine_system_methods, nullptr); + SW_SET_CLASS_CREATE(swoole_coroutine_system, sw_zend_create_object_deny); +} + +PHP_METHOD(swoole_coroutine_system, sleep) { + double seconds; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_DOUBLE(seconds) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (UNEXPECTED(seconds < SW_TIMER_MIN_SEC)) { + php_swoole_fatal_error(E_WARNING, "Timer must be greater than or equal to " ZEND_TOSTR(SW_TIMER_MIN_SEC)); + RETURN_FALSE; + } + RETURN_BOOL(System::sleep(seconds) == 0); +} + +PHP_METHOD(swoole_coroutine_system, readFile) { + char *filename; + size_t l_filename; + zend_long flags = 0; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STRING(filename, l_filename) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + auto result = System::read_file(filename, flags & LOCK_EX); + if (result == nullptr) { + RETURN_FALSE; + } else { + RETVAL_STRINGL(result->str, result->length); + } +} + +PHP_METHOD(swoole_coroutine_system, writeFile) { + char *filename; + size_t l_filename; + char *data; + size_t l_data; + zend_long flags = 0; + + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_STRING(filename, l_filename) + Z_PARAM_STRING(data, l_data) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + int _flags = 0; + if (flags & PHP_FILE_APPEND) { + _flags |= O_APPEND; + } else { + _flags |= O_TRUNC; + } + + ssize_t retval = System::write_file(filename, data, l_data, flags & LOCK_EX, _flags); + if (retval < 0) { + RETURN_FALSE; + } else { + RETURN_LONG(retval); + } +} + +PHP_FUNCTION(swoole_coroutine_gethostbyname) { + Coroutine::get_current_safe(); + + char *domain_name; + size_t l_domain_name; + zend_long family = AF_INET; + double timeout = -1; + + 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"); + RETURN_FALSE; + } + + if (family != AF_INET && family != AF_INET6) { + php_swoole_fatal_error(E_WARNING, "unknown protocol family, must be AF_INET or AF_INET6"); + RETURN_FALSE; + } + + std::string address = System::gethostbyname(std::string(domain_name, l_domain_name), family, timeout); + if (address.empty()) { + RETURN_FALSE; + } else { + RETURN_STRINGL(address.c_str(), address.length()); + } +} + +PHP_FUNCTION(swoole_clear_dns_cache) { + System::clear_dns_cache(); +} + +PHP_METHOD(swoole_coroutine_system, getaddrinfo) { + char *hostname; + size_t l_hostname; + zend_long family = AF_INET; + zend_long socktype = SOCK_STREAM; + zend_long protocol = IPPROTO_TCP; + char *service = nullptr; + size_t l_service = 0; + double timeout = -1; + + 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"); + RETURN_FALSE; + } + + if (family != AF_INET && family != AF_INET6) { + php_swoole_fatal_error(E_WARNING, "unknown protocol family, must be AF_INET or AF_INET6"); + RETURN_FALSE; + } + + std::string str_service(service ? service : ""); + std::vector result = System::getaddrinfo(hostname, family, socktype, protocol, str_service, timeout); + + if (result.empty()) { + RETURN_FALSE; + } + + array_init(return_value); + for (auto i = result.begin(); i != result.end(); i++) { + add_next_index_stringl(return_value, i->c_str(), i->length()); + } +} + +PHP_METHOD(swoole_coroutine_system, statvfs) { + char *path; + size_t l_path; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(path, l_path) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + struct statvfs _stat; + swoole_coroutine_statvfs(path, &_stat); + + array_init(return_value); + add_assoc_long(return_value, "bsize", _stat.f_bsize); + add_assoc_long(return_value, "frsize", _stat.f_frsize); + add_assoc_long(return_value, "blocks", _stat.f_blocks); + add_assoc_long(return_value, "bfree", _stat.f_bfree); + add_assoc_long(return_value, "bavail", _stat.f_bavail); + add_assoc_long(return_value, "files", _stat.f_files); + add_assoc_long(return_value, "ffree", _stat.f_ffree); + add_assoc_long(return_value, "favail", _stat.f_favail); + add_assoc_long(return_value, "fsid", _stat.f_fsid); + add_assoc_long(return_value, "flag", _stat.f_flag); + add_assoc_long(return_value, "namemax", _stat.f_namemax); +} + +PHP_METHOD(swoole_coroutine_system, exec) { + char *command; + size_t command_len; + zend_bool get_error_stream = 0; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STRING(command, command_len) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(get_error_stream) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + 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; + } + + auto str = zend::fetch_zend_string_by_val(buffer->str); + buffer->set_null_terminated(); + str->len = buffer->length; + buffer->release(); + + zval zdata; + ZVAL_STR(&zdata, str); + + 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) { + int status; + + Coroutine::get_current_safe(); + + if (pid < 0) { + pid = System::wait(&status, timeout); + } else { + pid = System::waitpid(pid, &status, 0, timeout); + } + if (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)); + } else { + swoole_set_last_error(errno); + RETURN_FALSE; + } +} + +PHP_METHOD(swoole_coroutine_system, wait) { + SW_MUST_BE_MAIN_THREAD(); + double timeout = -1; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + swoole_coroutine_system_wait(INTERNAL_FUNCTION_PARAM_PASSTHRU, -1, timeout); +} + +PHP_METHOD(swoole_coroutine_system, waitPid) { + SW_MUST_BE_MAIN_THREAD(); + zend_long pid; + double timeout = -1; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_LONG(pid) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + swoole_coroutine_system_wait(INTERNAL_FUNCTION_PARAM_PASSTHRU, pid, timeout); +} + +PHP_METHOD(swoole_coroutine_system, waitSignal) { + SW_MUST_BE_MAIN_THREAD(); + zval *zsignals; + double timeout = -1; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(zsignals) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + std::vector signals; + + if (ZVAL_IS_ARRAY(zsignals)) { + zval *item; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zsignals), item) { + signals.push_back(zval_get_long(item)); + } + ZEND_HASH_FOREACH_END(); + } else { + signals.push_back(zval_get_long(zsignals)); + } + + int signo = System::wait_signal(signals, timeout); + if (signo == -1) { + if (swoole_get_last_error() == EBUSY) { + php_swoole_fatal_error(E_WARNING, "Unable to wait signal, async signal listener has been registered"); + } else if (swoole_get_last_error() == EINVAL) { + php_swoole_fatal_error(E_WARNING, "Invalid signal in the given list"); + } + errno = swoole_get_last_error(); + RETURN_FALSE; + } + + RETURN_LONG(signo); +} + +PHP_METHOD(swoole_coroutine_system, waitEvent) { + zval *zfd; + zend_long events = SW_EVENT_READ; + double timeout = -1; + + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_ZVAL(zfd) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(events) + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + int fd = php_swoole_convert_to_fd(zfd); + if (fd < 0) { + 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 new file mode 100644 index 00000000000..e5960ccccc3 --- /dev/null +++ b/ext-src/swoole_event.cc @@ -0,0 +1,727 @@ +/* + +----------------------------------------------------------------------+ + | 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: Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#include "php_swoole_cxx.h" +#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; + +static std::unordered_map event_socket_map; + +zend_class_entry *swoole_event_ce; +static zend_object_handlers swoole_event_handlers; + +struct EventObject { + zval zsocket; + zend::Callable *readable_callback; + zend::Callable *writable_callback; +}; + +static int event_readable_callback(Reactor *reactor, Event *event); +static int event_writable_callback(Reactor *reactor, Event *event); +static int event_error_callback(Reactor *reactor, Event *event); +static void event_defer_callback(void *data); +static void event_end_callback(void *data); + +SW_EXTERN_C_BEGIN +static PHP_FUNCTION(swoole_event_add); +static PHP_FUNCTION(swoole_event_set); +static PHP_FUNCTION(swoole_event_del); +static PHP_FUNCTION(swoole_event_write); +static PHP_FUNCTION(swoole_event_wait); +static PHP_FUNCTION(swoole_event_rshutdown); +static PHP_FUNCTION(swoole_event_exit); +static PHP_FUNCTION(swoole_event_defer); +static PHP_FUNCTION(swoole_event_cycle); +static PHP_FUNCTION(swoole_event_dispatch); +static PHP_FUNCTION(swoole_event_isset); +SW_EXTERN_C_END + +// clang-format off +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_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", 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", 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->readable_callback) { + delete peo->readable_callback; + } + if (peo->writable_callback) { + delete peo->writable_callback; + } + zval_ptr_dtor((&peo->zsocket)); + efree(peo); +} + +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(fcc, 1, argv, nullptr, php_swoole_is_enable_coroutine()))) { + php_swoole_fatal_error(E_WARNING, + "%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; + swoole_event_defer(event_object_free, peo); + swoole_event_del(event->socket); + return SW_ERR; + } + + return SW_OK; +} + +static int event_writable_callback(Reactor *reactor, Event *event) { + EventObject *peo = (EventObject *) event->socket->object; + + zval argv[1]; + argv[0] = peo->zsocket; + auto fcc = peo->writable_callback->ptr(); + + if (UNEXPECTED(!zend::function::call(fcc, 1, argv, nullptr, php_swoole_is_enable_coroutine()))) { + php_swoole_fatal_error(E_WARNING, + "%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; + swoole_event_defer(event_object_free, peo); + swoole_event_del(event->socket); + return SW_ERR; + } + + return SW_OK; +} + +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); + } else { + return reactor->get_handler(SW_EVENT_WRITE, event->socket->fd_type)(reactor, event); + } + } + + int error; + if (event->socket->get_option(SOL_SOCKET, SO_ERROR, &error) < 0) { + php_swoole_sys_error(E_WARNING, "swoole_event->onError[1]: getsockopt[sock=%d] failed", event->fd); + } + + if (error != 0) { + php_swoole_fatal_error( + E_WARNING, "swoole_event->onError[1]: socket error. Error: %s [%d]", strerror(error), error); + } + + event_object_free(event->socket->object); + swoole_event_del(event->socket); + + return SW_OK; +} + +static void event_defer_callback(void *data) { + 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)); + } + delete cb; +} + +static void event_end_callback(void *data) { + 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)); + } +} + +int php_swoole_reactor_init() { + if (!SWOOLE_G(cli)) { + php_swoole_fatal_error(E_ERROR, "async-io must be used in PHP CLI mode"); + return SW_ERR; + } + + if (sw_server()) { + if (sw_server()->is_task_worker() && !sw_server()->task_enable_coroutine) { + php_swoole_fatal_error( + E_ERROR, "Unable to use async-io in task processes, please set `task_enable_coroutine` to true"); + return SW_ERR; + } + if (sw_server()->is_manager()) { + php_swoole_fatal_error(E_ERROR, "Unable to use async-io in manager process"); + return SW_ERR; + } + } + if (!sw_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"); + return SW_ERR; + } + + php_swoole_register_shutdown_function("Swoole\\Event::rshutdown"); + } + + if (sw_reactor() && SwooleG.user_exit_condition && + !sw_reactor()->isset_exit_condition(Reactor::EXIT_CONDITION_USER_AFTER_DEFAULT)) { + sw_reactor()->set_exit_condition(Reactor::EXIT_CONDITION_USER_AFTER_DEFAULT, SwooleG.user_exit_condition); + } + + return SW_OK; +} + +void php_swoole_event_wait() { + if (php_swoole_is_fatal_error() || !sw_reactor()) { + return; + } + if (swoole_coroutine_is_in()) { + php_swoole_fatal_error(E_ERROR, "Unable to call Event::wait() in coroutine"); + return; + } + 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) + zend_bool in_shutdown = EG(flags) & EG_FLAGS_IN_SHUTDOWN; + EG(flags) &= ~EG_FLAGS_IN_SHUTDOWN; +#endif + if (sw_reactor()->wait(nullptr) < 0) { + php_swoole_sys_error(E_ERROR, "reactor wait failed"); + } +#if defined(EG_FLAGS_IN_SHUTDOWN) && !defined(EG_FLAGS_OBJECT_STORE_NO_REUSE) + if (in_shutdown) { + EG(flags) |= EG_FLAGS_IN_SHUTDOWN; + } +#endif + } + swoole_event_free(); +} + +void php_swoole_event_exit() { + if (sw_reactor()) { + php_swoole_timer_clear_all(); + sw_reactor()->running = false; + } +} + +int php_swoole_convert_to_fd(zval *zsocket) { + int fd = -1; + + switch (Z_TYPE_P(zsocket)) { + case IS_RESOURCE: { + php_stream *stream; + if ((php_stream_from_zval_no_verify(stream, zsocket))) { + if (php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void **) &fd, 1) == + SUCCESS && + fd >= 0) { + return fd; + } + } + php_swoole_fatal_error(E_WARNING, "fd argument must be either valid PHP stream or valid PHP socket resource"); + return SW_ERR; + } + case IS_LONG: { + fd = Z_LVAL_P(zsocket); + if (fd < 0) { + php_swoole_fatal_error(E_WARNING, "invalid file descriptor#%d passed", fd); + return SW_ERR; + } + return fd; + } + case IS_OBJECT: { + zval *zfd = nullptr; + 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 (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 (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; + } + return Z_LVAL_P(zfd); + } + default: + php_swoole_fatal_error(E_WARNING, "invalid file descriptor passed"); + return SW_ERR; + } +} + +int php_swoole_convert_to_fd_ex(zval *zsocket, int *async) { + int fd; + + *async = 0; + if (Z_TYPE_P(zsocket) == IS_RESOURCE) { + php_stream *stream; + if ((php_stream_from_zval_no_verify(stream, zsocket))) { + if (php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void **) &fd, 1) == + SUCCESS && + fd >= 0) { + *async = (stream->wrapper && (stream->wrapper->wops == php_plain_files_wrapper.wops)) ? 0 : 1; + return fd; + } + } +#ifdef SWOOLE_SOCKETS_SUPPORT + else { + php_socket *php_sock; + if ((php_sock = SW_Z_SOCKET_P(zsocket))) { + fd = php_sock->bsd_socket; + *async = 1; + 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; +} + +#ifdef SWOOLE_SOCKETS_SUPPORT +php_socket *php_swoole_convert_to_socket(int sock) { + php_socket *socket_object; + zval zsocket; + object_init_ex(&zsocket, socket_ce); + socket_object = Z_SOCKET_P(&zsocket); + socket_import_file_descriptor(sock, socket_object); + return socket_object; +} +#endif + +static void event_check_reactor() { + php_swoole_check_reactor(); + + if (!swoole_event_isset_handler(SW_FD_USER)) { + swoole_event_set_handler(SW_FD_USER | SW_EVENT_READ, event_readable_callback); + swoole_event_set_handler(SW_FD_USER | SW_EVENT_WRITE, event_writable_callback); + swoole_event_set_handler(SW_FD_USER | SW_EVENT_ERROR, event_error_callback); + } +} + +static Socket *event_get_socket(int socket_fd) { + auto i = event_socket_map.find(socket_fd); + if (i == event_socket_map.end()) { + return nullptr; + } + return i->second; +} + +static PHP_FUNCTION(swoole_event_add) { + zval *zfd; + 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_ZVAL(zreadable_callback) + Z_PARAM_ZVAL(zwritable_callback) + Z_PARAM_LONG(events) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + event_check_reactor(); + + int socket_fd = php_swoole_convert_to_fd(zfd); + if (socket_fd < 0) { + php_swoole_fatal_error(E_WARNING, "unknown fd type"); + RETURN_FALSE; + } + if (socket_fd == 0 && (events & SW_EVENT_WRITE)) { + php_swoole_fatal_error(E_WARNING, "invalid socket fd [%d]", socket_fd); + RETURN_FALSE; + } + if (event_socket_map.find(socket_fd) != event_socket_map.end()) { + php_swoole_fatal_error(E_WARNING, "already exist"); + RETURN_FALSE; + } + if (!(events & (SW_EVENT_WRITE | SW_EVENT_READ))) { + php_swoole_fatal_error(E_WARNING, "invalid events"); + RETURN_FALSE; + } + Socket *socket = swoole::make_socket(socket_fd, SW_FD_USER); + if (!socket) { + RETURN_FALSE; + } + + 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; + } + + 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"); + socket->free(); + event_object_free(peo); + RETURN_FALSE; + } + + event_socket_map[socket_fd] = socket; + + RETURN_LONG(socket_fd); +} + +static PHP_FUNCTION(swoole_event_write) { + zval *zfd; + char *data; + size_t len; + + 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"); + RETURN_FALSE; + } + + int socket_fd = php_swoole_convert_to_fd(zfd); + if (socket_fd < 0) { + php_swoole_fatal_error(E_WARNING, "unknown type"); + RETURN_FALSE; + } + + Socket *socket = event_get_socket(socket_fd); + if (socket == nullptr) { + php_swoole_fatal_error(E_WARNING, "socket[%d] is not found in the reactor", socket_fd); + RETURN_FALSE; + } + + event_check_reactor(); + if (swoole_event_write(socket, data, len) < 0) { + RETURN_FALSE; + } else { + RETURN_TRUE; + } +} + +static PHP_FUNCTION(swoole_event_set) { + if (!sw_reactor()) { + php_swoole_fatal_error(E_WARNING, "reactor is not ready, cannot call swoole_event_set"); + RETURN_FALSE; + } + + zval *zfd; + 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_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) { + RETURN_FALSE; + } + + Socket *socket = event_get_socket(socket_fd); + if (socket == nullptr) { + php_swoole_fatal_error(E_WARNING, "socket[%d] is not found in the reactor", socket_fd); + RETURN_FALSE; + } + + 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); + } + peo->readable_callback = readable_callback; + } + if (writable_callback) { + if (peo->writable_callback) { + swoole_event_defer(sw_callable_free, peo->writable_callback); + } + peo->writable_callback = writable_callback; + } + if ((events & SW_EVENT_READ) && peo->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; + } + if ((events & SW_EVENT_WRITE) && peo->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); + RETURN_FALSE; + } + if (swoole_event_set(socket, events) < 0) { + php_swoole_fatal_error(E_WARNING, "%s::set failed", ZSTR_VAL(swoole_event_ce->name)); + RETURN_FALSE; + } + + RETURN_TRUE; +} + +static PHP_FUNCTION(swoole_event_del) { + zval *zfd; + + if (!sw_reactor()) { + php_swoole_fatal_error(E_WARNING, "reactor is not ready, cannot call swoole_event_del"); + 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, "unknown type"); + RETURN_FALSE; + } + + Socket *socket = event_get_socket(socket_fd); + if (!socket) { + RETURN_FALSE; + } + swoole_event_defer(event_object_free, socket->object); + int retval = swoole_event_del(socket); + event_socket_map.erase(socket_fd); + socket->fd = -1; + socket->free(); + RETURN_BOOL(retval == SW_OK); +} + +static PHP_FUNCTION(swoole_event_defer) { + zval *zfn; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zfn) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + php_swoole_check_reactor(); + auto fn = sw_callable_create(zfn); + swoole_event_defer(event_defer_callback, fn); + + RETURN_TRUE; +} + +static PHP_FUNCTION(swoole_event_cycle) { + zval *zcallback; + zend_bool before = 0; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(zcallback) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(before) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + event_check_reactor(); + + if (ZVAL_IS_NULL(zcallback)) { + if (sw_reactor()->idle_task.callback == nullptr) { + RETURN_FALSE; + } else { + 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; + } + } + + auto callback = sw_callable_create(zcallback); + if (!before) { + if (sw_reactor()->idle_task.data != nullptr) { + swoole_event_defer(sw_callable_free, sw_reactor()->idle_task.data); + } + + sw_reactor()->idle_task.callback = event_end_callback; + sw_reactor()->idle_task.data = callback; + } else { + if (sw_reactor()->future_task.data != nullptr) { + swoole_event_defer(sw_callable_free, sw_reactor()->future_task.data); + } + + sw_reactor()->future_task.callback = event_end_callback; + sw_reactor()->future_task.data = callback; + // Registration onBegin callback function + sw_reactor()->activate_future_task(); + } + + RETURN_TRUE; +} + +static PHP_FUNCTION(swoole_event_exit) { + php_swoole_event_exit(); +} + +static PHP_FUNCTION(swoole_event_wait) { + if (!sw_reactor()) { + return; + } + php_swoole_event_wait(); +} + +static PHP_FUNCTION(swoole_event_rshutdown) { + /* prevent the program from jumping out of the rshutdown */ + zend_try { + // 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(); + } + } + zend_end_try(); +} + +static PHP_FUNCTION(swoole_event_dispatch) { + if (!sw_reactor()) { + RETURN_FALSE; + } + sw_reactor()->once = true; + if (sw_reactor()->wait(nullptr) < 0) { + php_swoole_sys_error(E_ERROR, "reactor wait failed"); + } + sw_reactor()->once = false; + RETURN_TRUE; +} + +static PHP_FUNCTION(swoole_event_isset) { + if (!sw_reactor()) { + RETURN_FALSE; + } + + zval *zfd; + zend_long events = SW_EVENT_READ | SW_EVENT_WRITE; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|l", &zfd, &events) == FAILURE) { + RETURN_FALSE; + } + + int socket_fd = php_swoole_convert_to_fd(zfd); + if (socket_fd < 0) { + php_swoole_fatal_error(E_WARNING, "unknown type"); + RETURN_FALSE; + } + + Socket *_socket = event_get_socket(socket_fd); + if (_socket == nullptr || _socket->removed) { + RETURN_FALSE; + } + if (_socket->events & events) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc new file mode 100644 index 00000000000..f59d8050a96 --- /dev/null +++ b/ext-src/swoole_http2_client_coro.cc @@ -0,0 +1,1514 @@ +/* + +----------------------------------------------------------------------+ + | 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_http.h" + +#include "swoole_string.h" +#include "swoole_protocol.h" +#include "swoole_socket.h" +#include "swoole_util.h" + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_http2_client_coro_arginfo.h" +END_EXTERN_C() + +#include "swoole_http2.h" + +#define HTTP2_CLIENT_HOST_HEADER_INDEX 3 + +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; + +static zend_class_entry *swoole_http2_client_coro_exception_ce; +static zend_object_handlers swoole_http2_client_coro_exception_handlers; + +static zend_class_entry *swoole_http2_request_ce; +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 { + +struct Stream { + uint32_t stream_id; + uint8_t gzip; + uint8_t flags; + String *buffer; +#ifdef SW_HAVE_ZLIB + z_stream gzip_stream; + String *gzip_buffer; +#endif + zval zresponse; + + // flow control + uint32_t remote_window_size; + uint32_t local_window_size; +}; + +class Client { + public: + std::string host; + int port; + bool open_ssl; + double timeout = network::Socket::default_read_timeout; + + uint32_t stream_id = 0; // the next send stream id + uint32_t last_stream_id = 0; // the last received stream id + + Http2::Settings local_settings = {}; + Http2::Settings remote_settings = {}; + + // flow control + uint32_t remote_window_size = 0; + uint32_t local_window_size = 0; + + std::unordered_map streams; + std::queue send_queue; + + /* safety zval */ + zval _zobject; + zval *zobject; + Socket *socket_ = nullptr; + zval zsocket; + + Client(const char *_host, size_t _host_len, int _port, bool _ssl, zval *__zobject) { + host = std::string(_host, _host_len); + port = _port; + open_ssl = _ssl; + _zobject = *__zobject; + zobject = &_zobject; + Http2::init_settings(&local_settings); + local_window_size = local_settings.init_window_size; + } + + inline Stream *get_stream(uint32_t stream_id) { + auto i = streams.find(stream_id); + if (i == streams.end()) { + return nullptr; + } else { + return i->second; + } + } + + ssize_t build_header(zval *zobject, zval *zrequest, char *buffer); + + inline void update_error_properties(int code, const char *msg) { + php_swoole_socket_set_error_properties(zobject, code, msg); + } + + inline void io_error() { + update_error_properties(socket_->errCode, socket_->errMsg); + } + + inline void nghttp2_error(int code, const char *msg) { + update_error_properties(code, std_string::format("%s with error: %s", msg, nghttp2_strerror(code)).c_str()); + } + + inline 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; + } + + inline void apply_setting(zval *zset) { + if (socket_ && ZVAL_IS_ARRAY(zset)) { + php_swoole_socket_set(socket_, zset); + } + } + + inline bool recv_packet(double timeout) { + if (sw_unlikely(socket_->recv_packet(timeout) <= 0)) { + io_error(); + return false; + } + return true; + } + + bool connect(); + Stream *create_stream(uint32_t stream_id, uint8_t flags); + void destroy_stream(Stream *stream); + + inline bool delete_stream(uint32_t stream_id) { + auto i = streams.find(stream_id); + if (i == streams.end()) { + return false; + } + + destroy_stream(i->second); + streams.erase(i); + + return true; + } + + bool send_window_update(int stream_id, uint32_t size); + bool send_ping_frame(); + bool send_data(uint32_t stream_id, const char *p, size_t len, int flag); + 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); + 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 (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 *client; + zend_object std; +}; + +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 *http2_client_coro_get_client(zval *zobject) { + return http2_client_coro_fetch_object(Z_OBJ_P(zobject))->client; +} + +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(&h2o->std); +} + +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); + request->std.handlers = &swoole_http2_client_coro_handlers; + return &request->std; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_http2_client_coro, __construct); +static PHP_METHOD(swoole_http2_client_coro, __destruct); +static PHP_METHOD(swoole_http2_client_coro, set); +static PHP_METHOD(swoole_http2_client_coro, connect); +static PHP_METHOD(swoole_http2_client_coro, stats); +static PHP_METHOD(swoole_http2_client_coro, isStreamExist); +static PHP_METHOD(swoole_http2_client_coro, send); +static PHP_METHOD(swoole_http2_client_coro, write); +static PHP_METHOD(swoole_http2_client_coro, recv); +static PHP_METHOD(swoole_http2_client_coro, read); +static PHP_METHOD(swoole_http2_client_coro, ping); +static PHP_METHOD(swoole_http2_client_coro, goaway); +static PHP_METHOD(swoole_http2_client_coro, close); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_http2_client_methods[] = +{ + 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", "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, + 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", + "Co\\Http2\\Client\\Exception", + nullptr, + swoole_exception); + + 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", 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_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); + zend_declare_property_null(swoole_http2_request_ce, ZEND_STRL("headers"), ZEND_ACC_PUBLIC); + 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); + zend_declare_property_long(swoole_http2_response_ce, ZEND_STRL("statusCode"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_bool(swoole_http2_response_ce, ZEND_STRL("pipeline"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_http2_response_ce, ZEND_STRL("headers"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_http2_response_ce, ZEND_STRL("set_cookie_headers"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_http2_response_ce, ZEND_STRL("cookies"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_http2_response_ce, ZEND_STRL("data"), ZEND_ACC_PUBLIC); + + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_DATA", SW_HTTP2_TYPE_DATA); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_HEADERS", SW_HTTP2_TYPE_HEADERS); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_PRIORITY", SW_HTTP2_TYPE_PRIORITY); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_RST_STREAM", SW_HTTP2_TYPE_RST_STREAM); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_SETTINGS", SW_HTTP2_TYPE_SETTINGS); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_PUSH_PROMISE", SW_HTTP2_TYPE_PUSH_PROMISE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_PING", SW_HTTP2_TYPE_PING); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_GOAWAY", SW_HTTP2_TYPE_GOAWAY); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_WINDOW_UPDATE", SW_HTTP2_TYPE_WINDOW_UPDATE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_CONTINUATION", SW_HTTP2_TYPE_CONTINUATION); + + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_NO_ERROR", SW_HTTP2_ERROR_NO_ERROR); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_PROTOCOL_ERROR", SW_HTTP2_ERROR_PROTOCOL_ERROR); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_INTERNAL_ERROR", SW_HTTP2_ERROR_INTERNAL_ERROR); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_FLOW_CONTROL_ERROR", SW_HTTP2_ERROR_FLOW_CONTROL_ERROR); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_SETTINGS_TIMEOUT", SW_HTTP2_ERROR_SETTINGS_TIMEOUT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_STREAM_CLOSED", SW_HTTP2_ERROR_STREAM_CLOSED); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_FRAME_SIZE_ERROR", SW_HTTP2_ERROR_FRAME_SIZE_ERROR); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_REFUSED_STREAM", SW_HTTP2_ERROR_REFUSED_STREAM); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_CANCEL", SW_HTTP2_ERROR_CANCEL); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_COMPRESSION_ERROR", SW_HTTP2_ERROR_COMPRESSION_ERROR); + 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(socket_ != nullptr)) { + update_error_properties(EISCONN, strerror(EISCONN)); + return false; + } + + 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; + } + + 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 && !socket_->enable_ssl_encrypt()) { + io_error(); + close(); + return false; + } +#endif + 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 (!socket_->connect(host, port)) { + io_error(); + close(); + return false; + } + + stream_id = 1; + // [init]: we must set default value, server is not always send all the settings + Http2::init_settings(&remote_settings); + remote_window_size = remote_settings.init_window_size; + + int ret = nghttp2_hd_inflate_new2(&inflater, php_nghttp2_mem()); + if (ret != 0) { + nghttp2_error(ret, "nghttp2_hd_inflate_new2() failed"); + close(); + return false; + } + ret = nghttp2_hd_deflate_new2(&deflater, local_settings.header_table_size, php_nghttp2_mem()); + if (ret != 0) { + nghttp2_error(ret, "nghttp2_hd_deflate_new2() failed"); + close(); + return false; + } + + if (!send(ZEND_STRL(SW_HTTP2_PRI_STRING))) { + close(); + return false; + } + + if (!send_setting()) { + close(); + 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() { + /* + * 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(Z_OBJCE_P(zobject), SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 0); + if (!_socket->close()) { + update_error_properties(_socket->errCode, _socket->errMsg); + return false; + } + return true; +} + +ReturnCode Client::parse_frame(zval *return_value, bool pipeline_read) { + char *buf = 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 = Http2::get_length(buf); + buf += SW_HTTP2_FRAME_HEADER_SIZE; + + char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE]; + + if (stream_id > last_stream_id) { + last_stream_id = stream_id; + } + + uint16_t id = 0; + uint32_t value = 0; + + switch (type) { + case SW_HTTP2_TYPE_SETTINGS: { + if (flags & SW_HTTP2_FLAG_ACK) { + swoole_http2_frame_trace_log("ACK"); + return SW_CONTINUE; + } + + while (length > 0) { + id = ntohs(*(uint16_t *) (buf)); + value = ntohl(*(uint32_t *) (buf + sizeof(uint16_t))); + swoole_http2_frame_trace_log("id=%d, value=%d", id, value); + switch (id) { + case SW_HTTP2_SETTING_HEADER_TABLE_SIZE: + if (value != remote_settings.header_table_size) { + remote_settings.header_table_size = value; + int ret = nghttp2_hd_deflate_change_table_size(deflater, value); + if (ret != 0) { + nghttp2_error(ret, "nghttp2_hd_deflate_change_table_size() failed"); + return SW_ERROR; + } + } + 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; + swoole_trace_log(SW_TRACE_HTTP2, "setting: max_concurrent_streams=%u", value); + break; + case SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE: + 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; + 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) { + nghttp2_error(ret, "nghttp2_hd_inflate_change_table_size() failed"); + return SW_ERROR; + } +#endif + } + swoole_trace_log(SW_TRACE_HTTP2, "setting: max_header_list_size=%u", value); + break; + default: + // disable warning and ignore it because some websites are not following http2 protocol totally + // swoole_warning("unknown option[%d]: %d", id, value); + break; + } + buf += sizeof(id) + sizeof(value); + length -= sizeof(id) + sizeof(value); + } + + 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; + } + return SW_CONTINUE; + } + case SW_HTTP2_TYPE_WINDOW_UPDATE: { + value = ntohl(*(uint32_t *) buf); + swoole_trace_log( + SW_TRACE_HTTP2, "[" SW_ECHO_YELLOW "] stream_id=%d, size=%d", "WINDOW_UPDATE", stream_id, value); + if (stream_id == 0) { + remote_window_size += value; + } else { + Stream *stream = get_stream(stream_id); + if (stream) { + stream->remote_window_size += value; + } + } + return SW_CONTINUE; + } + case SW_HTTP2_TYPE_PING: { + swoole_http2_frame_trace_log("ping"); + if (!(flags & SW_HTTP2_FLAG_ACK)) { + Http2::set_frame_header( + frame, SW_HTTP2_TYPE_PING, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE, SW_HTTP2_FLAG_ACK, stream_id); + memcpy( + frame + SW_HTTP2_FRAME_HEADER_SIZE, buf + SW_HTTP2_FRAME_HEADER_SIZE, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE); + if (!send(frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE)) { + return SW_ERROR; + } + } + return SW_CONTINUE; + } + case SW_HTTP2_TYPE_GOAWAY: { + uint32_t server_last_stream_id = ntohl(*(uint32_t *) (buf)); + buf += 4; + value = ntohl(*(uint32_t *) (buf)); + buf += 4; + 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); + zend_update_property_stringl( + swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), buf, length - SW_HTTP2_GOAWAY_SIZE); + zend_update_property_long( + swoole_http2_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("serverLastStreamId"), server_last_stream_id); + close(); + return SW_CLOSE; + } + case SW_HTTP2_TYPE_RST_STREAM: { + value = ntohl(*(uint32_t *) (buf)); + swoole_http2_frame_trace_log("error_code=%d", value); + + // delete and free quietly + delete_stream(stream_id); + + return SW_CONTINUE; + } + /** + * TODO not support push_promise + */ + case SW_HTTP2_TYPE_PUSH_PROMISE: { +#ifdef SW_DEBUG + uint32_t promise_stream_id = ntohl(*(uint32_t *) (buf)) & 0x7fffffff; + 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); + // return SW_READY; + return SW_CONTINUE; + } + default: { + swoole_http2_frame_trace_log(""); + } + } + + Stream *stream = get_stream(stream_id); + // The stream is not found or has closed + if (stream == nullptr) { + 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) { + parse_header(stream, flags, buf, length); + } else if (type == SW_HTTP2_TYPE_DATA) { + if (!(flags & SW_HTTP2_FLAG_END_STREAM)) { + stream->flags |= SW_HTTP2_STREAM_PIPELINE_RESPONSE; + } + if (length > 0) { + if (!stream->buffer) { + stream->buffer = make_string(SW_HTTP2_DATA_BUFFER_SIZE); + } +#ifdef SW_HAVE_ZLIB + if (stream->gzip) { + if (php_swoole_zlib_decompress(&stream->gzip_stream, stream->gzip_buffer, buf, length) == SW_ERR) { + swoole_warning("decompress failed"); + return SW_ERROR; + } + stream->buffer->append(stream->gzip_buffer->str, stream->gzip_buffer->length); + } else +#endif + { + stream->buffer->append(buf, length); + } + + // now we control the connection flow only (not stream) + // our window size is unlimited, so we don't worry about subtraction overflow + local_window_size -= length; + stream->local_window_size -= length; + 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_window_size = local_settings.init_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 = local_settings.init_window_size; + } + } + } + + bool end = (flags & SW_HTTP2_FLAG_END_STREAM) || type == SW_HTTP2_TYPE_RST_STREAM || type == SW_HTTP2_TYPE_GOAWAY; + pipeline_read = ((pipeline_read || (stream->flags & SW_HTTP2_STREAM_USE_PIPELINE_READ)) && + (stream->flags & SW_HTTP2_STREAM_PIPELINE_RESPONSE)); + if (end || pipeline_read) { + zval *zresponse = &stream->zresponse; + if (type == SW_HTTP2_TYPE_RST_STREAM) { + zend_update_property_long(swoole_http2_response_ce, + SW_Z8_OBJ_P(zresponse), + ZEND_STRL("statusCode"), + -3 /* HTTP_CLIENT_ESTATUS_SERVER_RESET */); + zend_update_property_long(swoole_http2_response_ce, SW_Z8_OBJ_P(zresponse), ZEND_STRL("errCode"), value); + } + if (stream->buffer && stream->buffer->length > 0) { + zend_update_property_stringl(swoole_http2_response_ce, + SW_Z8_OBJ_P(zresponse), + ZEND_STRL("data"), + stream->buffer->str, + stream->buffer->length); + stream->buffer->clear(); + } + if (!end) { + zend_update_property_bool( + swoole_http2_response_ce, SW_Z8_OBJ_P(&stream->zresponse), ZEND_STRL("pipeline"), 1); + } + RETVAL_ZVAL(zresponse, end, 0); + if (!end) { + // reinit response object for the following frames + object_init_ex(zresponse, swoole_http2_response_ce); + zend_update_property_long( + swoole_http2_response_ce, SW_Z8_OBJ_P(&stream->zresponse), ZEND_STRL("streamId"), stream_id); + } else { + delete_stream(stream_id); + } + + return SW_READY; + } + + return SW_CONTINUE; +} + +#ifdef SW_HAVE_ZLIB +int php_swoole_zlib_decompress(z_stream *stream, String *buffer, char *body, int length) { + int status = 0; + + stream->avail_in = length; + stream->next_in = (Bytef *) body; + stream->total_in = 0; + stream->total_out = 0; + + 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(); + + while (1) { + stream->avail_out = buffer->size - buffer->length; + stream->next_out = (Bytef *) (buffer->str + buffer->length); + + status = inflate(stream, Z_SYNC_FLUSH); + + 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; + } + if (status == Z_STREAM_END) { + return SW_OK; + } else if (status == Z_OK) { + if (buffer->length + 4096 >= buffer->size) { + if (!buffer->extend()) { + return SW_ERR; + } + } + if (stream->avail_in == 0) { + return SW_OK; + } + } else { + return SW_ERR; + } + } + return SW_ERR; +} +#endif + +static PHP_METHOD(swoole_http2_client_coro, __construct) { + char *host; + size_t host_len; + zend_long port = 80; + zend_bool ssl = false; + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 3) + Z_PARAM_STRING(host, host_len) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(port) + Z_PARAM_BOOL(ssl) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (host_len == 0) { + zend_throw_exception(swoole_http2_client_coro_exception_ce, "host is empty", SW_ERROR_INVALID_PARAMS); + RETURN_FALSE; + } + + 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 client; + RETURN_FALSE; +#endif + } + + 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); + zend_update_property_long(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("port"), port); + zend_update_property_bool(swoole_http2_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("ssl"), ssl); +} + +static PHP_METHOD(swoole_http2_client_coro, set) { + Client *h2c = http2_client_coro_get_client(ZEND_THIS); + zval *zset; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(zset) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + zval *zsetting = + sw_zend_read_and_convert_property_array(swoole_http2_client_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0); + php_array_merge(Z_ARRVAL_P(zsetting), Z_ARRVAL_P(zset)); + + h2c->apply_setting(zset); + + RETURN_TRUE; +} + +/** + * called in read channel + */ +bool Client::send_window_update(int stream_id, uint32_t size) { + char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_WINDOW_UPDATE_SIZE]; + swoole_http2_send_trace_log("[" SW_ECHO_YELLOW "] stream_id=%d, size=%d", "WINDOW_UPDATE", stream_id, size); + *(uint32_t *) ((char *) frame + SW_HTTP2_FRAME_HEADER_SIZE) = htonl(size); + Http2::set_frame_header(frame, SW_HTTP2_TYPE_WINDOW_UPDATE, SW_HTTP2_WINDOW_UPDATE_SIZE, 0, stream_id); + return send(frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_WINDOW_UPDATE_SIZE); +} + +/** + * called on connect + */ +bool Client::send_setting() { + 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 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; + + if (flags & SW_HTTP2_FLAG_PRIORITY) { + // int stream_deps = ntohl(*(int *) (in)); + // uint8_t weight = in[4]; + in += 5; + inlen -= 5; + } + + zval *zheaders = + sw_zend_read_and_convert_property_array(swoole_http2_response_ce, zresponse, ZEND_STRL("headers"), 0); + zval *zcookies = + sw_zend_read_and_convert_property_array(swoole_http2_response_ce, zresponse, ZEND_STRL("cookies"), 0); + zval *zset_cookie_headers = sw_zend_read_and_convert_property_array( + swoole_http2_response_ce, zresponse, ZEND_STRL("set_cookie_headers"), 0); + + int inflate_flags = 0; + ssize_t rv; + + do { + nghttp2_nv nv; + + rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, (uchar *) in, inlen, 1); + if (rv < 0) { + nghttp2_error(rv, "nghttp2_hd_inflate_hd failed"); + return SW_ERR; + } + + in += (size_t) rv; + inlen -= (size_t) rv; + + 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] == ':') { + if (SW_STRCASEEQ((char *) nv.name + 1, nv.namelen - 1, "status")) { + zend_update_property_long(swoole_http2_response_ce, + SW_Z8_OBJ_P(zresponse), + ZEND_STRL("statusCode"), + atoi((char *) nv.value)); + } + } else { +#ifdef SW_HAVE_ZLIB + if (SW_STRCASEEQ((char *) nv.name, nv.namelen, "content-encoding") && + SW_STR_ISTARTS_WITH((char *) nv.value, nv.valuelen, "gzip")) { + /** + * init zlib stream + */ + stream->gzip = 1; + memset(&stream->gzip_stream, 0, sizeof(stream->gzip_stream)); + stream->gzip_buffer = make_string(8192); + stream->gzip_stream.zalloc = php_zlib_alloc; + stream->gzip_stream.zfree = php_zlib_free; + /** + * zlib decode + */ + if (Z_OK != inflateInit2(&stream->gzip_stream, MAX_WBITS + 16)) { + swoole_warning("inflateInit2() failed"); + return SW_ERR; + } + } else +#endif + if (SW_STRCASEEQ((char *) nv.name, nv.namelen, "set-cookie")) { + 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); + } + } + } while ([=] { + if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + nghttp2_hd_inflate_end_headers(inflater); + return false; + } + return inlen != 0; + }()); + + return SW_OK; +} + +ssize_t Client::build_header(zval *zobject, zval *zrequest, char *buffer) { + 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); + zval *zcookies = sw_zend_read_property_ex(swoole_http2_request_ce, zrequest, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIES), 0); + HeaderSet headers(8 + php_swoole_array_length_safe(zheaders) + php_swoole_array_length_safe(zcookies)); + bool find_host = 0; + + if (Z_TYPE_P(zmethod) != IS_STRING || Z_STRLEN_P(zmethod) == 0) { + headers.add(ZEND_STRL(":method"), ZEND_STRL("GET")); + } else { + headers.add(ZEND_STRL(":method"), Z_STRVAL_P(zmethod), Z_STRLEN_P(zmethod)); + } + if (Z_TYPE_P(zpath) != IS_STRING || Z_STRLEN_P(zpath) == 0) { + headers.add(ZEND_STRL(":path"), "/", 1); + } else { + headers.add(ZEND_STRL(":path"), Z_STRVAL_P(zpath), Z_STRLEN_P(zpath)); + } + if (h2c->open_ssl) { + headers.add(ZEND_STRL(":scheme"), ZEND_STRL("https")); + } else { + headers.add(ZEND_STRL(":scheme"), ZEND_STRL("http")); + } + // Host + headers.reserve_one(); + + if (ZVAL_IS_ARRAY(zheaders)) { + zend_string *key; + zval *zvalue; + + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheaders), key, zvalue) { + if (UNEXPECTED(!key || *ZSTR_VAL(key) == ':' || ZVAL_IS_NULL(zvalue))) { + continue; + } + zend::String str_value(zvalue); + if (SW_STRCASEEQ(ZSTR_VAL(key), ZSTR_LEN(key), "host")) { + headers.add(HTTP2_CLIENT_HOST_HEADER_INDEX, ZEND_STRL(":authority"), str_value.val(), str_value.len()); + find_host = true; + } else { + headers.add(ZSTR_VAL(key), ZSTR_LEN(key), str_value.val(), str_value.len()); + } + } + ZEND_HASH_FOREACH_END(); + } + if (!find_host) { + const std::string *host; + std::string _host; +#ifndef SW_USE_OPENSSL + if (h2c->port != 80) +#else + if (!h2c->open_ssl ? h2c->port != 80 : h2c->port != 443) +#endif + { + _host = std_string::format("%s:%d", h2c->host.c_str(), h2c->port); + host = &_host; + } else { + host = &h2c->host; + } + headers.add(HTTP2_CLIENT_HOST_HEADER_INDEX, ZEND_STRL(":authority"), host->c_str(), host->length()); + } + // http cookies + if (ZVAL_IS_ARRAY(zcookies)) { + zend_string *key; + zval *zvalue; + char *encoded_value; + size_t encoded_value_len; + String *buffer = sw_tg_buffer(); + + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zcookies), key, zvalue) { + if (UNEXPECTED(!key || ZVAL_IS_NULL(zvalue))) { + continue; + } + zend::String str_value(zvalue); + buffer->clear(); + buffer->append(ZSTR_VAL(key), ZSTR_LEN(key)); + buffer->append("=", 1); + 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); + efree(encoded_value); + headers.add(ZEND_STRL("cookie"), buffer->str, buffer->length); + } + } + ZEND_HASH_FOREACH_END(); + } + + size_t buflen = nghttp2_hd_deflate_bound(h2c->deflater, headers.get(), headers.len()); +#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"); + return -1; + } + return rv; +} + +void Client::destroy_stream(Stream *stream) { + if (stream->buffer) { + delete (stream->buffer); + } +#ifdef SW_HAVE_ZLIB + if (stream->gzip) { + inflateEnd(&stream->gzip_stream); + delete (stream->gzip_buffer); + } +#endif + zval_ptr_dtor(&stream->zresponse); + efree(stream); +} + +Stream *Client::create_stream(uint32_t stream_id, uint8_t flags) { + // malloc + Stream *stream = (Stream *) ecalloc(1, sizeof(Stream)); + // init + stream->stream_id = stream_id; + stream->flags = flags; + 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); + zend_update_property_long( + swoole_http2_response_ce, SW_Z8_OBJ_P(&stream->zresponse), ZEND_STRL("streamId"), stream_id); + + return stream; +} + +/** + * called in write channel + */ +bool Client::send_ping_frame() { + char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE]; + Http2::set_frame_header(frame, SW_HTTP2_TYPE_PING, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE, SW_HTTP2_FLAG_NONE, 0); + swoole_http2_send_trace_log("[" SW_ECHO_CYAN "]", "PING"); + return send(frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE); +} + +bool Client::send_data(uint32_t stream_id, const char *p, size_t len, int flag) { + uint8_t send_flag; + uint32_t send_len; + char header[SW_HTTP2_FRAME_HEADER_SIZE]; + while (len > 0) { + if (len > local_settings.max_frame_size) { + send_len = local_settings.max_frame_size; + send_flag = 0; + } else { + send_len = len; + send_flag = flag; + } + 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; + } + if (!send(p, send_len)) { + return false; + } + len -= send_len; + p += send_len; + } + 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); + zval *zdata = sw_zend_read_property_ex(swoole_http2_request_ce, zrequest, SW_ZSTR_KNOWN(SW_ZEND_STR_DATA), 0); + zval *zpipeline = + sw_zend_read_property_ex(swoole_http2_request_ce, zrequest, SW_ZSTR_KNOWN(SW_ZEND_STR_PIPELINE), 0); + zval ztmp, *zuse_pipeline_read = zend_read_property_ex( + Z_OBJCE_P(zrequest), SW_Z8_OBJ_P(zrequest), SW_ZSTR_KNOWN(SW_ZEND_STR_USE_PIPELINE_READ), 1, &ztmp); + bool is_data_empty = Z_TYPE_P(zdata) == IS_STRING ? Z_STRLEN_P(zdata) == 0 : !zval_is_true(zdata); + + if (ZVAL_IS_ARRAY(zdata)) { + add_assoc_stringl_ex( + zheaders, ZEND_STRL("content-type"), (char *) ZEND_STRL("application/x-www-form-urlencoded")); + } + + /** + * send headers + */ + char *buffer = sw_tg_buffer()->str; + ssize_t bytes = build_header(zobject, zrequest, buffer + SW_HTTP2_FRAME_HEADER_SIZE); + + if (bytes <= 0) { + return 0; + } + + uint8_t flags = 0; + if (zval_is_true(zpipeline)) { + flags |= SW_HTTP2_STREAM_PIPELINE_REQUEST; + } + if (zval_is_true(zuse_pipeline_read)) { + flags |= SW_HTTP2_STREAM_USE_PIPELINE_READ; + } + + auto stream = create_stream(stream_id, flags); + + flags = SW_HTTP2_FLAG_END_HEADERS; + + if (is_data_empty && !(stream->flags & SW_HTTP2_STREAM_PIPELINE_REQUEST)) { + flags |= SW_HTTP2_FLAG_END_STREAM; + } + + Http2::set_frame_header(buffer, SW_HTTP2_TYPE_HEADERS, bytes, flags, stream->stream_id); + + 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; + } + + /** + * send body + */ + if (!is_data_empty) { + char *p; + size_t len; + smart_str formstr_s = {}; + zend::String str_zpost_data; + + int flag = (stream->flags & SW_HTTP2_STREAM_PIPELINE_REQUEST) ? 0 : SW_HTTP2_FLAG_END_STREAM; + if (ZVAL_IS_ARRAY(zdata)) { + p = php_swoole_http_build_query(zdata, &len, &formstr_s); + if (p == nullptr) { + php_swoole_error(E_WARNING, "http_build_query failed"); + return 0; + } + } else { + str_zpost_data = zdata; + p = str_zpost_data.val(); + len = str_zpost_data.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; + } + + if (formstr_s.s) { + smart_str_free(&formstr_s); + } + } + + stream_id += 2; + + 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); + int flag = end ? SW_HTTP2_FLAG_END_STREAM : 0; + + if (stream == nullptr || !(stream->flags & SW_HTTP2_STREAM_PIPELINE_REQUEST) || + (stream->flags & SW_HTTP2_STREAM_REQUEST_END)) { + update_error_properties(EINVAL, + std_string::format("unable to found active pipeline stream#%u", stream_id).c_str()); + return false; + } + + if (ZVAL_IS_ARRAY(zdata)) { + size_t len; + smart_str formstr_s = {}; + char *formstr = php_swoole_http_build_query(zdata, &len, &formstr_s); + if (formstr == nullptr) { + php_swoole_error(E_WARNING, "http_build_query failed"); + return false; + } + 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; + } + smart_str_free(&formstr_s); + } else { + zend::String data(zdata); + 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; + } + } + + if (end) { + stream->flags |= SW_HTTP2_STREAM_REQUEST_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; + 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); + } + 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 = http2_client_coro_get_client(ZEND_THIS); + + if (!h2c->is_available()) { + RETURN_FALSE; + } + + zval *zrequest; + + ZEND_PARSE_PARAMETERS_START(1, 1) + 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; + } else { + RETURN_LONG(stream_id); + } +} + +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) + Z_PARAM_OPTIONAL + 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; + } + if (!h2c->recv_packet(timeout)) { + RETURN_FALSE; + } + ReturnCode ret = h2c->parse_frame(return_value, pipeline_read); + if (ret == SW_CONTINUE) { + continue; + } else if (ret == SW_READY) { + break; + } else { + RETURN_FALSE; + } + } +} + +static PHP_METHOD(swoole_http2_client_coro, recv) { + 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 = 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 = http2_client_coro_get_client(ZEND_THIS); + RETURN_BOOL(h2c->connect()); +} + +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("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 = http2_client_coro_get_client(ZEND_THIS); + zval _zarray, *zarray = &_zarray; + String key = {}; + + 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_client_settings_to_array(&h2c->local_settings, zarray); + RETURN_ZVAL(zarray, 0, 0); + } else if (SW_STREQ(key.str, key.length, "remote_settings")) { + 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()); + } + } else { + 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_client_settings_to_array(&h2c->local_settings, zarray); + add_assoc_zval_ex(return_value, ZEND_STRL("local_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()); + } +} + +static PHP_METHOD(swoole_http2_client_coro, isStreamExist) { + zend_long stream_id = 0; + + 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 = http2_client_coro_get_client(ZEND_THIS); + if (!h2c->socket_) { + RETURN_FALSE; + } else if (stream_id == 0) { + RETURN_TRUE; + } + Stream *stream = h2c->get_stream(stream_id); + RETURN_BOOL(stream ? 1 : 0); +} + +static PHP_METHOD(swoole_http2_client_coro, write) { + Client *h2c = http2_client_coro_get_client(ZEND_THIS); + + if (!h2c->is_available()) { + RETURN_FALSE; + } + + zend_long stream_id; + zval *data; + zend_bool end = 0; + + 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) { + http2_client_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} + +static PHP_METHOD(swoole_http2_client_coro, ping) { + Client *h2c = http2_client_coro_get_client(ZEND_THIS); + + if (!h2c->is_available()) { + RETURN_FALSE; + } + + RETURN_BOOL(h2c->send_ping_frame()); +} + +/** + * +-+-------------------------------------------------------------+ + * |R| Last-Stream-ID (31) | + * +-+-------------------------------------------------------------+ + * | Error Code (32) | + * +---------------------------------------------------------------+ + * | Additional Debug Data (*) | + * +---------------------------------------------------------------+ + */ +static PHP_METHOD(swoole_http2_client_coro, goaway) { + 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; + + if (!h2c->is_available()) { + 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)); +} diff --git a/ext-src/swoole_http2_server.cc b/ext-src/swoole_http2_server.cc new file mode 100644 index 00000000000..ddfe9103e71 --- /dev/null +++ b/ext-src/swoole_http2_server.cc @@ -0,0 +1,1346 @@ +/* + +----------------------------------------------------------------------+ + | 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_http_server.h" + +#include + +#include "swoole_static_handler.h" + +#include "main/php_variables.h" + +using namespace swoole; +using std::string; +using swoole::coroutine::System; +using swoole::http2::get_default_setting; +using swoole::http_server::StaticHandler; + +namespace Http2 = swoole::http2; + +using HttpContext = swoole::http::Context; +using Http2Stream = Http2::Stream; +using Http2Session = Http2::Session; + +static SW_THREAD_LOCAL std::unordered_map http2_sessions; + +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); + ctx->copy(client->default_ctx); + ctx->http2 = true; + ctx->stream = this; + ctx->keepalive = true; + id = _id; + 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; + ctx->free(); +} + +void Http2Stream::reset(uint32_t error_code) { + char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_RST_STREAM_SIZE]; + swoole_trace_log( + SW_TRACE_HTTP2, "send [" SW_ECHO_YELLOW "] stream_id=%u, error_code=%u", "RST_STREAM", id, error_code); + *(uint32_t *) ((char *) frame + SW_HTTP2_FRAME_HEADER_SIZE) = htonl(error_code); + http2::set_frame_header(frame, SW_HTTP2_TYPE_RST_STREAM, SW_HTTP2_RST_STREAM_SIZE, 0, id); + ctx->send(ctx, frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_RST_STREAM_SIZE); +} + +Http2Session::Session(SessionId _fd) { + fd = _fd; + Http2::init_settings(&local_settings); + // [init]: we must set default value, peer is not always send all the settings + Http2::init_settings(&remote_settings); + local_window_size = local_settings.init_window_size; + remote_window_size = remote_settings.init_window_size; + last_stream_id = 0; + shutting_down = false; + is_coro = false; + http2_sessions[_fd] = this; +} + +Http2Session::~Session() { + for (auto iter = streams.begin(); iter != streams.end(); iter++) { + delete iter->second; + } + if (inflater) { + nghttp2_hd_inflate_del(inflater); + } + if (deflater) { + nghttp2_hd_deflate_del(deflater); + } + if (default_ctx) { + delete default_ctx; + } + http2_sessions.erase(fd); +} + +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]; + swoole_trace_log( + SW_TRACE_HTTP2, "send [" SW_ECHO_YELLOW "] stream_id=%u, size=%u", "WINDOW_UPDATE", stream_id, size); + *(uint32_t *) ((char *) frame + SW_HTTP2_FRAME_HEADER_SIZE) = htonl(size); + Http2::set_frame_header(frame, SW_HTTP2_TYPE_WINDOW_UPDATE, SW_HTTP2_WINDOW_UPDATE_SIZE, 0, stream_id); + ctx->send(ctx, frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_WINDOW_UPDATE_SIZE); +} + +static ssize_t http2_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); + zend_string *key; + zval *zvalue; + + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(ztrailer), key, zvalue) { + if (UNEXPECTED(!key || ZVAL_IS_NULL(zvalue))) { + continue; + } + zend::String str_value(zvalue); + trailer.add(ZSTR_VAL(key), ZSTR_LEN(key), str_value.val(), str_value.len()); + } + ZEND_HASH_FOREACH_END(); + + ssize_t rv; + size_t buflen; + Http2Session *client = http2_sessions[ctx->fd]; + nghttp2_hd_deflater *deflater = client->deflater; + + if (!deflater) { + int ret = nghttp2_hd_deflate_new2(&deflater, client->remote_settings.header_table_size, php_nghttp2_mem()); + if (ret != 0) { + swoole_warning("nghttp2_hd_deflate_new2() failed with error: %s", nghttp2_strerror(ret)); + return -1; + } + client->deflater = deflater; + } + + buflen = nghttp2_hd_deflate_bound(deflater, trailer.get(), trailer.len()); +#if 0 + if (buflen > SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE) { + php_swoole_error(E_WARNING, + "header cannot bigger than remote max_header_list_size %u", + SW_HTTP2_DEFAULT_MAX_HEADER_LIST_SIZE); + return -1; + } +#endif + rv = nghttp2_hd_deflate_hd(deflater, (uchar *) buffer, buflen, trailer.get(), trailer.len()); + if (rv < 0) { + swoole_warning("nghttp2_hd_deflate_hd() failed with error: %s", nghttp2_strerror((int) rv)); + return -1; + } + return rv; + } + return 0; +} + +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) { + StaticHandler handler(serv, Z_STRVAL_P(zrequest_uri), Z_STRLEN_P(zrequest_uri)); + if (!handler.hit()) { + return false; + } + + if (handler.status_code == SW_HTTP_NOT_FOUND) { + String body(SW_STRL(SW_HTTP_PAGE_404)); + ctx->response.status = SW_HTTP_NOT_FOUND; + 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.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) { + string date_if_modified_since(Z_STRVAL_P(zdate_if_modified_since), Z_STRLEN_P(zdate_if_modified_since)); + if (!date_if_modified_since.empty() && handler.is_modified(date_if_modified_since)) { + ctx->response.status = SW_HTTP_NOT_MODIFIED; + return true; + } + } + + 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; + + // 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 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); + if (!conn) { + goto _destroy; + } + + server_fd = conn->server_fd; + serv_sock = serv->get_connection(server_fd); + + 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)); + add_assoc_double(zserver, "request_time_float", microtime()); + if (serv_sock) { + add_assoc_long(zserver, "server_port", serv_sock->info.get_port()); + } + 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", conn->last_recv_time); + add_assoc_string(zserver, "server_protocol", (char *) "HTTP/2"); + + 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)); + } + +_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_server_build_header(HttpContext *ctx, uchar *buffer, const String *body) { + zval *zheader = + sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADER), 0); + zval *zcookie = + sw_zend_read_property_ex(swoole_http_response_ce, ctx->response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIE), 0); + Http2::HeaderSet headers(32 + php_swoole_array_length_safe(zheader) + php_swoole_array_length_safe(zcookie)); + char intbuf[2][16]; + int ret; + + assert(ctx->send_header_ == 0); + + // status code + if (ctx->response.status == 0) { + ctx->response.status = SW_HTTP_OK; + } + 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)) { + 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()); + }; + + SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zheader), key, keylen, type, zvalue) { + if (UNEXPECTED(!key || ZVAL_IS_NULL(zvalue))) { + continue; + } + 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); + } + } + SW_HASHTABLE_FOREACH_END(); + (void) type; + +#ifdef SW_HAVE_COMPRESSION + if (ctx->accept_compression && ctx->compression_types) { + std::string str_content_type = content_type ? std::string(ZSTR_VAL(content_type), ZSTR_LEN(content_type)) + : std::string(ZEND_STRL(SW_HTTP_DEFAULT_CONTENT_TYPE)); + ctx->accept_compression = ctx->compression_types->find(str_content_type) != ctx->compression_types->end(); + if (content_type) { + zend_string_release(content_type); + } + } +#endif + } + + if (!(header_flags & HTTP_HEADER_SERVER)) { + headers.add(ZEND_STRL("server"), ZEND_STRL(SW_HTTP_SERVER_SOFTWARE)); + } + if (!(header_flags & HTTP_HEADER_DATE)) { + http2_server_set_date_header(&headers); + } + if (!(header_flags & HTTP_HEADER_CONTENT_TYPE)) { + headers.add(ZEND_STRL("content-type"), ZEND_STRL("text/html")); + } + + // cookies + if (ZVAL_IS_ARRAY(zcookie)) { + zval *zvalue; + SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(zcookie), zvalue) { + if (Z_TYPE_P(zvalue) != IS_STRING) { + continue; + } + headers.add(ZEND_STRL("set-cookie"), Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue)); + } + SW_HASHTABLE_FOREACH_END(); + } + + if (body) { + size_t content_length = body->length; + // content length +#ifdef SW_HAVE_COMPRESSION + 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 + ret = swoole_itoa(intbuf[1], content_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->remote_settings.header_table_size, php_nghttp2_mem()); + if (ret != 0) { + swoole_warning("nghttp2_hd_deflate_new2() failed with error: %s", nghttp2_strerror(ret)); + return -1; + } + client->deflater = deflater; + } + + size_t buflen = nghttp2_hd_deflate_bound(deflater, headers.get(), headers.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; + } + */ + ssize_t rv = nghttp2_hd_deflate_hd(deflater, (uchar *) buffer, buflen, headers.get(), headers.len()); + if (rv < 0) { + swoole_warning("nghttp2_hd_deflate_hd() failed with error: %s", nghttp2_strerror((int) rv)); + return -1; + } + + ctx->send_header_ = 1; + return rv; +} + +int swoole_http2_server_ping(HttpContext *ctx) { + char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE]; + Http2::set_frame_header(frame, SW_HTTP2_TYPE_PING, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE, SW_HTTP2_FLAG_NONE, 0); + 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(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; + 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); + } + ret = ctx->send(ctx, frame, length); + efree(frame); + client->shutting_down = true; + return ret; +} + +bool Http2Stream::send_header(const String *body, bool end_stream) { + char header_buffer[SW_BUFFER_SIZE_STD]; + ssize_t bytes = http2_server_build_header(ctx, (uchar *) header_buffer, body); + if (bytes < 0) { + return false; + } + + String *http_buffer = ctx->get_write_buffer(); + http_buffer->clear(); + + /** + +---------------+ + |Pad Length? (8)| + +-+-------------+-----------------------------------------------+ + |E| Stream Dependency? (31) | + +-+-------------+-----------------------------------------------+ + | Weight? (8) | + +-+-------------+-----------------------------------------------+ + | Header Block Fragment (*) ... + +---------------------------------------------------------------+ + | Padding (*) ... + +---------------------------------------------------------------+ + */ + char frame_header[SW_HTTP2_FRAME_HEADER_SIZE]; + + if (end_stream && body && body->length == 0) { + http2::set_frame_header( + frame_header, SW_HTTP2_TYPE_HEADERS, bytes, SW_HTTP2_FLAG_END_HEADERS | SW_HTTP2_FLAG_END_STREAM, id); + } else { + http2::set_frame_header(frame_header, SW_HTTP2_TYPE_HEADERS, bytes, SW_HTTP2_FLAG_END_HEADERS, id); + } + + 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)) { + ctx->send_header_ = 0; + return false; + } + + return true; +} + +bool Http2Stream::send_end_stream_data_frame() { + char frame_header[SW_HTTP2_FRAME_HEADER_SIZE]; + http2::set_frame_header(frame_header, SW_HTTP2_TYPE_DATA, 0, SW_HTTP2_FLAG_END_STREAM, id); + return ctx->send(ctx, frame_header, SW_HTTP2_FRAME_HEADER_SIZE); +} + +bool Http2Stream::send_body(const String *body, bool end_stream, size_t max_frame_size, off_t offset, size_t length) { + char frame_header[SW_HTTP2_FRAME_HEADER_SIZE]; + char *p = body->str + offset; + size_t l = length == 0 ? body->length : length; + + int flags = end_stream ? SW_HTTP2_FLAG_END_STREAM : SW_HTTP2_FLAG_NONE; + String *http_buffer = ctx->get_write_buffer(); + + while (l > 0) { + size_t send_n; + int _send_flags; + if (l > max_frame_size) { + send_n = max_frame_size; + _send_flags = 0; + } else { + send_n = l; + _send_flags = flags; + } + 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 { + 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; +} + +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(); + + http_buffer->clear(); + ssize_t bytes = http2_server_build_trailer(ctx, (uchar *) header_buffer); + if (bytes > 0) { + http2::set_frame_header( + frame_header, SW_HTTP2_TYPE_HEADERS, bytes, SW_HTTP2_FLAG_END_HEADERS | SW_HTTP2_FLAG_END_STREAM, id); + 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; + } + } + + return true; +} + +static bool http2_server_send_data( + HttpContext *ctx, Http2Session *client, Http2Stream *stream, const String *body, bool end_stream) { + bool error = false; + // If send_yield is not supported, ignore flow control + if (ctx->co_socket || !((Server *) ctx->private_data)->send_yield || !swoole_coroutine_is_in()) { + if (body->length > client->remote_window_size) { + swoole_warning("The data sent exceeded remote_window_size"); + } + if (!stream->send_body(body, end_stream, client->local_settings.max_frame_size)) { + error = true; + } + } else { + off_t offset = body->offset; + while (true) { + size_t send_len = body->length - offset; + + if (send_len == 0) { + break; + } + + if (stream->remote_window_size == 0) { + stream->waiting_coroutine = Coroutine::get_current(); + stream->waiting_coroutine->yield(); + stream->waiting_coroutine = nullptr; + continue; + } + + bool _end_stream; + if (send_len > stream->remote_window_size) { + send_len = stream->remote_window_size; + _end_stream = false; + } else { + _end_stream = true && end_stream; + } + + 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->remote_window_size -= send_len; + } + } + } + } + + 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) { + ctx->close(ctx); + } else { + client->streams.erase(stream->id); + delete stream; + } + + if (client->shutting_down && client->streams.size() == 0) { + ctx->close(ctx); + } + + return !error; +} + +static bool http2_server_send_range_file(HttpContext *ctx, StaticHandler *handler) { + Http2Session *client = http2_sessions[ctx->fd]; + 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 (!stream) { + /* closed */ + return false; + } + } else { + File fp(file, O_RDONLY); + if (!fp.ready()) { + return false; + } + body = fp.read_content(); + } + body->length = SW_MIN(length, body->length); + + zval *ztrailer = + 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; + } + + 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(body.get(), end_stream)) { + return false; + } + + /* headers has already been sent, retries are no longer allowed (even if send body failed) */ + end_ = 1; + + bool error = false; + + if (body->length > 0) { + if (!stream->send_body(body.get(), end_stream, client->local_settings.max_frame_size, offset, length)) { + error = true; + } else { + client->remote_window_size -= length; // TODO: flow control? + } + } + + if (!error && ztrailer) { + if (!stream->send_trailer()) { + error = true; + } + } + + if (error) { + close(this); + } else { + client->streams.erase(stream->id); + delete stream; + } + + return true; +} + +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) { + swoole_warning("nghttp2_hd_inflate_new2() failed, Error: %s[%d]", nghttp2_strerror(ret), ret); + return SW_ERR; + } + client->inflater = inflater; + } + + if (flags & SW_HTTP2_FLAG_PRIORITY) { + // int stream_deps = ntohl(*(int *) (in)); + // uint8_t weight = in[4]; + in += 5; + inlen -= 5; + } + + zval *zheader = ctx->request.zheader; + zval *zserver = ctx->request.zserver; + + ssize_t rv; + for (;;) { + nghttp2_nv nv; + int inflate_flags = 0; + size_t proclen; + + rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, (uchar *) in, inlen, 1); + if (rv < 0) { + swoole_warning("inflate failed, Error: %s[%zd]", nghttp2_strerror(rv), rv); + return SW_ERR; + } + + proclen = (size_t) rv; + + in += proclen; + inlen -= proclen; + + if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { + 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")) { + add_assoc_stringl_ex(zserver, ZEND_STRL("request_method"), (char *) nv.value, nv.valuelen); + } else if (SW_STRCASEEQ((char *) nv.name + 1, nv.namelen - 1, "path")) { + char *pathbuf = sw_tg_buffer()->str; + char *v_str = strchr((char *) nv.value, '?'); + zend_string *zstr_path; + if (v_str) { + v_str++; + int k_len = v_str - (char *) nv.value - 1; + int v_len = nv.valuelen - k_len - 1; + memcpy(pathbuf, nv.value, k_len); + pathbuf[k_len] = 0; + add_assoc_stringl_ex(zserver, ZEND_STRL("query_string"), v_str, v_len); + zstr_path = zend_string_init(pathbuf, k_len, 0); + // parse url params + sapi_module.treat_data( + PARSE_STRING, + estrndup(v_str, v_len), // 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"))); + } else { + zstr_path = zend_string_init((char *) nv.value, nv.valuelen, 0); + } + ctx->request.path = (char *) estrndup((char *) nv.value, nv.valuelen); + ctx->request.path_len = nv.valuelen; + add_assoc_str_ex(zserver, ZEND_STRL("request_uri"), zstr_path); + // path_info should be decoded + zstr_path = zend_string_dup(zstr_path, 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); + } else if (SW_STRCASEEQ((char *) nv.name + 1, nv.namelen - 1, "authority")) { + add_assoc_stringl_ex(zheader, ZEND_STRL("host"), (char *) nv.value, nv.valuelen); + } + } else { + if (SW_STRCASEEQ((char *) nv.name, nv.namelen, "content-type")) { + if (SW_STR_ISTARTS_WITH((char *) nv.value, nv.valuelen, "application/x-www-form-urlencoded")) { + ctx->request.post_form_urlencoded = 1; + } else if (SW_STR_ISTARTS_WITH((char *) nv.value, nv.valuelen, "multipart/form-data")) { + size_t offset = sizeof("multipart/form-data") - 1; + char *boundary_str; + int boundary_len; + if (!ctx->get_multipart_boundary( + (char *) nv.value, nv.valuelen, offset, &boundary_str, &boundary_len)) { + return SW_ERR; + } + ctx->init_multipart_parser(boundary_str, boundary_len); + ctx->parser.data = ctx; + } + } else if (SW_STRCASEEQ((char *) nv.name, nv.namelen, "cookie")) { + swoole_http_parse_cookie( + swoole_http_init_and_read_property( + swoole_http_request_ce, ctx->request.zobject, &ctx->request.zcookie, ZEND_STRL("cookie")), + (const char *) nv.value, + nv.valuelen); + continue; + } +#ifdef SW_HAVE_COMPRESSION + else if (ctx->enable_compression && SW_STRCASEEQ((char *) nv.name, nv.namelen, "accept-encoding")) { + 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); + } + } + + if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + nghttp2_hd_inflate_end_headers(inflater); + break; + } + + if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) { + break; + } + } + + return SW_OK; +} + +int swoole_http2_server_parse(Http2Session *client, const char *buf) { + Http2Stream *stream = nullptr; + int type = buf[3]; + int flags = buf[4]; + int retval = SW_ERR; + uint32_t stream_id = ntohl((*(int *) (buf + 5))) & 0x7fffffff; + + if (stream_id > client->last_stream_id) { + client->last_stream_id = stream_id; + } + + if (client->shutting_down) { + 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 = Http2::get_length(buf); + buf += SW_HTTP2_FRAME_HEADER_SIZE; + + uint16_t id = 0; + uint32_t value = 0; + + switch (type) { + case SW_HTTP2_TYPE_SETTINGS: { + if (flags & SW_HTTP2_FLAG_ACK) { + swoole_http2_frame_trace_log("ACK"); + break; + } + + while (length > 0) { + id = ntohs(*(uint16_t *) (buf)); + value = ntohl(*(uint32_t *) (buf + sizeof(uint16_t))); + swoole_http2_frame_trace_log("id=%d, value=%d", id, value); + switch (id) { + case SW_HTTP2_SETTING_HEADER_TABLE_SIZE: + 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) { + swoole_warning("nghttp2_hd_deflate_change_table_size() failed, errno=%d, errmsg=%s", + ret, + nghttp2_strerror(ret)); + return SW_ERR; + } + } + } + swoole_trace_log(SW_TRACE_HTTP2, "setting: header_table_size=%u", value); + break; + case SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + 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->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->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->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 + // swoole_warning("unknown option[%d]: %d", id, value); + break; + } + buf += sizeof(id) + sizeof(value); + length -= sizeof(id) + sizeof(value); + } + break; + } + case SW_HTTP2_TYPE_HEADERS: { + stream = client->streams[stream_id]; + 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)) { + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_HTTP2_STREAM_NO_HEADER, "http2 create stream#%d context error", stream_id); + return SW_ERR; + } + ctx = stream->ctx; + client->streams[stream_id] = stream; + zend_update_property_long( + swoole_http_request_ce, SW_Z8_OBJ_P(ctx->request.zobject), ZEND_STRL("streamId"), stream_id); + } else { + ctx = stream->ctx; + } + if (http2_server_parse_header(client, ctx, flags, buf, length) < 0) { + return SW_ERR; + } + + if (flags & SW_HTTP2_FLAG_END_STREAM) { + client->handle(client, stream); + } else { + // need continue frame + } + break; + } + case SW_HTTP2_TYPE_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; + HttpContext *ctx = stream->ctx; + + zend_update_property_long( + swoole_http_request_ce, SW_Z8_OBJ_P(ctx->request.zobject), ZEND_STRL("streamId"), stream_id); + + String *buffer = ctx->request.h2_data_buffer; + if (!buffer) { + buffer = new String(SW_HTTP2_DATA_BUFFER_SIZE); + ctx->request.h2_data_buffer = buffer; + } + buffer->append(buf, length); + + // flow control + client->local_window_size -= length; + stream->local_window_size -= length; + + if (length > 0) { + if (client->local_window_size < (client->local_settings.init_window_size / 4)) { + http2_server_send_window_update( + ctx, 0, client->local_settings.init_window_size - client->local_window_size); + client->local_window_size = client->local_settings.init_window_size; + } + if (stream->local_window_size < (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; + } + } + + if (flags & SW_HTTP2_FLAG_END_STREAM) { + if (ctx->parse_body && ctx->request.post_form_urlencoded) { + sapi_module.treat_data( + PARSE_STRING, + estrndup(buffer->str, buffer->length), // 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) { + if (!ctx->parse_multipart_data(buffer->str, buffer->length)) { + return SW_ERR; + } + } + + if (!client->is_coro) { + retval = SW_OK; + } + + client->handle(client, stream); + } + break; + } + case SW_HTTP2_TYPE_PING: { + swoole_http2_frame_trace_log("ping"); + if (!(flags & SW_HTTP2_FLAG_ACK)) { + char ping_frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE]; + Http2::set_frame_header( + 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( + client->default_ctx, ping_frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE); + } + break; + } + case SW_HTTP2_TYPE_WINDOW_UPDATE: { + value = ntohl(*(uint32_t *) buf); + if (stream_id == 0) { + 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(); + } + } + } + } + swoole_http2_frame_trace_log("window_size_increment=%d", value); + break; + } + case SW_HTTP2_TYPE_RST_STREAM: { + value = ntohl(*(int *) (buf)); + swoole_http2_frame_trace_log("error_code=%d", value); + if (client->streams.find(stream_id) != client->streams.end()) { + // TODO: i onRequest and use request->recv + // stream exist + stream = client->streams[stream_id]; + client->streams.erase(stream_id); + delete stream; + } + break; + } + case SW_HTTP2_TYPE_GOAWAY: { + uint32_t server_last_stream_id = ntohl(*(uint32_t *) (buf)); + buf += 4; + value = ntohl(*(uint32_t *) (buf)); + buf += 4; + 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: { + swoole_http2_frame_trace_log(""); + } + } + + return retval; +} + +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 = http2_server_onRequest; + if (!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->onBeforeRequest = http2_server_onBeforeRequest; + } + + zval zdata; + php_swoole_get_recv_data(serv, &zdata, req); + int retval = swoole_http2_server_parse(client, Z_STRVAL(zdata)); + zval_ptr_dtor(&zdata); + + return retval; +} + +void swoole_http2_server_session_free(Connection *conn) { + auto session_iterator = http2_sessions.find(conn->session_id); + if (session_iterator == http2_sessions.end()) { + return; + } + Http2Session *client = session_iterator->second; + delete client; +} + +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(http2_server_respond(this, &http_body)); +} + +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 new file mode 100644 index 00000000000..4f1eff0847e --- /dev/null +++ b/ext-src/swoole_http_client_coro.cc @@ -0,0 +1,2262 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Author: Twosee | + | Author: Fang | + | Author: Yuanyi Zhi | + +----------------------------------------------------------------------+ + */ + +#include "php_swoole_cxx.h" +#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" +#include "swoole_mime_type.h" +#include "swoole_base64.h" +#include "swoole_socket.h" + +SW_EXTERN_C_BEGIN + +#include "thirdparty/swoole_http_parser.h" +#include "stubs/php_swoole_http_client_coro_arginfo.h" + +#include "ext/standard/base64.h" + +SW_EXTERN_C_END +using swoole::File; +using swoole::String; +using swoole::coroutine::Socket; +using swoole::network::Address; + +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); +static int http_parser_on_headers_complete(swoole_http_parser *parser); +static int http_parser_on_body(swoole_http_parser *parser, const char *at, size_t length); +static int http_parser_on_message_complete(swoole_http_parser *parser); + +// clang-format off +static const swoole_http_parser_settings http_parser_settings = { + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + http_parser_on_header_field, + http_parser_on_header_value, + http_parser_on_headers_complete, + http_parser_on_body, + http_parser_on_message_complete +}; +// clang-format on + +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; + uint16_t port; +#ifdef SW_USE_OPENSSL + uint8_t ssl; +#endif + 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; + std::string basic_auth; + + /* for response parser */ + char *tmp_header_field_name = nullptr; + int tmp_header_field_name_len = 0; + String *body = nullptr; +#ifdef SW_HAVE_COMPRESSION + enum swHttpCompressMethod compress_method = HTTP_COMPRESS_NONE; + bool compression_error = false; +#endif + + /* options */ + 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 accept_websocket_compression = false; // websocket server accepts compression +#endif + bool in_callback = false; + bool has_upload_files = false; + + std::shared_ptr download_file; // save http response to file + zend::String download_file_name; // unlink the file on error + zend_long download_offset = 0; + + /* safety zval */ + zval _zobject; + zval *zobject = &_zobject; + zval zsocket; + zend::Callable *write_func = nullptr; + String *tmp_write_buffer = nullptr; + bool connection_close = false; + + 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 + bool gzip_stream_active = false; + z_stream gzip_stream = {}; +#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_request(); + void reset(); + + 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 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 void create_token(int length, char *buf) { + char characters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"§$%&/()=[]{}"; + int i; + assert(length < 1024); + for (i = 0; i < length; i++) { + buf[i] = characters[rand() % (sizeof(characters) - 1)]; + } + buf[length] = '\0'; + } + + public: +#ifdef SW_HAVE_COMPRESSION + bool decompress_response(const char *in, size_t in_len); +#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_response(double timeout = 0); + bool recv_websocket_frame(zval *zframe, double timeout = 0); + void add_header(const char *key, size_t key_len, const char *str, size_t length); + bool upgrade(std::string path); + bool push(zval *zdata, zend_long opcode = websocket::OPCODE_TEXT, uint8_t flags = websocket::FLAG_FIN); + bool close(const bool should_be_reset = true); + void socket_dtor(); + + void get_header_out(zval *return_value) { + String *buffer = nullptr; + if (socket == nullptr) { + if (tmp_write_buffer) { + buffer = tmp_write_buffer; + } + } else { + buffer = socket->get_write_buffer(); + } + if (buffer == nullptr) { + RETURN_FALSE; + } + off_t offset = swoole_strnpos(buffer->str, buffer->length, ZEND_STRL("\r\n\r\n")); + if (offset <= 0) { + RETURN_FALSE; + } + + RETURN_STRINGL(buffer->str, offset); + } + + void getsockname(zval *return_value) { + Address sa; + 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()); + } + + void getpeername(zval *return_value) { + Address sa; + 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()); + } + +#ifdef SW_USE_OPENSSL + void getpeercert(zval *return_value) { + if (!is_available()) { + RETURN_FALSE; + } + auto cert = socket->ssl_get_peer_cert(); + if (cert.empty()) { + php_swoole_socket_set_error_properties(zobject, socket); + RETURN_FALSE; + } else { + RETURN_STRINGL(cert.c_str(), cert.length()); + } + } +#endif + + ~Client(); + + private: + Socket *socket = nullptr; + NameResolver::Context resolve_context_ = {}; + SocketType socket_type = SW_SOCK_TCP; + swoole_http_parser parser = {}; + 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; + +static zend_class_entry *swoole_http_client_coro_exception_ce; +static zend_object_handlers swoole_http_client_coro_exception_handlers; + +using swoole::coroutine::http::Client; + +struct HttpClientObject { + Client *client; + zend_object std; +}; + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_http_client_coro, __construct); +static PHP_METHOD(swoole_http_client_coro, __destruct); +static PHP_METHOD(swoole_http_client_coro, set); +static PHP_METHOD(swoole_http_client_coro, getDefer); +static PHP_METHOD(swoole_http_client_coro, setDefer); +static PHP_METHOD(swoole_http_client_coro, setMethod); +static PHP_METHOD(swoole_http_client_coro, setHeaders); +static PHP_METHOD(swoole_http_client_coro, setBasicAuth); +static PHP_METHOD(swoole_http_client_coro, setCookies); +static PHP_METHOD(swoole_http_client_coro, setData); +static PHP_METHOD(swoole_http_client_coro, addFile); +static PHP_METHOD(swoole_http_client_coro, addData); +static PHP_METHOD(swoole_http_client_coro, execute); +static PHP_METHOD(swoole_http_client_coro, getsockname); +static PHP_METHOD(swoole_http_client_coro, getpeername); +static PHP_METHOD(swoole_http_client_coro, get); +static PHP_METHOD(swoole_http_client_coro, post); +static PHP_METHOD(swoole_http_client_coro, download); +static PHP_METHOD(swoole_http_client_coro, getBody); +static PHP_METHOD(swoole_http_client_coro, getHeaders); +static PHP_METHOD(swoole_http_client_coro, getCookies); +static PHP_METHOD(swoole_http_client_coro, getStatusCode); +static PHP_METHOD(swoole_http_client_coro, getHeaderOut); +#ifdef SW_USE_OPENSSL +static PHP_METHOD(swoole_http_client_coro, getPeerCert); +#endif +static PHP_METHOD(swoole_http_client_coro, upgrade); +static PHP_METHOD(swoole_http_client_coro, push); +static PHP_METHOD(swoole_http_client_coro, recv); +static PHP_METHOD(swoole_http_client_coro, close); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_http_client_coro_methods[] = +{ + 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_class_Swoole_Coroutine_Http_Client_getPeerCert, ZEND_ACC_PUBLIC) +#endif + 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 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; + + // key + p = (char *) memchr(at, '=', length); + if (p) { + key_len = p - at; + p++; // point to value + } else { + p = at; // key is empty + } + // value + eof = (char *) memchr(p, ';', at + length - p); + if (!eof) { + eof = at + length; + } + value_len = eof - p; + if (value_len != 0) { + ZVAL_STRINGL(&zvalue, p, value_len); + Z_STRLEN(zvalue) = php_url_decode(Z_STRVAL(zvalue), value_len); + } else { + ZVAL_EMPTY_STRING(&zvalue); + } + if (key_len == 0) { + add_next_index_zval(zcookies, &zvalue); + } else { + add_assoc_zval_ex(zcookies, at, key_len, &zvalue); + } + + // set_cookie_headers + add_next_index_stringl(zset_cookie_headers, (char *) at, length); +} + +static int http_parser_on_header_field(swoole_http_parser *parser, const char *at, size_t length) { + 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) { + Client *http = (Client *) parser->data; + zval *zobject = (zval *) http->zobject; + + 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.assign_tolower(header_name, header_len); + header_name = _header_name.get(); + } + + 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 (swoole_http_token_list_contains_value(at, length, "websocket")) { + http->websocket = true; + } + /* TODO: protocol error? */ + } +#ifdef SW_HAVE_ZLIB + else if (http->websocket && http->websocket_compression && + SW_STREQ(header_name, header_len, "sec-websocket-extensions")) { + if (swoole_strncasestr(at, length, SW_STRL("permessage-deflate"))) { + http->accept_websocket_compression = true; + } + } +#endif + else if (SW_STREQ(header_name, header_len, "set-cookie")) { + zval *zcookies = + 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); + 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_STR_ISTARTS_WITH(at, length, "br")) { + http->compress_method = HTTP_COMPRESS_BR; + } +#endif +#ifdef SW_HAVE_ZLIB + else if (SW_STR_ISTARTS_WITH(at, length, "gzip")) { + http->compress_method = HTTP_COMPRESS_GZIP; + } 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_STR_ISTARTS_WITH(at, length, "chunked")) { + http->chunked = true; + } 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) { + 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) { + 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 + 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; + } + } +#endif + else { +#ifdef SW_HAVE_COMPRESSION + _append_raw: +#endif + if (http->body->append(at, length) < 0) { + return -1; + } + } + 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::shared_ptr fp = std::make_shared(download_file_name, O_CREAT | O_WRONLY, 0664); + if (!fp->ready()) { + swoole_sys_warning("open(%s, O_CREAT | O_WRONLY) failed", download_file_name); + return -1; + } + if (http->download_offset == 0) { + if (!fp->truncate(0)) { + swoole_sys_warning("ftruncate(%s) failed", download_file_name); + return -1; + } + } else { + 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; + } + if (http_client_co_write(http->download_file->get_fd(), SW_STRINGL(http->body)) != + (ssize_t) http->body->length) { + return -1; + } + http->body->clear(); + } + return 0; +} + +static int http_parser_on_message_complete(swoole_http_parser *parser) { + Client *http = (Client *) parser->data; + zval *zobject = (zval *) http->zobject; + + if (parser->upgrade && !http->websocket) { + // not support, continue. + parser->upgrade = 0; + return 0; + } + + zend_update_property_long( + swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), parser->status_code); + if (http->download_file == nullptr) { + zend_update_property_stringl( + swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("body"), SW_STRINGL(http->body)); + } else { + http->download_file_name.release(); + } + + if (parser->upgrade) { + // return 1 will finish the parser and means yes we support it. + return 1; + } else { + return 0; + } +} + +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; +#endif + _zobject = *zobject; + // TODO: zend_read_property cache here (strong type properties) +} + +#ifdef SW_HAVE_COMPRESSION +bool Client::decompress_response(const char *in, size_t in_len) { + if (in_len == 0) { + return false; + } + + size_t reserved_body_length = body->length; + + switch (compress_method) { +#ifdef SW_HAVE_ZLIB + case HTTP_COMPRESS_GZIP: + case HTTP_COMPRESS_DEFLATE: { + int status; + int encoding = compress_method == HTTP_COMPRESS_GZIP ? SW_ZLIB_ENCODING_GZIP : SW_ZLIB_ENCODING_DEFLATE; + bool first_decompress = !gzip_stream_active; + size_t total_out; + + if (!gzip_stream_active) { + _retry: + memset(&gzip_stream, 0, sizeof(gzip_stream)); + gzip_stream.zalloc = php_zlib_alloc; + gzip_stream.zfree = php_zlib_free; + // gzip_stream.total_out = 0; + status = inflateInit2(&gzip_stream, encoding); + if (status != Z_OK) { + swoole_warning("inflateInit2() failed by %s", zError(status)); + return false; + } + gzip_stream_active = true; + } + + gzip_stream.next_in = (Bytef *) in; + gzip_stream.avail_in = in_len; + gzip_stream.total_in = 0; + + while (1) { + total_out = gzip_stream.total_out; + gzip_stream.avail_out = body->size - body->length; + gzip_stream.next_out = (Bytef *) (body->str + body->length); + SW_ASSERT(body->length <= body->size); + status = inflate(&gzip_stream, Z_SYNC_FLUSH); + if (status >= 0) { + body->length += (gzip_stream.total_out - total_out); + if (body->length + (SW_BUFFER_SIZE_STD / 2) >= body->size) { + if (!body->extend()) { + status = Z_MEM_ERROR; + break; + } + } + } + if (status == Z_STREAM_END || (status == Z_OK && gzip_stream.avail_in == 0)) { + return true; + } + if (status != Z_OK) { + break; + } + } + + if (status == Z_DATA_ERROR && first_decompress) { + first_decompress = false; + inflateEnd(&gzip_stream); + encoding = SW_ZLIB_ENCODING_RAW; + body->length = reserved_body_length; + goto _retry; + } + + swoole_warning("HttpClient::decompress_response failed by %s", zError(status)); + body->length = reserved_body_length; + return false; + } +#endif +#ifdef SW_HAVE_BROTLI + case HTTP_COMPRESS_BR: { + if (!brotli_decoder_state) { + brotli_decoder_state = BrotliDecoderCreateInstance(php_brotli_alloc, php_brotli_free, nullptr); + if (!brotli_decoder_state) { + swoole_warning("BrotliDecoderCreateInstance() failed"); + return false; + } + } + + const char *next_in = in; + size_t available_in = in_len; + while (1) { + size_t available_out = body->size - body->length, reserved_available_out = available_out; + char *next_out = body->str + body->length; + size_t total_out; + BrotliDecoderResult result; + SW_ASSERT(body->length <= body->size); + result = BrotliDecoderDecompressStream(brotli_decoder_state, + &available_in, + (const uint8_t **) &next_in, + &available_out, + (uint8_t **) &next_out, + &total_out); + body->length += reserved_available_out - available_out; + if (result == BROTLI_DECODER_RESULT_SUCCESS || result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) { + return true; + } else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) { + if (!body->extend()) { + swoole_warning("BrotliDecoderDecompressStream() failed, no memory is available"); + break; + } + } else { + swoole_warning("BrotliDecoderDecompressStream() failed, %s", + BrotliDecoderErrorString(BrotliDecoderGetErrorCode(brotli_decoder_state))); + break; + } + } + + 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; + } + + swoole_warning("HttpClient::decompress_response unknown compress method [%d]", compress_method); + return false; +} +#endif + +void Client::apply_setting(zval *zset, const bool check_all) { + if (!ZVAL_IS_ARRAY(zset) || php_swoole_array_length(zset) == 0) { + return; + } + if (check_all) { + zval *ztmp; + HashTable *vht = Z_ARRVAL_P(zset); + + if (php_swoole_array_get_value(vht, "connect_timeout", ztmp)) { + connect_timeout = zval_get_double(ztmp); + } + 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); + } + if (php_swoole_array_get_value(vht, "lowercase_header", ztmp)) { + lowercase_header = zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "keep_alive", ztmp)) { + keep_alive = zval_is_true(ztmp); + } + 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_socket_set(socket, zset); +#ifdef SW_USE_OPENSSL + if (socket->http_proxy && !socket->ssl_is_enable()) +#else + if (socket->http_proxy) +#endif + { + socket->http_proxy->dont_handshake = 1; + } + } +} + +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); + if (sw_likely(output)) { + size_t output_len = sprintf(output, "Basic "); + output_len += base64_encode((const unsigned char *) input.c_str(), input.size(), output + output_len); + basic_auth = std::string((const char *) output, output_len); + efree(output); + } +} + +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); + } + } +} + +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()) { + set_error(socket->errCode, socket->errMsg, ESTATUS_CONNECT_FAILED); + close(); + return false; + } +#endif + + // apply settings + apply_setting(sw_zend_read_property_ex(Z_OBJCE_P(zobject), zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0), false); + + // reset the properties that depend on the connection + websocket = false; +#ifdef SW_HAVE_ZLIB + accept_websocket_compression = false; +#endif + + double _timeout = connect_timeout == 0 ? network::Socket::default_connect_timeout : connect_timeout; + socket->set_timeout(_timeout, Socket::TIMEOUT_CONNECT); + socket->set_resolve_context(&resolve_context_); + socket->set_dtor([this](Socket *_socket) { socket_dtor(); }); + // socket->set_buffer_allocator(&SWOOLE_G(zend_string_allocator)); + + if (!socket->connect(host, port)) { + set_error(socket->errCode, socket->errMsg, ESTATUS_CONNECT_FAILED); + close(); + return false; + } + + zend_update_property(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("socket"), &zsocket); + zend_update_property_bool(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("connected"), 1); + return true; +} + +void 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); + set_error(socket->errCode, socket->errMsg, ESTATUS_SERVER_RESET); + close(false); + } + SW_LOOP_N(max_retries + 1) { + if (connect()) { + return true; + } + } + return false; + } + return true; +} + +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_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"), ""); + + if (!keep_liveness()) { + return false; + } + + 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 + String *buffer = socket->get_write_buffer(); + buffer->clear(); + // clear body + body->clear(); + + zmethod = sw_zend_read_property_not_null_ex( + swoole_http_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_METHOD), 0); + zheaders = + sw_zend_read_property_ex(swoole_http_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_HEADERS), 0); + zbody = sw_zend_read_property_not_null_ex( + swoole_http_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_REQUEST_BODY), 0); + zupload_files = + sw_zend_read_property_ex(swoole_http_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_UPLOAD_FILES), 0); + zcookies = sw_zend_read_property_ex(swoole_http_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIES), 0); + z_download_file = sw_zend_read_property_not_null_ex( + swoole_http_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_DOWNLOAD_FILE), 0); + + // ============ host ============ + zend::String str_host; + + if ((ZVAL_IS_ARRAY(zheaders)) && ((zvalue = zend_hash_str_find(Z_ARRVAL_P(zheaders), ZEND_STRL("Host"))) || + (zvalue = zend_hash_str_find(Z_ARRVAL_P(zheaders), ZEND_STRL("host"))))) { + str_host = zvalue; + } + + // ============ download ============ + if (z_download_file) { + download_file_name = z_download_file; + download_offset = zval_get_long(sw_zend_read_property_ex( + swoole_http_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_DOWNLOAD_OFFSET), 0)); + } + + // ============ method ============ + { + zend::String str_method; + const char *method; + size_t method_len; + if (zmethod) { + str_method = zmethod; + method = str_method.val(); + method_len = str_method.len(); + } else { + method = zbody ? "POST" : "GET"; + method_len = strlen(method); + } + 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 + if (socket->http_proxy) +#endif + { + const static char *pre = "https://"; + char *_host = (char *) host.c_str(); + size_t _host_len = host.length(); + if (str_host.get()) { + _host = str_host.val(); + _host_len = str_host.len(); + } + size_t proxy_uri_len = path.length() + _host_len + strlen(pre) + 10; + char *proxy_uri = (char *) emalloc(proxy_uri_len); + 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()); + } + + // ============ protocol ============ + buffer->append(ZEND_STRL(" HTTP/1.1\r\n")); + + // ============ headers ============ + char *key; + uint32_t keylen; + int keytype; + + // As much as possible to ensure that Host is the first header. + // See: http://tools.ietf.org/html/rfc7230#section-5.4 + if (str_host.get()) { + add_headers(buffer, ZEND_STRL("Host"), str_host.val(), str_host.len()); + } else { + // See: https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.23 + const std::string *_host; + std::string __host; +#ifndef SW_USE_OPENSSL + if (port != 80) +#else + if (!ssl ? port != 80 : port != 443) +#endif + { + __host = std_string::format("%s:%u", host.c_str(), port); + _host = &__host; + } else { + _host = &host; + } + add_headers(buffer, ZEND_STRL("Host"), _host->c_str(), _host->length()); + } + + if (ZVAL_IS_ARRAY(zheaders)) { + SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zheaders), key, keylen, keytype, zvalue) { + if (UNEXPECTED(HASH_KEY_IS_STRING != keytype || ZVAL_IS_NULL(zvalue))) { + continue; + } + if (SW_STRCASEEQ(key, keylen, "Host")) { + continue; + } + if (SW_STRCASEEQ(key, keylen, "Content-Length")) { + header_flag |= HTTP_HEADER_CONTENT_LENGTH; + // ignore custom Content-Length value + continue; + } + + if (SW_STRCASEEQ(key, keylen, "Accept-Encoding")) { +#ifdef SW_HAVE_COMPRESSION + header_flag |= HTTP_HEADER_ACCEPT_ENCODING; +#else + php_swoole_error(E_WARNING, "Missing a compression package, 'Accept-Encoding' is ignored"); + continue; +#endif + } + + zend::String str_value(zvalue); + add_headers(buffer, key, keylen, str_value.val(), str_value.len()); + + 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()); + } + if (!(header_flag & HTTP_HEADER_CONNECTION)) { + if (keep_alive) { + add_headers(buffer, ZEND_STRL("Connection"), ZEND_STRL("keep-alive")); + } else { + add_headers(buffer, ZEND_STRL("Connection"), ZEND_STRL("closed")); + } + } +#ifdef SW_HAVE_COMPRESSION + if (http_compression && !(header_flag & HTTP_HEADER_ACCEPT_ENCODING)) { + add_headers(buffer, + ZEND_STRL("Accept-Encoding"), +#if defined(SW_HAVE_ZLIB) && defined(SW_HAVE_BROTLI) + ZEND_STRL("gzip, deflate, br") +#else +#ifdef SW_HAVE_ZLIB + ZEND_STRL("gzip, deflate") +#else +#ifdef SW_HAVE_BROTLI + ZEND_STRL("br") +#else +#ifdef SW_HAVE_ZSTD + ZEND_STRL("zstd") +#endif +#endif +#endif +#endif + ); + } +#endif + + // ============ cookies ============ + if (ZVAL_IS_ARRAY(zcookies)) { + buffer->append(ZEND_STRL("Cookie: ")); + int n_cookie = php_swoole_array_length(zcookies); + int i = 0; + char *encoded_value; + + SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zcookies), key, keylen, keytype, zvalue) { + i++; + if (HASH_KEY_IS_STRING != keytype) { + continue; + } + zend::String str_value(zvalue); + if (str_value.len() == 0) { + continue; + } + buffer->append(key, keylen); + buffer->append("=", 1); + + 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); + efree(encoded_value); + } + if (i < n_cookie) { + buffer->append("; ", 2); + } + } + SW_HASHTABLE_FOREACH_END(); + buffer->append(ZEND_STRL("\r\n")); + } + + // ============ multipart/form-data ============ + if ((has_upload_files = (php_swoole_array_length_safe(zupload_files) > 0))) { + char header_buf[2048]; + char boundary_str[SW_HTTP_CLIENT_BOUNDARY_TOTAL_SIZE]; + int n; + + // ============ content-type ============ + memcpy(boundary_str, SW_HTTP_CLIENT_BOUNDARY_PREKEY, sizeof(SW_HTTP_CLIENT_BOUNDARY_PREKEY) - 1); + swoole_random_string(boundary_str + sizeof(SW_HTTP_CLIENT_BOUNDARY_PREKEY) - 1, + sizeof(boundary_str) - sizeof(SW_HTTP_CLIENT_BOUNDARY_PREKEY)); + n = sw_snprintf(header_buf, + sizeof(header_buf), + "Content-Type: multipart/form-data; boundary=%.*s\r\n", + (int) (sizeof(boundary_str) - 1), + boundary_str); + buffer->append(header_buf, n); + + // ============ content-length ============ + size_t content_length = 0; + + // calculate length before encode array + if (zbody && ZVAL_IS_ARRAY(zbody)) { + SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zbody), key, keylen, keytype, zvalue) + if (UNEXPECTED(HASH_KEY_IS_STRING != keytype || ZVAL_IS_NULL(zvalue))) { + continue; + } + zend::String str_value(zvalue); + // strlen("%.*s")*2 = 8 + // header + body + CRLF(2) + content_length += (sizeof(SW_HTTP_FORM_RAW_DATA_FMT) - SW_HTTP_FORM_RAW_DATA_FMT_LEN - 1) + + (sizeof(boundary_str) - 1) + keylen + str_value.len() + 2; + SW_HASHTABLE_FOREACH_END(); + } + + zval *zname; + zval *ztype; + zval *zsize = nullptr; + zval *zpath = nullptr; + zval *zcontent = nullptr; + zval *zfilename; + zval *zoffset; + + // calculate length of files + { + // upload files + SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zupload_files), key, keylen, keytype, zvalue) { + HashTable *ht = Z_ARRVAL_P(zvalue); + if (!(zname = zend_hash_str_find(ht, ZEND_STRL("name")))) { + continue; + } + if (!(zfilename = zend_hash_str_find(ht, ZEND_STRL("filename")))) { + continue; + } + if (!(zsize = zend_hash_str_find(ht, ZEND_STRL("size")))) { + continue; + } + if (!(ztype = zend_hash_str_find(ht, ZEND_STRL("type")))) { + continue; + } + // strlen("%.*s")*4 = 16 + // header + body + CRLF(2) + content_length += (sizeof(SW_HTTP_FORM_FILE_DATA_FMT) - SW_HTTP_FORM_FILE_DATA_FMT_LEN - 1) + + (sizeof(boundary_str) - 1) + Z_STRLEN_P(zname) + Z_STRLEN_P(zfilename) + + Z_STRLEN_P(ztype) + Z_LVAL_P(zsize) + 2; + } + SW_HASHTABLE_FOREACH_END(); + } + + add_content_length(buffer, content_length + sizeof(boundary_str) - 1 + 6); + + // ============ form-data body ============ + if (zbody && ZVAL_IS_ARRAY(zbody)) { + SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zbody), key, keylen, keytype, zvalue) { + if (UNEXPECTED(HASH_KEY_IS_STRING != keytype || ZVAL_IS_NULL(zvalue))) { + continue; + } + zend::String str_value(zvalue); + n = sw_snprintf(header_buf, + sizeof(header_buf), + SW_HTTP_FORM_RAW_DATA_FMT, + (int) (sizeof(boundary_str) - 1), + boundary_str, + keylen, + key); + buffer->append(header_buf, n); + buffer->append(str_value.val(), str_value.len()); + buffer->append(ZEND_STRL("\r\n")); + } + SW_HASHTABLE_FOREACH_END(); + } + + if (socket->send_all(buffer->str, buffer->length) != (ssize_t) buffer->length) { + goto _send_fail; + } + + { + // upload files + SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zupload_files), key, keylen, keytype, zvalue) { + if (!(zname = zend_hash_str_find(Z_ARRVAL_P(zvalue), ZEND_STRL("name")))) { + continue; + } + if (!(zfilename = zend_hash_str_find(Z_ARRVAL_P(zvalue), ZEND_STRL("filename")))) { + continue; + } + /** + * from disk file + */ + if (!(zcontent = zend_hash_str_find(Z_ARRVAL_P(zvalue), ZEND_STRL("content")))) { + // file path + if (!(zpath = zend_hash_str_find(Z_ARRVAL_P(zvalue), ZEND_STRL("path")))) { + continue; + } + // file offset + if (!(zoffset = zend_hash_str_find(Z_ARRVAL_P(zvalue), ZEND_STRL("offset")))) { + continue; + } + zcontent = nullptr; + } else { + zpath = nullptr; + zoffset = nullptr; + } + if (!(zsize = zend_hash_str_find(Z_ARRVAL_P(zvalue), ZEND_STRL("size")))) { + continue; + } + if (!(ztype = zend_hash_str_find(Z_ARRVAL_P(zvalue), ZEND_STRL("type")))) { + continue; + } + /** + * part header + */ + n = sw_snprintf(header_buf, + sizeof(header_buf), + SW_HTTP_FORM_FILE_DATA_FMT, + (int) (sizeof(boundary_str) - 1), + boundary_str, + (int) Z_STRLEN_P(zname), + Z_STRVAL_P(zname), + (int) Z_STRLEN_P(zfilename), + Z_STRVAL_P(zfilename), + (int) Z_STRLEN_P(ztype), + Z_STRVAL_P(ztype)); + /** + * from memory + */ + if (zcontent) { + buffer->clear(); + buffer->append(header_buf, n); + buffer->append(Z_STRVAL_P(zcontent), Z_STRLEN_P(zcontent)); + buffer->append("\r\n", 2); + + if (socket->send_all(buffer->str, buffer->length) != (ssize_t) buffer->length) { + goto _send_fail; + } + } + /** + * from disk file + */ + else { + if (socket->send_all(header_buf, n) != n) { + goto _send_fail; + } + if (!socket->sendfile(Z_STRVAL_P(zpath), Z_LVAL_P(zoffset), Z_LVAL_P(zsize))) { + goto _send_fail; + } + if (socket->send_all("\r\n", 2) != 2) { + goto _send_fail; + } + } + } + SW_HASHTABLE_FOREACH_END(); + } + + n = sw_snprintf(header_buf, sizeof(header_buf), "--%.*s--\r\n", (int) (sizeof(boundary_str) - 1), boundary_str); + if (socket->send_all(header_buf, n) != n) { + goto _send_fail; + } + wait_response = true; + return true; + } + // ============ x-www-form-urlencoded or raw ============ + else if (zbody) { + if (ZVAL_IS_ARRAY(zbody)) { + size_t len; + add_headers(buffer, ZEND_STRL("Content-Type"), ZEND_STRL("application/x-www-form-urlencoded")); + if (php_swoole_array_length(zbody) > 0) { + smart_str formstr_s = {}; + char *formstr = php_swoole_http_build_query(zbody, &len, &formstr_s); + if (formstr == nullptr) { + php_swoole_error(E_WARNING, "http_build_query failed"); + return false; + } + add_content_length(buffer, len); + buffer->append(formstr, len); + smart_str_free(&formstr_s); + } else { + add_content_length(buffer, 0); + } + } else { + char *body; + size_t body_length = php_swoole_get_send_data(zbody, &body); + add_content_length(buffer, body_length); + buffer->append(body, body_length); + } + } + // ============ no body ============ + else { + if (header_flag & HTTP_HEADER_CONTENT_LENGTH) { + add_content_length(buffer, 0); + } else { + buffer->append(ZEND_STRL("\r\n")); + } + } + + swoole_trace_log(SW_TRACE_HTTP_CLIENT, + "to [%s:%u%s] by fd#%d in cid#%ld with [%zu] bytes: <get_fd(), + Coroutine::get_current_cid(), + buffer->length, + (int) buffer->length, + buffer->str); + + if (socket->send_all(buffer->str, buffer->length) != (ssize_t) buffer->length) { + _send_fail: + set_error(socket->errCode, socket->errMsg, ESTATUS_SEND_FAILED); + close(); + return false; + } + wait_response = true; + return true; +} + +bool Client::exec(std::string _path) { + path = _path; + // bzero when make a new reqeust + resolve_context_ = {}; + if (use_default_port) { + resolve_context_.with_port = true; + } + SW_LOOP_N(max_retries + 1) { + if (send_request() == false) { + return false; + } + 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 Client::recv_response(double timeout) { + if (!wait_response) { + return false; + } + ssize_t retval = 0; + size_t total_bytes = 0, parsed_n = 0; + String *buffer = socket->get_read_buffer(); + bool header_completed = false; + off_t header_crlf_offset = 0; + + // re-init http response parser + swoole_http_parser_init(&parser, PHP_HTTP_RESPONSE); + parser.data = this; + + if (timeout == 0) { + timeout = response_timeout == 0 ? network::Socket::default_read_timeout : response_timeout; + } + Socket::TimeoutController tc(socket, timeout, Socket::TIMEOUT_READ); + bool success = false; + while (true) { + if (sw_unlikely(tc.has_timedout(Socket::TIMEOUT_READ))) { + break; + } + retval = socket->recv(buffer->str + buffer->length, buffer->size - buffer->length); + if (sw_unlikely(retval <= 0)) { + if (retval == 0) { + socket->set_err(ECONNRESET); + if (total_bytes > 0 && !swoole_http_should_keep_alive(&parser)) { + http_parser_on_message_complete(&parser); + success = true; + break; + } + } + 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 (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); + break; + } + header_crlf_offset = buffer->length > 4 ? buffer->length - 4 : 0; + continue; + } else { + header_completed = true; + header_crlf_offset = 0; + retval = buffer->length; + buffer->clear(); + } + } + + total_bytes += retval; + parsed_n = swoole_http_parser_execute(&parser, &http_parser_settings, buffer->str, retval); + swoole_trace_log(SW_TRACE_HTTP_CLIENT, + "parsed_n=%ld, retval=%ld, total_bytes=%ld, completed=%d", + parsed_n, + retval, + total_bytes, + parser.state == s_start_res); + 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) { + buffer->length = retval; + buffer->offset = parsed_n; + buffer->reduce(parsed_n); + } + success = true; + break; + } + if (sw_unlikely(parser.state == s_dead)) { + socket->set_err(SW_ERROR_HTTP_INVALID_PROTOCOL); + 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 Client::upgrade(std::string path) { + defer = false; + char buf[SW_WEBSOCKET_KEY_LENGTH + 1]; + zval *zheaders = + sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, zobject, ZEND_STRL("requestHeaders"), 0); + zend_update_property_string(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("requestMethod"), "GET"); + create_token(SW_WEBSOCKET_KEY_LENGTH, buf); + add_assoc_string(zheaders, "Connection", (char *) "Upgrade"); + add_assoc_string(zheaders, "Upgrade", (char *) "websocket"); + add_assoc_string(zheaders, "Sec-WebSocket-Version", (char *) SW_WEBSOCKET_VERSION); + add_assoc_str_ex(zheaders, + ZEND_STRL("Sec-WebSocket-Key"), + php_base64_encode((const unsigned char *) buf, SW_WEBSOCKET_KEY_LENGTH)); +#ifdef SW_HAVE_ZLIB + if (websocket_compression) { + add_assoc_string(zheaders, "Sec-Websocket-Extensions", (char *) SW_WEBSOCKET_EXTENSION_DEFLATE); + } +#endif + return exec(path); +} + +bool Client::push(zval *zdata, zend_long opcode, uint8_t flags) { + if (!websocket) { + swoole_set_last_error(SW_ERROR_WEBSOCKET_HANDSHAKE_FAILED); + php_swoole_fatal_error(E_WARNING, "websocket handshake failed, cannot push data"); + 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"), + "websocket handshake failed, cannot push data"); + zend_update_property_long( + swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), ESTATUS_CONNECT_FAILED); + return false; + } + String *buffer = socket->get_write_buffer(); + buffer->clear(); + if (php_swoole_websocket_frame_is_object(zdata)) { + if (php_swoole_websocket_frame_object_pack(buffer, zdata, websocket_mask, accept_websocket_compression) < 0) { + return false; + } + } else { + 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("statusCode"), ESTATUS_SERVER_RESET); + close(); + return false; + } else { + return true; + } +} + +void Client::reset() { + wait_response = false; +#ifdef SW_HAVE_COMPRESSION + compress_method = HTTP_COMPRESS_NONE; + compression_error = false; +#endif +#ifdef SW_HAVE_ZLIB + if (gzip_stream_active) { + inflateEnd(&gzip_stream); + gzip_stream_active = false; + } +#endif +#ifdef SW_HAVE_BROTLI + if (brotli_decoder_state) { + 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) { + 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")); + zend_update_property_long(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("downloadOffset"), 0); + } +} + +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) { + return false; + } + if (in_callback) { + _socket->get_socket()->close_wait = 1; + return true; + } + 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; +} + +Client::~Client() { + close(); + if (body) { + delete body; + } + if (tmp_write_buffer) { + delete tmp_write_buffer; + } + if (write_func) { + delete write_func; + } +} + +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 Client *http_client_coro_get_client(zval *zobject) { + Client *phc = http_client_coro_fetch_object(Z_OBJ_P(zobject))->client; + if (UNEXPECTED(!phc)) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); + } + return phc; +} + +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 *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; + return &hcc->std; +} + +void php_swoole_http_client_coro_minit(int module_number) { + SW_INIT_CLASS_ENTRY(swoole_http_client_coro, + "Swoole\\Coroutine\\Http\\Client", + "Co\\Http\\Client", + swoole_http_client_coro_methods); + SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_client_coro); + SW_SET_CLASS_CLONEABLE(swoole_http_client_coro, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http_client_coro, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT( + swoole_http_client_coro, 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); + zend_declare_property_string(swoole_http_client_coro_ce, ZEND_STRL("errMsg"), "", ZEND_ACC_PUBLIC); + zend_declare_property_bool(swoole_http_client_coro_ce, ZEND_STRL("connected"), 0, ZEND_ACC_PUBLIC); + + // client info + zend_declare_property_string(swoole_http_client_coro_ce, ZEND_STRL("host"), "", ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_http_client_coro_ce, ZEND_STRL("port"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_bool(swoole_http_client_coro_ce, ZEND_STRL("ssl"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL("setting"), ZEND_ACC_PUBLIC); + + // request properties + zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL("requestMethod"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL("requestHeaders"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL("requestBody"), ZEND_ACC_PUBLIC); + // always set by API (make it private?) + zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL("uploadFiles"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL("downloadFile"), ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_http_client_coro_ce, ZEND_STRL("downloadOffset"), 0, ZEND_ACC_PUBLIC); + + // response properties + zend_declare_property_long(swoole_http_client_coro_ce, ZEND_STRL("statusCode"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL("headers"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL("set_cookie_headers"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL("cookies"), ZEND_ACC_PUBLIC); + zend_declare_property_string(swoole_http_client_coro_ce, ZEND_STRL("body"), "", ZEND_ACC_PUBLIC); + + SW_INIT_CLASS_ENTRY_EX(swoole_http_client_coro_exception, + "Swoole\\Coroutine\\Http\\Client\\Exception", + "Co\\Http\\Client\\Exception", + nullptr, + swoole_exception); + + 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 = http_client_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); + char *host; + size_t host_len; + zend_long port = 0; + zend_bool ssl = 0; + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 3) + Z_PARAM_STRING(host, host_len) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(port) + Z_PARAM_BOOL(ssl) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + zend_update_property_stringl(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("host"), host, host_len); + zend_update_property_long(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("port"), port); + zend_update_property_bool(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("ssl"), ssl); + // check host + if (host_len == 0) { + zend_throw_exception_ex(swoole_http_client_coro_exception_ce, EINVAL, "host is empty"); + RETURN_FALSE; + } + // check ssl +#ifndef SW_USE_OPENSSL + if (ssl) { + zend_throw_exception_ex( + swoole_http_client_coro_exception_ce, + EPROTONOSUPPORT, + "you must configure with `--enable-openssl` to support ssl connection when compiling Swoole"); + RETURN_FALSE; + } +#endif + 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) { + Client *phc = http_client_coro_get_client(ZEND_THIS); + zval *zset; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(zset) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (php_swoole_array_length(zset) == 0) { + RETURN_FALSE; + } else { + zval *zsettings = + sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0); + php_array_merge(Z_ARRVAL_P(zsettings), Z_ARRVAL_P(zset)); + phc->apply_setting(zset); + RETURN_TRUE; + } +} + +static PHP_METHOD(swoole_http_client_coro, getDefer) { + Client *phc = http_client_coro_get_client(ZEND_THIS); + + RETURN_BOOL(phc->defer); +} + +static PHP_METHOD(swoole_http_client_coro, setDefer) { + Client *phc = http_client_coro_get_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); + + phc->defer = defer; + + RETURN_TRUE; +} + +static PHP_METHOD(swoole_http_client_coro, setMethod) { + char *method; + size_t method_length; + + // Notice: maybe string or array + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(method, method_length) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + zend_update_property_stringl( + swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("requestMethod"), method, method_length); + + RETURN_TRUE; +} + +static PHP_METHOD(swoole_http_client_coro, setHeaders) { + zval *headers; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_EX(headers, 0, 1) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + zend_update_property(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("requestHeaders"), headers); + + RETURN_TRUE; +} + +static PHP_METHOD(swoole_http_client_coro, setBasicAuth) { + Client *phc = http_client_coro_get_client(ZEND_THIS); + char *username, *password; + size_t username_len, password_len; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(username, username_len) + Z_PARAM_STRING(password, password_len) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + phc->set_basic_auth(std::string(username, username_len), std::string(password, password_len)); +} + +static PHP_METHOD(swoole_http_client_coro, setCookies) { + zval *cookies; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_EX(cookies, 0, 1) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + zend_update_property(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("cookies"), cookies); + + RETURN_TRUE; +} + +static PHP_METHOD(swoole_http_client_coro, setData) { + zval *zdata; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zdata) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + zend_update_property(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("requestBody"), zdata); + + RETURN_TRUE; +} + +static PHP_METHOD(swoole_http_client_coro, addFile) { + char *path; + size_t l_path; + char *name; + size_t l_name; + char *type = nullptr; + size_t l_type = 0; + char *filename = nullptr; + size_t l_filename = 0; + zend_long offset = 0; + zend_long length = 0; + + ZEND_PARSE_PARAMETERS_START(2, 6) + Z_PARAM_STRING(path, l_path) + Z_PARAM_STRING(name, l_name) + Z_PARAM_OPTIONAL + Z_PARAM_STRING(type, l_type) + Z_PARAM_STRING(filename, l_filename) + Z_PARAM_LONG(offset) + Z_PARAM_LONG(length) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (offset < 0) { + offset = 0; + } + if (length < 0) { + length = 0; + } + struct stat file_stat; + if (stat(path, &file_stat) < 0) { + php_swoole_sys_error(E_WARNING, "stat(%s) failed", path); + RETURN_FALSE; + } + if (file_stat.st_size == 0) { + php_swoole_sys_error(E_WARNING, "cannot send empty file[%s]", filename); + 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; + } + if (length > file_stat.st_size - offset) { + php_swoole_sys_error(E_WARNING, "parameter $length[" ZEND_LONG_FMT "] exceeds the file size", length); + RETURN_FALSE; + } + if (length == 0) { + length = file_stat.st_size - offset; + } + if (l_type == 0) { + type = (char *) swoole::mime_type::get(path).c_str(); + l_type = strlen(type); + } + if (l_filename == 0) { + char *dot = strrchr(path, '/'); + if (dot == nullptr) { + filename = path; + l_filename = l_path; + } else { + filename = dot + 1; + l_filename = strlen(filename); + } + } + + zval *zupload_files = + sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, ZEND_THIS, ZEND_STRL("uploadFiles"), 0); + zval zupload_file; + array_init(&zupload_file); + add_assoc_stringl_ex(&zupload_file, ZEND_STRL("path"), path, l_path); + add_assoc_stringl_ex(&zupload_file, ZEND_STRL("name"), name, l_name); + add_assoc_stringl_ex(&zupload_file, ZEND_STRL("filename"), filename, l_filename); + add_assoc_stringl_ex(&zupload_file, ZEND_STRL("type"), type, l_type); + add_assoc_long(&zupload_file, "size", length); + add_assoc_long(&zupload_file, "offset", offset); + + RETURN_BOOL(add_next_index_zval(zupload_files, &zupload_file) == SUCCESS); +} + +static PHP_METHOD(swoole_http_client_coro, addData) { + char *data; + size_t l_data; + char *name; + size_t l_name; + char *type = nullptr; + size_t l_type = 0; + char *filename = nullptr; + size_t l_filename = 0; + + ZEND_PARSE_PARAMETERS_START(2, 4) + Z_PARAM_STRING(data, l_data) + Z_PARAM_STRING(name, l_name) + Z_PARAM_OPTIONAL + Z_PARAM_STRING(type, l_type) + Z_PARAM_STRING(filename, l_filename) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (l_type == 0) { + type = (char *) "application/octet-stream"; + l_type = strlen(type); + } + if (l_filename == 0) { + filename = name; + l_filename = l_name; + } + + zval *zupload_files = + sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, ZEND_THIS, ZEND_STRL("uploadFiles"), 0); + zval zupload_file; + array_init(&zupload_file); + add_assoc_stringl_ex(&zupload_file, ZEND_STRL("content"), data, l_data); + add_assoc_stringl_ex(&zupload_file, ZEND_STRL("name"), name, l_name); + add_assoc_stringl_ex(&zupload_file, ZEND_STRL("filename"), filename, l_filename); + add_assoc_stringl_ex(&zupload_file, ZEND_STRL("type"), type, l_type); + add_assoc_long(&zupload_file, "size", l_data); + + RETURN_BOOL(add_next_index_zval(zupload_files, &zupload_file) == SUCCESS); +} + +static PHP_METHOD(swoole_http_client_coro, execute) { + Client *phc = http_client_coro_get_client(ZEND_THIS); + char *path = nullptr; + size_t path_len = 0; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(path, path_len) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_BOOL(phc->exec(std::string(path, path_len))); +} + +static PHP_METHOD(swoole_http_client_coro, get) { + Client *phc = http_client_coro_get_client(ZEND_THIS); + char *path = nullptr; + size_t path_len = 0; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(path, path_len) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + zend_update_property_string(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("requestMethod"), "GET"); + + RETURN_BOOL(phc->exec(std::string(path, path_len))); +} + +static PHP_METHOD(swoole_http_client_coro, post) { + Client *phc = http_client_coro_get_client(ZEND_THIS); + char *path = nullptr; + size_t path_len = 0; + zval *post_data; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(path, path_len) + Z_PARAM_ZVAL(post_data) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + zend_update_property_string(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("requestMethod"), "POST"); + zend_update_property(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("requestBody"), post_data); + + RETURN_BOOL(phc->exec(std::string(path, path_len))); +} + +static PHP_METHOD(swoole_http_client_coro, download) { + Client *phc = http_client_coro_get_client(ZEND_THIS); + char *path; + size_t path_len; + zval *download_file; + zend_long offset = 0; + + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_STRING(path, path_len) + Z_PARAM_ZVAL(download_file) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(offset) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + zend_update_property(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("downloadFile"), download_file); + zend_update_property_long(swoole_http_client_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("downloadOffset"), offset); + + RETURN_BOOL(phc->exec(std::string(path, path_len))); +} + +static PHP_METHOD(swoole_http_client_coro, upgrade) { + Client *phc = http_client_coro_get_client(ZEND_THIS); + char *path = nullptr; + size_t path_len = 0; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(path, path_len) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_BOOL(phc->upgrade(std::string(path, path_len))); +} + +static PHP_METHOD(swoole_http_client_coro, push) { + Client *phc = http_client_coro_get_client(ZEND_THIS); + if (!phc->is_available()) { + RETURN_FALSE; + } + + zval *zdata; + zend_long opcode = WebSocket::OPCODE_TEXT; + zval *zflags = nullptr; + zend_long flags = WebSocket::FLAG_FIN; + + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_ZVAL(zdata) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(opcode) + Z_PARAM_ZVAL_EX(zflags, 1, 0) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (zflags != nullptr) { + flags = zval_get_long(zflags); + } + SW_CLIENT_PRESERVE_SOCKET(&phc->zsocket); + RETURN_BOOL(phc->push(zdata, opcode, flags & WebSocket::FLAGS_ALL)); +} + +static PHP_METHOD(swoole_http_client_coro, recv) { + Client *phc = http_client_coro_get_client(ZEND_THIS); + if (!phc->is_available()) { + RETURN_FALSE; + } + + double timeout = 0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + 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) { + Client *phc = http_client_coro_get_client(ZEND_THIS); + SW_CLIENT_PRESERVE_SOCKET(&phc->zsocket); + RETURN_BOOL(phc->close()); +} + +static PHP_METHOD(swoole_http_client_coro, getBody) { + SW_RETURN_PROPERTY("body"); +} + +static PHP_METHOD(swoole_http_client_coro, getHeaders) { + SW_RETURN_PROPERTY("headers"); +} + +static PHP_METHOD(swoole_http_client_coro, getCookies) { + SW_RETURN_PROPERTY("cookies"); +} + +static PHP_METHOD(swoole_http_client_coro, getStatusCode) { + SW_RETURN_PROPERTY("statusCode"); +} + +static PHP_METHOD(swoole_http_client_coro, getHeaderOut) { + Client *phc = http_client_coro_get_client(ZEND_THIS); + phc->get_header_out(return_value); +} + +static PHP_METHOD(swoole_http_client_coro, getsockname) { + Client *phc = http_client_coro_get_client(ZEND_THIS); + phc->getsockname(return_value); +} + +static PHP_METHOD(swoole_http_client_coro, getpeername) { + 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) { + 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 new file mode 100644 index 00000000000..184cb9f4878 --- /dev/null +++ b/ext-src/swoole_http_request.cc @@ -0,0 +1,1014 @@ +/* + +----------------------------------------------------------------------+ + | 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_http_server.h" + +SW_EXTERN_C_BEGIN +#include "ext/standard/url.h" +#include "stubs/php_swoole_http_request_arginfo.h" +#include "thirdparty/php/main/SAPI.h" +SW_EXTERN_C_END + +enum http_upload_errno { + HTTP_UPLOAD_ERR_OK = 0, + HTTP_UPLOAD_ERR_INI_SIZE, + HTTP_UPLOAD_ERR_FORM_SIZE, + HTTP_UPLOAD_ERR_PARTIAL, + HTTP_UPLOAD_ERR_NO_FILE, + HTTP_UPLOAD_ERR_NO_TMP_DIR = 6, + HTTP_UPLOAD_ERR_CANT_WRITE, + HTTP_UPLOAD_ERR_FILE_READY, +}; + +using HttpRequest = swoole::http::Request; +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); +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); + +static int http_request_on_path(swoole_http_parser *parser, const char *at, size_t length) { + 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, size_t *len) { + size_t i; + char *tmp = ptr; + + // ltrim('"') + for (i = 0; i < *len; i++) { + if (tmp[0] == '"') { + (*len)--; + tmp++; + continue; + } else { + break; + } + } + // rtrim('"') + for (i = (*len); i > 0; i--) { + if (tmp[i - 1] == '"') { + tmp[i - 1] = 0; + (*len)--; + continue; + } else { + break; + } + } + return tmp; +} + +// 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 + +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; + +struct HttpRequestObject { + HttpContext *ctx; + zend_object std; +}; + +static sw_inline HttpRequestObject *php_swoole_http_request_fetch_object(zend_object *obj) { + return (HttpRequestObject *) ((char *) obj - swoole_http_request_handlers.offset); +} + +HttpContext *php_swoole_http_request_get_context(zval *zobject) { + return php_swoole_http_request_fetch_object(Z_OBJ_P(zobject))->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) { + 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(); + } + ctx->request.zobject = nullptr; + ctx->free(); + } + + zend_object_std_dtor(&request->std); +} + +static zend_object *php_swoole_http_request_create_object(zend_class_entry *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; + return &request->std; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_http_request, getData); +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 +const zend_function_entry swoole_http_request_methods[] = +{ + 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", 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, + HttpRequestObject, + std); + + zend_declare_property_long(swoole_http_request_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_http_request_ce, ZEND_STRL("streamId"), 0, ZEND_ACC_PUBLIC); + 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); + zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("get"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("files"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("post"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("tmpfiles"), ZEND_ACC_PUBLIC); +} + +static int http_request_on_query_string(swoole_http_parser *parser, const char *at, size_t 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 + swoole_php_treat_data( + PARSE_STRING, + estrndup(at, length), // it will be freed by treat_data + swoole_http_init_and_read_property( + swoole_http_request_ce, ctx->request.zobject, &ctx->request.zget, SW_ZSTR_KNOWN(SW_ZEND_STR_GET))); + return 0; +} + +static int http_request_on_header_field(swoole_http_parser *parser, const char *at, size_t length) { + HttpContext *ctx = (HttpContext *) parser->data; + ctx->current_header_name = at; + ctx->current_header_name_len = length; + return 0; +} + +bool HttpContext::init_multipart_parser(const char *boundary_str, int boundary_len) { + mt_parser = multipart_parser_init(boundary_str, boundary_len, &mt_parser_settings); + if (!mt_parser) { + php_swoole_fatal_error(E_WARNING, "multipart_parser_init() failed"); + return false; + } + form_data_buffer = new String(SW_BUFFER_SIZE_STD); + mt_parser->data = this; + return true; +} + +bool HttpContext::get_multipart_boundary( + const char *at, size_t length, size_t offset, char **out_boundary_str, int *out_boundary_len) { + if (!http_server::parse_multipart_boundary(at, length, offset, out_boundary_str, out_boundary_len)) { + swoole_warning("boundary of multipart/form-data not found, fd:%ld", fd); + /* make it same with protocol error */ + parser.state = s_dead; + return false; + } + return true; +} + +void swoole_http_parse_cookie(zval *zcookies, const char *at, size_t length) { + if (length == 0) { + return; + } + + swoole_php_treat_data(PARSE_COOKIE, estrndup(at, length), zcookies); +} + +static void http_request_add_upload_file(HttpContext *ctx, const char *file, size_t l_file) { + zval *zfiles = swoole_http_init_and_read_property( + swoole_http_request_ce, ctx->request.zobject, &ctx->request.ztmpfiles, SW_ZSTR_KNOWN(SW_ZEND_STR_TMPFILES)); + add_next_index_stringl(zfiles, file, l_file); + // support is_upload_file + zend_hash_str_add_ptr(SG(rfc1867_uploaded_files), file, l_file, (char *) file); +} + +bool swoole_http_token_list_contains_value(const char *at, size_t length, const char *value) { + if (0 == length) { + return false; + } + if (SW_STRCASEEQ(at, length, value)) { + return true; + } + + char *var; + const char *separator = ",\0"; + char *strtok_buf = nullptr; + size_t var_len; + + char *_c = sw_tg_buffer()->str; + memcpy(_c, at, length); + _c[length] = '\0'; + + var = php_strtok_r(_c, separator, &strtok_buf); + while (var) { + var_len = swoole::ltrim(&var, strlen(var)); + var_len = swoole::rtrim(var, var_len); + if (swoole_strcaseeq(var, var_len, value, strlen(value))) { + return true; + } + var = php_strtok_r(nullptr, separator, &strtok_buf); + } + return false; +} + +static int http_request_on_header_value(swoole_http_parser *parser, const char *at, size_t length) { + 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; + + 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, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIE)); + swoole_http_parse_cookie(zcookie, at, length); + return 0; + } 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; + } + 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_TRACE, SW_ERROR_SESSION_CLOSED, "session[%ld] is closed", ctx->fd); + return -1; + } + ListenPort *port = serv->get_port_by_server_fd(conn->server_fd); + if (port->open_websocket_protocol) { + 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_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_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; + } + 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_STRCASEEQ(header_name, header_len, "accept-encoding")) { + ctx->set_compression_method(at, length); + } +#endif + else if (SW_STRCASEEQ(header_name, header_len, "transfer-encoding") && SW_STR_ISTARTS_WITH(at, length, "chunked")) { + ctx->recv_chunked = 1; + } + +_add_header: + 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) { + HttpContext *ctx = (HttpContext *) parser->data; + const char *vpath = ctx->request.path, *end = vpath + ctx->request.path_len, *p = end; + + ctx->request.version = parser->http_major * 100 + parser->http_minor; + ctx->request.ext = end; + ctx->request.ext_len = 0; + + while (p > vpath) { + --p; + if (*p == '.') { + ++p; + ctx->request.ext = p; + ctx->request.ext_len = end - p; + break; + } + } + + 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); + + // 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)); + 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) { + 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]; + size_t value_len; + int ret = 0; + + HttpContext *ctx = (HttpContext *) p->data; + /** + * Hash collision attack + */ + if (ctx->input_var_num > PG(max_input_vars)) { + php_swoole_error(E_WARNING, + "Input variables exceeded " ZEND_LONG_FMT ". " + "To increase the limit change max_input_vars in php.ini", + PG(max_input_vars)); + return SW_OK; + } else { + ctx->input_var_num++; + } + + size_t header_len = ctx->current_header_name_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")) { + 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 + offset, length - offset); + + zval *zform_name; + if (!(zform_name = zend_hash_str_find(Z_ARRVAL(tmp_array), ZEND_STRL("name")))) { + return ret; + } + + if (Z_STRLEN_P(zform_name) >= SW_HTTP_FORM_KEYLEN) { + swoole_warning("form_name[%s] is too large", Z_STRVAL_P(zform_name)); + ret = -1; + return ret; + } + + swoole_strlcpy(value_buf, Z_STRVAL_P(zform_name), sizeof(value_buf)); + value_len = Z_STRLEN_P(zform_name); + char *tmp = http_trim_double_quote(value_buf, &value_len); + + zval *zfilename; + // POST form data + if (!(zfilename = zend_hash_str_find(Z_ARRVAL(tmp_array), ZEND_STRL("filename")))) { + ctx->current_form_data_name = estrndup(tmp, value_len); + ctx->current_form_data_name_len = value_len; + } + // upload file + else { + if (Z_STRLEN_P(zfilename) >= SW_HTTP_FORM_KEYLEN) { + swoole_warning("filename[%s] is too large", Z_STRVAL_P(zfilename)); + ret = -1; + return ret; + } + ctx->current_input_name = estrndup(tmp, value_len); + ctx->current_input_name_len = value_len; + + zval *z_multipart_header = sw_malloc_zval(); + array_init(z_multipart_header); + + 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, "size", 0); + + swoole_strlcpy(value_buf, Z_STRVAL_P(zfilename), sizeof(value_buf)); + value_len = Z_STRLEN_P(zfilename); + tmp = http_trim_double_quote(value_buf, &value_len); + + 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")) { + 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()); + } + return ret; +} + +static int multipart_body_on_data(multipart_parser *p, const char *at, size_t length) { + HttpContext *ctx = (HttpContext *) p->data; + if (ctx->current_form_data_name) { + ctx->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) { + zval *z_multipart_header = ctx->current_multipart_header; + add_assoc_long(z_multipart_header, "error", HTTP_UPLOAD_ERR_CANT_WRITE); + + fclose(p->fp); + p->fp = nullptr; + + swoole_sys_warning("write upload file failed"); + } + return 0; +} + +#if 0 +static void get_random_file_name(char *des, const char *src) +{ + unsigned char digest[16] = {}; + char buf[19] = {}; + int n = sprintf(buf, "%s%d", src, swoole_system_random(0, 9999)); + + PHP_MD5_CTX ctx; + PHP_MD5Init(&ctx); + PHP_MD5Update(&ctx, buf, n); + PHP_MD5Final(digest, &ctx); + make_digest_ex(des, digest, 16); +} +#endif + +static int multipart_body_on_header_complete(multipart_parser *p) { + HttpContext *ctx = (HttpContext *) p->data; + if (!ctx->current_input_name) { + return 0; + } + + zval *z_multipart_header = ctx->current_multipart_header; + zval *zerr = nullptr; + if (!(zerr = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL("error")))) { + return 0; + } + if (Z_TYPE_P(zerr) == IS_LONG && Z_LVAL_P(zerr) != HTTP_UPLOAD_ERR_OK) { + return 0; + } + + 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.c_str()); + int tmpfile = swoole_tmpfile(file_path); + if (tmpfile < 0) { + return 0; + } + + FILE *fp = fdopen(tmpfile, "wb+"); + if (fp == nullptr) { + add_assoc_long(z_multipart_header, "error", HTTP_UPLOAD_ERR_NO_TMP_DIR); + swoole_sys_warning("fopen(%s) failed", file_path); + return 0; + } + + p->fp = fp; + add_assoc_string(z_multipart_header, "tmp_name", 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) { + HttpContext *ctx = (HttpContext *) p->data; + + if (ctx->current_form_data_name) { + php_register_variable_safe( + ctx->current_form_data_name, + 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, 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; + ctx->form_data_buffer->clear(); + return 0; + } + + if (!ctx->current_input_name) { + return 0; + } + + zval *z_multipart_header = ctx->current_multipart_header; + if (p->fp != nullptr) { + long size = swoole::file_get_size(p->fp); + add_assoc_long(z_multipart_header, "size", size); + + 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, 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) { + char meta_name[SW_HTTP_FORM_KEYLEN + sizeof("[tmp_name]") - 1]; + char *input_path = ctx->current_input_name + input_path_pos; + char *meta_path = meta_name + input_path_pos; + size_t meta_path_len = sizeof(meta_name) - input_path_pos; + + swoole_strlcpy(meta_name, ctx->current_input_name, sizeof(meta_name)); + + zval *zname = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL("name")); + zval *ztype = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL("type")); + zval *zfile = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL("tmp_name")); + zval *zerr = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL("error")); + zval *zsize = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL("size")); + + sw_snprintf(meta_path, meta_path_len, "[name]%s", input_path); + php_register_variable_ex(meta_name, zname, zfiles); + + sw_snprintf(meta_path, meta_path_len, "[type]%s", input_path); + php_register_variable_ex(meta_name, ztype, zfiles); + + sw_snprintf(meta_path, meta_path_len, "[tmp_name]%s", input_path); + php_register_variable_ex(meta_name, zfile, zfiles); + + sw_snprintf(meta_path, meta_path_len, "[error]%s", input_path); + php_register_variable_ex(meta_name, zerr, zfiles); + + sw_snprintf(meta_path, meta_path_len, "[size]%s", input_path); + php_register_variable_ex(meta_name, zsize, zfiles); + } else { + php_register_variable_ex(ctx->current_input_name, z_multipart_header, zfiles); + } + + efree(ctx->current_input_name); + ctx->current_input_name = nullptr; + ctx->current_input_name_len = 0; + efree(ctx->current_multipart_header); + ctx->current_multipart_header = nullptr; + + return 0; +} + +static int http_request_on_body(swoole_http_parser *parser, const char *at, size_t length) { + if (length == 0) { + return 0; + } + + 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) { + if (ctx->request.chunked_body == nullptr) { + ctx->request.chunked_body = new swoole::String(SW_BUFFER_SIZE_STD); + } + ctx->request.chunked_body->append(at, length); + } else { + ctx->request.body_at = at - ctx->request.body_length; + ctx->request.body_length += length; + } + + if (ctx->mt_parser != nullptr) { + if (is_beginning) { + /* Compatibility: some clients may send extra EOL */ + do { + if (*at != '\r' && *at != '\n') { + break; + } + at++; + length--; + } while (length != 0); + } + if (!ctx->parse_multipart_data(at, length)) { + return -1; + } + } + + return 0; +} + +static int http_request_message_complete(swoole_http_parser *parser) { + 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 */ + 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, SW_ZSTR_KNOWN(SW_ZEND_STR_POST))); + } else if (!ctx->recv_chunked && ctx->parse_body && ctx->request.post_form_urlencoded && ctx->request.body_at) { + swoole_php_treat_data( + PARSE_STRING, + estrndup(ctx->request.body_at, ctx->request.body_length), // do not free, it will be freed by treat_data + swoole_http_init_and_read_property( + swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, SW_ZSTR_KNOWN(SW_ZEND_STR_POST))); + } + if (ctx->mt_parser) { + multipart_parser_free(ctx->mt_parser); + ctx->mt_parser = nullptr; + } + if (ctx->form_data_buffer) { + delete ctx->form_data_buffer; + ctx->form_data_buffer = nullptr; + } + + ctx->completed = 1; + + swoole_trace_log(SW_TRACE_HTTP, "request body length=%ld", content_length); + + return 1; /* return from execute */ +} + +#ifdef SW_HAVE_COMPRESSION +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) { + accept_compression = 1; + compression_method = HTTP_COMPRESS_BR; + } else +#endif + if (swoole_strnpos(accept_encoding, length, ZEND_STRL("gzip")) >= 0) { + accept_compression = 1; + compression_method = HTTP_COMPRESS_GZIP; + } else if (swoole_strnpos(accept_encoding, length, ZEND_STRL("deflate")) >= 0) { + accept_compression = 1; + compression_method = HTTP_COMPRESS_DEFLATE; +#ifdef SW_HAVE_ZSTD + } else if (swoole_strnpos(accept_encoding, length, ZEND_STRL("zstd")) >= 0) { + accept_compression = 1; + compression_method = HTTP_COMPRESS_ZSTD; +#endif + } else { + accept_compression = 0; + } +} + +const char *HttpContext::get_content_encoding() { + if (compression_method == HTTP_COMPRESS_GZIP) { + return "gzip"; + } else if (compression_method == HTTP_COMPRESS_DEFLATE) { + return "deflate"; + } +#ifdef SW_HAVE_BROTLI + 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; + } +} +#endif + +static PHP_METHOD(swoole_http_request, getContent) { + HttpContext *ctx = php_swoole_http_request_get_and_check_context(ZEND_THIS); + if (UNEXPECTED(!ctx)) { + RETURN_FALSE; + } + + 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); + } else if (req->h2_data_buffer && req->h2_data_buffer->length != 0) { + RETURN_STRINGL(req->h2_data_buffer->str, req->h2_data_buffer->length); + } + + RETURN_EMPTY_STRING(); +} + +static PHP_METHOD(swoole_http_request, getData) { + HttpContext *ctx = php_swoole_http_request_get_and_check_context(ZEND_THIS); + if (UNEXPECTED(!ctx)) { + RETURN_FALSE; + } + + if (ctx->http2) { + php_swoole_fatal_error(E_WARNING, "unable to get data from HTTP2 request"); + RETURN_FALSE; + } + + if (Z_TYPE(ctx->request.zdata) == IS_STRING) { + RETURN_ZVAL(&ctx->request.zdata, 1, 0); + } + + RETURN_EMPTY_STRING(); +} + +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 new file mode 100644 index 00000000000..ec05b9d61a3 --- /dev/null +++ b/ext-src/swoole_http_response.cc @@ -0,0 +1,1388 @@ +/* + +----------------------------------------------------------------------+ + | 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_http_server.h" +#include "swoole_util.h" + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_http_response_arginfo.h" +END_EXTERN_C() + +using swoole::Connection; +using swoole::Server; +using swoole::String; +using swoole::substr_len; +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 inline void http_header_key_format(char *key, int length) { + int i, state = 0; + for (i = 0; i < length; i++) { + if (state == 0) { + if (key[i] >= 97 && key[i] <= 122) { + key[i] -= 32; + } + state = 1; + } else if (key[i] == '-') { + state = 0; + } else { + if (key[i] >= 65 && key[i] <= 90) { + key[i] += 32; + } + } + } +} + +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; + } +} + +struct HttpResponseObject { + HttpContext *ctx; + zend_object std; +}; + +static sw_inline HttpResponseObject *php_swoole_http_response_fetch_object(zend_object *obj) { + return (HttpResponseObject *) ((char *) obj - swoole_http_response_handlers.offset); +} + +HttpContext *php_swoole_http_response_get_context(zval *zobject) { + return php_swoole_http_response_fetch_object(Z_OBJ_P(zobject))->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) { + 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->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; + } + if (ctx->http2) { + if (ctx->stream) { + ctx->http2_end(nullptr, &ztmp); + } + } else { + if (ctx->is_available()) { + ctx->end(nullptr, &ztmp); + } + } + } + ctx->response.zobject = nullptr; + ctx->free(); + } + + zend_object_std_dtor(&response->std); +} + +static zend_object *php_swoole_http_response_create_object(zend_class_entry *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; + return &response->std; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_http_response, write); +static PHP_METHOD(swoole_http_response, end); +static PHP_METHOD(swoole_http_response, sendfile); +static PHP_METHOD(swoole_http_response, redirect); +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); +/** + * for WebSocket Client + */ +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); +static PHP_METHOD(swoole_http_response, trailer); +static PHP_METHOD(swoole_http_response, ping); +static PHP_METHOD(swoole_http_response, goaway); +static PHP_METHOD(swoole_http_response, status); +SW_EXTERN_C_END + +// clang-format off +const zend_function_entry swoole_http_response_methods[] = +{ + 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_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", 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, + HttpResponseObject, + std); + + zend_declare_property_long(swoole_http_response_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_http_response_ce, ZEND_STRL("socket"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_http_response_ce, ZEND_STRL("header"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_http_response_ce, ZEND_STRL("cookie"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_http_response_ce, ZEND_STRL("trailer"), ZEND_ACC_PUBLIC); +} + +static PHP_METHOD(swoole_http_response, write) { + zval *zdata; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zdata) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + if (UNEXPECTED(!ctx)) { + RETURN_FALSE; + } + +#ifdef SW_HAVE_COMPRESSION + // 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. + ctx->accept_compression = 0; +#endif + + if (ctx->http2) { + ctx->http2_write(zdata, return_value); + } else { + ctx->write(zdata, return_value); + } +} + +static int parse_header_name(const char *key, size_t keylen) { + if (SW_STRCASEEQ(key, keylen, "Server")) { + return HTTP_HEADER_SERVER; + } else if (SW_STRCASEEQ(key, keylen, "Connection")) { + return HTTP_HEADER_CONNECTION; + } else if (SW_STRCASEEQ(key, keylen, "Date")) { + 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")) { + return HTTP_HEADER_CONTENT_TYPE; + } else if (SW_STRCASEEQ(key, keylen, "Transfer-Encoding")) { + return HTTP_HEADER_TRANSFER_ENCODING; + } else if (SW_STRCASEEQ(key, keylen, "Content-Encoding")) { + return HTTP_HEADER_CONTENT_ENCODING; + } + return 0; +} + +static void http_set_date_header(String *response) { + static SW_THREAD_LOCAL struct { + time_t time; + zend_string *date = nullptr; + } cache{}; + + time_t now = time(nullptr); + if (now != cache.time) { + if (cache.date) { + zend_string_release(cache.date); + } + + 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 { + const char *status = HttpServer::get_status_message(response.status); + http_buffer->append((char *) status, strlen(status)); + } + http_buffer->append(ZEND_STRL("\r\n")); + + // http headers + uint32_t header_flags = 0x0; + zval *zheader = + sw_zend_read_property_ex(swoole_http_response_ce, response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADER), 0); + if (ZVAL_IS_ARRAY(zheader)) { +#ifdef SW_HAVE_COMPRESSION + zend_string *content_type = nullptr; +#endif + zval *zvalue; + zend_string *string_key; + zend_ulong num_key; + + 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); + } + 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_custom_header(http_buffer, ZSTR_VAL(string_key), ZSTR_LEN(string_key), zvalue_2); + } + SW_HASHTABLE_FOREACH_END(); + } else { + add_custom_header(http_buffer, ZSTR_VAL(string_key), ZSTR_LEN(string_key), zvalue); + } + } + 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, 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 || swoole_http_has_crlf(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue))) { + continue; + } + 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)) { + 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 (upgrade == 1) { + http_buffer->append(ZEND_STRL("\r\n")); + send_header_ = 1; + return; + } + if (!(header_flags & HTTP_HEADER_CONNECTION)) { + if (keepalive) { + http_buffer->append(ZEND_STRL("Connection: keep-alive\r\n")); + } else { + http_buffer->append(ZEND_STRL("Connection: close\r\n")); + } + } + if (!(header_flags & HTTP_HEADER_CONTENT_TYPE)) { + http_buffer->append(ZEND_STRL("Content-Type: " SW_HTTP_DEFAULT_CONTENT_TYPE "\r\n")); + } + if (send_chunked) { + SW_ASSERT(length == 0); + if (!(header_flags & HTTP_HEADER_TRANSFER_ENCODING)) { + http_buffer->append(ZEND_STRL("Transfer-Encoding: chunked\r\n")); + } + } + // Content-Length + else if (length > 0 || parser.method != PHP_HTTP_HEAD) { +#ifdef SW_HAVE_COMPRESSION + if (compress(body, length)) { + length = zlib_buffer->length; + const char *content_encoding = get_content_encoding(); + http_buffer->append(ZEND_STRL("Content-Encoding: ")); + http_buffer->append((char *) content_encoding, strlen(content_encoding)); + http_buffer->append(ZEND_STRL("\r\n")); + } +#endif + if (!(header_flags & HTTP_HEADER_CONTENT_LENGTH)) { + 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")); + } + } + + http_buffer->append(ZEND_STRL("\r\n")); + send_header_ = 1; +} + +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, response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TRAILER), 0); + uint32_t size = php_swoole_array_length_safe(ztrailer); + + if (size > 0) { + const char *key; + uint32_t keylen; + int type; + zval *zvalue; + + SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(ztrailer), key, keylen, type, zvalue) { + if (UNEXPECTED(!key || ZVAL_IS_NULL(zvalue))) { + continue; + } + + if (!ZVAL_IS_NULL(zvalue)) { + 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()); + http_buffer->append(buf, n); + ret += n; + } + } + SW_HASHTABLE_FOREACH_END(); + (void) type; + http_buffer->append(ZEND_STRL("\r\n")); + } + + return ret; +} + +#ifdef SW_HAVE_ZLIB +voidpf php_zlib_alloc(voidpf opaque, uInt items, uInt size) { + return (voidpf) safe_emalloc(items, size, 0); +} + +void php_zlib_free(voidpf opaque, voidpf address) { + efree((void *) address); +} +#endif + +#ifdef SW_HAVE_BROTLI +void *php_brotli_alloc(void *opaque, size_t size) { + return emalloc(size); +} + +void php_brotli_free(void *opaque, void *address) { + efree(address); +} +#endif + +#ifdef SW_HAVE_COMPRESSION +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 (compression_method == HTTP_COMPRESS_GZIP) { + encoding = 0x1f; + } + // deflate: -0xf + else if (compression_method == HTTP_COMPRESS_DEFLATE) { + encoding = -0xf; + } +#endif +#ifdef SW_HAVE_BROTLI + 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); + 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 = zlib_buffer->size; + uint8_t *encoded_buffer = (uint8_t *) zlib_buffer->str; + + if (BROTLI_TRUE != BrotliEncoderCompress(compression_level, + BROTLI_DEFAULT_WINDOW, + BROTLI_DEFAULT_MODE, + input_size, + input_buffer, + &encoded_size, + encoded_buffer)) { + swoole_warning("BrotliEncoderCompress() failed"); + return false; + } else { + zlib_buffer->length = encoded_size; + content_compressed = 1; + return true; + } + } +#endif +#ifdef SW_HAVE_ZSTD + else if (compression_method == HTTP_COMPRESS_ZSTD) { + int zstd_compress_level = compression_level; + int zstd_max_level = ZSTD_maxCLevel(); + int zstd_min_level = ZSTD_minCLevel(); + zstd_compress_level = (zstd_compress_level > zstd_max_level) + ? zstd_max_level + : (zstd_compress_level < zstd_min_level ? zstd_min_level : zstd_compress_level); + + size_t compress_size = ZSTD_compressBound(length); + zlib_buffer = std::make_shared(compress_size);; + size_t zstd_compress_result = + ZSTD_compress((void *) zlib_buffer->str, compress_size, (void *) data, length, zstd_compress_level); + + if (ZSTD_isError(zstd_compress_result)) { + swoole_warning("ZSTD_compress() failed, Error: [%s]", ZSTD_getErrorName(zstd_compress_result)); + return false; + } + + zlib_buffer->length = zstd_compress_result; + content_compressed = 1; + return true; + } +#endif + else { + swoole_warning("Unknown compression method"); + return false; + } +#ifdef SW_HAVE_ZLIB + 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, compression_level, Z_DEFLATED, encoding, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); + if (status != Z_OK) { + swoole_warning("deflateInit2() failed, Error: [%d]", status); + return false; + } + + zstream.next_in = (Bytef *) data; + zstream.avail_in = length; + zstream.next_out = (Bytef *) zlib_buffer->str; + zstream.avail_out = zlib_buffer->size; + + status = deflate(&zstream, Z_FINISH); + deflateEnd(&zstream); + if (status != Z_STREAM_END) { + swoole_warning("deflate() failed, Error: [%d]", status); + return false; + } + + zlib_buffer->length = zstream.total_out; + zlib_buffer->offset = 0; + content_compressed = 1; + return true; +#endif +} +#endif + +static PHP_METHOD(swoole_http_response, initHeader) { + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + if (UNEXPECTED(!ctx)) { + RETURN_FALSE; + } + zval *zresponse_object = ctx->response.zobject; + swoole_http_init_and_read_property( + swoole_http_response_ce, zresponse_object, &ctx->response.zheader, ZEND_STRL("header")); + swoole_http_init_and_read_property( + swoole_http_response_ce, zresponse_object, &ctx->response.zcookie, ZEND_STRL("cookie")); + swoole_http_init_and_read_property( + swoole_http_response_ce, zresponse_object, &ctx->response.ztrailer, ZEND_STRL("trailer")); + 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) { + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + if (UNEXPECTED(!ctx)) { + RETURN_FALSE; + } + + zval *zdata = nullptr; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL_EX(zdata, 1, 0) + ZEND_PARSE_PARAMETERS_END_EX(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) { + ctx->http2_end(zdata, return_value); + } else { + ctx->end(zdata, return_value); + } +} + +void HttpContext::send_trailer(zval *return_value) { + String *http_buffer = get_write_buffer(); + + http_buffer->clear(); + if (build_trailer(http_buffer) == 0) { + return; + } + if (!send(this, http_buffer->str, http_buffer->length)) { + end_ = 1; + close(this); + RETURN_FALSE; + } +} + +bool HttpContext::send_file(const char *file, uint32_t l_file, off_t offset, size_t length) { + zval *zheader = + sw_zend_read_and_convert_property_array(swoole_http_response_ce, response.zobject, ZEND_STRL("header"), 0); + if (!zend_hash_str_exists(Z_ARRVAL_P(zheader), ZEND_STRL("Content-Type"))) { + add_assoc_string(zheader, "Content-Type", (char *) swoole::mime_type::get(file).c_str()); + } + + if (!send_header_) { +#ifdef SW_HAVE_COMPRESSION + accept_compression = 0; +#endif + String *http_buffer = get_write_buffer(); + http_buffer->clear(); + + build_header(http_buffer, nullptr, length); + + if (!send(this, http_buffer->str, http_buffer->length)) { + send_header_ = 0; + return false; + } + } + + if (length > 0 && !sendfile(this, file, l_file, offset, length)) { + close(this); + return false; + } + + end_ = 1; + + if (!keepalive) { + close(this); + } + return true; +} + +void HttpContext::write(zval *zdata, zval *return_value) { + String *http_buffer = get_write_buffer(); + + if (!send_header_) { + send_chunked = 1; + http_buffer->clear(); + build_header(http_buffer, nullptr, 0); + if (!send(this, http_buffer->str, http_buffer->length)) { + send_chunked = 0; + send_header_ = 0; + RETURN_FALSE; + } + } + + char *data = nullptr; + size_t length = php_swoole_get_send_data(zdata, &data); + + if (length == 0) { + php_swoole_error_ex(E_WARNING, SW_ERROR_NO_PAYLOAD, "the data sent must not be empty"); + RETURN_FALSE; + } + + http_buffer->clear(); + char *hex_string = swoole_dec2hex(length, 16); + int hex_len = strlen(hex_string); + //"%.*s\r\n%.*s\r\n", hex_len, hex_string, body.length, body.str + http_buffer->append(hex_string, hex_len); + http_buffer->append(ZEND_STRL("\r\n")); + http_buffer->append(data, length); + http_buffer->append(ZEND_STRL("\r\n")); + sw_free(hex_string); + + RETURN_BOOL(send(this, http_buffer->str, http_buffer->length)); +} + +void HttpContext::end(zval *zdata, zval *return_value) { + if (send_chunked) { + if (zdata && Z_STRLEN_P(zdata) > 0) { + zval retval; + write(zdata, &retval); + if (ZVAL_IS_FALSE(&retval)) { + RETURN_FALSE; + } + } + if (send_trailer_) { + if (!send(this, ZEND_STRL("0\r\n"))) { + RETURN_FALSE; + } + send_trailer(return_value); + send_trailer_ = 0; + } else { + if (!send(this, ZEND_STRL("0\r\n\r\n"))) { + RETURN_FALSE; + } + } + 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_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 + + build_header(http_buffer, data, length); + + if (length > 0) { +#ifdef SW_HAVE_COMPRESSION + if (content_compressed) { + data = zlib_buffer->str; + length = zlib_buffer->length; + } +#endif + // send twice to reduce memory copy + if (length > SW_HTTP_MAX_APPEND_DATA) { + if (!send(this, http_buffer->str, http_buffer->length)) { + send_header_ = 0; + RETURN_FALSE; + } + if (!send(this, data, length)) { + end_ = 1; + close(this); + RETURN_FALSE; + } + goto _skip_copy; + } else { + if (http_buffer->append(data, length) < 0) { + send_header_ = 0; + RETURN_FALSE; + } + } + } + + if (!send(this, http_buffer->str, http_buffer->length)) { + end_ = 1; + close(this); + RETURN_FALSE; + } + } + +_skip_copy: + 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; + keepalive = 0; + } + } + } + if (!keepalive) { + close(this); + } + end_ = 1; + RETURN_TRUE; +} + +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 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"); + return false; + } + + if (swoole_http_has_crlf(k, klen)) { + return false; + } + + zval *zheader = swoole_http_init_and_read_property( + 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); + if (http2) { + swoole_strtolower(sw_tg_buffer()->str, klen); + } else { + http_header_key_format(sw_tg_buffer()->str, klen); + } + k = sw_tg_buffer()->str; + } + zend::array_set(zheader, k, klen, zvalue); + return true; +} + +static PHP_METHOD(swoole_http_response, sendfile) { + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + if (UNEXPECTED(!ctx)) { + RETURN_FALSE; + } + + if (ctx->send_chunked) { + php_swoole_fatal_error(E_WARNING, "can't use sendfile when HTTP chunk is enabled"); + RETURN_FALSE; + } + + char *file; + size_t l_file; + zend_long offset = 0; + zend_long length = 0; + + 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"); + RETURN_FALSE; + } + + struct stat file_stat; + if (stat(file, &file_stat) < 0) { + 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; + } + if (length > file_stat.st_size - offset) { + php_swoole_error(E_WARNING, "parameter $length[" ZEND_LONG_FMT "] exceeds the file size", length); + RETURN_FALSE; + } + if (length == 0) { + length = file_stat.st_size - offset; + } + + 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)); + } +} + +static bool inline php_swoole_http_response_create_cookie(HttpCookie *cookie, zval *zobject) { + HttpContext *ctx = php_swoole_http_response_get_and_check_context(zobject); + + zend_string *cookie_str = cookie->toString(); + if (!cookie_str) { + cookie->reset(); + return false; + } + + 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_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; + zend_bool secure = false, httpOnly = false, partitioned = false; + bool result; + + ZEND_PARSE_PARAMETERS_START(1, 10) + Z_PARAM_ZVAL(name_or_object) + Z_PARAM_OPTIONAL + Z_PARAM_STR(value) + Z_PARAM_LONG(expires) + Z_PARAM_STR(path) + Z_PARAM_STR(domain) + Z_PARAM_BOOL(secure) + Z_PARAM_BOOL(httpOnly) + Z_PARAM_STR(sameSite) + Z_PARAM_STR(priority) + Z_PARAM_BOOL(partitioned) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + 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 { + php_swoole_error(E_WARNING, "The first argument must be a string or an cookie object"); + result = false; + } + + RETURN_BOOL(result); +} + +static PHP_METHOD(swoole_http_response, cookie) { + php_swoole_http_response_set_cookie(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} + +static PHP_METHOD(swoole_http_response, rawcookie) { + php_swoole_http_response_set_cookie(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); +} + +static PHP_METHOD(swoole_http_response, status) { + zend_long http_status; + char *reason = nullptr; + size_t reason_len = 0; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_LONG(http_status) + Z_PARAM_OPTIONAL + Z_PARAM_STRING(reason, reason_len) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + if (UNEXPECTED(!ctx)) { + RETURN_FALSE; + } + + ctx->response.status = http_status; + ctx->response.reason = reason_len > 0 ? estrndup(reason, reason_len) : nullptr; + RETURN_TRUE; +} + +static PHP_METHOD(swoole_http_response, header) { + char *k; + size_t klen; + zval *zvalue; + zend_bool format = 1; + + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_STRING(k, klen) + Z_PARAM_ZVAL(zvalue) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(format) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + if (UNEXPECTED(!ctx)) { + RETURN_FALSE; + } + RETURN_BOOL(ctx->set_header(k, klen, zvalue, format)); +} + +static PHP_METHOD(swoole_http_response, trailer) { + char *k, *v; + size_t klen, vlen; + char key_buf[SW_HTTP_HEADER_KEY_SIZE]; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(k, klen) + Z_PARAM_STRING_EX(v, vlen, 1, 0) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + if (!ctx) { + RETURN_FALSE; + } + if (UNEXPECTED(klen > SW_HTTP_HEADER_KEY_SIZE - 1)) { + php_swoole_error(E_WARNING, "trailer key is too long"); + RETURN_FALSE; + } + zval *ztrailer = swoole_http_init_and_read_property( + swoole_http_response_ce, ctx->response.zobject, &ctx->response.ztrailer, ZEND_STRL("trailer")); + swoole_strlcpy(key_buf, k, sizeof(key_buf)); + swoole_strtolower(key_buf, klen); + if (UNEXPECTED(!v)) { + add_assoc_null_ex(ztrailer, key_buf, klen); + } else { + add_assoc_stringl_ex(ztrailer, key_buf, klen, v, vlen); + } + ctx->send_trailer_ = 1; + RETURN_TRUE; +} + +static PHP_METHOD(swoole_http_response, ping) { + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + if (UNEXPECTED(!ctx)) { + RETURN_FALSE; + } + if (UNEXPECTED(!ctx->http2)) { + php_swoole_fatal_error(E_WARNING, "fd[%ld] is not a HTTP2 conncetion", ctx->fd); + RETURN_FALSE; + } + SW_CHECK_RETURN(swoole_http2_server_ping(ctx)); +} + +static PHP_METHOD(swoole_http_response, goaway) { + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + if (UNEXPECTED(!ctx)) { + RETURN_FALSE; + } + if (UNEXPECTED(!ctx->http2)) { + 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; + + 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)); +} + +static PHP_METHOD(swoole_http_response, upgrade) { + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + if (UNEXPECTED(!ctx)) { + RETURN_FALSE; + } + if (UNEXPECTED(!ctx->co_socket)) { + php_swoole_fatal_error(E_WARNING, "async server dose not support protocol upgrade"); + RETURN_FALSE; + } + RETVAL_BOOL(swoole_websocket_handshake(ctx)); +} + +static PHP_METHOD(swoole_http_response, push) { + HttpContext *ctx = php_swoole_http_response_get_context(ZEND_THIS); + if (UNEXPECTED(!ctx)) { + swoole_set_last_error(SW_ERROR_SESSION_CLOSED); + RETURN_FALSE; + } + if (UNEXPECTED(!ctx->co_socket || !ctx->upgrade)) { + php_swoole_fatal_error(E_WARNING, "fd[%ld] is not a websocket conncetion", ctx->fd); + RETURN_FALSE; + } + + zval *zdata; + zend_long opcode = WebSocket::OPCODE_TEXT; + zval *zflags = nullptr; + zend_long flags = WebSocket::FLAG_FIN; + + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_ZVAL(zdata) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(opcode) + Z_PARAM_ZVAL_EX(zflags, 1, 0) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (zflags != nullptr) { + flags = zval_get_long(zflags); + } + + 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) { + RETURN_FALSE; + } + } else { + if (php_swoole_websocket_frame_pack( + http_buffer, zdata, opcode, flags & WebSocket::FLAGS_ALL, 0, ctx->websocket_compression) < 0) { + RETURN_FALSE; + } + } + RETURN_BOOL(ctx->send(ctx, http_buffer->str, http_buffer->length)); +} + +static PHP_METHOD(swoole_http_response, close) { + HttpContext *ctx = php_swoole_http_response_get_context(ZEND_THIS); + if (UNEXPECTED(!ctx)) { + swoole_set_last_error(SW_ERROR_SESSION_CLOSED); + RETURN_FALSE; + } + RETURN_BOOL(ctx->close(ctx)); +} + +static PHP_METHOD(swoole_http_response, recv) { + HttpContext *ctx = php_swoole_http_response_get_context(ZEND_THIS); + if (UNEXPECTED(!ctx)) { + swoole_set_last_error(SW_ERROR_SESSION_CLOSED); + RETURN_FALSE; + } + if (UNEXPECTED(!ctx->co_socket || !ctx->upgrade)) { + php_swoole_fatal_error(E_WARNING, "fd[%ld] is not a websocket conncetion", ctx->fd); + RETURN_FALSE; + } + + double timeout = 0; + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + Socket *sock = (Socket *) ctx->private_data; + ssize_t retval = sock->recv_packet(timeout); + String _tmp; + + if (retval < 0) { + swoole_set_last_error(sock->errCode); + RETURN_FALSE; + } else if (retval == 0) { + RETURN_EMPTY_STRING(); + } else { + _tmp.str = sock->get_read_buffer()->str; + _tmp.length = retval; + +#ifdef SW_HAVE_ZLIB + php_swoole_websocket_frame_unpack_ex(&_tmp, return_value, ctx->websocket_compression); +#else + php_swoole_websocket_frame_unpack(&_tmp, return_value); +#endif + zend_update_property_long( + swoole_websocket_frame_ce, SW_Z8_OBJ_P(return_value), ZEND_STRL("fd"), sock->get_fd()); + } +} + +static PHP_METHOD(swoole_http_response, detach) { + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + if (!ctx) { + RETURN_FALSE; + } + ctx->detached = 1; + RETURN_TRUE; +} + +static PHP_METHOD(swoole_http_response, create) { + zval *zobject = nullptr; + zval *zrequest = nullptr; + zend_long fd = -1; + Server *serv = nullptr; + Socket *sock = nullptr; + HttpContext *ctx = nullptr; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(zobject) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(fd) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + 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 { + fd = zval_get_long(zobject); + serv = sw_server(); + } + + if (serv && !serv->is_started()) { + php_swoole_fatal_error(E_WARNING, "server is not running"); + RETURN_FALSE; + } + + if (!ctx) { + 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) { + zval *zurl; + zval *zhttp_code = nullptr; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(zurl) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL_EX(zhttp_code, 1, 0) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + if (UNEXPECTED(!ctx)) { + RETURN_FALSE; + } + + // status + if (zhttp_code) { + ctx->response.status = zval_get_long(zhttp_code); + } else { + ctx->response.status = 302; + } + + zval zkey; + ZVAL_STRINGL(&zkey, "Location", 8); + sw_zend_call_method_with_2_params(ZEND_THIS, nullptr, nullptr, "header", return_value, &zkey, zurl); + zval_ptr_dtor(&zkey); + if (!Z_BVAL_P(return_value)) { + return; + } + ctx->end(nullptr, return_value); +} diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc new file mode 100644 index 00000000000..7e1eee796e3 --- /dev/null +++ b/ext-src/swoole_http_server.cc @@ -0,0 +1,455 @@ +/* + +----------------------------------------------------------------------+ + | 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_http_server.h" +#include "swoole_process_pool.h" +BEGIN_EXTERN_C() +#include "rfc1867.h" +END_EXTERN_C() + +using namespace swoole; +using swoole::coroutine::Socket; + +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 SW_THREAD_LOCAL std::queue queued_http_contexts; +static SW_THREAD_LOCAL std::unordered_map client_ips; + +static bool http_context_send_data(HttpContext *ctx, const char *data, size_t length); +static bool http_context_sendfile(HttpContext *ctx, const char *file, uint32_t l_file, off_t offset, size_t length); +static bool http_context_disconnect(HttpContext *ctx); + +static void http_server_process_request(Server *serv, zend::Callable *cb, HttpContext *ctx) { + zval args[2]; + args[0] = *ctx->request.zobject; + args[1] = *ctx->response.zobject; + if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, serv->is_enable_coroutine()))) { + php_swoole_error(E_WARNING, "%s->onRequest handler error", ZSTR_VAL(swoole_http_server_ce->name)); +#ifdef SW_HTTP_SERVICE_UNAVAILABLE_PACKET + ctx->send(ctx, SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET)); +#endif + ctx->close(ctx); + } +} + +int php_swoole_http_server_onReceive(Server *serv, RecvData *req) { + SessionId session_id = req->info.fd; + int server_fd = req->info.server_fd; + + Connection *conn = serv->get_connection_verify_no_ssl(session_id); + if (!conn) { + swoole_error_log(SW_LOG_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 && 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) { + return swoole_websocket_onMessage(serv, req); + } + + if (conn->http2_stream) { + return swoole_http2_server_onReceive(serv, conn, req); + } + + 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); + + swoole_trace_log(SW_TRACE_SERVER, + "http request from %ld with %d bytes: <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 = ctx->parse(Z_STRVAL_P(zdata), Z_STRLEN_P(zdata)); + if (ctx->parser.state == s_dead) { + ctx->send(ctx, SW_STRL(SW_HTTP_BAD_REQUEST_PACKET)); + ctx->close(ctx); + 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) { + 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()); + } + } + + 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::Callable *cb = 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; + ctx->upgrade = 1; + } + } else { + cb = php_swoole_server_get_callback(serv, server_fd, SW_SERVER_CB_onRequest); + if (cb == nullptr) { + swoole_websocket_onRequest(ctx); + goto _dtor_and_return; + } + } + 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: + zval_ptr_dtor(zrequest_object); + zval_ptr_dtor(zresponse_object); + + return SW_OK; +} + +void php_swoole_http_server_onClose(Server *serv, DataHead *info) { + client_ips.erase(info->fd); + php_swoole_server_onClose(serv, info); +} + +void php_swoole_http_server_minit(int module_number) { + SW_INIT_CLASS_ENTRY_EX(swoole_http_server, "Swoole\\Http\\Server", nullptr, nullptr, swoole_server); + SW_SET_CLASS_NOT_SERIALIZABLE(swoole_http_server); + SW_SET_CLASS_CLONEABLE(swoole_http_server, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_http_server, sw_zend_class_unset_property_deny); +} + +void php_swoole_http_server_rinit() { + // for is_uploaded_file and move_uploaded_file + if (!SG(rfc1867_uploaded_files)) { + ALLOC_HASHTABLE(SG(rfc1867_uploaded_files)); + zend_hash_init(SG(rfc1867_uploaded_files), 8, nullptr, nullptr, 0); + } +} + +void php_swoole_http_server_rshutdown() { + if (SG(rfc1867_uploaded_files)) { + destroy_uploaded_files_hash(); + SG(rfc1867_uploaded_files) = nullptr; + } + + client_ips.clear(); + while (!queued_http_contexts.empty()) { + HttpContext *ctx = queued_http_contexts.front(); + queued_http_contexts.pop(); + ctx->end_ = 1; + ctx->onAfterResponse = nullptr; + zval_ptr_dtor(ctx->request.zobject); + zval_ptr_dtor(ctx->response.zobject); + } +} + +HttpContext *swoole_http_context_new(SessionId fd) { + HttpContext *ctx = new HttpContext(); + + zval *zrequest_object = &ctx->request._zobject; + ctx->request.zobject = zrequest_object; + 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; + ZVAL_OBJ(zresponse_object, swoole_http_response_ce->create_object(swoole_http_response_ce)); + php_swoole_http_response_set_context(zresponse_object, ctx); + + 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.zheader, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADER)); + + ctx->fd = fd; + + return ctx; +} + +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 + enable_compression = serv->http_compression; + compression_level = serv->http_compression_level; + compression_min_length = serv->compression_min_length; + compression_types = serv->http_compression_types; +#endif + upload_tmp_dir = serv->upload_tmp_dir; + bind(serv); +} + +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 + enable_compression = ctx->enable_compression; + compression_level = ctx->compression_level; + compression_min_length = ctx->compression_min_length; + compression_types = ctx->compression_types; +#endif + co_socket = ctx->co_socket; + private_data = ctx->private_data; + 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 HttpContext::free() { + /* http context can only be free'd after request and response were free'd */ + if (request.zobject || response.zobject) { + return; + } + if (stream) { + return; + } + + HttpRequest *req = &request; + HttpResponse *res = &response; + if (req->path) { + efree(req->path); + } + if (Z_TYPE(req->zdata) == IS_STRING) { + zend_string_release(Z_STR(req->zdata)); + } + if (req->chunked_body) { + delete req->chunked_body; + } + if (req->h2_data_buffer) { + delete req->h2_data_buffer; + } + if (res->reason) { + efree(res->reason); + } + if (mt_parser) { + multipart_parser_free(mt_parser); + mt_parser = nullptr; + } + if (form_data_buffer) { + delete form_data_buffer; + form_data_buffer = nullptr; + } + if (write_buffer) { + delete write_buffer; + } + delete this; +} + +HttpContext *php_swoole_http_request_get_and_check_context(zval *zobject) { + HttpContext *ctx = php_swoole_http_request_get_context(zobject); + if (!ctx) { + swoole_set_last_error(SW_ERROR_HTTP_CONTEXT_UNAVAILABLE); + } + return ctx; +} + +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(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); + } + 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_disconnect(HttpContext *ctx) { + Server *serv = (Server *) ctx->private_data; + return serv->close(ctx->fd, 0); +} + +bool swoole_http_server_onBeforeRequest(HttpContext *ctx) { + ctx->onBeforeRequest = nullptr; + ctx->onAfterResponse = swoole_http_server_onAfterResponse; + Server *serv = (Server *) ctx->private_data; + if (!sw_server() || !sw_worker() || sw_worker()->is_shutdown()) { + return false; + } + + auto worker = sw_worker(); + swoole_trace("serv->gs->concurrency=%u, max_concurrency=%u", serv->gs->concurrency, serv->gs->max_concurrency); + sw_atomic_add_fetch(&serv->gs->concurrency, 1); + worker->concurrency++; + if (worker->concurrency > serv->worker_max_concurrency) { + swoole_trace_log(SW_TRACE_COROUTINE, + "exceed worker_max_concurrency[%u] limit, request[%p] queued", + serv->worker_max_concurrency, + ctx); + queued_http_contexts.push(ctx); + return false; + } + + return true; +} + +void swoole_http_server_onAfterResponse(HttpContext *ctx) { + ctx->onAfterResponse = nullptr; + Server *serv = (Server *) ctx->private_data; + if (sw_unlikely(!sw_server() || !sw_worker())) { + return; + } + + if (sw_unlikely(sw_worker()->is_shutdown())) { + while (!queued_http_contexts.empty()) { + HttpContext *ctx = queued_http_contexts.front(); + queued_http_contexts.pop(); + ctx->send(ctx, SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET)); + ctx->close(ctx); + } + return; + } + + auto worker = sw_worker(); + swoole_trace("serv->gs->concurrency=%u, max_concurrency=%u", serv->gs->concurrency, serv->gs->max_concurrency); + sw_atomic_sub_fetch(&serv->gs->concurrency, 1); + worker->concurrency--; + + if (!queued_http_contexts.empty()) { + HttpContext *ctx = queued_http_contexts.front(); + swoole_trace("[POP 1] concurrency=%u, ctx=%p, request=%p", worker->concurrency, ctx, ctx->request.zobject); + queued_http_contexts.pop(); + swoole_event_defer( + [](void *private_data) { + HttpContext *ctx = (HttpContext *) private_data; + Server *serv = (Server *) ctx->private_data; + zend::Callable *cb = (zend::Callable *) ctx->private_data_2; + swoole_trace("[POP 2] ctx=%p, request=%p", ctx, ctx->request.zobject); + http_server_process_request(serv, cb, ctx); + zval_ptr_dtor(ctx->request.zobject); + zval_ptr_dtor(ctx->response.zobject); + }, + ctx); + } +} diff --git a/ext-src/swoole_http_server_coro.cc b/ext-src/swoole_http_server_coro.cc new file mode 100644 index 00000000000..2f3a18fe546 --- /dev/null +++ b/ext-src/swoole_http_server_coro.cc @@ -0,0 +1,745 @@ +/* + +----------------------------------------------------------------------+ + | 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_http_server.h" + +#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 HttpRequest = swoole::http::Request; +using HttpResponse = swoole::http::Response; +using HttpContext = swoole::http::Context; + +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(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 http2_server_onRequest(Http2Session *session, Http2Stream *stream); + +namespace swoole { +namespace coroutine { + +class HttpServer { + public: + Socket *socket; + zend::Callable *default_handler; + std::unordered_map handlers; + bool running; + zval zclients; + + /* options */ + bool parse_cookie; + bool parse_post; + bool parse_files; +#ifdef SW_HAVE_COMPRESSION + bool compression; +#endif +#ifdef SW_HAVE_ZLIB + bool websocket_compression; +#endif + char *upload_tmp_dir; +#ifdef SW_HAVE_COMPRESSION + uint8_t compression_level; + uint32_t compression_min_length; + std::shared_ptr> compression_types = nullptr; +#endif + + HttpServer(enum swSocketType type) { + socket = new Socket(type); + default_handler = nullptr; + array_init(&zclients); + running = true; + + parse_cookie = true; + parse_post = true; + parse_files = false; +#ifdef SW_HAVE_COMPRESSION + compression = true; + compression_level = SW_Z_BEST_SPEED; + compression_min_length = SW_COMPRESSION_MIN_LENGTH_DEFAULT; +#endif +#ifdef SW_HAVE_ZLIB + websocket_compression = false; +#endif + upload_tmp_dir = sw_strdup("/tmp"); + } + + ~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; + } + + 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 = cb; + } + return true; + } + + zend::Callable *get_handler(HttpContext *ctx) { + for (auto i = handlers.begin(); i != handlers.end(); i++) { + if (i->second == default_handler) { + continue; + } + 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; + } + + 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 = 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->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_ex( + swoole_http_response_ce, SW_Z8_OBJ_P(ctx->response.zobject), SW_ZSTR_KNOWN(SW_ZEND_STR_SOCKET), zconn); + + return ctx; + } + + void recv_http2_frame(HttpContext *ctx) { + Socket *sock = (Socket *) ctx->private_data; + 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 = http2::get_frame_length; + + Http2Session session(ctx->fd); + session.default_ctx = ctx; + session.handle = http2_server_onRequest; + session.private_data = this; + session.is_coro = true; + + while (true) { + auto buffer = sock->get_read_buffer(); + ssize_t retval = sock->recv_packet(); + if (sw_unlikely(retval <= 0)) { + break; + } + swoole_http2_server_parse(&session, buffer->str); + } + + /* default_ctx does not blong to session object */ + session.default_ctx = nullptr; + + ctx->detached = 1; + zval_dtor(ctx->request.zobject); + zval_dtor(ctx->response.zobject); + } +}; +}; // namespace coroutine +}; // namespace swoole + +using swoole::coroutine::HttpServer; + +struct HttpServerObject { + HttpServer *server; + zend_object std; +}; + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_http_server_coro, __construct); +static PHP_METHOD(swoole_http_server_coro, set); +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); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_http_server_coro_methods[] = +{ + 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) { + 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 HttpServerObject *php_swoole_http_server_coro_fetch_object(zend_object *obj) { + return (HttpServerObject *) ((char *) obj - swoole_http_server_coro_handlers.offset); +} + +static sw_inline HttpServer *http_server_get_object(zend_object *obj) { + return php_swoole_http_server_coro_fetch_object(obj)->server; +} + +static inline void http_server_set_error(zval *zobject, Socket *sock) { + zend_update_property_long(swoole_http_server_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), sock->errCode); + 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(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(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(HttpContext *ctx) { + Socket *sock = (Socket *) ctx->private_data; + return sock->close(); +} + +static void php_swoole_http_server_coro_free_object(zend_object *object) { + HttpServerObject *hsc = php_swoole_http_server_coro_fetch_object(object); + if (hsc->server) { + 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", + "Co\\Http\\Server", + swoole_http_server_coro_methods); + 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, + HttpServerObject, + std); + swoole_http_server_coro_ce->ce_flags |= ZEND_ACC_FINAL; + + 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); + zend_declare_property_long(swoole_http_server_coro_ce, ZEND_STRL("port"), -1, ZEND_ACC_PUBLIC); + zend_declare_property_bool(swoole_http_server_coro_ce, ZEND_STRL("ssl"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_http_server_coro_ce, ZEND_STRL("settings"), ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_http_server_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_string(swoole_http_server_coro_ce, ZEND_STRL("errMsg"), "", ZEND_ACC_PUBLIC); +} + +static PHP_METHOD(swoole_http_server_coro, __construct) { + char *host; + size_t l_host; + zend_long port = 0; + zend_bool ssl = 0; + zend_bool reuse_port = 0; + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 4) + Z_PARAM_STRING(host, l_host) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(port) + Z_PARAM_BOOL(ssl) + Z_PARAM_BOOL(reuse_port) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + zend_update_property_stringl(swoole_http_server_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("host"), host, l_host); + zend_update_property_bool(swoole_http_server_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("ssl"), ssl); + + // check host + if (l_host == 0) { + zend_throw_exception_ex(swoole_exception_ce, EINVAL, "host is empty"); + RETURN_FALSE; + } + + HttpServerObject *hsc = php_swoole_http_server_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); + std::string host_str(host, l_host); + hsc->server = new HttpServer(swoole::network::Socket::convert_to_type(host_str)); + Socket *sock = hsc->server->socket; + + if (reuse_port) { + sock->get_socket()->set_reuse_port(); + } + + if (!sock->bind(host_str, port)) { + http_server_set_error(ZEND_THIS, sock); + zend_throw_exception_ex(swoole_exception_ce, sock->errCode, "bind(%s:%d) failed", host, (int) port); + RETURN_FALSE; + } + // check ssl + if (ssl) { +#ifndef SW_USE_OPENSSL + zend_throw_exception_ex( + swoole_exception_ce, + EPROTONOSUPPORT, + "you must configure with `--enable-openssl` to support ssl connection when compiling Swoole"); + RETURN_FALSE; +#else + /* we have to call ssl_check_context after user setProtocols */ + zval *zsettings = + sw_zend_read_and_convert_property_array(swoole_http_server_coro_ce, ZEND_THIS, ZEND_STRL("settings"), 0); + add_assoc_bool(zsettings, "open_ssl", 1); + sock->enable_ssl_encrypt(); +#endif + } + if (!sock->listen()) { + http_server_set_error(ZEND_THIS, sock); + zend_throw_exception_ex(swoole_exception_ce, sock->errCode, "listen() failed"); + RETURN_FALSE; + } + + zend_update_property_long(swoole_http_server_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("fd"), sock->get_fd()); + zend_update_property_long( + swoole_http_server_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("port"), sock->get_bind_port()); +} + +static PHP_METHOD(swoole_http_server_coro, handle) { + char *pattern; + size_t pattern_len; + zval *zfn; + + 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_ZVAL(zfn) + ZEND_PARSE_PARAMETERS_END(); + + std::string key(pattern, pattern_len); + RETURN_BOOL(hs->set_handler(key, zfn)); +} + +static PHP_METHOD(swoole_http_server_coro, set) { + zval *zset; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(zset) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (php_swoole_array_length(zset) == 0) { + RETURN_FALSE; + } else { + zval *zsettings = + sw_zend_read_and_convert_property_array(swoole_http_server_coro_ce, ZEND_THIS, ZEND_STRL("settings"), 0); + php_array_merge(Z_ARRVAL_P(zsettings), Z_ARRVAL_P(zset)); + RETURN_TRUE; + } +} + +static PHP_METHOD(swoole_http_server_coro, start) { + 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; + 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; + } + efree(func_name); + + /* check settings */ + zval *zsettings = + sw_zend_read_and_convert_property_array(swoole_http_server_coro_ce, ZEND_THIS, ZEND_STRL("settings"), 0); + php_swoole_socket_set_protocol(hs->socket, zsettings); + HashTable *vht = Z_ARRVAL_P(zsettings); + zval *ztmp; + // parse cookie header + if (php_swoole_array_get_value(vht, "http_parse_cookie", 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->parse_post = zval_is_true(ztmp); + } + // parse multipart/form-data file uploads + if (php_swoole_array_get_value(vht, "http_parse_files", 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->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) { + level = UINT8_MAX; + } else if (level < 0) { + level = 0; + } + 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 + if (php_swoole_array_get_value(vht, "websocket_compression", ztmp)) { + hs->websocket_compression = zval_is_true(ztmp); + } +#endif + // temporary directory for HTTP uploaded file. + if (php_swoole_array_get_value(vht, "upload_tmp_dir", ztmp)) { + zend::String str_v(ztmp); + if (php_swoole_create_dir(str_v.val(), str_v.len()) < 0) { + php_swoole_fatal_error(E_ERROR, "Unable to create upload_tmp_dir[%s]", str_v.val()); + return; + } + if (hs->upload_tmp_dir) { + sw_free(hs->upload_tmp_dir); + } + hs->upload_tmp_dir = str_v.dup(); + } + + 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, zcallback.ptr()); + zval_dtor(&zsocket); + if (cid < 0) { + goto _wait_1s; + } + } else { + /* + * Too many connection, wait 1s + */ + if (sock->errCode == EMFILE || sock->errCode == ENFILE) { + _wait_1s: + System::sleep(SW_ACCEPT_RETRY_TIME); + } else if (sock->errCode == ETIMEDOUT || sock->errCode == SW_ERROR_SSL_BAD_CLIENT) { + continue; + } else if (sock->errCode == ECANCELED) { + http_server_set_error(ZEND_THIS, sock); + break; + } else { + http_server_set_error(ZEND_THIS, sock); + php_swoole_fatal_error(E_WARNING, "accept failed, Error: %s[%d]", sock->errMsg, sock->errCode); + break; + } + } + } + + RETURN_TRUE; +} + +static PHP_METHOD(swoole_http_server_coro, onAccept) { + HttpServer *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); + zval *zconn; + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1) + Z_PARAM_OBJECT(zconn) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + Coroutine *co = Coroutine::get_current(); + Socket *sock = php_swoole_get_socket(zconn); + sock->set_buffer_allocator(sw_zend_string_allocator()); + String *buffer = sock->get_read_buffer(); + HttpContext *ctx = nullptr; + bool header_completed = false; + off_t header_crlf_offset = 0; + size_t total_length; + +#ifdef SW_USE_OPENSSL + if (sock->ssl_is_enable() && !sock->ssl_handshake()) { + RETURN_FALSE; + } +#endif + Z_TRY_ADDREF_P(zconn); + zend_hash_index_add(Z_ARRVAL_P(&hs->zclients), co->get_cid(), zconn); + zend::Variable remote_addr = zend::Variable(sock->get_ip()); + + while (true) { + _recv_request : { + sock->get_socket()->recv_wait = 1; + ssize_t retval = sock->recv(buffer->str + buffer->length, buffer->size - buffer->length); + if (sw_unlikely(retval <= 0)) { + break; + } + buffer->length += retval; + } + + _parse_request: + if (!ctx) { + ctx = hs->create_context(sock, zconn); + } + + if (!header_completed) { + 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; + } + + size_t header_length = header_crlf_offset + pos; + header_completed = true; + header_crlf_offset = 0; + + // 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; + } + 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 (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; + } + } + + 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); + hs->recv_http2_frame(ctx); + /* ownership of ctx has been transferred */ + ctx = nullptr; + break; + } + + zend::assign_zend_string_by_val(&ctx->request.zdata, buffer->pop(SW_BUFFER_SIZE_BIG), total_length); + + zval *zserver = ctx->request.zserver; + 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 (cb) { + if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, 0))) { + php_swoole_error(E_WARNING, "handler error"); + } + } else { + ctx->response.status = SW_HTTP_NOT_FOUND; + } + + zval_dtor(&args[0]); + zval_dtor(&args[1]); + ctx = nullptr; + + if (!hs->running || !keep_alive || php_swoole_socket_is_closed(zconn)) { + break; + } else { + header_completed = false; + if (buffer->length > 0) { + goto _parse_request; + } else { + goto _recv_request; + } + } + } + + if (ctx) { + zval_dtor(ctx->request.zobject); + zval_dtor(ctx->response.zobject); + } + zend_hash_index_del(Z_ARRVAL_P(&hs->zclients), co->get_cid()); +} + +static PHP_METHOD(swoole_http_server_coro, shutdown) { + HttpServer *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); + hs->running = false; + hs->socket->cancel(SW_EVENT_READ); + + 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); + } + } + ZEND_HASH_FOREACH_END(); +} + +static void http2_server_onRequest(Http2Session *session, Http2Stream *stream) { + HttpContext *ctx = stream->ctx; + HttpServer *hs = (HttpServer *) session->private_data; + Socket *sock = (Socket *) ctx->private_data; + zval *zserver = ctx->request.zserver; + + add_assoc_long(zserver, "request_time", time(nullptr)); + add_assoc_double(zserver, "request_time_float", microtime()); + add_assoc_long(zserver, "server_port", hs->socket->get_bind_port()); + add_assoc_long(zserver, "remote_port", sock->get_port()); + add_assoc_string(zserver, "remote_addr", (char *) sock->get_ip()); + add_assoc_string(zserver, "server_protocol", (char *) "HTTP/2"); + + zend::Callable *cb = hs->get_handler(ctx); + zval args[2] = {*ctx->request.zobject, *ctx->response.zobject}; + + 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)); + } + } else { + ctx->response.status = SW_HTTP_NOT_FOUND; + } + + zval_ptr_dtor(&args[0]); + zval_ptr_dtor(&args[1]); +} diff --git a/ext-src/swoole_lock.cc b/ext-src/swoole_lock.cc new file mode 100644 index 00000000000..fdd6806d379 --- /dev/null +++ b/ext-src/swoole_lock.cc @@ -0,0 +1,210 @@ +/* + +----------------------------------------------------------------------+ + | 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_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 + +static zend_class_entry *swoole_lock_ce; +static zend_object_handlers swoole_lock_handlers; + +struct LockObject { + Lock *lock; + zend_object std; +}; + +static sw_inline LockObject *php_swoole_lock_fetch_object(zend_object *obj) { + return (LockObject *) ((char *) obj - swoole_lock_handlers.offset); +} + +static Lock *php_swoole_lock_get_ptr(zval *zobject) { + return php_swoole_lock_fetch_object(Z_OBJ_P(zobject))->lock; +} + +static Lock *php_swoole_lock_get_and_check_ptr(zval *zobject) { + Lock *lock = php_swoole_lock_get_ptr(zobject); + if (UNEXPECTED(!lock)) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); + } + return lock; +} + +void php_swoole_lock_set_ptr(zval *zobject, Lock *ptr) { + php_swoole_lock_fetch_object(Z_OBJ_P(zobject))->lock = ptr; +} + +static void php_swoole_lock_free_object(zend_object *object) { + zend_object_std_dtor(object); +} + +static zend_object *php_swoole_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_lock_handlers; + return &lock->std; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_lock, __construct); +static PHP_METHOD(swoole_lock, __destruct); +static PHP_METHOD(swoole_lock, lock); +static PHP_METHOD(swoole_lock, lockwait); +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); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_lock_methods[] = +{ + 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", 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("MUTEX"), Lock::MUTEX); +#ifdef HAVE_RWLOCK + zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL("RWLOCK"), Lock::RW_LOCK); +#endif +#ifdef HAVE_SPINLOCK + zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL("SPINLOCK"), Lock::SPIN_LOCK); +#endif + zend_declare_property_long(swoole_lock_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); + + SW_REGISTER_LONG_CONSTANT("SWOOLE_MUTEX", Lock::MUTEX); +#ifdef HAVE_RWLOCK + SW_REGISTER_LONG_CONSTANT("SWOOLE_RWLOCK", Lock::RW_LOCK); +#endif +#ifdef HAVE_SPINLOCK + SW_REGISTER_LONG_CONSTANT("SWOOLE_SPINLOCK", Lock::SPIN_LOCK); +#endif +} + +static PHP_METHOD(swoole_lock, __construct) { + Lock *lock = php_swoole_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_long type = Lock::MUTEX; + char *filelock; + size_t filelock_len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ls", &type, &filelock, &filelock_len) == FAILURE) { + RETURN_FALSE; + } + + switch (type) { +#ifdef HAVE_SPINLOCK + case Lock::SPIN_LOCK: + lock = new SpinLock(1); + break; +#endif +#ifdef HAVE_RWLOCK + case Lock::RW_LOCK: + lock = new RWLock(1); + break; +#endif + case Lock::MUTEX: + 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; +} + +static PHP_METHOD(swoole_lock, __destruct) {} + +static PHP_METHOD(swoole_lock, lock) { + Lock *lock = php_swoole_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->lock()); +} + +static PHP_METHOD(swoole_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 = 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); + 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_lock, unlock) { + Lock *lock = php_swoole_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->unlock()); +} + +static PHP_METHOD(swoole_lock, trylock) { + Lock *lock = php_swoole_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->trylock()); +} + +static PHP_METHOD(swoole_lock, trylock_read) { + Lock *lock = php_swoole_lock_get_and_check_ptr(ZEND_THIS); + SW_LOCK_CHECK_RETURN(lock->trylock_rd()); +} + +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()); +} 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 new file mode 100644 index 00000000000..a93bb50f4a1 --- /dev/null +++ b/ext-src/swoole_process.cc @@ -0,0 +1,1179 @@ +/* + +----------------------------------------------------------------------+ + | 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_process.h" + +#include "swoole_server.h" +#include "swoole_msg_queue.h" +#include "swoole_signal.h" + +#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 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; + zend_object std; +}; + +static sw_inline ProcessObject *php_swoole_process_fetch_object(zend_object *obj) { + return (ProcessObject *) ((char *) obj - swoole_process_handlers.offset); +} + +static sw_inline ProcessObject *php_swoole_process_fetch_object(zval *zobj) { + return php_swoole_process_fetch_object(Z_OBJ_P(zobj)); +} + +Worker *php_swoole_process_get_worker(zval *zobject) { + return php_swoole_process_fetch_object(Z_OBJ_P(zobject))->worker; +} + +Worker *php_swoole_process_get_and_check_worker(zval *zobject) { + Worker *worker = php_swoole_process_get_worker(zobject); + if (UNEXPECTED(!worker)) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); + } + return worker; +} + +void php_swoole_process_set_worker(zval *zobject, Worker *worker) { + php_swoole_process_fetch_object(Z_OBJ_P(zobject))->worker = worker; +} + +static void php_swoole_process_free_object(zend_object *object) { + ProcessObject *process = php_swoole_process_fetch_object(object); + Worker *worker = process->worker; + + if (worker) { + UnixSocket *_pipe = worker->pipe_object; + if (_pipe && !worker->shared) { + delete _pipe; + } + if (worker->queue) { + delete worker->queue; + } + zend::Process *proc = (zend::Process *) worker->ptr2; + if (proc) { + delete proc; + } + delete worker; + } + + zend_object_std_dtor(object); +} + +static zend_object *php_swoole_process_create_object(zend_class_entry *ce) { + ProcessObject *process = (ProcessObject *) zend_object_alloc(sizeof(ProcessObject), ce); + zend_object_std_init(&process->std, ce); + object_properties_init(&process->std, ce); + process->std.handlers = &swoole_process_handlers; + return &process->std; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_process, __construct); +static PHP_METHOD(swoole_process, __destruct); +static PHP_METHOD(swoole_process, useQueue); +static PHP_METHOD(swoole_process, statQueue); +static PHP_METHOD(swoole_process, freeQueue); +static PHP_METHOD(swoole_process, pop); +static PHP_METHOD(swoole_process, push); +static PHP_METHOD(swoole_process, kill); +static PHP_METHOD(swoole_process, signal); +static PHP_METHOD(swoole_process, alarm); +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); +static PHP_METHOD(swoole_process, setBlocking); +static PHP_METHOD(swoole_process, setPriority); +static PHP_METHOD(swoole_process, getPriority); +static PHP_METHOD(swoole_process, start); +static PHP_METHOD(swoole_process, write); +static PHP_METHOD(swoole_process, read); +static PHP_METHOD(swoole_process, close); +static PHP_METHOD(swoole_process, exit); +static PHP_METHOD(swoole_process, exec); +static PHP_METHOD(swoole_process, exportSocket); +SW_EXTERN_C_END + +static void php_swoole_onSignal(int signo); + +// clang-format off +#define MSGQUEUE_NOWAIT (1 << 8) + +static const zend_function_entry swoole_process_methods[] = +{ + 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_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_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", 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( + swoole_process, php_swoole_process_create_object, php_swoole_process_free_object, ProcessObject, std); + + zend_declare_class_constant_long(swoole_process_ce, ZEND_STRL("IPC_NOWAIT"), MSGQUEUE_NOWAIT); + zend_declare_class_constant_long(swoole_process_ce, ZEND_STRL("PIPE_MASTER"), SW_PIPE_CLOSE_MASTER); + zend_declare_class_constant_long(swoole_process_ce, ZEND_STRL("PIPE_WORKER"), SW_PIPE_CLOSE_WORKER); + zend_declare_class_constant_long(swoole_process_ce, ZEND_STRL("PIPE_READ"), SW_PIPE_CLOSE_READ); + zend_declare_class_constant_long(swoole_process_ce, ZEND_STRL("PIPE_WRITE"), SW_PIPE_CLOSE_WRITE); + + zend_declare_property_null(swoole_process_ce, ZEND_STRL("pipe"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_process_ce, ZEND_STRL("msgQueueId"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_process_ce, ZEND_STRL("msgQueueKey"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_process_ce, ZEND_STRL("pid"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_process_ce, ZEND_STRL("id"), ZEND_ACC_PUBLIC); + + zend_declare_property_null(swoole_process_ce, ZEND_STRL("callback"), ZEND_ACC_PRIVATE); + + /** + * 31 signal constants + */ + if (!zend_hash_str_find(&module_registry, ZEND_STRL("pcntl"))) { + REGISTER_LONG_CONSTANT("SIGHUP", (zend_long) SIGHUP, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGINT", (zend_long) SIGINT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGQUIT", (zend_long) SIGQUIT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGILL", (zend_long) SIGILL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGTRAP", (zend_long) SIGTRAP, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGABRT", (zend_long) SIGABRT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGBUS", (zend_long) SIGBUS, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGFPE", (zend_long) SIGFPE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGKILL", (zend_long) SIGKILL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGUSR1", (zend_long) SIGUSR1, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGSEGV", (zend_long) SIGSEGV, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGUSR2", (zend_long) SIGUSR2, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGPIPE", (zend_long) SIGPIPE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGALRM", (zend_long) SIGALRM, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGTERM", (zend_long) SIGTERM, CONST_CS | CONST_PERSISTENT); +#ifdef SIGSTKFLT + REGISTER_LONG_CONSTANT("SIGSTKFLT", (zend_long) SIGSTKFLT, CONST_CS | CONST_PERSISTENT); +#endif + REGISTER_LONG_CONSTANT("SIGCHLD", (zend_long) SIGCHLD, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGCONT", (zend_long) SIGCONT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGSTOP", (zend_long) SIGSTOP, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGTSTP", (zend_long) SIGTSTP, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGTTIN", (zend_long) SIGTTIN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGTTOU", (zend_long) SIGTTOU, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGURG", (zend_long) SIGURG, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGXCPU", (zend_long) SIGXCPU, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGXFSZ", (zend_long) SIGXFSZ, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGVTALRM", (zend_long) SIGVTALRM, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGPROF", (zend_long) SIGPROF, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGWINCH", (zend_long) SIGWINCH, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SIGIO", (zend_long) SIGIO, CONST_CS | CONST_PERSISTENT); +#ifdef SIGPWR + REGISTER_LONG_CONSTANT("SIGPWR", (zend_long) SIGPWR, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef SIGSYS + REGISTER_LONG_CONSTANT("SIGSYS", (zend_long) SIGSYS, CONST_CS | CONST_PERSISTENT); +#endif + REGISTER_LONG_CONSTANT("SIG_IGN", (zend_long) SIG_IGN, CONST_CS | CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("PRIO_PROCESS", (zend_long) PRIO_PROCESS, CONST_CS | CONST_PERSISTENT); + 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) { + auto po = php_swoole_process_fetch_object(ZEND_THIS); + Server *server = sw_server(); + + 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)) { + zend_throw_error(NULL, "%s can only be used in PHP CLI mode", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } + + if (server && server->is_started() && server->is_master()) { + zend_throw_error(NULL, "%s can't be used in master process", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } + + if (SwooleTG.async_threads) { + zend_throw_error(NULL, "unable to create %s with async-io threads", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } + + zend::Function func; + zend_bool redirect_stdin_and_stdout = 0; + 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 + Z_PARAM_BOOL(redirect_stdin_and_stdout) + Z_PARAM_LONG(pipe_type) + Z_PARAM_BOOL(enable_coroutine) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + 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; + } + + 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_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::Process *proc = new zend::Process((enum zend::PipeType) pipe_type, enable_coroutine); + process->ptr2 = proc; + + zend_update_property( + swoole_process_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("callback"), ZEND_CALL_ARG(execute_data, 1)); + php_swoole_process_set_worker(ZEND_THIS, process); +} + +static PHP_METHOD(swoole_process, __destruct) {} + +static PHP_METHOD(swoole_process, wait) { + zend_bool blocking = 1; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &blocking) == FAILURE) { + RETURN_FALSE; + } + + 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", 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; + } +} + +static PHP_METHOD(swoole_process, useQueue) { + long msgkey = 0; + long mode = SW_MSGQUEUE_BALANCE; + long capacity = -1; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|lll", &msgkey, &mode, &capacity) == FAILURE) { + RETURN_FALSE; + } + + Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); + + if (msgkey <= 0) { + msgkey = ftok(zend_get_executed_filename(), 1); + } + + MsgQueue *queue = new MsgQueue(msgkey); + if (!queue->ready()) { + delete queue; + RETURN_FALSE; + } + if (mode & MSGQUEUE_NOWAIT) { + queue->set_blocking(false); + mode = mode & (~MSGQUEUE_NOWAIT); + } + if (capacity > 0) { + queue->set_capacity(capacity); + } + process->queue = queue; + 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; +} + +static PHP_METHOD(swoole_process, statQueue) { + Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); + if (!process->queue) { + php_swoole_fatal_error(E_WARNING, "no queue, can't get stats of the queue"); + RETURN_FALSE; + } + + size_t queue_num = -1; + size_t queue_bytes = -1; + if (process->queue->stat(&queue_num, &queue_bytes)) { + array_init(return_value); + add_assoc_long_ex(return_value, ZEND_STRL("queue_num"), queue_num); + add_assoc_long_ex(return_value, ZEND_STRL("queue_bytes"), queue_bytes); + } else { + RETURN_FALSE; + } +} + +static PHP_METHOD(swoole_process, freeQueue) { + Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); + if (process->queue && process->queue->destroy()) { + delete process->queue; + process->queue = nullptr; + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} + +static PHP_METHOD(swoole_process, kill) { + zend_long pid; + zend_long sig = SIGTERM; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &pid, &sig) == FAILURE) { + RETURN_FALSE; + } + + int ret = swoole_kill((int) pid, (int) sig); + if (ret < 0) { + if (!(sig == 0 && errno == ESRCH)) { + php_swoole_sys_error(E_WARNING, "kill(%d, %d) failed", (int) pid, (int) sig); + } + RETURN_FALSE; + } + RETURN_TRUE; +} + +static PHP_METHOD(swoole_process, signal) { + SW_MUST_BE_MAIN_THREAD(); + zend_long signo = 0; + zval *zcallback = nullptr; + zend::Callable *fci_cache = nullptr; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_LONG(signo) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL_EX(zcallback, 1, 0) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (!SWOOLE_G(cli)) { + php_swoole_fatal_error(E_ERROR, "%s::signal can only be used in CLI mode", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } + + if (signo < 0 || signo >= SW_SIGNO_MAX) { + php_swoole_fatal_error(E_WARNING, "invalid signal number [" ZEND_LONG_FMT "]", signo); + RETURN_FALSE; + } + + 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); + RETURN_FALSE; + } + + if (zcallback == nullptr) { + fci_cache = signal_fci_caches[signo]; + if (fci_cache) { +#ifdef SW_USE_THREAD_CONTEXT + swoole_event_defer([signo](void *) { swoole_signal_set(signo, nullptr); }, nullptr); +#else + swoole_signal_set(signo, nullptr); +#endif + signal_fci_caches[signo] = nullptr; + swoole_event_defer(sw_callable_free, fci_cache); + SwooleTG.signal_listener_num--; + RETURN_TRUE; + } else { + php_swoole_error(E_WARNING, "unable to find the callback of signal [" ZEND_LONG_FMT "]", signo); + RETURN_FALSE; + } + } else if (Z_TYPE_P(zcallback) == IS_LONG && Z_LVAL_P(zcallback) == (zend_long) SIG_IGN) { + handler = nullptr; + } else { + fci_cache = sw_callable_create(zcallback); + if (!fci_cache) { + RETURN_FALSE; + } + handler = php_swoole_onSignal; + } + + 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, 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_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; +} + +static PHP_METHOD(swoole_process, alarm) { + zend_long usec; + zend_long type = ITIMER_REAL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &usec, &type) == FAILURE) { + RETURN_FALSE; + } + + if (!SWOOLE_G(cli)) { + php_swoole_fatal_error(E_ERROR, "cannot use %s::alarm here", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; + } + + if (SwooleTG.timer) { + php_swoole_fatal_error(E_WARNING, "cannot use both 'timer' and 'alarm' at the same time"); + RETURN_FALSE; + } + + struct itimerval timer_set = {}; + + if (usec > 0) { + long _sec = usec / 1000000; + long _usec = usec - (_sec * 1000000); + + timer_set.it_interval.tv_sec = _sec; + timer_set.it_interval.tv_usec = _usec; + + timer_set.it_value.tv_sec = _sec; + timer_set.it_value.tv_usec = _usec; + + if (timer_set.it_value.tv_usec > 1e6) { + timer_set.it_value.tv_usec = timer_set.it_value.tv_usec - 1e6; + timer_set.it_value.tv_sec += 1; + } + } + + if (setitimer(type, &timer_set, nullptr) < 0) { + php_swoole_sys_error(E_WARNING, "setitimer() failed"); + RETURN_FALSE; + } + + RETURN_TRUE; +} + +/** + * safe signal + */ +static void php_swoole_onSignal(int signo) { + auto fci_cache = signal_fci_caches[signo]; + + if (fci_cache) { + zval argv[1]; + ZVAL_LONG(&argv[0], signo); + + if (UNEXPECTED(!zend::function::call(fci_cache->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); + } + } +} + +zend_bool php_swoole_signal_isset_handler(int signo) { + if (signo < 0 || signo >= SW_SIGNO_MAX) { + php_swoole_fatal_error(E_WARNING, "invalid signal number [%d]", signo); + return 0; + } + return signal_fci_caches[signo] != nullptr; +} + +void php_swoole_process_clean() { + for (int i = 0; i < SW_SIGNO_MAX; i++) { + auto fci_cache = signal_fci_caches[i]; + if (fci_cache) { + sw_callable_free(fci_cache); + signal_fci_caches[i] = nullptr; + } + } +#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); + auto fci_cache = sw_callable_create(zcallback); + if (!fci_cache) { + return SW_ERR; + } + + zend::Process *proc = (zend::Process *) process->ptr2; + + process->pipe_current = process->pipe_worker; + process->pid = getpid(); + + if (process->redirect_stdin) { + if (dup2(process->pipe_current->fd, STDIN_FILENO) < 0) { + php_swoole_sys_error(E_WARNING, "dup2() failed"); + } + } + + if (process->redirect_stdout) { + if (dup2(process->pipe_current->fd, STDOUT_FILENO) < 0) { + php_swoole_sys_error(E_WARNING, "dup2() failed"); + } + } + + if (process->redirect_stderr) { + if (dup2(process->pipe_current->fd, STDERR_FILENO) < 0) { + php_swoole_sys_error(E_WARNING, "dup2() failed"); + } + } + + php_swoole_process_clean(); + 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); + if (process->pipe_current) { + zend_update_property_long( + swoole_process_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("pipe"), process->pipe_current->fd); + } + // eventloop create + if (proc->enable_coroutine && php_swoole_reactor_init() < 0) { + return SW_ERR; + } + // main function + 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 + zend_bailout(); + + return SW_OK; +} + +static PHP_METHOD(swoole_process, start) { + Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); + + if (process->pid && swoole_kill(process->pid, 0) == 0) { + php_swoole_fatal_error(E_WARNING, "process has already been started"); + RETURN_FALSE; + } + + pid_t pid = swoole_fork(0); + if (pid < 0) { + php_swoole_sys_error(E_WARNING, "fork() failed"); + RETURN_FALSE; + } else if (pid > 0) { + process->pid = pid; + process->child_process = 0; + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("pid"), process->pid); + RETURN_LONG(pid); + } else { + process->child_process = 1; + SW_CHECK_RETURN(php_swoole_process_start(process, ZEND_THIS)); + } + RETURN_TRUE; +} + +static PHP_METHOD(swoole_process, read) { + long buf_size = 8192; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &buf_size) == FAILURE) { + RETURN_FALSE; + } + + Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); + + if (process->pipe_current == nullptr) { + php_swoole_fatal_error(E_WARNING, "no pipe, cannot read from pipe"); + RETURN_FALSE; + } + + 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) { + php_swoole_sys_error(E_WARNING, "read() failed"); + } + RETURN_FALSE; + } + buf->val[ret] = 0; + buf->len = ret; + RETURN_STR(buf); +} + +static PHP_METHOD(swoole_process, write) { + char *data = nullptr; + size_t data_len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &data, &data_len) == FAILURE) { + RETURN_FALSE; + } + + if (data_len < 1) { + php_swoole_fatal_error(E_WARNING, "the data to send is empty"); + RETURN_FALSE; + } + + Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); + if (process->pipe_current == nullptr) { + php_swoole_fatal_error(E_WARNING, "no pipe, cannot write into pipe"); + RETURN_FALSE; + } + + ssize_t ret; + + // async write + if (SwooleTG.reactor) { + if (process->pipe_current->nonblock) { + ret = swoole_event_write(process->pipe_current, data, (size_t) data_len); + } else { + goto _blocking_read; + } + } else { + _blocking_read: + ret = process->pipe_current->send_blocking(data, data_len); + } + + if (ret < 0) { + php_swoole_sys_error(E_WARNING, "write() failed"); + RETURN_FALSE; + } + ZVAL_LONG(return_value, ret); +} + +/** + * export Swoole\Coroutine\Socket object + */ +static PHP_METHOD(swoole_process, exportSocket) { + Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); + if (process->pipe_current == nullptr) { + php_swoole_fatal_error(E_WARNING, "no pipe, cannot export stream"); + RETURN_FALSE; + } + zend::Process *proc = (zend::Process *) process->ptr2; + if (!proc->zsocket) { + proc->zsocket = + php_swoole_dup_socket(process->pipe_current->fd, + proc->pipe_type == zend::PIPE_TYPE_STREAM ? SW_SOCK_UNIX_STREAM : SW_SOCK_UNIX_DGRAM); + if (!proc->zsocket) { + RETURN_FALSE; + } + } + GC_ADDREF(proc->zsocket); + RETURN_OBJ(proc->zsocket); +} + +static PHP_METHOD(swoole_process, push) { + char *data; + size_t length; + + struct { + long type; + char data[SW_MSGMAX]; + } message; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &data, &length) == FAILURE) { + RETURN_FALSE; + } + + if (length <= 0) { + php_swoole_fatal_error(E_WARNING, "the data to push is empty"); + RETURN_FALSE; + } else if (length >= sizeof(message.data)) { + php_swoole_fatal_error(E_WARNING, "the data to push is too big"); + RETURN_FALSE; + } + + Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); + + if (!process->queue) { + php_swoole_fatal_error(E_WARNING, "no msgqueue, cannot use push()"); + RETURN_FALSE; + } + + message.type = process->id + 1; + memcpy(message.data, data, length); + + if (!process->queue->push((QueueNode *) &message, length)) { + RETURN_FALSE; + } + RETURN_TRUE; +} + +static PHP_METHOD(swoole_process, pop) { + long maxsize = SW_MSGMAX; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &maxsize) == FAILURE) { + RETURN_FALSE; + } + + if (maxsize > SW_MSGMAX || maxsize <= 0) { + maxsize = SW_MSGMAX; + } + + Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); + if (!process->queue) { + php_swoole_fatal_error(E_WARNING, "no msgqueue, cannot use pop()"); + RETURN_FALSE; + } + + struct { + long type; + char data[SW_MSGMAX]; + } message; + + if (process->msgqueue_mode == SW_MSGQUEUE_BALANCE) { + message.type = 0; + } else { + message.type = process->id + 1; + } + + ssize_t n = process->queue->pop((QueueNode *) &message, maxsize); + if (n < 0) { + RETURN_FALSE; + } + RETURN_STRINGL(message.data, n); +} + +static PHP_METHOD(swoole_process, exec) { + char *execfile = nullptr; + size_t execfile_len = 0; + zval *args; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &execfile, &execfile_len, &args) == FAILURE) { + RETURN_FALSE; + } + + if (execfile_len < 1) { + php_swoole_fatal_error(E_WARNING, "exec file name is empty"); + RETURN_FALSE; + } + + int exec_argc = php_swoole_array_length(args); + char **exec_args = (char **) emalloc(sizeof(char *) * (exec_argc + 2)); + + zval *value = nullptr; + exec_args[0] = sw_strdup(execfile); + int i = 1; + + SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(args), value) + convert_to_string(value); + Z_TRY_ADDREF_P(value); + exec_args[i] = Z_STRVAL_P(value); + i++; + SW_HASHTABLE_FOREACH_END(); + exec_args[i] = nullptr; + + if (execv(execfile, exec_args) < 0) { + php_swoole_sys_error(E_WARNING, "execv(%s) failed", execfile); + RETURN_FALSE; + } else { + RETURN_TRUE; + } +} + +static PHP_METHOD(swoole_process, daemon) { + zend_bool nochdir = 1; + zend_bool noclose = 1; + zval *zpipes = nullptr; + + ZEND_PARSE_PARAMETERS_START(0, 3) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(nochdir) + Z_PARAM_BOOL(noclose) + Z_PARAM_ARRAY(zpipes) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + zval *elem; + int fd = 0; + + if (zpipes) { + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zpipes), elem) { + if (!ZVAL_IS_NULL(elem)) { + int new_fd = php_swoole_convert_to_fd(elem); + if (new_fd >= 0) { + if (dup2(new_fd, fd) < 0) { + swoole_sys_warning("dup2(%d, %d) failed", new_fd, fd); + } + } + } + if (fd++ == 2) { + break; + } + } + ZEND_HASH_FOREACH_END(); + } + + RETURN_BOOL(swoole_daemon(nochdir, noclose) == 0); +} + +#ifdef HAVE_CPU_AFFINITY +bool php_swoole_array_to_cpu_set(zval *array, cpu_set_t *cpu_set) { + if (php_swoole_array_length(array) == 0) { + 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; + } + + zval *value = nullptr; + 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; + } + 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) { + long ret_code = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &ret_code) == FAILURE) { + RETURN_FALSE; + } + + Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); + + if (getpid() != process->pid) { + php_swoole_fatal_error(E_WARNING, "not current process"); + RETURN_FALSE; + } + + if (ret_code < 0 || ret_code > 255) { + php_swoole_fatal_error(E_WARNING, "exit ret_code range is [>0 and <255] "); + ret_code = 1; + } + + if (swoole_event_is_available()) { + swoole_event_free(); + } + + exit(ret_code); +} + +static PHP_METHOD(swoole_process, close) { + long which = 0; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &which) == FAILURE) { + RETURN_FALSE; + } + + Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); + if (process->pipe_current == nullptr) { + php_swoole_fatal_error(E_WARNING, "no pipe, cannot close the pipe"); + RETURN_FALSE; + } + + if (process->pipe_object == nullptr) { + php_swoole_fatal_error(E_WARNING, "cannot close the pipe"); + RETURN_FALSE; + } + + int ret; + if (which == SW_PIPE_CLOSE_READ) { + ret = process->pipe_current->shutdown(SHUT_RD); + } else if (which == SW_PIPE_CLOSE_WRITE) { + ret = process->pipe_current->shutdown(SHUT_WR); + } else { + ret = process->pipe_object->close(which); + } + if (ret < 0) { + php_swoole_sys_error(E_WARNING, "close() failed"); + RETURN_FALSE; + } + if (which == 0) { + delete process->pipe_object; + process->pipe_object = nullptr; + process->pipe_current = nullptr; + } + RETURN_TRUE; +} + +static PHP_METHOD(swoole_process, set) { + zval *zset = nullptr; + HashTable *vht = nullptr; + zval *ztmp; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(zset) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + vht = Z_ARRVAL_P(zset); + + Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); + zend::Process *proc = (zend::Process *) process->ptr2; + + if (php_swoole_array_get_value(vht, "enable_coroutine", ztmp)) { + proc->enable_coroutine = zval_is_true(ztmp); + } +} + +static PHP_METHOD(swoole_process, setTimeout) { + double seconds; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &seconds) == FAILURE) { + RETURN_FALSE; + } + + Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); + if (process->pipe_current == nullptr) { + php_swoole_fatal_error(E_WARNING, "no pipe, cannot setTimeout the pipe"); + RETURN_FALSE; + } + RETURN_BOOL(process->pipe_current->set_timeout(seconds)); +} + +static PHP_METHOD(swoole_process, setBlocking) { + zend_bool blocking; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "b", &blocking) == FAILURE) { + RETURN_FALSE; + } + + Worker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); + if (process->pipe_current == nullptr) { + php_swoole_fatal_error(E_WARNING, "no pipe, cannot setBlocking the pipe"); + RETURN_FALSE; + } + if (blocking) { + process->pipe_current->set_block(); + } else { + process->pipe_current->set_nonblock(); + } +} + +#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, 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); + + 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, 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); + + 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 new file mode 100644 index 00000000000..fcf02d3a94c --- /dev/null +++ b/ext-src/swoole_process_pool.cc @@ -0,0 +1,748 @@ +/* + +----------------------------------------------------------------------+ + | 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_process.h" + +#include "swoole_server.h" +#include "swoole_signal.h" + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_process_pool_arginfo.h" +END_EXTERN_C() + +using namespace swoole; + +static zend_class_entry *swoole_process_pool_ce; +static zend_object_handlers swoole_process_pool_handlers; +static ProcessPool *current_pool = nullptr; +static Worker *current_worker = nullptr; + +struct ProcessPoolObject { + ProcessPool *pool; + zend::Callable *onStart; + zend::Callable *onShutdown; + zend::Callable *onWorkerStart; + zend::Callable *onWorkerStop; + zend::Callable *onWorkerExit; + zend::Callable *onMessage; + zend_bool enable_coroutine; + zend_bool enable_message_bus; + zend_object std; +}; + +static void process_pool_signal_handler(int sig); + +static sw_inline ProcessPoolObject *process_pool_fetch_object(zend_object *obj) { + return (ProcessPoolObject *) ((char *) obj - swoole_process_pool_handlers.offset); +} + +static sw_inline ProcessPoolObject *process_pool_fetch_object(zval *zobject) { + return process_pool_fetch_object(Z_OBJ_P(zobject)); +} + +static sw_inline ProcessPool *process_pool_get_pool(zval *zobject) { + return process_pool_fetch_object(Z_OBJ_P(zobject))->pool; +} + +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 pool; +} + +static void process_pool_free_object(zend_object *object) { + ProcessPoolObject *pp = process_pool_fetch_object(object); + + ProcessPool *pool = pp->pool; + if (pool) { + efree(pool->ptr); + pool->destroy(); + efree(pool); + } + + 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 *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 +static PHP_METHOD(swoole_process_pool, __construct); +static PHP_METHOD(swoole_process_pool, __destruct); +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 +static const zend_function_entry swoole_process_pool_methods[] = +{ + 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", 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, 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 process_pool_onWorkerStart(ProcessPool *pool, Worker *worker) { + zval *zobject = (zval *) pool->ptr; + ProcessPoolObject *pp = process_pool_fetch_object(zobject); + php_swoole_process_clean(); + + 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)); + } + } + + 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; + 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); + } + } + 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 process_pool_onWorkerStop(ProcessPool *pool, Worker *worker) { + zval *zobject = (zval *) pool->ptr; + ProcessPoolObject *pp = process_pool_fetch_object(zobject); + zval args[2]; + + 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->onWorkerStop == nullptr) { + return; + } + + 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 process_pool_onWorkerExit(ProcessPool *pool, Worker *worker) { + zval *zobject = (zval *) pool->ptr; + ProcessPoolObject *pp = process_pool_fetch_object(zobject); + zval args[2]; + + 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); + + 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 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->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; + zend_long ipc_type = SW_IPC_NONE; + zend_long msgq_key = 0; + zend_bool enable_coroutine = 0; + + // only cli env + if (!SWOOLE_G(cli)) { + 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()) { + 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, "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; + zend_throw_error(NULL, "the parameter $ipc_type must be SWOOLE_IPC_UNIXSOCK when enable coroutine"); + RETURN_FALSE; + } + + ProcessPool *pool = (ProcessPool *) emalloc(sizeof(*pool)); + *pool = {}; + if (pool->create(worker_num, (key_t) msgq_key, (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; + + ProcessPoolObject *pp = process_pool_fetch_object(ZEND_THIS); + + pp->enable_coroutine = enable_coroutine; + pp->pool = pool; +} + +static PHP_METHOD(swoole_process_pool, set) { + zval *zset = nullptr; + HashTable *vht = nullptr; + zval *ztmp; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(zset) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + vht = Z_ARRVAL_P(zset); + + 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)) { + 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; + + 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"); + RETURN_FALSE; + } + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 2, 2) + Z_PARAM_STRING(name, l_name) + Z_PARAM_ZVAL(zfn); + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + ProcessPoolObject *pp = process_pool_fetch_object(ZEND_THIS); + + if (SW_STRCASEEQ(name, l_name, "WorkerStart")) { + if (pp->onWorkerStart) { + sw_callable_free(pp->onWorkerStart); + } + pp->onWorkerStart = sw_callable_create(zfn); + } else if (SW_STRCASEEQ(name, l_name, "Message")) { + if (pool->ipc_mode == SW_IPC_NONE) { + zend_throw_exception( + swoole_exception_ce, "cannot set `onMessage` event with ipc_type=0", SW_ERROR_INVALID_PARAMS); + RETURN_FALSE; + } + if (pp->onMessage) { + sw_callable_free(pp->onMessage); + } + pp->onMessage = sw_callable_create(zfn); + } else if (SW_STRCASEEQ(name, l_name, "WorkerStop")) { + if (pp->onWorkerStop) { + 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->onWorkerExit = sw_callable_create(zfn); + } else if (SW_STRCASEEQ(name, l_name, "Start")) { + if (pp->onStart) { + sw_callable_free(pp->onStart); + } + pp->onStart = sw_callable_create(zfn); + } else if (SW_STRCASEEQ(name, l_name, "Shutdown")) { + if (pp->onShutdown) { + sw_callable_free(pp->onShutdown); + } + pp->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) { + char *host; + size_t l_host; + zend_long port = 0; + zend_long backlog = 2048; + + 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"); + RETURN_FALSE; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ll", &host, &l_host, &port, &backlog) == FAILURE) { + RETURN_FALSE; + } + + if (pool->ipc_mode != SW_IPC_SOCKET) { + php_swoole_fatal_error(E_WARNING, "unsupported ipc type[%d]", pool->ipc_mode); + RETURN_FALSE; + } + + int ret; + // unix socket + if (SW_STR_ISTARTS_WITH(host, l_host, "unix:/")) { + ret = pool->listen(host + 5, backlog); + } else { + ret = pool->listen(host, port, backlog); + } + pool->stream_info_->socket->set_fd_option(0, 1); + + SW_CHECK_RETURN(ret); +} + +static PHP_METHOD(swoole_process_pool, write) { + char *data; + size_t length; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(data, length) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + 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; + } + if (length == 0) { + RETURN_FALSE; + } + SW_CHECK_RETURN(pool->response(data, length)); +} + +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; + } + + char *message; + size_t l_message; + zend_long worker_id; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(message, l_message) + Z_PARAM_LONG(worker_id) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + Worker *worker = pool->get_worker(worker_id); + if (pool->message_bus) { + SendData _task{}; + _task.info.reactor_id = current_worker ? current_worker->pid : -1; + _task.info.len = l_message; + _task.data = message; + RETURN_BOOL(pool->message_bus->write(worker->pipe_master, &_task)); + } else { + RETURN_BOOL(worker->pipe_master->send_async(message, l_message)); + } +} + +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; + + // 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 (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 { + 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; + } + } + + 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; + + 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(); + pool->shutdown(); + + current_pool = nullptr; + + for (auto iter = ori_handlers.begin(); iter != ori_handlers.end(); iter++) { + swoole_signal_set(iter->first, iter->second); + } +} + +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; + + if (current_pool == nullptr) { + RETURN_FALSE; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &worker_id) == FAILURE) { + RETURN_FALSE; + } + + if (worker_id >= current_pool->worker_num) { + php_swoole_error(E_WARNING, "invalid worker_id[%ld]", worker_id); + RETURN_FALSE; + } else if (worker_id < 0) { + worker_id = swoole_get_process_id(); + } + + zval *zworkers = + sw_zend_read_and_convert_property_array(swoole_process_pool_ce, ZEND_THIS, ZEND_STRL("workers"), 0); + zval *zprocess = zend_hash_index_find(Z_ARRVAL_P(zworkers), worker_id); + zval zobject; + + if (zprocess == nullptr || ZVAL_IS_NULL(zprocess)) { + zprocess = &zobject; + /** + * Separation from shared memory + */ + Worker *worker = (Worker *) emalloc(sizeof(Worker)); + *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"), 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 == swoole_get_process_id()) { + worker->pipe_current = worker->pipe_worker; + } else { + worker->pipe_current = worker->pipe_master; + } + /** + * Forbidden to close pipe in the php layer + */ + worker->pipe_object = nullptr; + 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); + 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) { + 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_server.cc b/ext-src/swoole_redis_server.cc new file mode 100644 index 00000000000..7ecaa79b765 --- /dev/null +++ b/ext-src/swoole_redis_server.cc @@ -0,0 +1,377 @@ +/* + +----------------------------------------------------------------------+ + | 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_server.h" +#include "swoole_redis.h" + +#include +#include + +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; + +namespace Redis = swoole::redis; + +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, setHandler); +static PHP_METHOD(swoole_redis_server, getHandler); +static PHP_METHOD(swoole_redis_server, format); +SW_EXTERN_C_END + +// clang-format off +const zend_function_entry swoole_redis_server_methods[] = +{ + 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", 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"), 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_callable_free(i->second); + } + redis_handlers.clear(); +} + +int php_swoole_redis_server_onReceive(Server *serv, RecvData *req) { + int fd = req->info.fd; + Connection *conn = serv->get_connection_by_session_id(fd); + if (!conn) { + swoole_warning("connection[%d] is closed", fd); + return SW_ERR; + } + + ListenPort *port = serv->get_port_by_fd(conn->fd); + // other server port + if (!port->open_redis_protocol) { + return php_swoole_server_onReceive(serv, req); + } + + zval zdata; + php_swoole_get_recv_data(serv, &zdata, req); + const char *p = Z_STRVAL(zdata); + const char *pe = p + Z_STRLEN(zdata); + int ret; + int length = 0; + + zval zparams{}; + array_init(&zparams); + + int state = Redis::STATE_RECEIVE_TOTAL_LINE; + int add_param = 0; + const char *command = nullptr; + int command_len = 0; + + do { + switch (state) { + case Redis::STATE_RECEIVE_TOTAL_LINE: + if (*p == '*' && (p = Redis::get_number(p, &ret))) { + state = Redis::STATE_RECEIVE_LENGTH; + break; + } + /* no break */ + + 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 = Redis::STATE_RECEIVE_STRING; + break; + } + // integer + else if (*p == ':' && (p = Redis::get_number(p, &ret))) { + add_next_index_long(&zparams, ret); + break; + } + /* no break */ + + case Redis::STATE_RECEIVE_STRING: + if (add_param == 0) { + command = p; + command_len = length; + add_param = 1; + } else { + add_next_index_stringl(&zparams, p, length); + } + p += length + SW_CRLF_LEN; + state = Redis::STATE_RECEIVE_LENGTH; + break; + + default: + break; + } + } while (p < pe); + + if (command_len >= SW_REDIS_MAX_COMMAND_SIZE) { + php_swoole_error(E_WARNING, "command [%.8s...](length=%d) is too long", command, command_len); + serv->close(fd, false); + return SW_OK; + } + + 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()) { + char err_msg[256]; + length = sw_snprintf(err_msg, sizeof(err_msg), "-ERR unknown command '%.*s'\r\n", command_len, command); + return serv->send(fd, err_msg, length) ? SW_OK : SW_ERR; + } + + 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->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), + command_len, + command); + } + + if (Z_TYPE_P(&retval) == IS_STRING) { + serv->send(fd, Z_STRVAL_P(&retval), Z_STRLEN_P(&retval)); + } + zval_ptr_dtor(&retval); + zval_ptr_dtor(&zdata); + zval_ptr_dtor(&zparams); + + return SW_OK; +} + +static PHP_METHOD(swoole_redis_server, setHandler) { + char *command; + size_t command_len; + zval *zcallback; + + 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; + } + + auto fci_cache = sw_callable_create(zcallback); + if (!fci_cache) { + return; + } + + 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_callable_free(i->second); + } + + redis_handlers[key] = fci_cache; + + RETURN_TRUE; +} + +static PHP_METHOD(swoole_redis_server, getHandler) { + char *command; + size_t command_len; + + 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 void redis_response_format_array_item(String *buf, zval *item) { + switch (Z_TYPE_P(item)) { + case IS_LONG: + case IS_FALSE: + case IS_TRUE: + redis_response_format(buf, Redis::REPLY_INT, item); + break; + case IS_ARRAY: + if (zend_array_is_list(Z_ARRVAL_P(item))) { + redis_response_format(buf, Redis::REPLY_SET, item); + } else { + redis_response_format(buf, Redis::REPLY_MAP, item); + } + break; + default: + redis_response_format(buf, Redis::REPLY_STRING, item); + break; + } +} + +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); + SW_STRING_FORMAT(buf, "%c%.*s\r\n", flag, (int) str_value.len(), str_value.val()); + } else { + SW_STRING_FORMAT(buf, "%c%s\r\n", flag, default_message); + } + } else if (type == Redis::REPLY_INT) { + if (!value) { + goto _no_value; + } + 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; + } + 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; + } + 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"); + } + SW_STRING_FORMAT(buf, "*%d\r\n", zend_hash_num_elements(Z_ARRVAL_P(value))); + + zval *item; + 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"); + } + SW_STRING_FORMAT(buf, "*%d\r\n", 2 * zend_hash_num_elements(Z_ARRVAL_P(value))); + + zend_string *key; + zend_ulong num_key; + zval *item; + 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_HASH_FOREACH_END(); + } else { + 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 new file mode 100644 index 00000000000..8bc3dd332eb --- /dev/null +++ b/ext-src/swoole_runtime.cc @@ -0,0 +1,2394 @@ +/* + +----------------------------------------------------------------------+ + | 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 "swoole_socket.h" +#include "swoole_util.h" + +#include "thirdparty/php/standard/proc_open.h" + +#ifdef SW_USE_CURL +#include "swoole_curl_interface.h" +#endif + +#include + +BEGIN_EXTERN_C() +#include "stubs/php_swoole_runtime_arginfo.h" + +#ifdef SW_USE_PGSQL +extern void swoole_pgsql_set_blocking(bool blocking); +#endif + +#ifdef SW_USE_ODBC +extern void swoole_odbc_set_blocking(bool blocking); +#endif + +#ifdef SW_USE_ORACLE +extern void swoole_oracle_set_blocking(bool blocking); +#endif + +#ifdef SW_USE_SQLITE +extern void swoole_sqlite_set_blocking(bool blocking); +#endif +END_EXTERN_C() + +/* openssl */ +#ifndef OPENSSL_NO_ECDH +#define HAVE_ECDH 1 +#endif +#ifndef OPENSSL_NO_TLSEXT +#define HAVE_TLS_SNI 1 +#if OPENSSL_VERSION_NUMBER >= 0x10002000L +#define HAVE_TLS_ALPN 1 +#endif +#endif +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) +#define HAVE_SEC_LEVEL 1 +#endif + +using swoole::Coroutine; +using swoole::PHPCoroutine; +using swoole::coroutine::PollSocket; +using swoole::coroutine::Socket; +using swoole::coroutine::System; + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_runtime, enableCoroutine); +static PHP_METHOD(swoole_runtime, getHookFlags); +static PHP_METHOD(swoole_runtime, setHookFlags); +static PHP_FUNCTION(swoole_sleep); +static PHP_FUNCTION(swoole_usleep); +static PHP_FUNCTION(swoole_time_nanosleep); +static PHP_FUNCTION(swoole_time_sleep_until); +static PHP_FUNCTION(swoole_stream_select); +static PHP_FUNCTION(swoole_stream_socket_pair); +static PHP_FUNCTION(swoole_user_func_handler); +#if defined(HAVE_PUTENV) && defined(SW_THREAD) +static PHP_FUNCTION(swoole_putenv); +#endif +#if PHP_VERSION_ID >= 80400 +extern PHP_FUNCTION(swoole_exit); +#endif +SW_EXTERN_C_END + +static void inherit_class(const char *child_name, size_t child_length, const char *parent_name, size_t parent_length); +static void detach_parent_class(const char *child_name); +static void clear_class_entries(); +static int socket_set_option(php_stream *stream, int option, int value, void *ptrparam); +static php_stream_size_t socket_read(php_stream *stream, char *buf, size_t count); +static php_stream_size_t socket_write(php_stream *stream, const char *buf, size_t count); +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 +static zend_class_entry *swoole_runtime_ce; + +static php_stream_ops socket_ops { + socket_write, + socket_read, + socket_close, + socket_flush, + "socket/coroutine", + nullptr, /* seek */ + socket_cast, + socket_stat, + socket_set_option, +}; + +struct NetStream { + php_netstream_data_t stream; + std::shared_ptr socket; + bool blocking; +}; + +static struct { + php_stream_transport_factory tcp; + php_stream_transport_factory udp; + php_stream_transport_factory _unix; + php_stream_transport_factory udg; + php_stream_transport_factory ssl; + php_stream_transport_factory tls; +} ori_factory = { + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static std::vector unsafe_functions { + "pcntl_fork", + "pcntl_rfork", + "pcntl_wait", + "pcntl_waitpid", + "pcntl_sigtimedwait", + "pcntl_sigwaitinfo", +}; + +#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 unhook_func(const char *name, size_t l_name); + +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)) + +#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" +#include "thirdparty/php/streams/plain_wrapper.c" +SW_EXTERN_C_END + +void php_swoole_runtime_minit(int module_number) { + 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); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_UDP", PHPCoroutine::HOOK_UDP); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_UNIX", PHPCoroutine::HOOK_UNIX); + SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_UDG", PHPCoroutine::HOOK_UDG); + 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); + // 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_minit(module_number); +#endif + swoole_proc_open_init(module_number); +} + +struct real_func { + zend_function *function; + zif_handler ori_handler; + zend_internal_arg_info *ori_arg_info; + zend_internal_arg_info *arg_info_copy; + uint32_t ori_fn_flags; + uint32_t ori_num_args; + zend::Callable *fci_cache; + zval name; +}; + +void php_swoole_runtime_rinit() { + tmp_function_table = (zend_array *) emalloc(sizeof(zend_array)); + zend_hash_init(tmp_function_table, 8, nullptr, nullptr, 0); + +#if defined(HAVE_PUTENV) && defined(SW_THREAD) + /** + * There are issues with the implementation of putenv in PHP, + * which can lead to memory invalid read in multi-thread environment. + */ + SW_HOOK_FUNC(putenv); +#endif + + if (!sw_is_main_thread()) { + return; + } + +#if PHP_VERSION_ID >= 80400 + SW_HOOK_FUNC(exit); +#endif + + HashTable *xport_hash = php_stream_xport_get_hash(); + ori_factory.tcp = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("tcp")); + ori_factory.udp = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("udp")); + ori_factory._unix = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("unix")); + ori_factory.udg = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("udg")); + ori_factory.ssl = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("ssl")); + ori_factory.tls = (php_stream_transport_factory) zend_hash_str_find_ptr(xport_hash, ZEND_STRL("tls")); + + memcpy((void *) &ori_php_plain_files_wrapper, &php_plain_files_wrapper, sizeof(php_plain_files_wrapper)); + memcpy((void *) &ori_php_stream_stdio_ops, &php_stream_stdio_ops, sizeof(php_stream_stdio_ops)); +} + +void php_swoole_runtime_rshutdown() { + if (sw_is_main_thread()) { + PHPCoroutine::disable_hook(); + ori_func_handlers.clear(); + ori_func_arg_infos.clear(); + } + + void *ptr; + ZEND_HASH_FOREACH_PTR(tmp_function_table, ptr) { + real_func *rf = reinterpret_cast(ptr); + /** + * php library function + */ + if (rf->fci_cache) { + zval_dtor(&rf->name); + 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; + } + 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_mshutdown(); +#endif +} + +static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *portno, int get_err, zend_string **err) { + char *colon; + char *host = nullptr; + char *p; + + if (*(str) == '[' && str_len > 1) { + /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */ + p = (char *) memchr(str + 1, ']', str_len - 2); + if (!p || *(p + 1) != ':') { + if (get_err) { + *err = strpprintf(0, "Failed to parse IPv6 address \"%s\"", str); + } + return nullptr; + } + *portno = atoi(p + 2); + return estrndup(str + 1, p - str - 1); + } + if (str_len) { + colon = (char *) memchr(str, ':', str_len - 1); + } else { + colon = nullptr; + } + if (colon) { + *portno = atoi(colon + 1); + host = estrndup(str, colon - str); + } else { + if (get_err) { + *err = strpprintf(0, "Failed to parse address \"%s\"", str); + } + return nullptr; + } + + return host; +} + +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 = (NetStream *) stream->abstract; + if (UNEXPECTED(!abstract || !abstract->socket)) { + goto _exit; + } + sock = abstract->socket; + + if (abstract->blocking) { + didwrite = sock->send_all(buf, count); + } else { + didwrite = sock->get_socket()->send(buf, count, 0); + sock->set_err(errno); + } + + 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 */ + didwrite = -1; + abstract->stream.timeout_event = (sock->errCode == ETIMEDOUT); + php_error_docref(NULL, + E_NOTICE, + "Send of " ZEND_LONG_FMT " bytes failed with errno=%d %s", + (zend_long) count, + sock->errCode, + sock->errMsg); + } else { + php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), didwrite, 0); + } + + if (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; + } + +_exit: + return didwrite; +} + +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 = (NetStream *) stream->abstract; + if (UNEXPECTED(!abstract || !abstract->socket)) { + goto _exit; + } + sock = abstract->socket; + + if (abstract->blocking) { + nr_bytes = sock->recv(buf, count); + } else { + nr_bytes = sock->get_socket()->recv(buf, count, 0); + sock->set_err(errno); + } + + if (nr_bytes > 0) { + php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), nr_bytes, 0); + } + + if (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; + } + +_exit: + return nr_bytes; +} + +static int socket_flush(php_stream *stream) { + return 0; +} + +static int socket_close(php_stream *stream, int close_handle) { + NetStream *abstract = (NetStream *) stream->abstract; + if (UNEXPECTED(!abstract)) { + return FAILURE; + } + /** set it null immediately */ + stream->abstract = nullptr; + /** + * 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. + */ + if (abstract->socket) { + abstract->socket->close(); + } + delete abstract; + return SUCCESS; +} + +enum { + STREAM_XPORT_OP_BIND, + STREAM_XPORT_OP_CONNECT, + STREAM_XPORT_OP_LISTEN, + STREAM_XPORT_OP_ACCEPT, + STREAM_XPORT_OP_CONNECT_ASYNC, + STREAM_XPORT_OP_GET_NAME, + STREAM_XPORT_OP_GET_PEER_NAME, + STREAM_XPORT_OP_RECV, + STREAM_XPORT_OP_SEND, + STREAM_XPORT_OP_SHUTDOWN, +}; + +enum { STREAM_XPORT_CRYPTO_OP_SETUP, STREAM_XPORT_CRYPTO_OP_ENABLE }; + +static int socket_cast(php_stream *stream, int castas, void **ret) { + 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) { + *(FILE **) ret = fdopen(sock->get_fd(), stream->mode); + if (*ret) { + return SUCCESS; + } + return FAILURE; + } + return SUCCESS; + case PHP_STREAM_AS_FD_FOR_SELECT: + case PHP_STREAM_AS_FD: + case PHP_STREAM_AS_SOCKETD: + if (ret) *(php_socket_t *) ret = sock->get_fd(); + return SUCCESS; + default: + return FAILURE; + } +} + +static int socket_stat(php_stream *stream, php_stream_statbuf *ssb) { + NetStream *abstract = (NetStream *) stream->abstract; + if (UNEXPECTED(!abstract)) { + return FAILURE; + } + if (UNEXPECTED(!abstract->socket)) { + return FAILURE; + } + return zend_fstat(abstract->socket->get_fd(), &ssb->sb); +} + +static inline int socket_connect(php_stream *stream, Socket *sock, php_stream_xport_param *xparam) { + char *host = nullptr, *bindto = nullptr; + int portno = 0, bindport = 0; + int ret = 0; + zval *tmpzval = nullptr; + char *ip_address = nullptr; + + if (UNEXPECTED(sock->get_fd() < 0)) { + return FAILURE; + } + + if (sock->get_socket()->is_inet()) { + ip_address = parse_ip_address_ex( + xparam->inputs.name, xparam->inputs.namelen, &portno, xparam->want_errortext, &xparam->outputs.error_text); + host = ip_address; + if (sock->get_sock_type() == SOCK_STREAM) { + sock->get_socket()->set_tcp_nodelay(); + } + } else { + host = xparam->inputs.name; + } + if (host == nullptr) { + return FAILURE; + } + ON_SCOPE_EXIT { + if (ip_address) { + efree(ip_address); + } + }; + if (PHP_STREAM_CONTEXT(stream) && + (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "bindto")) != nullptr) { + if (Z_TYPE_P(tmpzval) != IS_STRING) { + if (xparam->want_errortext) { + xparam->outputs.error_text = strpprintf(0, "local_addr context option is not a string."); + } + return FAILURE; + } + bindto = parse_ip_address_ex( + Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval), &bindport, xparam->want_errortext, &xparam->outputs.error_text); + if (bindto == nullptr) { + return FAILURE; + } + ON_SCOPE_EXIT { + if (bindto) { + efree(bindto); + } + }; + if (!sock->bind(bindto, bindport)) { + return FAILURE; + } + } + + if (xparam->inputs.timeout) { + sock->set_timeout(xparam->inputs.timeout, Socket::TIMEOUT_CONNECT); + } + if (sock->connect(host, portno) == false) { + xparam->outputs.error_code = sock->errCode; + if (sock->errMsg) { + xparam->outputs.error_text = zend_string_init(sock->errMsg, strlen(sock->errMsg), 0); + } + ret = -1; + } + return ret; +} + +static inline int socket_bind(php_stream *stream, Socket *sock, php_stream_xport_param *xparam STREAMS_DC) { + char *host = nullptr; + int portno = 0; + char *ip_address = nullptr; + + if (sock->get_socket()->is_inet()) { + ip_address = parse_ip_address_ex( + xparam->inputs.name, xparam->inputs.namelen, &portno, xparam->want_errortext, &xparam->outputs.error_text); + host = ip_address; + } else { + host = xparam->inputs.name; + } + if (host == nullptr) { + sock->set_err(EINVAL); + return -1; + } + int ret = sock->bind(host, portno) ? 0 : -1; + if (ip_address) { + efree(ip_address); + } + return ret; +} + +static inline int socket_accept(php_stream *stream, Socket *sock, php_stream_xport_param *xparam STREAMS_DC) { + int tcp_nodelay = 0; + zval *tmpzval = nullptr; + + xparam->outputs.client = nullptr; + + if ((nullptr != PHP_STREAM_CONTEXT(stream)) && + (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != nullptr && + zval_is_true(tmpzval)) { + tcp_nodelay = 1; + } + + zend_string **textaddr = xparam->want_textaddr ? &xparam->outputs.textaddr : nullptr; + struct sockaddr **addr = xparam->want_addr ? &xparam->outputs.addr : nullptr; + socklen_t *addrlen = xparam->want_addr ? &xparam->outputs.addrlen : nullptr; + + struct timeval *timeout = xparam->inputs.timeout; + zend_string **error_string = xparam->want_errortext ? &xparam->outputs.error_text : nullptr; + int *error_code = &xparam->outputs.error_code; + + int error = 0; + php_sockaddr_storage sa; + socklen_t sl = sizeof(sa); + + if (timeout) { + sock->set_timeout(timeout, Socket::TIMEOUT_READ); + } + + 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; + clisock.reset(); + } + } +#endif + + if (clisock == nullptr) { + error = sock->errCode; + if (error_code) { + *error_code = error; + } + if (error_string) { + *error_string = php_socket_error_str(error); + } + return FAILURE; + } else { + php_network_populate_name_from_sockaddr((struct sockaddr *) &sa, sl, textaddr, addr, addrlen); +#ifdef TCP_NODELAY + if (tcp_nodelay) { + clisock->get_socket()->set_tcp_nodelay(tcp_nodelay); + } +#endif + auto abstract = new NetStream(); + abstract->socket = clisock; + abstract->blocking = true; + + xparam->outputs.client = php_stream_alloc_rel(stream->ops, (void *) abstract, nullptr, "r+"); + if (xparam->outputs.client) { + xparam->outputs.client->ctx = stream->ctx; + if (stream->ctx) { + GC_ADDREF(stream->ctx); + } + } + return SUCCESS; + } +} + +static inline int socket_recvfrom( + Socket *sock, char *buf, size_t buflen, zend_string **textaddr, struct sockaddr **addr, socklen_t *addrlen) { + int ret; + int want_addr = textaddr || addr; + + if (want_addr) { + php_sockaddr_storage sa; + socklen_t sl = sizeof(sa); + ret = sock->recvfrom(buf, buflen, (struct sockaddr *) &sa, &sl); + if (sl) { + php_network_populate_name_from_sockaddr((struct sockaddr *) &sa, sl, textaddr, addr, addrlen); + } else { + if (textaddr) { + *textaddr = ZSTR_EMPTY_ALLOC(); + } + if (addr) { + *addr = nullptr; + *addrlen = 0; + } + } + } else { + ret = sock->recv(buf, buflen); + } + + return ret; +} + +static inline int socket_sendto( + Socket *sock, const char *buf, size_t buflen, struct sockaddr *addr, socklen_t addrlen) { + if (addr) { + return sendto(sock->get_fd(), buf, buflen, 0, addr, addrlen); + } else { + return sock->send(buf, buflen); + } +} + +#ifdef SW_USE_OPENSSL + +#define GET_VER_OPT(name) \ + (PHP_STREAM_CONTEXT(stream) && \ + (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", name)) != nullptr) +#define GET_VER_OPT_STRING(name, str) \ + if (GET_VER_OPT(name)) { \ + convert_to_string_ex(val); \ + str = Z_STRVAL_P(val); \ + } +#define GET_VER_OPT_LONG(name, num) \ + if (GET_VER_OPT(name)) { \ + convert_to_long_ex(val); \ + num = Z_LVAL_P(val); \ + } + +static int socket_setup_crypto(php_stream *stream, Socket *sock, php_stream_xport_crypto_param *cparam STREAMS_DC) { + 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 (!socket_ssl_set_options(sock, context)) { + return -1; + } + if (!sock->ssl_handshake()) { + return -1; + } + } else if (!cparam->inputs.activate && sock->ssl_is_available()) { + sock->ssl_shutdown(); + 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 + +static inline int socket_xport_api(php_stream *stream, Socket *sock, php_stream_xport_param *xparam STREAMS_DC) { + static const int shutdown_how[] = {SHUT_RD, SHUT_WR, SHUT_RDWR}; + + switch (xparam->op) { + case STREAM_XPORT_OP_LISTEN: { + xparam->outputs.returncode = sock->listen(xparam->inputs.backlog) ? 0 : -1; + break; + } + 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) { + zval *tmpzval = nullptr; + php_stream_context *ctx = PHP_STREAM_CONTEXT(stream); + if (!ctx) { + break; + } +#ifdef SO_REUSEADDR + sock->get_socket()->set_reuse_addr(); +#endif + +#ifdef IPV6_V6ONLY + if ((tmpzval = php_stream_context_get_option(ctx, "socket", "ipv6_v6only")) != nullptr && + zval_is_true(tmpzval)) { + sock->get_socket()->set_option(IPPROTO_IPV6, IPV6_V6ONLY, 1); + } +#endif + +#ifdef SO_REUSEPORT + if ((tmpzval = php_stream_context_get_option(ctx, "socket", "so_reuseport")) != nullptr && + zval_is_true(tmpzval)) { + sock->get_socket()->set_reuse_port(); + } +#endif + +#ifdef SO_BROADCAST + if ((tmpzval = php_stream_context_get_option(ctx, "socket", "so_broadcast")) != nullptr && + zval_is_true(tmpzval)) { + sock->set_option(SOL_SOCKET, SO_BROADCAST, 1); + } +#endif + } + xparam->outputs.returncode = socket_bind(stream, sock, xparam STREAMS_CC); + break; + } + case STREAM_XPORT_OP_ACCEPT: + xparam->outputs.returncode = socket_accept(stream, sock, xparam STREAMS_CC); + break; + case STREAM_XPORT_OP_GET_NAME: + xparam->outputs.returncode = + php_network_get_sock_name(sock->get_fd(), + xparam->want_textaddr ? &xparam->outputs.textaddr : nullptr, + xparam->want_addr ? &xparam->outputs.addr : nullptr, + xparam->want_addr ? &xparam->outputs.addrlen : nullptr); + break; + case STREAM_XPORT_OP_GET_PEER_NAME: + xparam->outputs.returncode = + php_network_get_peer_name(sock->get_fd(), + xparam->want_textaddr ? &xparam->outputs.textaddr : nullptr, + xparam->want_addr ? &xparam->outputs.addr : nullptr, + xparam->want_addr ? &xparam->outputs.addrlen : nullptr); + break; + + case STREAM_XPORT_OP_SEND: + if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) { + php_swoole_error(E_WARNING, "STREAM_OOB flags is not supports"); + xparam->outputs.returncode = -1; + break; + } + xparam->outputs.returncode = + socket_sendto(sock, xparam->inputs.buf, xparam->inputs.buflen, xparam->inputs.addr, xparam->inputs.addrlen); + if (xparam->outputs.returncode == -1) { + char *err = php_socket_strerror(php_socket_errno(), nullptr, 0); + php_error_docref(nullptr, E_WARNING, "%s\n", err); + efree(err); + } + break; + + case STREAM_XPORT_OP_RECV: + if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) { + php_swoole_error(E_WARNING, "STREAM_OOB flags is not supports"); + xparam->outputs.returncode = -1; + break; + } + if ((xparam->inputs.flags & STREAM_PEEK) == STREAM_PEEK) { + xparam->outputs.returncode = sock->peek(xparam->inputs.buf, xparam->inputs.buflen); + } else { + xparam->outputs.returncode = socket_recvfrom(sock, + xparam->inputs.buf, + xparam->inputs.buflen, + xparam->want_textaddr ? &xparam->outputs.textaddr : nullptr, + xparam->want_addr ? &xparam->outputs.addr : nullptr, + xparam->want_addr ? &xparam->outputs.addrlen : nullptr); + } + break; + case STREAM_XPORT_OP_SHUTDOWN: + xparam->outputs.returncode = sock->shutdown(shutdown_how[xparam->how]); + break; + default: +#ifdef SW_DEBUG + php_swoole_fatal_error(E_WARNING, "socket_xport_api: unsupported option %d", xparam->op); +#endif + break; + } + return PHP_STREAM_OPTION_RETURN_OK; +} + +static int socket_set_option(php_stream *stream, int option, int value, void *ptrparam) { + NetStream *abstract = (NetStream *) stream->abstract; + if (UNEXPECTED(!abstract || !abstract->socket)) { + return PHP_STREAM_OPTION_RETURN_ERR; + } + 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; + break; + case PHP_STREAM_OPTION_XPORT_API: { + return socket_xport_api(stream, sock, (php_stream_xport_param *) ptrparam STREAMS_CC); + } + case PHP_STREAM_OPTION_META_DATA_API: { +#ifdef SW_USE_OPENSSL + SSL *ssl = sock->get_socket() ? sock->get_socket()->ssl : nullptr; + if (ssl) { + zval tmp; + const char *proto_str; + const SSL_CIPHER *cipher; + + array_init(&tmp); + switch (SSL_version(ssl)) { +#ifdef TLS1_3_VERSION + case TLS1_3_VERSION: + proto_str = "TLSv1.3"; + break; +#endif +#ifdef TLS1_2_VERSION + case TLS1_2_VERSION: + proto_str = "TLSv1.2"; + break; +#endif +#ifdef TLS1_1_VERSION + case TLS1_1_VERSION: + proto_str = "TLSv1.1"; + break; +#endif + case TLS1_VERSION: + proto_str = "TLSv1"; + break; +#ifdef SSL3_VERSION + case SSL3_VERSION: + proto_str = "SSLv3"; + break; +#endif + default: + proto_str = "UNKNOWN"; + break; + } + + cipher = SSL_get_current_cipher(ssl); + add_assoc_string(&tmp, "protocol", (char *) proto_str); + add_assoc_string(&tmp, "cipher_name", (char *) SSL_CIPHER_get_name(cipher)); + add_assoc_long(&tmp, "cipher_bits", SSL_CIPHER_get_bits(cipher, nullptr)); + add_assoc_string(&tmp, "cipher_version", (char *) SSL_CIPHER_get_version(cipher)); + add_assoc_zval((zval *) ptrparam, "crypto", &tmp); + } +#endif + add_assoc_bool((zval *) ptrparam, "timed_out", sock->errCode == ETIMEDOUT); + add_assoc_bool((zval *) ptrparam, "eof", stream->eof); + add_assoc_bool((zval *) ptrparam, "blocked", 1); + break; + } + case PHP_STREAM_OPTION_READ_TIMEOUT: { + abstract->socket->set_timeout((struct timeval *) ptrparam, Socket::TIMEOUT_READ); + break; + } +#ifdef SW_USE_OPENSSL + case PHP_STREAM_OPTION_CRYPTO_API: { + php_stream_xport_crypto_param *cparam = (php_stream_xport_crypto_param *) ptrparam; + switch (cparam->op) { + case STREAM_XPORT_CRYPTO_OP_SETUP: + cparam->outputs.returncode = socket_setup_crypto(stream, sock, cparam STREAMS_CC); + return PHP_STREAM_OPTION_RETURN_OK; + case STREAM_XPORT_CRYPTO_OP_ENABLE: + cparam->outputs.returncode = socket_enable_crypto(stream, sock, cparam STREAMS_CC); + return PHP_STREAM_OPTION_RETURN_OK; + default: + /* never here */ + SW_ASSERT(0); + break; + } + break; + } +#endif + case PHP_STREAM_OPTION_CHECK_LIVENESS: { + return sock->check_liveness() ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR; + } + case PHP_STREAM_OPTION_READ_BUFFER: + case PHP_STREAM_OPTION_WRITE_BUFFER: { + // TODO: read/write buffer + break; + } + default: +#ifdef SW_DEBUG + php_swoole_fatal_error(E_WARNING, "socket_set_option: unsupported option %d with value %d", option, value); +#endif + break; + } + 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, + size_t resourcenamelen, + const char *persistent_id, + int options, + int flags, + struct timeval *timeout, + php_stream_context *context STREAMS_DC) { + php_stream *stream = nullptr; + Socket *sock = nullptr; + + 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")) { + 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 + sock = new Socket(resourcename[0] == '[' ? SW_SOCK_TCP6 : SW_SOCK_TCP); + sock->enable_ssl_encrypt(); +#else + php_swoole_error(E_WARNING, + "you must configure with `--enable-openssl` to support ssl connection when compiling Swoole"); + return nullptr; +#endif + } else if (SW_STREQ(proto, protolen, "unix")) { + sock = new Socket(SW_SOCK_UNIX_STREAM); + } else if (SW_STREQ(proto, protolen, "udp")) { + sock = new Socket(SW_SOCK_UDP); + } else if (SW_STREQ(proto, protolen, "udg")) { + sock = new Socket(SW_SOCK_UNIX_DGRAM); + } else { + php_swoole_fatal_error(E_WARNING, "unknown protocol '%s'", proto); + return nullptr; + } + + if (UNEXPECTED(sock->get_fd() < 0)) { + _failed: + if (!stream) { + delete sock; + } else { + php_stream_close(stream); + } + return nullptr; + } + + sock->set_zero_copy(true); + + auto abstract = new NetStream(); + abstract->socket.reset(sock); + abstract->stream.socket = sock->get_fd(); + abstract->blocking = true; + + stream = php_stream_alloc_rel(&socket_ops, abstract, persistent_id, "r+"); + if (stream == nullptr) { + delete abstract; + goto _failed; + } + + if (!socket_ssl_set_options(sock, context)) { + goto _failed; + } + + return stream; +} + +static ZEND_FUNCTION(swoole_display_disabled_function) { + zend_error(E_WARNING, "%s() has been disabled for security reasons", get_active_function_name()); +} + +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; + } + + zend_function *zf = (zend_function *) zend_hash_str_find_ptr(EG(function_table), name, l_name); + if (zf == nullptr) { + return false; + } + + rf = (real_func *) emalloc(sizeof(real_func)); + 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; + } + + 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) { + disable_func(f.c_str(), f.length()); + } +} + +void PHPCoroutine::enable_unsafe_function() { + for (auto &f : unsafe_functions) { + enable_func(f.c_str(), f.length()); + } +} + +static void hook_stream_throw_exception(const char *type) { + zend_throw_exception_ex( + swoole_exception_ce, SW_ERROR_PHP_FATAL_ERROR, "failed to register `%s` stream transport factory", type); +} + +static void hook_remove_stream_flags(uint32_t *flags_ptr) { + uint32_t flags = *flags_ptr; + // stream factory + if (flags & PHPCoroutine::HOOK_TCP) { + flags ^= PHPCoroutine::HOOK_TCP; + } + if (flags & PHPCoroutine::HOOK_UDP) { + flags ^= PHPCoroutine::HOOK_UDP; + } + if (flags & PHPCoroutine::HOOK_UNIX) { + flags ^= PHPCoroutine::HOOK_UNIX; + } + if (flags & PHPCoroutine::HOOK_UDG) { + flags ^= PHPCoroutine::HOOK_UDG; + } + if (flags & PHPCoroutine::HOOK_SSL) { + flags ^= PHPCoroutine::HOOK_SSL; + } + if (flags & PHPCoroutine::HOOK_TLS) { + flags ^= PHPCoroutine::HOOK_TLS; + } + // stream ops + if (flags & PHPCoroutine::HOOK_FILE) { + flags ^= PHPCoroutine::HOOK_FILE; + } + if (flags & PHPCoroutine::HOOK_STDIO) { + flags ^= PHPCoroutine::HOOK_STDIO; + } + *flags_ptr = flags; +} + +static void hook_stream_factory(uint32_t *flags_ptr) { + uint32_t flags = *flags_ptr; + + if (flags & PHPCoroutine::HOOK_TCP) { + if (!(runtime_hook_flags & PHPCoroutine::HOOK_TCP)) { + if (php_stream_xport_register("tcp", socket_create) != SUCCESS) { + flags ^= PHPCoroutine::HOOK_TCP; + hook_stream_throw_exception("tcp"); + } + } + } else { + if (runtime_hook_flags & PHPCoroutine::HOOK_TCP) { + php_stream_xport_register("tcp", ori_factory.tcp); + } + } + if (flags & PHPCoroutine::HOOK_UDP) { + 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 { + if (runtime_hook_flags & PHPCoroutine::HOOK_UDP) { + php_stream_xport_register("udp", ori_factory.udp); + } + } + if (flags & PHPCoroutine::HOOK_UNIX) { + 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 { + if (runtime_hook_flags & PHPCoroutine::HOOK_UNIX) { + php_stream_xport_register("unix", ori_factory._unix); + } + } + if (flags & PHPCoroutine::HOOK_UDG) { + 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 { + if (runtime_hook_flags & PHPCoroutine::HOOK_UDG) { + php_stream_xport_register("udg", ori_factory.udg); + } + } + if (flags & PHPCoroutine::HOOK_SSL) { + 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 { + if (runtime_hook_flags & PHPCoroutine::HOOK_SSL) { + if (ori_factory.ssl != nullptr) { + php_stream_xport_register("ssl", ori_factory.ssl); + } else { + php_stream_xport_unregister("ssl"); + } + } + } + if (flags & PHPCoroutine::HOOK_TLS) { + 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 { + if (runtime_hook_flags & PHPCoroutine::HOOK_TLS) { + if (ori_factory.tls != nullptr) { + php_stream_xport_register("tls", ori_factory.tls); + } else { + php_stream_xport_unregister("tls"); + } + } + } + *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); + SW_HOOK_FUNC(stream_socket_pair); + } + } else { + if (runtime_hook_flags & PHPCoroutine::HOOK_STREAM_FUNCTION) { + SW_UNHOOK_FUNC(stream_select); + SW_UNHOOK_FUNC(stream_socket_pair); + } + } + // sleep + if (flags & PHPCoroutine::HOOK_SLEEP) { + if (!(runtime_hook_flags & PHPCoroutine::HOOK_SLEEP)) { + SW_HOOK_FUNC(sleep); + SW_HOOK_FUNC(usleep); + SW_HOOK_FUNC(time_nanosleep); + SW_HOOK_FUNC(time_sleep_until); + } + } else { + if (runtime_hook_flags & PHPCoroutine::HOOK_SLEEP) { + SW_UNHOOK_FUNC(sleep); + SW_UNHOOK_FUNC(usleep); + SW_UNHOOK_FUNC(time_nanosleep); + SW_UNHOOK_FUNC(time_sleep_until); + } + } + // proc_open + if (flags & PHPCoroutine::HOOK_PROC) { + if (!(runtime_hook_flags & PHPCoroutine::HOOK_PROC)) { + SW_HOOK_FUNC(proc_open); + SW_HOOK_FUNC(proc_close); + SW_HOOK_FUNC(proc_get_status); + SW_HOOK_FUNC(proc_terminate); + } + } else { + if (runtime_hook_flags & PHPCoroutine::HOOK_PROC) { + SW_UNHOOK_FUNC(proc_open); + SW_UNHOOK_FUNC(proc_close); + SW_UNHOOK_FUNC(proc_get_status); + SW_UNHOOK_FUNC(proc_terminate); + } + } + // blocking function + if (flags & PHPCoroutine::HOOK_BLOCKING_FUNCTION) { + if (!(runtime_hook_flags & PHPCoroutine::HOOK_BLOCKING_FUNCTION)) { + hook_func(ZEND_STRL("gethostbyname"), PHP_FN(swoole_coroutine_gethostbyname)); + SW_HOOK_WITH_PHP_FUNC(exec); + SW_HOOK_WITH_PHP_FUNC(shell_exec); + } + } else { + if (runtime_hook_flags & PHPCoroutine::HOOK_BLOCKING_FUNCTION) { + SW_UNHOOK_FUNC(gethostbyname); + SW_UNHOOK_FUNC(exec); + SW_UNHOOK_FUNC(shell_exec); + } + } + // ext-sockets + if (flags & PHPCoroutine::HOOK_SOCKETS) { + if (!(runtime_hook_flags & PHPCoroutine::HOOK_SOCKETS)) { + 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) { + SW_UNHOOK_FUNC(socket_create); + SW_UNHOOK_FUNC(socket_create_listen); + SW_UNHOOK_FUNC(socket_create_pair); + SW_UNHOOK_FUNC(socket_connect); + SW_UNHOOK_FUNC(socket_write); + SW_UNHOOK_FUNC(socket_read); + SW_UNHOOK_FUNC(socket_send); + SW_UNHOOK_FUNC(socket_recv); + SW_UNHOOK_FUNC(socket_sendto); + SW_UNHOOK_FUNC(socket_recvfrom); + SW_UNHOOK_FUNC(socket_bind); + SW_UNHOOK_FUNC(socket_listen); + SW_UNHOOK_FUNC(socket_accept); + SW_UNHOOK_FUNC(socket_getpeername); + SW_UNHOOK_FUNC(socket_getsockname); + SW_UNHOOK_FUNC(socket_getopt); + SW_UNHOOK_FUNC(socket_get_option); + SW_UNHOOK_FUNC(socket_setopt); + SW_UNHOOK_FUNC(socket_set_option); + SW_UNHOOK_FUNC(socket_set_block); + SW_UNHOOK_FUNC(socket_set_nonblock); + SW_UNHOOK_FUNC(socket_shutdown); + SW_UNHOOK_FUNC(socket_close); + SW_UNHOOK_FUNC(socket_clear_error); + SW_UNHOOK_FUNC(socket_last_error); + SW_UNHOOK_FUNC(socket_import_stream); + + detach_parent_class("Swoole\\Coroutine\\Socket"); + } + } + +#ifdef SW_USE_CURL + // curl native + if (flags & PHPCoroutine::HOOK_NATIVE_CURL) { + if (flags & PHPCoroutine::HOOK_CURL) { + php_swoole_fatal_error(E_WARNING, "cannot enable both hooks HOOK_NATIVE_CURL and HOOK_CURL at same time"); + flags ^= PHPCoroutine::HOOK_CURL; + } + if (!(runtime_hook_flags & PHPCoroutine::HOOK_NATIVE_CURL)) { + SW_HOOK_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) { + SW_UNHOOK_FUNC(curl_close); + SW_UNHOOK_FUNC(curl_copy_handle); + SW_UNHOOK_FUNC(curl_errno); + SW_UNHOOK_FUNC(curl_error); + SW_UNHOOK_FUNC(curl_exec); + SW_UNHOOK_FUNC(curl_getinfo); + SW_UNHOOK_FUNC(curl_init); + SW_UNHOOK_FUNC(curl_setopt); + SW_UNHOOK_FUNC(curl_setopt_array); + SW_UNHOOK_FUNC(curl_reset); + 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)) { + 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) { + SW_UNHOOK_FUNC(curl_init); + SW_UNHOOK_FUNC(curl_setopt); + SW_UNHOOK_FUNC(curl_setopt_array); + SW_UNHOOK_FUNC(curl_exec); + SW_UNHOOK_FUNC(curl_getinfo); + SW_UNHOOK_FUNC(curl_errno); + SW_UNHOOK_FUNC(curl_error); + 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; +} + +bool PHPCoroutine::disable_hook() { + return enable_hook(0); +} + +static PHP_METHOD(swoole_runtime, enableCoroutine) { + if (!SWOOLE_G(cli)) { + php_swoole_fatal_error(E_ERROR, "must be used in PHP CLI mode"); + RETURN_FALSE; + } + zend_long flags = PHPCoroutine::HOOK_ALL; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + PHPCoroutine::set_hook_flags(flags); + RETURN_BOOL(PHPCoroutine::enable_hook(flags)); +} + +static PHP_METHOD(swoole_runtime, getHookFlags) { + RETURN_LONG(PHPCoroutine::get_hook_flags()); +} + +static PHP_METHOD(swoole_runtime, setHookFlags) { + if (!SWOOLE_G(cli)) { + php_swoole_fatal_error(E_ERROR, "must be used in PHP CLI mode"); + RETURN_FALSE; + } + zend_long flags = PHPCoroutine::HOOK_ALL; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + PHPCoroutine::set_hook_flags(flags); + RETURN_BOOL(PHPCoroutine::enable_hook(flags)); +} + +static PHP_FUNCTION(swoole_sleep) { + zend_long num; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &num) == FAILURE) { + RETURN_FALSE; + } + if (num < 0) { + php_error_docref(nullptr, E_WARNING, "Number of seconds must be greater than or equal to 0"); + RETURN_FALSE; + } + + if (Coroutine::get_current()) { + RETURN_LONG(System::sleep((double) num) < 0 ? num : 0); + } else { + RETURN_LONG(php_sleep((unsigned int) num)); + } +} + +static PHP_FUNCTION(swoole_usleep) { + zend_long num; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &num) == FAILURE) { + RETURN_FALSE; + } + if (num < 0) { + php_error_docref(nullptr, E_WARNING, "Number of seconds must be greater than or equal to 0"); + RETURN_FALSE; + } + double sec = (double) num / 1000000; + if (Coroutine::get_current()) { + System::sleep(sec); + } else { + usleep((unsigned int) num); + } +} + +static PHP_FUNCTION(swoole_time_nanosleep) { + zend_long tv_sec, tv_nsec; + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &tv_sec, &tv_nsec) == FAILURE) { + RETURN_FALSE; + } + + if (tv_sec < 0) { + php_error_docref(nullptr, E_WARNING, "The seconds value must be greater than 0"); + RETURN_FALSE; + } + if (tv_nsec < 0) { + php_error_docref(nullptr, E_WARNING, "The nanoseconds value must be greater than 0"); + RETURN_FALSE; + } + double _time = (double) tv_sec + (double) tv_nsec / 1000000000.00; + if (Coroutine::get_current()) { + System::sleep(_time); + } else { + struct timespec php_req, php_rem; + php_req.tv_sec = (time_t) tv_sec; + php_req.tv_nsec = (long) tv_nsec; + + if (nanosleep(&php_req, &php_rem) == 0) { + RETURN_TRUE; + } else if (errno == EINTR) { + array_init(return_value); + add_assoc_long_ex(return_value, "seconds", sizeof("seconds") - 1, php_rem.tv_sec); + add_assoc_long_ex(return_value, "nanoseconds", sizeof("nanoseconds") - 1, php_rem.tv_nsec); + } else if (errno == EINVAL) { + php_swoole_error(E_WARNING, "nanoseconds was not in the range 0 to 999 999 999 or seconds was negative"); + } + } + RETURN_TRUE; +} + +static PHP_FUNCTION(swoole_time_sleep_until) { + double d_ts, c_ts; + struct timeval tm; + struct timespec php_req, php_rem; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &d_ts) == FAILURE) { + RETURN_FALSE; + } + + if (gettimeofday((struct timeval *) &tm, nullptr) != 0) { + RETURN_FALSE; + } + + c_ts = (double) (d_ts - tm.tv_sec - tm.tv_usec / 1000000.00); + if (c_ts < 0) { + php_error_docref(nullptr, E_WARNING, "Sleep until to time is less than current time"); + RETURN_FALSE; + } + + php_req.tv_sec = (time_t) c_ts; + if (php_req.tv_sec > c_ts) { + php_req.tv_sec--; + } + php_req.tv_nsec = (long) ((c_ts - php_req.tv_sec) * 1000000000.00); + + double _time = (double) php_req.tv_sec + (double) php_req.tv_nsec / 1000000000.00; + if (Coroutine::get_current()) { + System::sleep(_time); + } else { + while (nanosleep(&php_req, &php_rem)) { + if (errno == EINTR) { + php_req.tv_sec = php_rem.tv_sec; + php_req.tv_nsec = php_rem.tv_nsec; + } else { + RETURN_FALSE; + } + } + } + RETURN_TRUE; +} + +static void stream_array_to_fd_set(zval *stream_array, std::unordered_map &fds, int event) { + zval *elem; + zend_ulong index; + zend_string *key; + php_socket_t sock; + + if (!ZVAL_IS_ARRAY(stream_array)) { + return; + } + + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(stream_array), index, key, elem) { + ZVAL_DEREF(elem); + sock = php_swoole_convert_to_fd(elem); + if (sock < 0) { + continue; + } + auto i = fds.find(sock); + if (i == fds.end()) { + fds.emplace(std::make_pair(sock, PollSocket(event, new zend::KeyValue(index, key, elem)))); + } else { + i->second.events |= event; + } + } + ZEND_HASH_FOREACH_END(); +} + +static int stream_array_emulate_read_fd_set(zval *stream_array) { + zval *elem, *dest_elem, new_array; + HashTable *ht; + php_stream *stream; + int ret = 0; + zend_ulong num_ind; + zend_string *key; + + if (!ZVAL_IS_ARRAY(stream_array)) { + return 0; + } + + array_init_size(&new_array, zend_hash_num_elements(Z_ARRVAL_P(stream_array))); + ht = Z_ARRVAL(new_array); + + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(stream_array), num_ind, key, elem) { + ZVAL_DEREF(elem); + php_stream_from_zval_no_verify(stream, elem); + if (stream == nullptr) { + continue; + } + if ((stream->writepos - stream->readpos) > 0) { + /* allow readable non-descriptor based streams to participate in stream_select. + * Non-descriptor streams will only "work" if they have previously buffered the + * data. Not ideal, but better than nothing. + * This branch of code also allows blocking streams with buffered data to + * operate correctly in stream_select. + * */ + dest_elem = !key ? zend_hash_index_update(ht, num_ind, elem) : zend_hash_update(ht, key, elem); + zval_add_ref(dest_elem); + ret++; + continue; + } + } + ZEND_HASH_FOREACH_END(); + + if (ret > 0) { + /* destroy old array and add new one */ + zend_array_destroy(Z_ARR_P(stream_array)); + ZVAL_ARR(stream_array, ht); + } else { + zend_array_destroy(ht); + } + + return ret; +} + +static PHP_FUNCTION(swoole_stream_select) { + Coroutine::get_current_safe(); + + 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_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) { + stream_array_to_fd_set(r_array, fds, SW_EVENT_READ); + } + + if (w_array != nullptr) { + stream_array_to_fd_set(w_array, fds, SW_EVENT_WRITE); + } + + if (e_array != nullptr) { + stream_array_to_fd_set(e_array, fds, SW_EVENT_ERROR); + } + + if (fds.size() == 0) { + php_error_docref(nullptr, E_WARNING, "No stream arrays were passed"); + RETURN_FALSE; + } + + ON_SCOPE_EXIT { + for (auto &i : fds) { + zend::KeyValue *kv = (zend::KeyValue *) i.second.ptr; + delete kv; + } + }; + + /* 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 + * that we selected, but return only the readable sockets */ + if (r_array != nullptr) { + retval = stream_array_emulate_read_fd_set(r_array); + if (retval > 0) { + if (w_array != nullptr) { + zend_hash_clean(Z_ARRVAL_P(w_array)); + } + if (e_array != nullptr) { + zend_hash_clean(Z_ARRVAL_P(e_array)); + } + RETURN_LONG(retval); + } + } + + if (r_array != nullptr) { + zend_hash_clean(Z_ARRVAL_P(r_array)); + } + if (w_array != nullptr) { + zend_hash_clean(Z_ARRVAL_P(w_array)); + } + if (e_array != nullptr) { + zend_hash_clean(Z_ARRVAL_P(e_array)); + } + + /** + * timeout or add failed + */ + if (!System::socket_poll(fds, timeout)) { + RETURN_LONG(0); + } + + for (auto &i : fds) { + zend::KeyValue *kv = (zend::KeyValue *) i.second.ptr; + int revents = i.second.revents; + SW_ASSERT((revents & (~(SW_EVENT_READ | SW_EVENT_WRITE | SW_EVENT_ERROR))) == 0); + if (revents > 0) { + if ((revents & SW_EVENT_READ) && r_array) { + kv->add_to(r_array); + } + if ((revents & SW_EVENT_WRITE) && w_array) { + kv->add_to(w_array); + } + if ((revents & SW_EVENT_ERROR) && e_array) { + kv->add_to(e_array); + } + retval++; + } + } + + RETURN_LONG(retval); +} + +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; + /** + * use php library function + */ + if (handler == nullptr) { + handler = PHP_FN(swoole_user_func_handler); + use_php_func = true; + } + if (rf) { + rf->function->internal_function.handler = handler; + if (arg_info) { + rf->function->internal_function.arg_info = arg_info; + } + return; + } + + zend_function *zf = (zend_function *) zend_hash_str_find_ptr(EG(function_table), name, l_name); + if (zf == nullptr) { + 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, fn_str->val, fn_str->len); + + ZVAL_STRINGL(&rf->name, func, fn_str->len + 7); + auto fci_cache = sw_callable_create(&rf->name); + if (!fci_cache) { + return; + } + rf->fci_cache = fci_cache; + } + + zend_hash_add_ptr(tmp_function_table, fn_str, rf); +} + +static void unhook_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 == 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) { + auto *abstract = new NetStream(); + abstract->socket = std::make_shared(_fd, domain, type, protocol); + if (FG(default_socket_timeout) > 0) { + abstract->socket->set_timeout((double) FG(default_socket_timeout)); + } + abstract->stream.timeout.tv_sec = FG(default_socket_timeout); + 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 abstract; + } else { + stream->flags |= PHP_STREAM_FLAG_AVOID_BLOCKING; + } + + 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; + php_socket_t pair[2]; + + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_LONG(domain) + Z_PARAM_LONG(type) + Z_PARAM_LONG(protocol) + 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)); + RETURN_FALSE; + } + + array_init(return_value); + + php_swoole_check_reactor(); + + s1 = php_swoole_create_stream_from_socket(pair[0], domain, type, protocol STREAMS_CC); + s2 = php_swoole_create_stream_from_socket(pair[1], domain, type, protocol STREAMS_CC); + + /* set the __exposed flag. + * php_stream_to_zval() does, add_next_index_resource() does not */ + php_stream_auto_cleanup(s1); + php_stream_auto_cleanup(s2); + + add_next_index_resource(return_value, s1->res); + add_next_index_resource(return_value, s2->res); +} + +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; + fci.retval = return_value; + fci.param_count = ZEND_NUM_ARGS(); + fci.params = ZEND_CALL_ARG(execute_data, 1); + 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 + if (putenv((char *) iter->second.c_str()) == 0) { /* success */ +#endif + +#ifdef HAVE_TZSET + if (zend_binary_strcasecmp(key.c_str(), key.length(), ZEND_STRL("TZ")) == 0) { + tzset(); + } +#endif + result = true; + } else { + result = false; + } + + tsrm_env_unlock(); + RETURN_BOOL(result); +} +/* }}} */ +#endif diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc new file mode 100644 index 00000000000..da8d55951b9 --- /dev/null +++ b/ext-src/swoole_server.cc @@ -0,0 +1,4007 @@ +/* + +----------------------------------------------------------------------+ + | 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_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" +#include "zend_smart_str.h" + +#ifdef SW_HAVE_ZLIB +#include +#endif + +BEGIN_EXTERN_C() +#include "ext/json/php_json.h" +#include "stubs/php_swoole_server_arginfo.h" +END_EXTERN_C() + +using namespace swoole; + +struct ConnectionIterator { + int current_fd; + SessionId session_id; + Server *serv; + ListenPort *port; + int index; +}; + +struct ServerEvent { + enum php_swoole_server_callback_type type; + std::string name; + ServerEvent(enum php_swoole_server_callback_type type, std::string &&name) : type(type), name(name) {} +}; + +// clang-format off +static std::unordered_map server_event_map({ + { "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 + +// 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); + +void php_swoole_server_rshutdown() { + if (!sw_server() || !sw_worker()) { + return; + } + + Server *serv = sw_server(); + Worker *worker = sw_worker(); + serv->drain_worker_pipe(); + + 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", + php_swoole_get_last_error_message(), + php_swoole_get_last_error_file(), + PG(last_error_lineno)); + } else { + swoole_error_log( + SW_LOG_NOTICE, SW_ERROR_SERVER_WORKER_TERMINATED, "worker process is terminated by exit()/die()"); + } + } +} + +zend_class_entry *swoole_server_ce; +zend_object_handlers swoole_server_handlers; + +zend_class_entry *swoole_connection_iterator_ce; +static zend_object_handlers swoole_connection_iterator_handlers; + +static zend_class_entry *swoole_server_task_ce; +static zend_object_handlers swoole_server_task_handlers; + +static zend_class_entry *swoole_server_event_ce; +static zend_object_handlers swoole_server_event_handlers; + +static zend_class_entry *swoole_server_packet_ce; +static zend_object_handlers swoole_server_packet_handlers; + +static zend_class_entry *swoole_server_pipe_message_ce; +static zend_object_handlers swoole_server_pipe_message_handlers; + +static zend_class_entry *swoole_server_status_info_ce; +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); +} + +static sw_inline Server *server_get_ptr(zval *zobject) { + return server_fetch_object(Z_OBJ_P(zobject))->serv; +} + +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, "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; +} + +static void server_free_object(zend_object *object) { + ServerObject *server_object = server_fetch_object(object); + ServerProperty *property = server_object->property; + Server *serv = server_object->serv; + + if (serv) { + if (serv->private_data_3) { + sw_callable_free(serv->private_data_3); + } + for (int i = 0; i < PHP_SWOOLE_SERVER_CALLBACK_NUM; i++) { + auto fci_cache = property->callbacks[i]; + if (fci_cache) { + sw_callable_free(fci_cache); + property->callbacks[i] = nullptr; + } + } + for (auto i = property->user_processes.begin(); i != property->user_processes.end(); i++) { + sw_zval_free(*i); + } + for (auto zport : property->ports) { + php_swoole_server_port_deref(Z_OBJ_P(zport)); + efree(zport); + } + 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; + } +} + +static zend_object *server_create_object(zend_class_entry *ce) { + ServerObject *server_object = (ServerObject *) zend_object_alloc(sizeof(ServerObject), ce); + zend_object_std_init(&server_object->std, ce); + object_properties_init(&server_object->std, ce); + server_object->std.handlers = &swoole_server_handlers; + server_object->property = new ServerProperty(); + return &server_object->std; +} + +struct ConnectionIteratorObject { + ConnectionIterator iterator; + zend_object std; +}; + +static sw_inline ConnectionIteratorObject *php_swoole_connection_iterator_fetch_object(zend_object *obj) { + return (ConnectionIteratorObject *) ((char *) obj - swoole_connection_iterator_handlers.offset); +} + +static sw_inline ConnectionIterator *php_swoole_connection_iterator_get_ptr(zval *zobject) { + return &php_swoole_connection_iterator_fetch_object(Z_OBJ_P(zobject))->iterator; +} + +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, "Invalid instance of %s", SW_Z_OBJCE_NAME_VAL_P(zobject)); + } + return iterator; +} + +static void php_swoole_connection_iterator_free_object(zend_object *object) { + zend_object_std_dtor(object); +} + +static zend_object *php_swoole_connection_iterator_create_object(zend_class_entry *ce) { + ConnectionIteratorObject *connection = + (ConnectionIteratorObject *) zend_object_alloc(sizeof(ConnectionIteratorObject), ce); + zend_object_std_init(&connection->std, ce); + object_properties_init(&connection->std, ce); + connection->std.handlers = &swoole_connection_iterator_handlers; + return &connection->std; +} + +struct ServerTaskObject { + Server *serv; + DataHead info; + zend_object std; +}; + +static sw_inline ServerTaskObject *php_swoole_server_task_fetch_object(zend_object *obj) { + return (ServerTaskObject *) ((char *) obj - swoole_server_task_handlers.offset); +} + +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, "Invalid instance of %s", SW_Z_OBJCE_NAME_VAL_P(zobject)); + } + return serv; +} + +static sw_inline void php_swoole_server_task_set_server(zval *zobject, Server *serv) { + php_swoole_server_task_fetch_object(Z_OBJ_P(zobject))->serv = serv; +} + +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, "Invalid instance of %s", SW_Z_OBJCE_NAME_VAL_P(zobject)); + } + return &task->info; +} + +static sw_inline void php_swoole_server_task_set_info(zval *zobject, DataHead *info) { + php_swoole_server_task_fetch_object(Z_OBJ_P(zobject))->info = *info; +} + +static void php_swoole_server_task_free_object(zend_object *object) { + zend_object_std_dtor(object); +} + +static zend_object *php_swoole_server_task_create_object(zend_class_entry *ce) { + ServerTaskObject *server_task = (ServerTaskObject *) zend_object_alloc(sizeof(ServerTaskObject), ce); + zend_object_std_init(&server_task->std, ce); + object_properties_init(&server_task->std, ce); + server_task->std.handlers = &swoole_server_task_handlers; + return &server_task->std; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_server, __construct); +static PHP_METHOD(swoole_server, __destruct); +static PHP_METHOD(swoole_server, set); +static PHP_METHOD(swoole_server, on); +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); +static PHP_METHOD(swoole_server, sendfile); +static PHP_METHOD(swoole_server, stats); +static PHP_METHOD(swoole_server, bind); +static PHP_METHOD(swoole_server, sendto); +static PHP_METHOD(swoole_server, sendwait); +static PHP_METHOD(swoole_server, exists); +static PHP_METHOD(swoole_server, protect); +static PHP_METHOD(swoole_server, close); +static PHP_METHOD(swoole_server, pause); +static PHP_METHOD(swoole_server, resume); +static PHP_METHOD(swoole_server, task); +static PHP_METHOD(swoole_server, taskwait); +static PHP_METHOD(swoole_server, taskWaitMulti); +static PHP_METHOD(swoole_server, taskCo); +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); +static PHP_METHOD(swoole_server, getWorkerPid); +static PHP_METHOD(swoole_server, getWorkerStatus); +static PHP_METHOD(swoole_server, getManagerPid); +static PHP_METHOD(swoole_server, getMasterPid); +#ifdef SWOOLE_SOCKETS_SUPPORT +static PHP_METHOD(swoole_server, getSocket); +#endif + +/** + * Server\Connection + */ +static PHP_METHOD(swoole_connection_iterator, count); +static PHP_METHOD(swoole_connection_iterator, rewind); +static PHP_METHOD(swoole_connection_iterator, next); +static PHP_METHOD(swoole_connection_iterator, current); +static PHP_METHOD(swoole_connection_iterator, key); +static PHP_METHOD(swoole_connection_iterator, valid); +static PHP_METHOD(swoole_connection_iterator, offsetExists); +static PHP_METHOD(swoole_connection_iterator, offsetGet); +static PHP_METHOD(swoole_connection_iterator, offsetSet); +static PHP_METHOD(swoole_connection_iterator, offsetUnset); +static PHP_METHOD(swoole_connection_iterator, __construct); +static PHP_METHOD(swoole_connection_iterator, __destruct); + +/** + * Server\Task + */ +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_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_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_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_class_Swoole_Server_getSocket, ZEND_ACC_PUBLIC) +#endif + 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_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_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", 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); + + // ---------------------------------------Task------------------------------------- + SW_INIT_CLASS_ENTRY(swoole_server_task, "Swoole\\Server\\Task", nullptr, swoole_server_task_methods); + swoole_server_task_ce->ce_flags |= ZEND_ACC_FINAL; + SW_SET_CLASS_NOT_SERIALIZABLE(swoole_server_task); + SW_SET_CLASS_CLONEABLE(swoole_server_task, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_server_task, sw_zend_class_unset_property_deny); + SW_SET_CLASS_CUSTOM_OBJECT(swoole_server_task, + php_swoole_server_task_create_object, + php_swoole_server_task_free_object, + ServerTaskObject, + std); + zend_declare_property_null(swoole_server_task_ce, ZEND_STRL("data"), ZEND_ACC_PUBLIC); + zend_declare_property_double(swoole_server_task_ce, ZEND_STRL("dispatch_time"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_server_task_ce, ZEND_STRL("id"), -1, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_server_task_ce, ZEND_STRL("worker_id"), -1, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_server_task_ce, ZEND_STRL("flags"), 0, ZEND_ACC_PUBLIC); + // ---------------------------------------Event------------------------------------- + SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_server_event, "Swoole\\Server\\Event"); + zend_declare_property_long(swoole_server_event_ce, ZEND_STRL("reactor_id"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_server_event_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_double(swoole_server_event_ce, ZEND_STRL("dispatch_time"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_server_event_ce, ZEND_STRL("data"), ZEND_ACC_PUBLIC); + // ---------------------------------------Packet------------------------------------- + SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_server_packet, "Swoole\\Server\\Packet"); + zend_declare_property_long(swoole_server_packet_ce, ZEND_STRL("server_socket"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_server_packet_ce, ZEND_STRL("server_port"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_double(swoole_server_packet_ce, ZEND_STRL("dispatch_time"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_server_packet_ce, ZEND_STRL("address"), ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_server_packet_ce, ZEND_STRL("port"), 0, ZEND_ACC_PUBLIC); + // ---------------------------------------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------------------------------------- + SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_server_status_info, "Swoole\\Server\\StatusInfo"); + zend_declare_property_long(swoole_server_status_info_ce, ZEND_STRL("worker_id"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_server_status_info_ce, ZEND_STRL("worker_pid"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_server_status_info_ce, ZEND_STRL("status"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_server_status_info_ce, ZEND_STRL("exit_code"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_server_status_info_ce, ZEND_STRL("signal"), 0, ZEND_ACC_PUBLIC); + // ---------------------------------------TaskResult------------------------------------- + SW_INIT_CLASS_ENTRY_DATA_OBJECT(swoole_server_task_result, "Swoole\\Server\\TaskResult"); + zend_declare_property_long(swoole_server_task_result_ce, ZEND_STRL("task_id"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_server_task_result_ce, ZEND_STRL("task_worker_id"), 0, ZEND_ACC_PUBLIC); + 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", 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, + php_swoole_connection_iterator_create_object, + php_swoole_connection_iterator_free_object, + ConnectionIteratorObject, + std); + zend_class_implements(swoole_connection_iterator_ce, 2, zend_ce_iterator, zend_ce_arrayaccess); +#ifdef SW_HAVE_COUNTABLE + zend_class_implements(swoole_connection_iterator_ce, 1, zend_ce_countable); +#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); + zend_declare_property_null(swoole_server_ce, ZEND_STRL("onBeforeReload"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_server_ce, ZEND_STRL("onAfterReload"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_server_ce, ZEND_STRL("onWorkerExit"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_server_ce, ZEND_STRL("onWorkerError"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_server_ce, ZEND_STRL("onTask"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_server_ce, ZEND_STRL("onFinish"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_server_ce, ZEND_STRL("onManagerStart"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_server_ce, ZEND_STRL("onManagerStop"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_server_ce, ZEND_STRL("onPipeMessage"), ZEND_ACC_PRIVATE); + + zend_declare_property_null(swoole_server_ce, ZEND_STRL("setting"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_server_ce, ZEND_STRL("connections"), ZEND_ACC_PUBLIC); + 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); + zend_declare_property_long(swoole_server_ce, ZEND_STRL("manager_pid"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_server_ce, ZEND_STRL("worker_id"), -1, ZEND_ACC_PUBLIC); + 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 + + /** + * 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); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TASK_NONBLOCK", SW_TASK_NONBLOCK); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TASK_CALLBACK", SW_TASK_CALLBACK); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TASK_WAITALL", SW_TASK_WAITALL); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TASK_COROUTINE", SW_TASK_COROUTINE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TASK_PEEK", SW_TASK_PEEK); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TASK_NOREPLY", SW_TASK_NOREPLY); + + SW_REGISTER_LONG_CONSTANT("SWOOLE_WORKER_BUSY", SW_WORKER_BUSY); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WORKER_IDLE", SW_WORKER_IDLE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WORKER_EXIT", SW_WORKER_EXIT); +} + +zend::Callable *php_swoole_server_get_callback(Server *serv, int server_fd, int event_type) { + ListenPort *port = serv->get_port_by_server_fd(server_fd); + ServerPortProperty *property = php_swoole_server_get_port_property(port); + zend::Callable *cb; + + if (sw_unlikely(!port)) { + return nullptr; + } + if (property && (cb = property->callbacks[event_type])) { + return cb; + } else { + return php_swoole_server_get_port_property(serv->get_primary_port())->callbacks[event_type]; + } +} + +int php_swoole_create_dir(const char *path, size_t length) { + if (access(path, F_OK) == 0) { + return 0; + } + return php_stream_mkdir(path, 0777, PHP_STREAM_MKDIR_RECURSIVE | REPORT_ERRORS, nullptr) ? 0 : -1; +} + +static TaskId php_swoole_server_task_pack(zval *zdata, EventData *task) { + smart_str serialized_data = {}; + php_serialize_data_t var_hash; + + bool serialized = false; + char *task_data_str; + size_t task_data_len = 0; + // need serialize + if (Z_TYPE_P(zdata) != IS_STRING) { + // serialize + serialized = true; + + PHP_VAR_SERIALIZE_INIT(var_hash); + php_var_serialize(&serialized_data, zdata, &var_hash); + PHP_VAR_SERIALIZE_DESTROY(var_hash); + + if (!serialized_data.s) { + return -1; + } + task_data_str = ZSTR_VAL(serialized_data.s); + task_data_len = ZSTR_LEN(serialized_data.s); + } else { + task_data_str = Z_STRVAL_P(zdata); + task_data_len = Z_STRLEN_P(zdata); + } + + 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; +} + +void php_swoole_get_recv_data(Server *serv, zval *zdata, RecvData *req) { + const char *data = req->data; + uint32_t length = req->info.len; + if (length == 0) { + ZVAL_EMPTY_STRING(zdata); + } else { + if (req->info.flags & SW_EVENT_DATA_OBJ_PTR) { + 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); + zend::assign_zend_string_by_val(zdata, recv_buffer->pop(serv->recv_buffer_size), length); + } else { + ZVAL_STRINGL(zdata, data, length); + } + } +} + +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 > 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; + } + if (UNEXPECTED(serv->is_task_worker())) { + php_swoole_fatal_error(E_WARNING, "Server->task() cannot use in the task-worker"); + return SW_ERR; + } + return SW_OK; +} + +static bool php_swoole_server_task_unpack(zval *zresult, EventData *task_result) { + php_unserialize_data_t var_hash; + PacketPtr packet; + + if (!Server::task_unpack(task_result, sw_tg_buffer(), &packet)) { + return false; + } + + if (task_result->info.ext_flags & SW_TASK_SERIALIZE) { + char *p = packet.data; + size_t l = packet.length; + PHP_VAR_UNSERIALIZE_INIT(var_hash); + zend_bool unserialized = php_var_unserialize(zresult, (const uchar **) &p, (const uchar *) (p + l), &var_hash); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + if (!unserialized) { + swoole_warning("unserialize() failed, Error at offset " ZEND_LONG_FMT " of %zd bytes", + (zend_long) ((char *) p - packet.data), + l); + return false; + } + } else { + ZVAL_STRINGL(zresult, packet.data, packet.length); + } + + 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 ServerPortProperty *php_swoole_server_port_get_property(zval *zobject); + +static zval *php_swoole_server_add_port(ServerObject *server_object, ListenPort *port) { + Server *serv = server_object->serv; + + zval *zport = sw_malloc_zval(); + object_init_ex(zport, swoole_server_port_ce); + server_object->property->ports.push_back(zport); + + /* port ptr */ + php_swoole_server_port_set_ptr(zport, port); + + /* port property */ + ServerPortProperty *property = php_swoole_server_port_get_property(zport); + property->serv = serv; + property->port = port; + + /* linked */ + php_swoole_server_set_port_property(port, property); + + zend_update_property_string(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("host"), port->get_host()); + zend_update_property_long(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("port"), port->get_port()); + zend_update_property_long(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("type"), port->get_type()); + zend_update_property_long(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("sock"), port->get_fd()); + zend_update_property_bool(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("ssl"), port->ssl); + + do { + zval *zserv = 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); + + /* 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; + iterator->port = port; + + zend_update_property(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("connections"), &connection_iterator); + zval_ptr_dtor(&connection_iterator); + } while (0); + + return zport; +} + +void ServerObject::copy_setting(zval *zsetting) { + zend_array *new_array = zend_array_dup(Z_ARRVAL_P(zsetting)); + zend_hash_apply(new_array, [](zval *el) -> int { + return sw_zval_is_serializable(el) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_REMOVE; + }); + zval znew_array; + ZVAL_ARR(&znew_array, new_array); + serv->private_data_4 = php_swoole_serialize(&znew_array); + zval_ptr_dtor(&znew_array); +} + +void ServerObject::on_before_start() { + /** + * create swoole server + */ + if (serv->create() < 0) { + php_swoole_fatal_error(E_ERROR, "failed to create the server. Error: %s", sw_error); + return; + } + + zval *zobject = php_swoole_server_zval_ptr(serv); + auto primary_port = serv->get_primary_port(); + +#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) { + serv->reload_async = 1; + } + + if (serv->send_yield) { + if (serv->onClose == nullptr && serv->is_support_unsafe_events()) { + serv->onClose = php_swoole_server_onClose; + } + } + + serv->message_bus.set_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(get_ce(), SW_Z8_OBJ_P(zobject), ZEND_STRL("master_pid"), getpid()); + + 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); + } + if (!zend_hash_str_exists(Z_ARRVAL_P(zsetting), ZEND_STRL("task_worker_num"))) { + add_assoc_long(zsetting, "task_worker_num", serv->task_worker_num); + } + if (!zend_hash_str_exists(Z_ARRVAL_P(zsetting), ZEND_STRL("output_buffer_size"))) { + add_assoc_long(zsetting, "output_buffer_size", serv->output_buffer_size); + } + if (!zend_hash_str_exists(Z_ARRVAL_P(zsetting), ZEND_STRL("max_connection"))) { + add_assoc_long(zsetting, "max_connection", serv->get_max_connection()); + } + // for admin_server + if (zend_hash_str_exists(Z_ARRVAL_P(zsetting), ZEND_STRL("admin_server"))) { + swoole::register_admin_server_commands(serv); + } + + bool find_http_port = false; + if (is_redis_server()) { + 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 (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); + sw_zend_call_method_with_1_params(zport, swoole_server_port_ce, nullptr, "set", nullptr, zsetting); + } + } + + 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, isset_callback(port, SW_SERVER_CB_onPacket))) { + php_swoole_fatal_error(E_ERROR, "require onPacket callback"); + return; + } + +#ifdef SW_USE_OPENSSL + if (port->ssl_context && port->ssl_context->verify_peer && port->ssl_context->client_cert_file.empty()) { + php_swoole_fatal_error(E_ERROR, "server open verify peer require client_cert_file config"); + return; + } +#endif + if (port->open_http2_protocol && !serv->is_hash_dispatch_mode()) { + php_swoole_fatal_error( + E_ERROR, + "server dispatch mode should be FDMOD(%d) or IPMOD(%d) if open_http2_protocol is true", + Server::DISPATCH_FDMOD, + Server::DISPATCH_IPMOD); + return; + } + + if (!port->open_http_protocol) { + port->open_http_protocol = port->open_websocket_protocol || port->open_http2_protocol; + } + if (port->open_http_protocol) { + find_http_port = true; + if (port->open_websocket_protocol) { + 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 && !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) { + // 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; + } + } + } + + if (find_http_port) { + serv->onReceive = php_swoole_http_server_onReceive; + if (serv->is_base_mode()) { + serv->onClose = php_swoole_http_server_onClose; + } + } + +#ifdef SW_THREAD + if (serv->is_thread_mode()) { + copy_setting(zsetting); + } +#endif + + if (SWOOLE_G(enable_library)) { + zend::function::call("\\Swoole\\Server\\Helper::onBeforeStart", 1, zobject); + } +} + +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; + + // data plane + if (property->callbacks[SW_SERVER_CB_onTask] != nullptr) { + serv->onTask = php_swoole_server_onTask; + serv->onFinish = php_swoole_server_onFinish; + } + if (property->callbacks[SW_SERVER_CB_onPipeMessage] != nullptr) { + serv->onPipeMessage = php_swoole_server_onPipeMessage; + } + if (serv->send_yield && serv->is_support_unsafe_events()) { + serv->onBufferEmpty = php_swoole_server_onBufferEmpty; + } +} + +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; + + // need serialize + if (Z_TYPE_P(zdata) != IS_STRING) { + // serialize + flags |= SW_TASK_SERIALIZE; + + PHP_VAR_SERIALIZE_INIT(var_hash); + php_var_serialize(&serialized_data, zdata, &var_hash); + PHP_VAR_SERIALIZE_DESTROY(var_hash); + data_str = ZSTR_VAL(serialized_data.s); + data_len = ZSTR_LEN(serialized_data.s); + + } else { + data_str = Z_STRVAL_P(zdata); + data_len = Z_STRLEN_P(zdata); + } + + bool success = serv->finish(data_str, data_len, flags, current_task); + smart_str_free(&serialized_data); + return success; +} + +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); + + zend::Variable zresult; + if (UNEXPECTED(!php_swoole_server_task_unpack(zresult.ptr(), req))) { + return; + } + + 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; + args[0] = *zserv; + + 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) 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) serv->get_task_src_worker_id(req)); + args[2] = zresult.value; + argc = 3; + } + + 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]); + } +} + +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 (cb) { + zval *zserv = php_swoole_server_zval_ptr(serv); + zval args[4]; + int argc; + + args[0] = *zserv; + + if (serv->event_object) { + 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); + 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); + argc = 2; + } else { + ZVAL_LONG(&args[1], (zend_long) req->info.fd); + ZVAL_LONG(&args[2], (zend_long) req->info.reactor_id); + php_swoole_get_recv_data(serv, &args[3], req); + argc = 4; + } + + 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); + } + if (serv->event_object) { + zval_ptr_dtor(&args[1]); + } else { + zval_ptr_dtor(&args[3]); + } + } + + return SW_OK; +} + +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; + + 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); + + 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()); + } + + 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"), + 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"), + 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"), + 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); + args[1] = zobject; + argc = 2; + } else { + zval zaddr; + array_init(&zaddr); + add_assoc_long(&zaddr, "server_socket", req->info.server_fd); + add_assoc_double(&zaddr, "dispatch_time", req->info.time); + Connection *server_sock = serv->get_connection(req->info.server_fd); + if (server_sock) { + add_assoc_long(&zaddr, "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)); + add_assoc_string(&zaddr, "address", address); + add_assoc_long(&zaddr, "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)); + add_assoc_string(&zaddr, "address", address); + add_assoc_long(&zaddr, "port", ntohs(packet->socket_addr.addr.inet_v6.sin6_port)); + } else if (packet->socket_type == SW_SOCK_UNIX_DGRAM) { + add_assoc_string(&zaddr, "address", packet->socket_addr.addr.un.sun_path); + } + + ZVAL_STRINGL(&args[1], packet->data, packet->length); + args[2] = zaddr; + argc = 3; + } + + 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)); + } + + zval_ptr_dtor(&args[1]); + if (!serv->event_object) { + zval_ptr_dtor(&args[2]); + } + + return SW_OK; +} + +static sw_inline void php_swoole_create_task_object(zval *ztask, Server *serv, EventData *req, zval *zdata) { + object_init_ex(ztask, swoole_server_task_ce); + 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("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) req->info.ext_flags); +} + +static int php_swoole_server_onTask(Server *serv, EventData *req) { + sw_atomic_fetch_sub(&serv->gs->tasking_num, 1); + + zval *zserv = php_swoole_server_zval_ptr(serv); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); + + zend::Variable zresult; + if (!php_swoole_server_task_unpack(zresult.ptr(), req)) { + return SW_ERR; + } + + zval retval; + uint32_t argc; + zval argv[4]; + + if (serv->task_enable_coroutine || serv->task_object) { + argc = 2; + argv[0] = *zserv; + php_swoole_create_task_object(&argv[1], serv, req, zresult.ptr()); + } else { + argc = 4; + argv[0] = *zserv; + 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->get_callback(SW_SERVER_CB_onTask)->ptr(), + argc, + argv, + &retval, + serv->task_enable_coroutine))) { + php_swoole_error(E_WARNING, "%s->onTask handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); + } + + if (argc == 2) { + zval_ptr_dtor(&argv[1]); + } + + if (!ZVAL_IS_NULL(&retval)) { + php_swoole_server_task_finish(serv, &retval, req); + zval_ptr_dtor(&retval); + } + + return SW_OK; +} + +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); + + zend::Variable zresult; + if (!php_swoole_server_task_unpack(zresult.ptr(), req)) { + return SW_ERR; + } + + 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); + return SW_OK; + } + TaskCo *task_co = task_co_iterator->second; + // Server->taskwait + if (task_co->list == nullptr) { + ZVAL_COPY_VALUE(task_co->result, zresult.ptr()); + zresult.reset(); + task_co->co->resume(); + return SW_OK; + } + // Server->taskCo + int task_index = -1; + SW_LOOP_N(task_co->count) { + if (task_co->list[i] == task_id) { + task_index = i; + break; + } + } + if (task_index < 0) { + php_swoole_fatal_error(E_WARNING, "task[%ld] is invalid", task_id); + return SW_OK; + } + (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(task_co->result) == task_co->count) { + task_co->co->resume(); + } + return SW_OK; + } + + 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()) { + req->info.ext_flags = req->info.ext_flags & (~SW_TASK_CALLBACK); + } else { + cb = callback_iterator->second; + } + } else { + cb = server_object->get_callback(SW_SERVER_CB_onFinish); + } + + if (UNEXPECTED(cb == nullptr)) { + php_swoole_fatal_error(E_WARNING, "require 'onFinish' callback"); + return SW_ERR; + } + + zval args[3]; + int argc; + args[0] = *zserv; + + 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) 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], (zend_long) task_id); + args[2] = zresult.value; + argc = 3; + } + + if (UNEXPECTED(!zend::function::call(cb, argc, args, nullptr, serv->enable_coroutine))) { + php_swoole_error(E_WARNING, "%s->onFinish handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); + } + if (req->info.ext_flags & SW_TASK_CALLBACK) { + sw_callable_free(server_object->property->task_callbacks[task_id]); + server_object->property->task_callbacks.erase(task_id); + } + if (serv->event_object) { + zval_ptr_dtor(&args[1]); + } + + return SW_OK; +} + +static void php_swoole_server_onStart(Server *serv) { + zval *zserv = php_swoole_server_zval_ptr(serv); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); + auto 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 (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)); + } +} + +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)); + 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 (SWOOLE_G(enable_library)) { + zend::function::call("\\Swoole\\Server\\Helper::onManagerStart", 1, zserv); + } + + if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, false))) { + php_swoole_error(E_WARNING, "%s->onManagerStart handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); + } +} + +static void php_swoole_server_onManagerStop(Server *serv) { + zval *zserv = 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_onManagerStop); + + 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_server_onBeforeShutdown(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_onBeforeShutdown); + + 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)); + } +} + +static void php_swoole_server_onWorkerStart(Server *serv, Worker *worker) { + zval *zserv = php_swoole_server_zval_ptr(serv); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); + auto 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_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); + + if (SWOOLE_G(enable_library)) { + zend::function::call("\\Swoole\\Server\\Helper::onWorkerStart", 2, args); + } + + if (cb && UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, serv->is_enable_coroutine()))) { + php_swoole_error(E_WARNING, "%s->onWorkerStart handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); + } +} + +static void php_swoole_server_onBeforeReload(Server *serv) { + zval *zserv = 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_onBeforeReload); + + if (SWOOLE_G(enable_library)) { + zend::function::call("\\Swoole\\Server\\Helper::onBeforeReload", 1, zserv); + } + + if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, false))) { + php_swoole_error(E_WARNING, "%s->onBeforeReload handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); + } +} + +static void php_swoole_server_onAfterReload(Server *serv) { + zval *zserv = 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_onAfterReload); + + if (SWOOLE_G(enable_library)) { + zend::function::call("\\Swoole\\Server\\Helper::onAfterReload", 1, zserv); + } + + if (cb && UNEXPECTED(!zend::function::call(cb, 1, zserv, nullptr, false))) { + php_swoole_error(E_WARNING, "%s->onAfterReload handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); + } +} + +static void php_swoole_server_onWorkerStop(Server *serv, Worker *worker) { + if (!worker->is_running()) { + return; + } + + 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_onWorkerStop); + + zval args[2]; + args[0] = *zserv; + ZVAL_LONG(&args[1], worker->id); + + if (SWOOLE_G(enable_library)) { + zend::function::call("\\Swoole\\Server\\Helper::onWorkerStop", 2, args); + } + + if (cb && UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, false))) { + php_swoole_error(E_WARNING, "%s->onWorkerStop handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); + } +} + +static void php_swoole_server_onWorkerExit(Server *serv, Worker *worker) { + zval *zserv = php_swoole_server_zval_ptr(serv); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); + auto fci_cache = server_object->get_callback(SW_SERVER_CB_onWorkerExit); + + zval args[2]; + args[0] = *zserv; + ZVAL_LONG(&args[1], worker->id); + + if (SWOOLE_G(enable_library)) { + zend::function::call("\\Swoole\\Server\\Helper::onWorkerExit", 2, args); + } + + 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_server_onUserWorkerStart(Server *serv, Worker *worker) { + zval *object; + zval *zserv = php_swoole_server_zval_ptr(serv); + + if (serv->is_thread_mode()) { + ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); + int index = worker->id - serv->worker_num - serv->task_worker_num; + object = server_object->property->user_processes[index]; + serv->get_worker_message_bus()->set_allocator(sw_zend_string_allocator()); + } else { + object = (zval *) worker->ptr; + } + + zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(object), ZEND_STRL("id"), worker->id); + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("master_pid"), serv->gs->master_pid); + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("manager_pid"), serv->gs->manager_pid); + + php_swoole_process_start(worker, object); +} + +static void php_swoole_server_onWorkerError(Server *serv, Worker *worker, const ExitStatus &exit_status) { + zval *zserv = php_swoole_server_zval_ptr(serv); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); + auto fci_cache = server_object->get_callback(SW_SERVER_CB_onWorkerError); + + zval args[5]; + int argc; + args[0] = *zserv; + + 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"), 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], exit_status.get_pid()); + ZVAL_LONG(&args[3], exit_status.get_code()); + ZVAL_LONG(&args[4], exit_status.get_signal()); + argc = 5; + } + + 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)); + } + + if (serv->event_object) { + zval_ptr_dtor(&args[1]); + } +} + +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; + } + + zval *zserv = php_swoole_server_zval_ptr(serv); + zval args[3]; + int argc; + args[0] = *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 (UNEXPECTED(!zend::function::call(cb, argc, args, nullptr, serv->enable_coroutine))) { + php_swoole_error(E_WARNING, "%s->onConnect handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); + } + + if (serv->event_object) { + zval_ptr_dtor(&args[1]); + } +} + +void php_swoole_server_onClose(Server *serv, DataHead *info) { + zval *zserv = 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_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); + co->resume(); + } + delete co_list; + } + } + + 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; + + 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) 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], session_id); + ZVAL_LONG(&args[2], info->reactor_id); + argc = 3; + } + + 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)); + } + + if (serv->event_object) { + zval_ptr_dtor(&args[1]); + } + } + if (conn->http2_stream) { + swoole_http2_server_session_free(conn); + } +} + +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 (cb) { + zval args[2]; + + args[0] = *zserv; + ZVAL_LONG(&args[1], info->fd); + + if (UNEXPECTED(!zend::function::call(cb, 2, args, nullptr, false))) { + php_swoole_error(E_WARNING, "%s->onBufferFull handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); + } + } +} + +void php_swoole_server_send_yield(Server *serv, SessionId session_id, zval *zdata, zval *return_value) { + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); + Coroutine *co = Coroutine::get_current_safe(); + char *data; + size_t length = php_swoole_get_send_data(zdata, &data); + + if (length == 0) { + RETURN_FALSE; + } + + 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(session_id, data, length); + if (!ret && swoole_get_last_error() == SW_ERROR_OUTPUT_SEND_YIELD && serv->send_yield) { + continue; + } else { + RETURN_BOOL(ret); + } + } +} + +static int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendData *data) { + 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 = *(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(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); + } + 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); + if (worker_id >= (zend_long) serv->worker_num) { + php_swoole_fatal_error(E_WARNING, "invalid target worker-id[" ZEND_LONG_FMT "]", worker_id); + worker_id = -1; + } + zval_ptr_dtor(&retval); + } + if (zdata) { + zval_ptr_dtor(zdata); + } + + /* the exception should only be thrown after unlocked */ + if (UNEXPECTED(EG(exception))) { + zend_exception_error(EG(exception), E_ERROR); + } + + return worker_id; +} + +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)); + + if (serv->send_yield) { + 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 (!co_list->empty()) { + Coroutine *co = co_list->front(); + co_list->pop_front(); + co->resume(); + } + delete co_list; + } + } + + 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(cb, 2, args, nullptr, false))) { + php_swoole_error(E_WARNING, "%s->onBufferEmpty handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); + } + } +} + +static void server_ctor(zval *zserv, Server *serv) { + ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); + *php_swoole_server_zval_ptr(serv) = *zserv; + server_set_ptr(zserv, serv); + + /* primary port */ + for (auto ls : serv->ports) { + php_swoole_server_add_port(server_object, ls); + } + + /* iterator */ + do { + zval connection_iterator; + object_init_ex(&connection_iterator, swoole_connection_iterator_ce); + + ConnectionIterator *iterator = php_swoole_connection_iterator_get_ptr(&connection_iterator); + iterator->serv = serv; + + zend_update_property(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("connections"), &connection_iterator); + zval_ptr_dtor(&connection_iterator); + } while (0); + + /* info */ + auto port = serv->get_primary_port(); + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("mode"), serv->get_mode()); + zend_update_property_stringl( + swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("host"), port->host.c_str(), port->host.length()); + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("port"), port->get_port()); + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("type"), port->get_type()); + zend_update_property_bool(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("ssl"), port->ssl); +} + +static PHP_METHOD(swoole_server, __construct) { + ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); + Server *serv = server_object->serv; + if (serv) { + 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; + char *host; + size_t host_len = 0; + zend_long sock_type = SW_SOCK_TCP; + zend_long serv_port = 0; + zend_long serv_mode = Server::MODE_BASE; + + if (!SWOOLE_G(cli)) { + zend_throw_exception_ex( + swoole_exception_ce, -1, "%s can only be used in CLI mode", 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 + Z_PARAM_LONG(serv_port) + Z_PARAM_LONG(serv_mode) + Z_PARAM_LONG(sock_type) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + 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); + + if (serv_mode == Server::MODE_BASE) { + serv->reactor_num = 1; + serv->worker_num = 1; + } + + 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; + } + } 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_ctor(zserv, serv); +} + +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)); + RETURN_FALSE; + } + + zval *zset = nullptr, *ztmp; + HashTable *vht; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(zset) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + vht = Z_ARRVAL_P(zset); + + 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, "chroot", ztmp)) { + serv->chroot_ = zend::String(ztmp).to_std_string(); + } + if (php_swoole_array_get_value(vht, "user", ztmp)) { + serv->user_ = zend::String(ztmp).to_std_string(); + } + if (php_swoole_array_get_value(vht, "group", ztmp)) { + serv->group_ = zend::String(ztmp).to_std_string(); + } + if (php_swoole_array_get_value(vht, "daemonize", ztmp)) { + serv->daemonize = zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "pid_file", ztmp)) { + serv->pid_file = zend::String(ztmp).to_std_string(); + } + if (php_swoole_array_get_value(vht, "reactor_num", ztmp)) { + zend_long v = zval_get_long(ztmp); + serv->reactor_num = SW_MAX(0, SW_MIN(v, UINT16_MAX)); + if (serv->reactor_num == 0) { + serv->reactor_num = SW_CPU_NUM; + } + } + if (php_swoole_array_get_value(vht, "single_thread", ztmp)) { + serv->single_thread = zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "worker_num", ztmp)) { + zend_long v = zval_get_long(ztmp); + serv->worker_num = SW_MAX(0, SW_MIN(v, UINT32_MAX)); + if (serv->worker_num == 0) { + serv->worker_num = SW_CPU_NUM; + } + } + if (php_swoole_array_get_value(vht, "max_wait_time", ztmp)) { + zend_long v = zval_get_long(ztmp); + 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 = 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 = SwooleG.enable_coroutine; + } + if (php_swoole_array_get_value(vht, "send_timeout", ztmp)) { + serv->send_timeout = zval_get_double(ztmp); + } + if (php_swoole_array_get_value(vht, "dispatch_mode", ztmp)) { + zend_long v = zval_get_long(ztmp); + serv->dispatch_mode = SW_MAX(0, SW_MIN(v, UINT8_MAX)); + } + if (php_swoole_array_get_value(vht, "send_yield", ztmp)) { + serv->send_yield = zval_is_true(ztmp); + if (serv->send_yield && + !(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 = false; + } + } else { + serv->send_yield = serv->enable_coroutine; + } + if (php_swoole_array_get_value(vht, "dispatch_func", ztmp)) { + auto fci_cache = sw_callable_create(ztmp); + if (fci_cache) { + if (serv->private_data_3) { + sw_callable_free(serv->private_data_3); + } + serv->private_data_3 = fci_cache; + serv->dispatch_func = php_swoole_server_dispatch_func; + serv->single_thread = true; + } + } + /** + * for dispatch_mode = 1/3 + */ + if (php_swoole_array_get_value(vht, "discard_timeout_request", ztmp)) { + serv->discard_timeout_request = zval_is_true(ztmp); + } + // onConnect/onClose event + if (php_swoole_array_get_value(vht, "enable_unsafe_event", ztmp)) { + serv->enable_unsafe_event = zval_is_true(ztmp); + } + // delay receive + if (php_swoole_array_get_value(vht, "enable_delay_receive", ztmp)) { + serv->enable_delay_receive = zval_is_true(ztmp); + } +#if defined(__linux__) and defined(HAVE_REUSEPORT) + if (php_swoole_array_get_value(vht, "enable_reuse_port", ztmp)) { + serv->enable_reuse_port = zval_is_true(ztmp); + } +#endif + // task use object + if (php_swoole_array_get_value(vht, "task_use_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)) { + serv->event_object = zval_is_true(ztmp); + if (serv->event_object) { + serv->task_object = true; + } + } + // task coroutine + if (php_swoole_array_get_value(vht, "task_enable_coroutine", ztmp)) { + serv->task_enable_coroutine = zval_is_true(ztmp); + } + // task_worker_num + if (php_swoole_array_get_value(vht, "task_worker_num", ztmp)) { + zend_long v = zval_get_long(ztmp); + serv->task_worker_num = SW_MAX(0, SW_MIN(v, UINT32_MAX)); + } + // task ipc mode, 1,2,3 + if (php_swoole_array_get_value(vht, "task_ipc_mode", ztmp)) { + zend_long v = zval_get_long(ztmp); + serv->task_ipc_mode = SW_MAX(0, SW_MIN(v, UINT8_MAX)); + } + /** + * Temporary file directory for task_worker + */ + if (php_swoole_array_get_value(vht, "task_tmpdir", ztmp)) { + zend::String str_v(ztmp); + swoole_set_task_tmpdir(str_v.to_std_string()); + } + // task_max_request + if (php_swoole_array_get_value(vht, "task_max_request", ztmp)) { + zend_long v = zval_get_long(ztmp); + serv->task_max_request = SW_MAX(0, SW_MIN(v, UINT32_MAX)); + // task_max_request_grace + if (php_swoole_array_get_value(vht, "task_max_request_grace", ztmp)) { + zend_long v = zval_get_long(ztmp); + serv->task_max_request_grace = SW_MAX(0, SW_MIN(v, UINT32_MAX)); + } else if (serv->task_max_request > SW_WORKER_MIN_REQUEST) { + serv->task_max_request_grace = serv->task_max_request / 2; + } + } + // max_connection + if (php_swoole_array_get_value(vht, "max_connection", ztmp) || php_swoole_array_get_value(vht, "max_conn", ztmp)) { + zend_long v = zval_get_long(ztmp); + serv->set_max_connection(SW_MAX(0, SW_MIN(v, UINT32_MAX))); + } + if (php_swoole_array_get_value(vht, "start_session_id", ztmp)) { + serv->set_start_session_id(zval_get_long(ztmp)); + } + // heartbeat_check_interval + 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)); + } else if (php_swoole_array_get_value(vht, "heartbeat_idle_time", ztmp)) { + zend_long v = zval_get_long(ztmp); + serv->heartbeat_check_interval = v > 2 ? v / 2 : 1; + } + // max_request + if (php_swoole_array_get_value(vht, "max_request", ztmp)) { + zend_long v = zval_get_long(ztmp); + serv->max_request = SW_MAX(0, SW_MIN(v, UINT32_MAX)); + // max_request_grace + if (php_swoole_array_get_value(vht, "max_request_grace", ztmp)) { + zend_long v = zval_get_long(ztmp); + serv->max_request_grace = SW_MAX(0, SW_MIN(v, UINT32_MAX)); + } else if (serv->max_request > SW_WORKER_MIN_REQUEST) { + serv->max_request_grace = serv->max_request / 2; + } + } + // reload async + if (php_swoole_array_get_value(vht, "reload_async", ztmp)) { + serv->reload_async = zval_is_true(ztmp); + } + // cpu affinity + if (php_swoole_array_get_value(vht, "open_cpu_affinity", ztmp)) { + serv->open_cpu_affinity = zval_is_true(ztmp); + } + // cpu affinity set + if (php_swoole_array_get_value(vht, "cpu_affinity_ignore", ztmp)) { + int ignore_num = zend_hash_num_elements(Z_ARRVAL_P(ztmp)); + if (ignore_num >= SW_CPU_NUM) { + php_swoole_fatal_error(E_ERROR, "cpu_affinity_ignore num must be less than cpu num (%d)", SW_CPU_NUM); + RETURN_FALSE; + } + int available_num = SW_CPU_NUM - ignore_num; + int *available_cpu = (int *) sw_malloc(sizeof(int) * available_num); + if (!available_cpu) { + php_swoole_fatal_error(E_WARNING, "malloc() failed"); + RETURN_FALSE; + } + int flag, i, available_i = 0; + + zval *zval_core = nullptr; + for (i = 0; i < SW_CPU_NUM; i++) { + flag = 1; + SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmp), zval_core) + if (i == zval_get_long(zval_core)) { + flag = 0; + break; + } + SW_HASHTABLE_FOREACH_END(); + if (flag) { + available_cpu[available_i] = i; + available_i++; + } + } + serv->cpu_affinity_available_num = available_num; + if (serv->cpu_affinity_available) { + sw_free(serv->cpu_affinity_available); + } + serv->cpu_affinity_available = available_cpu; + } + // parse cookie header + if (php_swoole_array_get_value(vht, "http_parse_cookie", ztmp)) { + serv->http_parse_cookie = zval_is_true(ztmp); + } + // parse x-www-form-urlencoded form data + if (php_swoole_array_get_value(vht, "http_parse_post", ztmp)) { + serv->http_parse_post = zval_is_true(ztmp); + } + // parse multipart/form-data file uploads + if (php_swoole_array_get_value(vht, "http_parse_files", ztmp)) { + serv->http_parse_files = zval_is_true(ztmp); + } +#ifdef SW_HAVE_COMPRESSION + // http content compression + if (php_swoole_array_get_value(vht, "http_compression", ztmp)) { + 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) { + level = UINT8_MAX; + } else if (level < 0) { + level = 0; + } + 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 + if (php_swoole_array_get_value(vht, "websocket_compression", ztmp)) { + serv->websocket_compression = zval_is_true(ztmp); + } +#endif + + // temporary directory for HTTP uploaded file. + if (php_swoole_array_get_value(vht, "upload_tmp_dir", ztmp)) { + zend::String str_v(ztmp); + if (php_swoole_create_dir(str_v.val(), str_v.len()) < 0) { + php_swoole_fatal_error(E_ERROR, "Unable to create upload_tmp_dir[%s]", str_v.val()); + return; + } + 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 + */ + if (php_swoole_array_get_value(vht, "enable_static_handler", ztmp)) { + serv->enable_static_handler = zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "document_root", ztmp)) { + zend::String str_v(ztmp); + if (str_v.len() >= PATH_MAX) { + php_swoole_fatal_error(E_ERROR, "The length of document_root must be less than %d", PATH_MAX); + return; + } + serv->set_document_root(std::string(str_v.val(), str_v.len())); + } + if (php_swoole_array_get_value(vht, "http_autoindex", ztmp)) { + serv->http_autoindex = zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "http_index_files", ztmp)) { + if (ZVAL_IS_ARRAY(ztmp)) { + zval *_http_index_files; + SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmp), _http_index_files) + zend::String __http_index_files(_http_index_files); + if (__http_index_files.len() > 0) { + serv->add_static_handler_index_files(__http_index_files.to_std_string()); + } + SW_HASHTABLE_FOREACH_END(); + } else { + php_swoole_fatal_error(E_ERROR, "http_index_files must be array"); + 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 + */ + if (php_swoole_array_get_value(vht, "static_handler_locations", ztmp)) { + if (ZVAL_IS_ARRAY(ztmp)) { + zval *_location; + SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmp), _location) + zend::String __location(_location); + if (__location.len() > 0 && __location.val()[0] == '/') { + serv->add_static_handler_location(__location.to_std_string()); + } + SW_HASHTABLE_FOREACH_END(); + } else { + php_swoole_fatal_error(E_ERROR, "static_handler_locations num must be array"); + RETURN_FALSE; + } + } + /** + * buffer input size + */ + if (php_swoole_array_get_value(vht, "input_buffer_size", ztmp) || + php_swoole_array_get_value(vht, "buffer_input_size", ztmp)) { + zend_long v = php_swoole_parse_to_size(ztmp); + serv->input_buffer_size = SW_MAX(0, SW_MIN(v, UINT32_MAX)); + } + /** + * buffer output size + */ + if (php_swoole_array_get_value(vht, "output_buffer_size", ztmp) || + php_swoole_array_get_value(vht, "buffer_output_size", 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 + if (php_swoole_array_get_value(vht, "message_queue_key", ztmp)) { + zend_long v = zval_get_long(ztmp); + serv->message_queue_key = SW_MAX(0, SW_MIN(v, INT64_MAX)); + } +#ifdef SW_THREAD + // bootstrap + if (php_swoole_array_get_value(vht, "bootstrap", ztmp)) { + zend::object_set(ZEND_THIS, ZEND_STRL("bootstrap"), ztmp); + } else { + zend::object_set(ZEND_THIS, ZEND_STRL("bootstrap"), SG(request_info).path_translated); + } + // thread arguments + if (php_swoole_array_get_value(vht, "init_arguments", ztmp)) { + server_object->init_arguments = *ztmp; + } else { + ZVAL_NULL(&server_object->init_arguments); + } +#endif + + if (serv->task_enable_coroutine && + (serv->task_ipc_mode == Server::TASK_IPC_MSGQUEUE || serv->task_ipc_mode == Server::TASK_IPC_PREEMPTIVE)) { + php_swoole_fatal_error(E_ERROR, "cannot use msgqueue when task_enable_coroutine is enable"); + RETURN_FALSE; + } + + sw_zend_call_method_with_1_params( + server_object->property->ports.at(0), swoole_server_port_ce, nullptr, "set", nullptr, zset); + + zval *zsetting = sw_zend_read_and_convert_property_array(swoole_server_ce, ZEND_THIS, ZEND_STRL("setting"), 0); + php_array_merge(Z_ARRVAL_P(zsetting), Z_ARRVAL_P(zset)); + + RETURN_TRUE; +} + +static PHP_METHOD(swoole_server, on) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + 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; + } + + zval *name; + zval *cb; + + 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); + + ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); + + auto i = server_event_map.find(_event_name_tolower.to_std_string()); + if (i == server_event_map.end()) { + zval *port_object = server_object->property->ports.at(0); + zval retval; + 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 { + int event_type = i->second.type; + std::string property_name = "on" + i->second.name; + + zend_update_property( + swoole_server_ce, SW_Z8_OBJ_P(ZEND_THIS), property_name.c_str(), property_name.length(), cb); + + if (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; + } +} + +static PHP_METHOD(swoole_server, getCallback) { + zval *name; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(name) + 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); + auto i = server_event_map.find(_event_name_tolower.to_std_string()); + if (i != server_event_map.end()) { + std::string property_name = "on" + i->second.name; + // Notice: we should use Z_OBJCE_P instead of swoole_server_ce, because we need to consider the subclasses. + zval rv, + *property = zend_read_property( + Z_OBJCE_P(ZEND_THIS), SW_Z8_OBJ_P(ZEND_THIS), property_name.c_str(), property_name.length(), 1, &rv); + if (!ZVAL_IS_NULL(property)) { + RETURN_ZVAL(property, 1, 0); + } + } + + ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); + sw_zend_call_method_with_1_params( + server_object->property->ports.at(0), swoole_server_port_ce, nullptr, "getcallback", return_value, name); +} + +static PHP_METHOD(swoole_server, listen) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + 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; + zend_long sock_type; + zend_long port; + + 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; + 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; + } + + ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); + zval *port_object = php_swoole_server_add_port(server_object, ls); + RETURN_ZVAL(port_object, 1, 0); +} + +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_worker_thread() && serv->is_started()) { + php_swoole_fatal_error(E_WARNING, "server is running, cannot add process"); + RETURN_FALSE; + } + + int worker_id; + Worker *worker; + zval *process; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(process) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + 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_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); + + 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; + } + + char *name; + size_t l_name; + zend_long accepted_process_types; + zval *zfn; + + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_STRING(name, l_name) + Z_PARAM_LONG(accepted_process_types) + Z_PARAM_ZVAL(zfn) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (accepted_process_types & Server::Command::REACTOR_THREAD) { + php_swoole_fatal_error(E_WARNING, "only support worker/task_worker process"); + RETURN_FALSE; + } + + auto cb = sw_callable_create(zfn); + if (!cb) { + RETURN_FALSE; + } + + Server::Command::Handler fn = [cb](Server *serv, const std::string &msg) { + zval *zserv = php_swoole_server_zval_ptr(serv); + zval argv[2]; + argv[0] = *zserv; + ZVAL_STRINGL(&argv[1], msg.c_str(), msg.length()); + zval return_value; + + if (UNEXPECTED(!zend::function::call(cb, 2, argv, &return_value, false))) { + php_swoole_fatal_error(E_WARNING, "%s: command handler error", ZSTR_VAL(swoole_server_ce->name)); + return std::string("{\"data\": \"failed to call function\", \"code\": -1}"); + } + + if (!ZVAL_IS_STRING(&return_value)) { + return std::string("{\"data\": \"wrong return type\", \"code\": -2}"); + } + + return std::string(Z_STRVAL(return_value), Z_STRLEN(return_value)); + }; + + 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)); + RETURN_FALSE; + } + if (serv->is_shutdown()) { + php_swoole_fatal_error( + E_WARNING, "server have been shutdown, unable to execute %s->start()", SW_Z_OBJCE_NAME_VAL_P(zserv)); + RETURN_FALSE; + } + + if (sw_reactor()) { + php_swoole_fatal_error( + E_WARNING, "eventLoop has already been created, unable to start %s", SW_Z_OBJCE_NAME_VAL_P(zserv)); + RETURN_FALSE; + } + + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); + +#ifdef SW_THREAD + zend_string *bootstrap = nullptr; + ZendArray *thread_argv = nullptr; + + if (serv->is_thread_mode()) { + zval *_bootstrap = zend::object_get(ZEND_THIS, ZEND_STRL("bootstrap")); + bootstrap = zend_string_dup(Z_STR_P(_bootstrap), 1); + + if (!ZVAL_IS_NULL(&server_object->init_arguments)) { + zval _thread_argv; + call_user_function(NULL, NULL, &server_object->init_arguments, &_thread_argv, 0, NULL); + if (ZVAL_IS_ARRAY(&_thread_argv)) { + thread_argv = ZendArray::from(Z_ARRVAL(_thread_argv)); + } + zval_ptr_dtor(&_thread_argv); + } + + serv->worker_thread_start = [bootstrap, thread_argv](const WorkerFn &fn) { + worker_thread_fn = fn; + zend_string *bootstrap_copy = zend_string_dup(bootstrap, 1); + if (thread_argv) { + thread_argv->add_ref(); + } + php_swoole_thread_start(bootstrap_copy, thread_argv); + }; + + serv->worker_thread_get_exit_status = [](pthread_t ptid) -> int { + return php_swoole_thread_get_exit_status(ptid); + }; + + serv->worker_thread_join = [](pthread_t ptid) { php_swoole_thread_join(ptid); }; + + /** + *The hook must be enabled before creating child threads. + *The stream factory and ops are global variables, not thread-local resources. + *These runtime hooks must be modified in a single-threaded environment. + */ + if (PHPCoroutine::get_hook_flags() > 0) { + PHPCoroutine::enable_hook(PHPCoroutine::get_hook_flags()); + } + } +#endif + + server_object->register_callback(); + server_object->on_before_start(); + + if (serv->start() < 0) { + php_swoole_fatal_error(E_ERROR, "failed to start server. Error: %s", serv->get_startup_error_message()); + } + +#ifdef SW_THREAD + if (bootstrap) { + zend_string_release(bootstrap); + } + if (thread_argv) { + thread_argv->del_ref(); + } +#endif + + RETURN_TRUE; +} + +static PHP_METHOD(swoole_server, send) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (sw_unlikely(!serv->is_started())) { + php_swoole_fatal_error(E_WARNING, "server is not running"); + RETURN_FALSE; + } + + zend_long fd; + zval *zfd; + zval *zdata; + zend_long server_socket = -1; + + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_ZVAL(zfd) + Z_PARAM_ZVAL(zdata) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(server_socket) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (UNEXPECTED(ZVAL_IS_NULL(zfd))) { + php_swoole_fatal_error(E_WARNING, "fd can not be null"); + RETURN_FALSE; + } + + char *data; + 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; + } + + // UNIX DGRAM SOCKET + if (serv->have_dgram_sock && Z_TYPE_P(zfd) == IS_STRING && Z_STRVAL_P(zfd)[0] == '/') { + network::Socket *sock = server_socket == -1 ? serv->dgram_socket : serv->get_server_socket(server_socket); + if (sock == nullptr) { + RETURN_FALSE; + } + RETURN_BOOL(sock->sendto(Z_STRVAL_P(zfd), 0, data, length) > 0); + } + + fd = zval_get_long(zfd); + if (UNEXPECTED(fd <= 0)) { + php_swoole_fatal_error(E_WARNING, "invalid fd[" ZEND_LONG_FMT "]", fd); + RETURN_FALSE; + } + bool ret = serv->send(fd, data, length); + if (!ret && swoole_get_last_error() == SW_ERROR_OUTPUT_SEND_YIELD) { + php_swoole_server_send_yield(serv, fd, zdata, return_value); + } else { + RETURN_BOOL(ret); + } +} + +static PHP_METHOD(swoole_server, sendto) { + 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; + } + + char *addr; + size_t addr_len; + zend_long port; + char *data; + size_t len; + zend_long server_socket_fd = -1; + enum swSocketType type; + + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_STRING(addr, addr_len) + Z_PARAM_LONG(port) + Z_PARAM_STRING(data, len) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(server_socket_fd) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (len == 0) { + php_swoole_error_ex(E_WARNING, SW_ERROR_NO_PAYLOAD, "the data sent must not be empty"); + RETURN_FALSE; + } + + if (addr[0] == '/') { + type = SW_SOCK_UNIX_DGRAM; + } else if (strchr(addr, ':')) { + type = SW_SOCK_UDP6; + } else { + type = SW_SOCK_UDP; + } + + network::Socket *server_socket = nullptr; + + switch (type) { + case SW_SOCK_UDP: + if (!serv->udp_socket_ipv4) { + php_swoole_fatal_error(E_WARNING, "UDP listener has to be added before executing sendto"); + RETURN_FALSE; + } else { + server_socket = server_socket_fd < 0 ? serv->udp_socket_ipv4 : serv->get_server_socket(server_socket_fd); + } + break; + case SW_SOCK_UDP6: + if (!serv->udp_socket_ipv6) { + php_swoole_fatal_error(E_WARNING, "UDP6 listener has to be added before executing sendto"); + RETURN_FALSE; + } else { + server_socket = server_socket_fd < 0 ? serv->udp_socket_ipv6 : serv->get_server_socket(server_socket_fd); + } + break; + case SW_SOCK_UNIX_DGRAM: + if (!serv->dgram_socket) { + php_swoole_fatal_error(E_WARNING, "UnixDgram listener has to be added before executing sendto"); + RETURN_FALSE; + } else { + server_socket = server_socket_fd < 0 ? serv->dgram_socket : serv->get_server_socket(server_socket_fd); + } + break; + default: + abort(); + break; + } + SW_CHECK_RETURN(server_socket->sendto(addr, port, data, len)); +} + +static PHP_METHOD(swoole_server, sendfile) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (sw_unlikely(!serv->is_started())) { + php_swoole_fatal_error(E_WARNING, "server is not running"); + RETURN_FALSE; + } + + zend_long fd; + char *filename; + size_t len; + zend_long offset = 0; + zend_long length = 0; + + 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); + RETURN_FALSE; + } + + RETURN_BOOL(serv->sendfile(fd, filename, len, offset, length)); +} + +static PHP_METHOD(swoole_server, close) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (sw_unlikely(!serv->is_started())) { + php_swoole_fatal_error(E_WARNING, "server is not running"); + RETURN_FALSE; + } + + zend_long fd; + zend_bool reset = false; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_LONG(fd) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(reset) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_BOOL(serv->close(fd, reset)); +} + +static PHP_METHOD(swoole_server, pause) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (sw_unlikely(!serv->is_started())) { + php_swoole_fatal_error(E_WARNING, "server is not running"); + RETURN_FALSE; + } + + zend_long fd; + + 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); + RETURN_FALSE; + } + RETURN_BOOL(serv->feedback(conn, SW_SERVER_EVENT_PAUSE_RECV)); +} + +static PHP_METHOD(swoole_server, resume) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (sw_unlikely(!serv->is_started())) { + php_swoole_fatal_error(E_WARNING, "server is not running"); + RETURN_FALSE; + } + + zend_long fd; + + 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); + RETURN_FALSE; + } + RETURN_BOOL(serv->feedback(conn, SW_SERVER_EVENT_RESUME_RECV)); +} + +static PHP_METHOD(swoole_server, stats) { + 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; + } + + 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->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); + 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); + 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)) { + add_assoc_long_ex(return_value, ZEND_STRL("task_queue_num"), queue_num); + add_assoc_long_ex(return_value, ZEND_STRL("task_queue_bytes"), queue_bytes); + } + } + + 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) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (sw_unlikely(!serv->is_started())) { + php_swoole_fatal_error(E_WARNING, "server is not running"); + RETURN_FALSE; + } + + zend_bool only_reload_task_workers = 0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(only_reload_task_workers) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_BOOL(serv->reload(!only_reload_task_workers)); +} + +static PHP_METHOD(swoole_server, heartbeat) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (sw_unlikely(!serv->is_started())) { + php_swoole_fatal_error(E_WARNING, "server is not running"); + RETURN_FALSE; + } + + zend_bool close_connection = 0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(close_connection) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (serv->heartbeat_check_interval < 1) { + RETURN_FALSE; + } + + array_init(return_value); + double now = microtime(); + + 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(session_id, false); + } + add_next_index_long(return_value, session_id); + }); +} + +static PHP_METHOD(swoole_server, taskwait) { + 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 (!serv->is_worker()) { + php_swoole_fatal_error(E_WARNING, "taskwait method can only be used in the worker process"); + RETURN_FALSE; + } + + zval *zdata; + double timeout = SW_TASKWAIT_TIMEOUT; + zend_long dst_worker_id = -1; + + 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_server_task_check_param(serv, dst_worker_id) < 0) { + RETURN_FALSE; + } + + EventData buf; + if (php_swoole_server_task_pack(zdata, &buf) < 0) { + RETURN_FALSE; + } + + TaskId task_id = serv->get_task_id(&buf); + + // coroutine + if (swoole_coroutine_is_in()) { + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); + buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE); + + TaskCo task_co{}; + task_co.co = Coroutine::get_current_safe(); + task_co.count = 1; + task_co.result = return_value; + + if (!serv->task(&buf, (int *) &dst_worker_id)) { + RETURN_FALSE; + } + + 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 (!retval) { + RETURN_FALSE; + } + } else { + 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); + } + } +} + +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"); + RETURN_FALSE; + } + if (!serv->is_worker()) { + php_swoole_fatal_error(E_WARNING, "taskWaitMulti method can only be used in the worker process"); + RETURN_FALSE; + } + + zval *ztasks; + double timeout = SW_TASKWAIT_TIMEOUT; + + 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); + + int dst_worker_id; + int i = 0; + int n_task = php_swoole_array_length(ztasks); + + if (n_task >= SW_MAX_CONCURRENT_TASK) { + php_swoole_fatal_error(E_WARNING, "too many concurrent tasks"); + RETURN_FALSE; + } + + TaskId list_of_id[SW_MAX_CONCURRENT_TASK] = {}; + + uint64_t notify; + EventData *task_result = serv->get_task_result(); + sw_memset_zero(task_result, sizeof(*task_result)); + 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()) { + RETURN_FALSE; + } + std::string file_path = fp.get_path(); + fp.close(); + + int *finish_count = (int *) task_result->data; + + worker->lock->lock(); + *finish_count = 0; + + swoole_strlcpy(task_result->data + 4, file_path.c_str(), SW_TASK_TMP_PATH_SIZE); + worker->lock->unlock(); + + // 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) { + } + task_notify_socket->set_block(); + + zval *ztask; + SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztasks), 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; + } + buf.info.ext_flags |= SW_TASK_WAITALL; + dst_worker_id = -1; + sw_atomic_fetch_add(&serv->gs->tasking_num, 1); + 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); + n_task--; + } else { + sw_atomic_fetch_sub(&serv->gs->tasking_num, 1); + } + list_of_id[i] = task_id; + i++; + SW_HASHTABLE_FOREACH_END(); + + if (n_task == 0) { + swoole_set_last_error(SW_ERROR_TASK_DISPATCH_FAIL); + RETURN_FALSE; + } + + pipe->set_timeout(timeout); + double _now = microtime(); + while (n_task > 0) { + int ret = pipe->read(¬ify, sizeof(notify)); + if (ret > 0 && *finish_count < n_task) { + if (microtime() - _now < timeout) { + continue; + } + } + break; + } + + worker->lock->lock(); + auto content = swoole::file_get_contents(file_path); + worker->lock->unlock(); + + if (content.get() == nullptr) { + RETURN_FALSE; + } + + do { + 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, &zresult); + _next: + content->offset += result->size(); + } while (content->offset < 0 || (size_t) content->offset < content->length); + // delete tmp file + unlink(file_path.c_str()); +} + +static PHP_METHOD(swoole_server, taskCo) { + 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 (!serv->is_worker()) { + php_swoole_fatal_error(E_WARNING, "taskCo method can only be used in the worker process"); + RETURN_FALSE; + } + + ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); + + zval *ztasks; + double timeout = SW_TASKWAIT_TIMEOUT; + + 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); + + if (n_task >= SW_MAX_CONCURRENT_TASK) { + php_swoole_fatal_error(E_WARNING, "too many concurrent tasks"); + RETURN_FALSE; + } + + if (php_swoole_server_task_check_param(serv, dst_worker_id) < 0) { + RETURN_FALSE; + } + + TaskId *list = (TaskId *) ecalloc(n_task, sizeof(TaskId)); + if (list == nullptr) { + RETURN_FALSE; + } + + TaskCo task_co; + task_co.co = Coroutine::get_current_safe(); + + array_init_size(return_value, n_task); + + 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++; + } + SW_HASHTABLE_FOREACH_END(); + + if (n_task == 0) { + swoole_set_last_error(SW_ERROR_TASK_DISPATCH_FAIL); + RETURN_FALSE; + } + + task_co.result = return_value; + task_co.list = list; + task_co.count = n_task; + + if (!task_co.co->yield_ex(timeout)) { + bool is_called_in_taskCo = strcasecmp(EX(func)->internal_function.function_name->val, "taskCo") == 0; + for (uint32_t i = 0; i < n_task; i++) { + if (!zend_hash_index_exists(Z_ARRVAL_P(return_value), i)) { + if (is_called_in_taskCo) { + add_index_bool(return_value, i, 0); + } + server_object->property->task_coroutine_map.erase(list[i]); + } + } + } +} + +static PHP_METHOD(swoole_server, task) { + 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; + } + ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); + + zval *zdata; + zend_long dst_worker_id = -1; + zval *zfn = nullptr; + + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_ZVAL(zdata) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(dst_worker_id) + Z_PARAM_ZVAL(zfn) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (php_swoole_server_task_check_param(serv, dst_worker_id) < 0) { + RETURN_FALSE; + } + + EventData buf; + TaskId task_id = php_swoole_server_task_pack(zdata, &buf); + if (task_id < 0) { + RETURN_FALSE; + } + + if (!serv->is_worker()) { + buf.info.ext_flags |= SW_TASK_NOREPLY; + } else if (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; + } + + buf.info.ext_flags |= SW_TASK_NONBLOCK; + + if (serv->task(&buf, (int *) &dst_worker_id)) { + RETURN_LONG(task_id); + } else { + RETURN_FALSE; + } +} + +static PHP_METHOD(swoole_server, command) { + char *name; + size_t l_name; + zend_long process_id, process_type; + zval *zdata; + zend_bool json_decode = true; + + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (sw_unlikely(!serv->is_started())) { + php_swoole_fatal_error(E_WARNING, "server is not running"); + RETURN_FALSE; + } + + ZEND_PARSE_PARAMETERS_START(4, 5) + Z_PARAM_STRING(name, l_name) + Z_PARAM_LONG(process_id) + Z_PARAM_LONG(process_type) + Z_PARAM_ZVAL(zdata) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(json_decode) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + 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) { + 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 (!serv->onPipeMessage) { + php_swoole_fatal_error(E_WARNING, "onPipeMessage is null, can't use sendMessage"); + RETURN_FALSE; + } + + zval *zmessage; + zend_long worker_id = -1; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(zmessage) + Z_PARAM_LONG(worker_id) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + 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 < 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; + if (php_swoole_server_task_pack(zmessage, &buf) < 0) { + RETURN_FALSE; + } + + RETURN_BOOL(serv->send_pipe_message(worker_id, &buf)); +} + +static PHP_METHOD(swoole_server, finish) { + 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 (sw_unlikely(serv->task_enable_coroutine)) { + php_swoole_fatal_error(E_ERROR, + "please use %s->finish instead when task_enable_coroutine is enable", + ZSTR_VAL(swoole_server_task_ce->name)); + RETURN_FALSE; + } + + zval *zdata; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zdata) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_BOOL(php_swoole_server_task_finish(serv, zdata, nullptr)); +} + +static PHP_METHOD(swoole_server_task, finish) { + Server *serv = php_swoole_server_task_get_server(ZEND_THIS); + if (sw_unlikely(!serv->is_started())) { + php_swoole_fatal_error(E_WARNING, "server is not running"); + RETURN_FALSE; + } + + zval *zdata; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zdata) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + DataHead *info = php_swoole_server_task_get_info(ZEND_THIS); + RETURN_BOOL(php_swoole_server_task_finish(serv, zdata, (EventData *) info)); +} + +static PHP_METHOD(swoole_server_task, pack) { + zval *zdata; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zdata) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + EventData buf; + if (php_swoole_server_task_pack(zdata, &buf) < 0) { + RETURN_FALSE; + } + buf.info.ext_flags |= (SW_TASK_NONBLOCK | SW_TASK_NOREPLY); + + RETURN_STRINGL((char *) &buf, 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) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (sw_unlikely(!serv->is_started())) { + php_swoole_fatal_error(E_WARNING, "server is not running"); + RETURN_FALSE; + } + + zend_long fd = 0; + zend_long uid = 0; + + 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_verify(fd); + if (conn == nullptr) { + RETURN_FALSE; + } + + sw_spinlock(&conn->lock); + if (conn->uid != 0) { + RETVAL_FALSE; + } else { + conn->uid = (uint32_t) uid; + RETVAL_TRUE; + } + sw_spinlock_release(&conn->lock); +} + +#ifdef SWOOLE_SOCKETS_SUPPORT +static PHP_METHOD(swoole_server, getSocket) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + + zend_long port = 0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(port) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + const ListenPort *lp = serv->get_port(port); + php_socket *socket_object = php_swoole_convert_to_socket(lp->get_fd()); + + if (!socket_object) { + RETURN_FALSE; + } + SW_ZVAL_SOCKET(return_value, socket_object); + zval *zsocket = sw_zval_dup(return_value); + Z_TRY_ADDREF_P(zsocket); +} +#endif + +static PHP_METHOD(swoole_server, getClientInfo) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (sw_unlikely(!serv->is_started())) { + php_swoole_fatal_error(E_WARNING, "server is not running"); + RETURN_FALSE; + } + + zend_long fd; + zend_long reactor_id = -1; + zend_bool dont_check_connection = 0; + + 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) { + RETURN_FALSE; + } + // connection is closed + if (conn->active == 0 && !dont_check_connection) { + RETURN_FALSE; + } else { + array_init(return_value); + + 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) { + add_assoc_long(return_value, "websocket_status", conn->websocket_status); + } + +#ifdef SW_USE_OPENSSL + if (conn->ssl_client_cert && conn->ssl_client_cert_pid == SwooleG.pid) { + add_assoc_stringl( + return_value, "ssl_client_cert", conn->ssl_client_cert->str, conn->ssl_client_cert->length); + } +#endif + // server socket + 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_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_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); + add_assoc_long(return_value, "close_errno", conn->close_errno); + add_assoc_long(return_value, "recv_queued_bytes", conn->recv_queued_bytes); + add_assoc_long(return_value, "send_queued_bytes", conn->send_queued_bytes); + } +} + +static PHP_METHOD(swoole_server, getClientList) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (sw_unlikely(!serv->is_started())) { + php_swoole_fatal_error(E_WARNING, "server is not running"); + RETURN_FALSE; + } + + zend_long start_session_id = 0; + zend_long find_count = 10; + + 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) { + php_swoole_fatal_error(E_WARNING, "swoole connection list max_find_count=%d", SW_MAX_FIND_COUNT); + RETURN_FALSE; + } + + // copy it out to avoid being overwritten by other processes + int serv_max_fd = serv->get_maxfd(); + int start_fd; + + if (start_session_id == 0) { + start_fd = serv->get_minfd(); + } else { + Connection *conn = serv->get_connection_verify(start_session_id); + if (!conn) { + RETURN_FALSE; + } + start_fd = conn->fd; + } + + if ((int) start_fd >= serv_max_fd) { + RETURN_FALSE; + } + + array_init(return_value); + int fd = start_fd + 1; + + for (; fd <= serv_max_fd; fd++) { + 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; + } + add_next_index_long(return_value, session_id); + find_count--; + } + // finish fetch + if (find_count <= 0) { + break; + } + } +} + +static PHP_METHOD(swoole_server, sendwait) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (sw_unlikely(!serv->is_started())) { + php_swoole_fatal_error(E_WARNING, "server is not running"); + RETURN_FALSE; + } + + zend_long fd; + zval *zdata; + + 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_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 only be used with base mode and must be within worker process"); + RETURN_FALSE; + } + + RETURN_BOOL(serv->sendwait(fd, data, length)); +} + +static PHP_METHOD(swoole_server, exists) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (sw_unlikely(!serv->is_started())) { + php_swoole_fatal_error(E_WARNING, "server is not running"); + RETURN_FALSE; + } + + zend_long session_id; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(session_id) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + Connection *conn = serv->get_connection_verify(session_id); + if (!conn || conn->closed) { + RETURN_FALSE; + } else { + RETURN_TRUE; + } +} + +static PHP_METHOD(swoole_server, protect) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (sw_unlikely(!serv->is_started())) { + php_swoole_fatal_error(E_WARNING, "server is not running"); + RETURN_FALSE; + } + + zend_long session_id; + zend_bool value = 1; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_LONG(session_id) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(value) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + Connection *conn = serv->get_connection_verify(session_id); + if (!conn || conn->closed) { + RETURN_FALSE; + } else { + conn->protect = value; + RETURN_TRUE; + } +} + +static PHP_METHOD(swoole_server, getWorkerId) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (!serv->is_worker() && !serv->is_task_worker()) { + RETURN_FALSE; + } else { + RETURN_LONG(swoole_get_process_id()); + } +} + +static PHP_METHOD(swoole_server, getWorkerStatus) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (sw_unlikely(!serv->is_started())) { + php_swoole_fatal_error(E_WARNING, "server is not running"); + RETURN_FALSE; + } + + zend_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; + } else { + RETURN_LONG(worker->status); + } +} + +static PHP_METHOD(swoole_server, getWorkerPid) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + 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->get_manager_pid()); +} + +static PHP_METHOD(swoole_server, getMasterPid) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + RETURN_LONG(serv->get_master_pid()); +} + +static PHP_METHOD(swoole_server, shutdown) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + RETURN_BOOL(serv->shutdown()); +} + +static PHP_METHOD(swoole_server, stop) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (sw_unlikely(!serv->is_started())) { + php_swoole_fatal_error(E_WARNING, "server is not running"); + RETURN_FALSE; + } + + zend_bool wait_reactor = 0; + zend_long worker_id = -1; + + 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) { + zend_throw_error(NULL, "please use the Swoole\\Server->connections"); + return; +} + +static PHP_METHOD(swoole_connection_iterator, rewind) { + ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); + iterator->index = 0; + iterator->current_fd = iterator->serv->get_minfd(); +} + +static PHP_METHOD(swoole_connection_iterator, valid) { + ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); + int fd = iterator->current_fd; + int max_fd = iterator->serv->get_maxfd(); + + for (; fd <= max_fd; fd++) { + 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; +} + +static PHP_METHOD(swoole_connection_iterator, current) { + ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); + RETURN_LONG(iterator->session_id); +} + +static PHP_METHOD(swoole_connection_iterator, next) { + ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); + iterator->current_fd++; +} + +static PHP_METHOD(swoole_connection_iterator, key) { + ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); + RETURN_LONG(iterator->index); +} + +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->get_connection_num()); + } else { + 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 = php_swoole_server_zval_ptr(iterator->serv); + zval *zfd; + zval retval; + + 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 = php_swoole_server_zval_ptr(iterator->serv); + zval *zfd; + zval retval; + + 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); +} + +static PHP_METHOD(swoole_connection_iterator, offsetSet) {} +static PHP_METHOD(swoole_connection_iterator, offsetUnset) {} +static PHP_METHOD(swoole_connection_iterator, __destruct) {} diff --git a/ext-src/swoole_server_port.cc b/ext-src/swoole_server_port.cc new file mode 100644 index 00000000000..9e5fdadb0a8 --- /dev/null +++ b/ext-src/swoole_server_port.cc @@ -0,0 +1,684 @@ +/* + +----------------------------------------------------------------------+ + | 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_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 ServerPortEvent { + enum php_swoole_server_port_callback_type type; + std::string 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", 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 ServerPortObject { + ListenPort *port; + ServerPortProperty property; + zend_object std; +}; + +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) { + return php_swoole_server_port_fetch_object(Z_OBJ_P(zobject))->port; +} + +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, "Invalid instance of %s", SW_Z_OBJCE_NAME_VAL_P(zobject)); + } + return port; +} + +void php_swoole_server_port_set_ptr(zval *zobject, ListenPort *port) { + php_swoole_server_port_fetch_object(Z_OBJ_P(zobject))->port = port; +} + +ServerPortProperty *php_swoole_server_port_get_property(zval *zobject) { + return &php_swoole_server_port_fetch_object(Z_OBJ_P(zobject))->property; +} + +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, "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) { + 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->callbacks[j]) { + sw_callable_free(property->callbacks[j]); + property->callbacks[j] = nullptr; + } + } + property->serv = nullptr; + } + + ListenPort *port = server_port->port; + if (port) { + if (port->protocol.private_data_1) { + sw_callable_free(port->protocol.private_data_1); + port->protocol.private_data_1 = nullptr; + } + server_port->port = nullptr; + } +} + +static void php_swoole_server_port_free_object(zend_object *object) { + php_swoole_server_port_deref(object); + zend_object_std_dtor(object); +} + +static zend_object *php_swoole_server_port_create_object(zend_class_entry *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; + return &server_port->std; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_server_port, __construct); +static PHP_METHOD(swoole_server_port, __destruct); +static PHP_METHOD(swoole_server_port, on); +static PHP_METHOD(swoole_server_port, set); +static PHP_METHOD(swoole_server_port, getCallback); + +#ifdef SWOOLE_SOCKETS_SUPPORT +static PHP_METHOD(swoole_server_port, getSocket); +#endif +SW_EXTERN_C_END + +// clang-format off +const zend_function_entry swoole_server_port_methods[] = +{ + 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_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", 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, + ServerPortObject, + std); + + zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onConnect"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onReceive"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onClose"), ZEND_ACC_PRIVATE); + zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onPacket"), ZEND_ACC_PRIVATE); + 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("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/Worker] + */ +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; + + 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); + zval_ptr_dtor(&retval); + } + zval_ptr_dtor(&zdata); + + /* the exception should only be thrown after unlocked */ + if (UNEXPECTED(EG(exception))) { + zend_exception_error(EG(exception), E_ERROR); + } + + return ret; +} + +static PHP_METHOD(swoole_server_port, __construct) { + zend_throw_error(NULL, "please use the Swoole\\Server->listen method"); + RETURN_FALSE; +} + +static PHP_METHOD(swoole_server_port, __destruct) {} + +#ifdef SW_USE_OPENSSL +static bool php_swoole_server_set_ssl_option(zend_array *vht, SSLContext *ctx) { + zval *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) { + php_swoole_fatal_error(E_ERROR, "ssl cert file[%s] not found", str_v.val()); + return false; + } + ctx->cert_file = str_v.to_std_string(); + } + if (php_swoole_array_get_value(vht, "ssl_key_file", ztmp)) { + zend::String str_v(ztmp); + if (access(str_v.val(), R_OK) < 0) { + php_swoole_fatal_error(E_ERROR, "ssl key file[%s] not found", str_v.val()); + return false; + } + ctx->key_file = str_v.to_std_string(); + } + return true; +} +#endif + +static PHP_METHOD(swoole_server_port, set) { + zval *zset = nullptr; + HashTable *vht; + zval *ztmp; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(zset) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + vht = Z_ARRVAL_P(zset); + + ListenPort *port = php_swoole_server_port_get_and_check_ptr(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"); + return; + } + + // backlog + if (php_swoole_array_get_value(vht, "backlog", ztmp)) { + zend_long v = zval_get_long(ztmp); + port->backlog = SW_MAX(0, SW_MIN(v, UINT16_MAX)); + } + if (php_swoole_array_get_value(vht, "socket_buffer_size", 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; + } + } + /** + * !!! Don't set this option, for tests only. + */ + if (php_swoole_array_get_value(vht, "kernel_socket_recv_buffer_size", 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; + } + } + /** + * !!! Don't set this option, for tests only. + */ + if (php_swoole_array_get_value(vht, "kernel_socket_send_buffer_size", 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; + } + } + // 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 = php_swoole_parse_to_size(ztmp); + port->buffer_low_watermark = SW_MAX(0, SW_MIN(v, UINT32_MAX)); + } + // server: tcp_nodelay + if (php_swoole_array_get_value(vht, "open_tcp_nodelay", ztmp)) { + port->open_tcp_nodelay = zval_is_true(ztmp); + } else { + port->open_tcp_nodelay = 1; + } + // tcp_defer_accept + if (php_swoole_array_get_value(vht, "tcp_defer_accept", ztmp)) { + zend_long v = zval_get_long(ztmp); + port->tcp_defer_accept = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX)); + } + // tcp_keepalive + if (php_swoole_array_get_value(vht, "open_tcp_keepalive", ztmp)) { + port->open_tcp_keepalive = zval_is_true(ztmp); + } + // buffer: eof check + if (php_swoole_array_get_value(vht, "open_eof_check", ztmp)) { + port->open_eof_check = zval_is_true(ztmp); + } + // buffer: split package with eof + if (php_swoole_array_get_value(vht, "open_eof_split", ztmp)) { + port->protocol.split_by_eof = zval_is_true(ztmp); + if (port->protocol.split_by_eof) { + port->open_eof_check = 1; + } + } + // package eof + if (php_swoole_array_get_value(vht, "package_eof", ztmp)) { + zend::String str_v(ztmp); + port->protocol.package_eof_len = str_v.len(); + if (port->protocol.package_eof_len == 0) { + php_swoole_fatal_error(E_ERROR, "package_eof cannot be an empty string"); + RETURN_FALSE; + } else if (port->protocol.package_eof_len > SW_DATA_EOF_MAXLEN) { + php_swoole_fatal_error(E_ERROR, "package_eof max length is %d", SW_DATA_EOF_MAXLEN); + RETURN_FALSE; + } + memcpy(port->protocol.package_eof, str_v.val(), str_v.len()); + } + // http_protocol + if (php_swoole_array_get_value(vht, "open_http_protocol", ztmp)) { + port->open_http_protocol = zval_is_true(ztmp); + } + // websocket protocol + if (php_swoole_array_get_value(vht, "open_websocket_protocol", ztmp)) { + port->open_websocket_protocol = zval_is_true(ztmp); + if (port->open_websocket_protocol) { + port->open_http_protocol = 1; + } + } + if (php_swoole_array_get_value(vht, "websocket_subprotocol", ztmp)) { + port->websocket_subprotocol = zend::String(ztmp).to_std_string(); + } + if (php_swoole_array_get_value(vht, "open_websocket_close_frame", ztmp)) { + port->open_websocket_close_frame = zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "open_websocket_ping_frame", ztmp)) { + port->open_websocket_ping_frame = zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "open_websocket_pong_frame", ztmp)) { + port->open_websocket_pong_frame = zval_is_true(ztmp); + } + // http2 protocol + if (php_swoole_array_get_value(vht, "open_http2_protocol", ztmp)) { + port->open_http2_protocol = zval_is_true(ztmp); + if (port->open_http2_protocol) { + port->open_http_protocol = 1; + } + } + // buffer: mqtt protocol + if (php_swoole_array_get_value(vht, "open_mqtt_protocol", ztmp)) { + port->open_mqtt_protocol = zval_is_true(ztmp); + } + // redis protocol + if (php_swoole_array_get_value(vht, "open_redis_protocol", ztmp)) { + port->open_redis_protocol = zval_get_long(ztmp); + } + if (php_swoole_array_get_value(vht, "max_idle_time", 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)) { + zend_long v = zval_get_long(ztmp); + port->tcp_keepidle = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX)); + } + // tcp_keepinterval + if (php_swoole_array_get_value(vht, "tcp_keepinterval", ztmp)) { + zend_long v = zval_get_long(ztmp); + port->tcp_keepinterval = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX)); + } + // tcp_keepcount + if (php_swoole_array_get_value(vht, "tcp_keepcount", ztmp)) { + zend_long v = zval_get_long(ztmp); + port->tcp_keepcount = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX)); + } + // tcp_user_timeout + if (php_swoole_array_get_value(vht, "tcp_user_timeout", ztmp)) { + zend_long v = zval_get_long(ztmp); + port->tcp_user_timeout = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX)); + } + // tcp_fastopen + if (php_swoole_array_get_value(vht, "tcp_fastopen", ztmp)) { + port->tcp_fastopen = zval_is_true(ztmp); + } + // open length check + if (php_swoole_array_get_value(vht, "open_length_check", ztmp)) { + port->open_length_check = zval_is_true(ztmp); + } + // package length size + if (php_swoole_array_get_value(vht, "package_length_type", ztmp)) { + zend::String str_v(ztmp); + 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, "unknown package_length_type, see pack(). Link: http://php.net/pack"); + RETURN_FALSE; + } + } + // package length offset + if (php_swoole_array_get_value(vht, "package_length_offset", ztmp)) { + zend_long v = zval_get_long(ztmp); + port->protocol.package_length_offset = SW_MAX(0, SW_MIN(v, UINT16_MAX)); + if (port->protocol.package_length_offset > SW_IPC_BUFFER_SIZE) { + php_swoole_fatal_error(E_ERROR, "'package_length_offset' value is too large"); + } + } + // package body start + if (php_swoole_array_get_value(vht, "package_body_offset", ztmp) || + php_swoole_array_get_value(vht, "package_body_start", ztmp)) { + zend_long v = zval_get_long(ztmp); + port->protocol.package_body_offset = SW_MAX(0, SW_MIN(v, UINT16_MAX)); + if (port->protocol.package_body_offset > SW_IPC_BUFFER_SIZE) { + php_swoole_fatal_error(E_ERROR, "'package_body_offset' value is too large"); + } + } + // length function + if (php_swoole_array_get_value(vht, "package_length_func", ztmp)) { + auto cb = sw_callable_create(ztmp); + if (cb) { + port->protocol.get_package_length = php_swoole_server_length_func; + if (port->protocol.private_data_1) { + sw_callable_free(port->protocol.private_data_1); + } + 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; + } + } + /** + * package max length + */ + if (php_swoole_array_get_value(vht, "package_max_length", ztmp)) { + zend_long v = php_swoole_parse_to_size(ztmp); + port->protocol.package_max_length = SW_MAX(0, SW_MIN(v, UINT32_MAX)); + } + +#ifdef SW_USE_OPENSSL + if (port->ssl) { + if (!php_swoole_server_set_ssl_option(vht, port->ssl_context)) { + RETURN_FALSE; + } + if (php_swoole_array_get_value(vht, "ssl_compress", ztmp)) { + port->ssl_context->disable_compress = !zval_is_true(ztmp); + } + 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); + } + if (php_swoole_array_get_value(vht, "ssl_allow_self_signed", ztmp)) { + port->ssl_context->allow_self_signed = zval_is_true(ztmp); + } + // verify client cert + if (php_swoole_array_get_value(vht, "ssl_client_cert_file", ztmp)) { + zend::String str_v(ztmp); + if (access(str_v.val(), R_OK) < 0) { + php_swoole_fatal_error(E_ERROR, "ssl_client_cert_file[%s] not found", str_v.val()); + return; + } + port->ssl_context->client_cert_file = str_v.to_std_string(); + } + if (php_swoole_array_get_value(vht, "ssl_verify_depth", ztmp)) { + zend_long v = zval_get_long(ztmp); + port->ssl_context->verify_depth = SW_MAX(0, SW_MIN(v, UINT8_MAX)); + } + if (php_swoole_array_get_value(vht, "ssl_prefer_server_ciphers", ztmp)) { + port->ssl_context->prefer_server_ciphers = zval_is_true(ztmp); + } + // if ((v = zend_hash_str_find(vht, ZEND_STRL("ssl_session_tickets")))) + // { + // port->ssl_context->session_tickets = zval_is_true(v); + // } + // if ((v = zend_hash_str_find(vht, ZEND_STRL("ssl_stapling")))) + // { + // port->ssl_context->stapling = zval_is_true(v); + // } + // if ((v = zend_hash_str_find(vht, ZEND_STRL("ssl_stapling_verify")))) + // { + // port->ssl_context->stapling_verify = zval_is_true(v); + // } + if (php_swoole_array_get_value(vht, "ssl_ciphers", ztmp)) { + port->ssl_context->ciphers = zend::String(ztmp).to_std_string(); + } + if (php_swoole_array_get_value(vht, "ssl_ecdh_curve", ztmp)) { + port->ssl_context->ecdh_curve = zend::String(ztmp).to_std_string(); + } + if (php_swoole_array_get_value(vht, "ssl_dhparam", ztmp)) { + port->ssl_context->dhparam = zend::String(ztmp).to_std_string(); + } + if (php_swoole_array_get_value(vht, "ssl_sni_certs", ztmp)) { + if (Z_TYPE_P(ztmp) != IS_ARRAY) { + php_swoole_fatal_error(E_WARNING, "ssl_sni_certs requires an array mapping host names to cert paths"); + RETURN_FALSE; + } + + zval *current; + zend_string *key; + zend_ulong key_index; + + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(ztmp), key_index, key, current) { + (void) key_index; + if (!key) { + php_swoole_fatal_error(E_WARNING, "ssl_sni_certs array requires string host name keys"); + RETURN_FALSE; + } + if (Z_TYPE_P(current) != IS_ARRAY) { + php_swoole_fatal_error(E_WARNING, "invalid SNI_cert setting"); + RETURN_FALSE; + } + 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(); + } + + if (!port->ssl_context->cert_file.empty() || port->sni_contexts.empty()) { + if (!port->ssl_init()) { + php_swoole_fatal_error(E_ERROR, "ssl_init() failed"); + RETURN_FALSE; + } + } + // if ((v = zend_hash_str_find(vht, ZEND_STRL("ssl_session_cache")))) + // { + // port->ssl_context->session_cache = zend::string_dup(v); + // } + } +#endif + + if (SWOOLE_G(enable_library)) { + zval params[1] = { + *zset, + }; + zend::function::call("\\Swoole\\Server\\Helper::checkOptions", 1, params); + } + + zval *zsetting = sw_zend_read_and_convert_property_array(swoole_server_port_ce, ZEND_THIS, ZEND_STRL("setting"), 0); + php_array_merge(Z_ARRVAL_P(zsetting), Z_ARRVAL_P(zset)); + property->zsetting = zsetting; +} + +static PHP_METHOD(swoole_server_port, on) { + char *name = nullptr; + size_t len; + zval *cb; + + ServerPortProperty *property = php_swoole_server_port_get_and_check_property(ZEND_THIS); + Server *serv = property->serv; + 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; + } + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(name, len) + Z_PARAM_ZVAL(cb) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + + 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; + } + + 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 (!found) { + php_swoole_error(E_WARNING, "unknown event types[%s]", name); + RETURN_FALSE; + } + RETURN_TRUE; +} + +static PHP_METHOD(swoole_server_port, getCallback) { + zval *name; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(name) + 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); + auto i = server_port_event_map.find(_event_name_tolower.to_std_string()); + if (i != server_port_event_map.end()) { + std::string property_name = "on" + i->second.name; + zval rv, + *property = zend_read_property( + swoole_server_port_ce, SW_Z8_OBJ_P(ZEND_THIS), property_name.c_str(), property_name.length(), 1, &rv); + if (!ZVAL_IS_NULL(property)) { + RETURN_ZVAL(property, 1, 0); + } + } + RETURN_NULL(); +} + +#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->get_fd()); + if (!socket_object) { + RETURN_FALSE; + } + SW_ZVAL_SOCKET(return_value, socket_object); + zval *zsocket = sw_zval_dup(return_value); + Z_TRY_ADDREF_P(zsocket); +} +#endif diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc new file mode 100644 index 00000000000..0c762eded08 --- /dev/null +++ b/ext-src/swoole_socket_coro.cc @@ -0,0 +1,2220 @@ +/* + +----------------------------------------------------------------------+ + | 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_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; + +zend_class_entry *swoole_socket_coro_ce; +static zend_object_handlers swoole_socket_coro_handlers; + +static zend_class_entry *swoole_socket_coro_exception_ce; +static zend_object_handlers swoole_socket_coro_exception_handlers; + +struct SocketObject { + Socket *socket; + zval zstream; + bool reference; + zend_object std; +}; + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_socket_coro, __construct); +static PHP_METHOD(swoole_socket_coro, bind); +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); +static PHP_METHOD(swoole_socket_coro, readVector); +static PHP_METHOD(swoole_socket_coro, readVectorAll); +static PHP_METHOD(swoole_socket_coro, writeVector); +static PHP_METHOD(swoole_socket_coro, writeVectorAll); +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); +static PHP_METHOD(swoole_socket_coro, setOption); +static PHP_METHOD(swoole_socket_coro, setProtocol); +#ifdef SW_USE_OPENSSL +static PHP_METHOD(swoole_socket_coro, sslHandshake); +#endif +static PHP_METHOD(swoole_socket_coro, shutdown); +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 +static const zend_function_entry swoole_socket_coro_methods[] = +{ + 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_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 swoole_get_socket_coro(_sock, _zobject) \ + SocketObject *_sock = socket_coro_fetch_object(Z_OBJ_P(_zobject)); \ + if (UNEXPECTED(!sock->socket)) { \ + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); \ + } \ + 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 *socket_coro_fetch_object(zend_object *obj) { + return (SocketObject *) ((char *) obj - swoole_socket_coro_handlers.offset); +} + +/** + * 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 *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(), + * because extending classes may use properties. (Generally a lot of the stuff you will do is + * for the sake of not breaking extending classes). */ + object_properties_init(&sock->std, ce); + sock->std.handlers = &swoole_socket_coro_handlers; + return &sock->std; +} + +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); + REGISTER_LONG_CONSTANT("SOCK_STREAM", SOCK_STREAM, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SOCK_DGRAM", SOCK_DGRAM, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SOCK_RAW", SOCK_RAW, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SOCK_SEQPACKET", SOCK_SEQPACKET, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SOCK_RDM", SOCK_RDM, CONST_CS | CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("MSG_OOB", MSG_OOB, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("MSG_WAITALL", MSG_WAITALL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("MSG_CTRUNC", MSG_CTRUNC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("MSG_TRUNC", MSG_TRUNC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("MSG_PEEK", MSG_PEEK, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("MSG_DONTROUTE", MSG_DONTROUTE, CONST_CS | CONST_PERSISTENT); +#ifdef MSG_EOR + REGISTER_LONG_CONSTANT("MSG_EOR", MSG_EOR, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef MSG_EOF + REGISTER_LONG_CONSTANT("MSG_EOF", MSG_EOF, CONST_CS | CONST_PERSISTENT); +#endif + +#ifdef MSG_CONFIRM + REGISTER_LONG_CONSTANT("MSG_CONFIRM", MSG_CONFIRM, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef MSG_ERRQUEUE + REGISTER_LONG_CONSTANT("MSG_ERRQUEUE", MSG_ERRQUEUE, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef MSG_NOSIGNAL + REGISTER_LONG_CONSTANT("MSG_NOSIGNAL", MSG_NOSIGNAL, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef MSG_DONTWAIT + REGISTER_LONG_CONSTANT("MSG_DONTWAIT", MSG_DONTWAIT, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef MSG_MORE + REGISTER_LONG_CONSTANT("MSG_MORE", MSG_MORE, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef MSG_WAITFORONE + REGISTER_LONG_CONSTANT("MSG_WAITFORONE", MSG_WAITFORONE, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef MSG_CMSG_CLOEXEC + REGISTER_LONG_CONSTANT("MSG_CMSG_CLOEXEC", MSG_CMSG_CLOEXEC, CONST_CS | CONST_PERSISTENT); +#endif + + REGISTER_LONG_CONSTANT("SO_DEBUG", SO_DEBUG, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_REUSEADDR", SO_REUSEADDR, CONST_CS | CONST_PERSISTENT); +#ifdef SO_REUSEPORT + REGISTER_LONG_CONSTANT("SO_REUSEPORT", SO_REUSEPORT, CONST_CS | CONST_PERSISTENT); +#endif + REGISTER_LONG_CONSTANT("SO_KEEPALIVE", SO_KEEPALIVE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_DONTROUTE", SO_DONTROUTE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_LINGER", SO_LINGER, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_BROADCAST", SO_BROADCAST, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_OOBINLINE", SO_OOBINLINE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_SNDBUF", SO_SNDBUF, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_RCVBUF", SO_RCVBUF, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_SNDLOWAT", SO_SNDLOWAT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_RCVLOWAT", SO_RCVLOWAT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_SNDTIMEO", SO_SNDTIMEO, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_RCVTIMEO", SO_RCVTIMEO, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_TYPE", SO_TYPE, CONST_CS | CONST_PERSISTENT); +#ifdef SO_FAMILY + REGISTER_LONG_CONSTANT("SO_FAMILY", SO_FAMILY, CONST_CS | CONST_PERSISTENT); +#endif + REGISTER_LONG_CONSTANT("SO_ERROR", SO_ERROR, CONST_CS | CONST_PERSISTENT); +#ifdef SO_BINDTODEVICE + REGISTER_LONG_CONSTANT("SO_BINDTODEVICE", SO_BINDTODEVICE, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef SO_USER_COOKIE + REGISTER_LONG_CONSTANT("SO_LABEL", SO_LABEL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_PEERLABEL", SO_PEERLABEL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_LISTENQLIMIT", SO_LISTENQLIMIT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_LISTENQLEN", SO_LISTENQLEN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SO_USER_COOKIE", SO_USER_COOKIE, CONST_CS | CONST_PERSISTENT); +#endif + REGISTER_LONG_CONSTANT("SOL_SOCKET", SOL_SOCKET, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SOMAXCONN", SOMAXCONN, CONST_CS | CONST_PERSISTENT); +#ifdef TCP_NODELAY + REGISTER_LONG_CONSTANT("TCP_NODELAY", TCP_NODELAY, CONST_CS | CONST_PERSISTENT); +#endif + + REGISTER_LONG_CONSTANT("MCAST_JOIN_GROUP", PHP_MCAST_JOIN_GROUP, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("MCAST_LEAVE_GROUP", PHP_MCAST_LEAVE_GROUP, CONST_CS | CONST_PERSISTENT); +#ifdef HAS_MCAST_EXT + REGISTER_LONG_CONSTANT("MCAST_BLOCK_SOURCE", PHP_MCAST_BLOCK_SOURCE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("MCAST_UNBLOCK_SOURCE", PHP_MCAST_UNBLOCK_SOURCE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("MCAST_JOIN_SOURCE_GROUP", PHP_MCAST_JOIN_SOURCE_GROUP, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("MCAST_LEAVE_SOURCE_GROUP", PHP_MCAST_LEAVE_SOURCE_GROUP, CONST_CS | CONST_PERSISTENT); +#endif + + REGISTER_LONG_CONSTANT("IP_MULTICAST_IF", IP_MULTICAST_IF, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IP_MULTICAST_TTL", IP_MULTICAST_TTL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IP_MULTICAST_LOOP", IP_MULTICAST_LOOP, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IPV6_MULTICAST_IF", IPV6_MULTICAST_IF, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IPV6_MULTICAST_HOPS", IPV6_MULTICAST_HOPS, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IPV6_MULTICAST_LOOP", IPV6_MULTICAST_LOOP, CONST_CS | CONST_PERSISTENT); + +#ifdef IPV6_V6ONLY + REGISTER_LONG_CONSTANT("IPV6_V6ONLY", IPV6_V6ONLY, CONST_CS | CONST_PERSISTENT); +#endif + +#ifdef EPERM + /* Operation not permitted */ + REGISTER_LONG_CONSTANT("SOCKET_EPERM", EPERM, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENOENT + /* No such file or directory */ + REGISTER_LONG_CONSTANT("SOCKET_ENOENT", ENOENT, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EINTR + /* Interrupted system call */ + REGISTER_LONG_CONSTANT("SOCKET_EINTR", EINTR, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EIO + /* I/O error */ + REGISTER_LONG_CONSTANT("SOCKET_EIO", EIO, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENXIO + /* No such device or address */ + REGISTER_LONG_CONSTANT("SOCKET_ENXIO", ENXIO, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef E2BIG + /* Arg list too long */ + REGISTER_LONG_CONSTANT("SOCKET_E2BIG", E2BIG, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EBADF + /* Bad file number */ + REGISTER_LONG_CONSTANT("SOCKET_EBADF", EBADF, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EAGAIN + /* Try again */ + REGISTER_LONG_CONSTANT("SOCKET_EAGAIN", EAGAIN, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENOMEM + /* Out of memory */ + REGISTER_LONG_CONSTANT("SOCKET_ENOMEM", ENOMEM, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EACCES + /* Permission denied */ + REGISTER_LONG_CONSTANT("SOCKET_EACCES", EACCES, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EFAULT + /* Bad address */ + REGISTER_LONG_CONSTANT("SOCKET_EFAULT", EFAULT, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENOTBLK + /* Block device required */ + REGISTER_LONG_CONSTANT("SOCKET_ENOTBLK", ENOTBLK, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EBUSY + /* Device or resource busy */ + REGISTER_LONG_CONSTANT("SOCKET_EBUSY", EBUSY, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EEXIST + /* File exists */ + REGISTER_LONG_CONSTANT("SOCKET_EEXIST", EEXIST, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EXDEV + /* Cross-device link */ + REGISTER_LONG_CONSTANT("SOCKET_EXDEV", EXDEV, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENODEV + /* No such device */ + REGISTER_LONG_CONSTANT("SOCKET_ENODEV", ENODEV, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENOTDIR + /* Not a directory */ + REGISTER_LONG_CONSTANT("SOCKET_ENOTDIR", ENOTDIR, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EISDIR + /* Is a directory */ + REGISTER_LONG_CONSTANT("SOCKET_EISDIR", EISDIR, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EINVAL + /* Invalid argument */ + REGISTER_LONG_CONSTANT("SOCKET_EINVAL", EINVAL, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENFILE + /* File table overflow */ + REGISTER_LONG_CONSTANT("SOCKET_ENFILE", ENFILE, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EMFILE + /* Too many open files */ + REGISTER_LONG_CONSTANT("SOCKET_EMFILE", EMFILE, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENOTTY + /* Not a typewriter */ + REGISTER_LONG_CONSTANT("SOCKET_ENOTTY", ENOTTY, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENOSPC + /* No space left on device */ + REGISTER_LONG_CONSTANT("SOCKET_ENOSPC", ENOSPC, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ESPIPE + /* Illegal seek */ + REGISTER_LONG_CONSTANT("SOCKET_ESPIPE", ESPIPE, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EROFS + /* Read-only file system */ + REGISTER_LONG_CONSTANT("SOCKET_EROFS", EROFS, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EMLINK + /* Too many links */ + REGISTER_LONG_CONSTANT("SOCKET_EMLINK", EMLINK, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EPIPE + /* Broken pipe */ + REGISTER_LONG_CONSTANT("SOCKET_EPIPE", EPIPE, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENAMETOOLONG + /* File name too long */ + REGISTER_LONG_CONSTANT("SOCKET_ENAMETOOLONG", ENAMETOOLONG, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENOLCK + /* No record locks available */ + REGISTER_LONG_CONSTANT("SOCKET_ENOLCK", ENOLCK, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENOSYS + /* Function not implemented */ + REGISTER_LONG_CONSTANT("SOCKET_ENOSYS", ENOSYS, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENOTEMPTY + /* Directory not empty */ + REGISTER_LONG_CONSTANT("SOCKET_ENOTEMPTY", ENOTEMPTY, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ELOOP + /* Too many symbolic links encountered */ + REGISTER_LONG_CONSTANT("SOCKET_ELOOP", ELOOP, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EWOULDBLOCK + /* Operation would block */ + REGISTER_LONG_CONSTANT("SOCKET_EWOULDBLOCK", EWOULDBLOCK, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENOMSG + /* No message of desired type */ + REGISTER_LONG_CONSTANT("SOCKET_ENOMSG", ENOMSG, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EIDRM + /* Identifier removed */ + REGISTER_LONG_CONSTANT("SOCKET_EIDRM", EIDRM, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ECHRNG + /* Channel number out of range */ + REGISTER_LONG_CONSTANT("SOCKET_ECHRNG", ECHRNG, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EL2NSYNC + /* Level 2 not synchronized */ + REGISTER_LONG_CONSTANT("SOCKET_EL2NSYNC", EL2NSYNC, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EL3HLT + /* Level 3 halted */ + REGISTER_LONG_CONSTANT("SOCKET_EL3HLT", EL3HLT, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EL3RST + /* Level 3 reset */ + REGISTER_LONG_CONSTANT("SOCKET_EL3RST", EL3RST, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ELNRNG + /* Link number out of range */ + REGISTER_LONG_CONSTANT("SOCKET_ELNRNG", ELNRNG, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EUNATCH + /* Protocol driver not attached */ + REGISTER_LONG_CONSTANT("SOCKET_EUNATCH", EUNATCH, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENOCSI + /* No CSI structure available */ + REGISTER_LONG_CONSTANT("SOCKET_ENOCSI", ENOCSI, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EL2HLT + /* Level 2 halted */ + REGISTER_LONG_CONSTANT("SOCKET_EL2HLT", EL2HLT, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EBADE + /* Invalid exchange */ + REGISTER_LONG_CONSTANT("SOCKET_EBADE", EBADE, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EBADR + /* Invalid request descriptor */ + REGISTER_LONG_CONSTANT("SOCKET_EBADR", EBADR, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EXFULL + /* Exchange full */ + REGISTER_LONG_CONSTANT("SOCKET_EXFULL", EXFULL, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENOANO + /* No anode */ + REGISTER_LONG_CONSTANT("SOCKET_ENOANO", ENOANO, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EBADRQC + /* Invalid request code */ + REGISTER_LONG_CONSTANT("SOCKET_EBADRQC", EBADRQC, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EBADSLT + /* Invalid slot */ + REGISTER_LONG_CONSTANT("SOCKET_EBADSLT", EBADSLT, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENOSTR + /* Device not a stream */ + REGISTER_LONG_CONSTANT("SOCKET_ENOSTR", ENOSTR, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENODATA + /* No data available */ + REGISTER_LONG_CONSTANT("SOCKET_ENODATA", ENODATA, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ETIME + /* Timer expired */ + REGISTER_LONG_CONSTANT("SOCKET_ETIME", ETIME, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENOSR + /* Out of streams resources */ + REGISTER_LONG_CONSTANT("SOCKET_ENOSR", ENOSR, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENONET + /* Machine is not on the network */ + REGISTER_LONG_CONSTANT("SOCKET_ENONET", ENONET, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EREMOTE + /* Object is remote */ + REGISTER_LONG_CONSTANT("SOCKET_EREMOTE", EREMOTE, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENOLINK + /* Link has been severed */ + REGISTER_LONG_CONSTANT("SOCKET_ENOLINK", ENOLINK, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EADV + /* Advertise error */ + REGISTER_LONG_CONSTANT("SOCKET_EADV", EADV, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ESRMNT + /* Srmount error */ + REGISTER_LONG_CONSTANT("SOCKET_ESRMNT", ESRMNT, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ECOMM + /* Communication error on send */ + REGISTER_LONG_CONSTANT("SOCKET_ECOMM", ECOMM, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EPROTO + /* Protocol error */ + REGISTER_LONG_CONSTANT("SOCKET_EPROTO", EPROTO, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EMULTIHOP + /* Multihop attempted */ + REGISTER_LONG_CONSTANT("SOCKET_EMULTIHOP", EMULTIHOP, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EBADMSG + /* Not a data message */ + REGISTER_LONG_CONSTANT("SOCKET_EBADMSG", EBADMSG, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENOTUNIQ + /* Name not unique on network */ + REGISTER_LONG_CONSTANT("SOCKET_ENOTUNIQ", ENOTUNIQ, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EBADFD + /* File descriptor in bad state */ + REGISTER_LONG_CONSTANT("SOCKET_EBADFD", EBADFD, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EREMCHG + /* Remote address changed */ + REGISTER_LONG_CONSTANT("SOCKET_EREMCHG", EREMCHG, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ERESTART + /* Interrupted system call should be restarted */ + REGISTER_LONG_CONSTANT("SOCKET_ERESTART", ERESTART, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ESTRPIPE + /* Streams pipe error */ + REGISTER_LONG_CONSTANT("SOCKET_ESTRPIPE", ESTRPIPE, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EUSERS + /* Too many users */ + REGISTER_LONG_CONSTANT("SOCKET_EUSERS", EUSERS, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENOTSOCK + /* Socket operation on non-socket */ + REGISTER_LONG_CONSTANT("SOCKET_ENOTSOCK", ENOTSOCK, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EDESTADDRREQ + /* Destination address required */ + REGISTER_LONG_CONSTANT("SOCKET_EDESTADDRREQ", EDESTADDRREQ, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EMSGSIZE + /* Message too long */ + REGISTER_LONG_CONSTANT("SOCKET_EMSGSIZE", EMSGSIZE, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EPROTOTYPE + /* Protocol wrong type for socket */ + REGISTER_LONG_CONSTANT("SOCKET_EPROTOTYPE", EPROTOTYPE, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENOPROTOOPT + /* Protocol not available */ + REGISTER_LONG_CONSTANT("SOCKET_ENOPROTOOPT", ENOPROTOOPT, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EPROTONOSUPPORT + /* Protocol not supported */ + REGISTER_LONG_CONSTANT("SOCKET_EPROTONOSUPPORT", EPROTONOSUPPORT, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ESOCKTNOSUPPORT + /* Socket type not supported */ + REGISTER_LONG_CONSTANT("SOCKET_ESOCKTNOSUPPORT", ESOCKTNOSUPPORT, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EOPNOTSUPP + /* Operation not supported on transport endpoint */ + REGISTER_LONG_CONSTANT("SOCKET_EOPNOTSUPP", EOPNOTSUPP, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EPFNOSUPPORT + /* Protocol family not supported */ + REGISTER_LONG_CONSTANT("SOCKET_EPFNOSUPPORT", EPFNOSUPPORT, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EAFNOSUPPORT + /* Address family not supported by protocol */ + REGISTER_LONG_CONSTANT("SOCKET_EAFNOSUPPORT", EAFNOSUPPORT, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EADDRINUSE + /* Address already in use */ + REGISTER_LONG_CONSTANT("SOCKET_EADDRINUSE", EADDRINUSE, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EADDRNOTAVAIL + /* Cannot assign requested address */ + REGISTER_LONG_CONSTANT("SOCKET_EADDRNOTAVAIL", EADDRNOTAVAIL, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENETDOWN + /* Network is down */ + REGISTER_LONG_CONSTANT("SOCKET_ENETDOWN", ENETDOWN, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENETUNREACH + /* Network is unreachable */ + REGISTER_LONG_CONSTANT("SOCKET_ENETUNREACH", ENETUNREACH, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENETRESET + /* Network dropped connection because of reset */ + REGISTER_LONG_CONSTANT("SOCKET_ENETRESET", ENETRESET, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ECONNABORTED + /* Software caused connection abort */ + REGISTER_LONG_CONSTANT("SOCKET_ECONNABORTED", ECONNABORTED, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ECONNRESET + /* Connection reset by peer */ + REGISTER_LONG_CONSTANT("SOCKET_ECONNRESET", ECONNRESET, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENOBUFS + /* No buffer space available */ + REGISTER_LONG_CONSTANT("SOCKET_ENOBUFS", ENOBUFS, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EISCONN + /* Transport endpoint is already connected */ + REGISTER_LONG_CONSTANT("SOCKET_EISCONN", EISCONN, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENOTCONN + /* Transport endpoint is not connected */ + REGISTER_LONG_CONSTANT("SOCKET_ENOTCONN", ENOTCONN, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ESHUTDOWN + /* Cannot send after transport endpoint shutdown */ + REGISTER_LONG_CONSTANT("SOCKET_ESHUTDOWN", ESHUTDOWN, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ETOOMANYREFS + /* Too many references: cannot splice */ + REGISTER_LONG_CONSTANT("SOCKET_ETOOMANYREFS", ETOOMANYREFS, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ETIMEDOUT + /* Connection timed out */ + REGISTER_LONG_CONSTANT("SOCKET_ETIMEDOUT", ETIMEDOUT, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ECONNREFUSED + /* Connection refused */ + REGISTER_LONG_CONSTANT("SOCKET_ECONNREFUSED", ECONNREFUSED, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EHOSTDOWN + /* Host is down */ + REGISTER_LONG_CONSTANT("SOCKET_EHOSTDOWN", EHOSTDOWN, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EHOSTUNREACH + /* No route to host */ + REGISTER_LONG_CONSTANT("SOCKET_EHOSTUNREACH", EHOSTUNREACH, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EALREADY + /* Operation already in progress */ + REGISTER_LONG_CONSTANT("SOCKET_EALREADY", EALREADY, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EINPROGRESS + /* Operation now in progress */ + REGISTER_LONG_CONSTANT("SOCKET_EINPROGRESS", EINPROGRESS, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EISNAM + /* Is a named type file */ + REGISTER_LONG_CONSTANT("SOCKET_EISNAM", EISNAM, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EREMOTEIO + /* Remote I/O error */ + REGISTER_LONG_CONSTANT("SOCKET_EREMOTEIO", EREMOTEIO, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EDQUOT + /* Quota exceeded */ + REGISTER_LONG_CONSTANT("SOCKET_EDQUOT", EDQUOT, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef ENOMEDIUM + /* No medium found */ + REGISTER_LONG_CONSTANT("SOCKET_ENOMEDIUM", ENOMEDIUM, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef EMEDIUMTYPE + /* Wrong medium type */ + REGISTER_LONG_CONSTANT("SOCKET_EMEDIUMTYPE", EMEDIUMTYPE, CONST_CS | CONST_PERSISTENT); +#endif + + REGISTER_LONG_CONSTANT("IPPROTO_IP", IPPROTO_IP, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IPPROTO_IPV6", IPPROTO_IPV6, CONST_CS | CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("SOL_TCP", IPPROTO_TCP, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SOL_UDP", IPPROTO_UDP, CONST_CS | CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("IPV6_UNICAST_HOPS", IPV6_UNICAST_HOPS, CONST_CS | CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("AI_PASSIVE", AI_PASSIVE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("AI_CANONNAME", AI_CANONNAME, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("AI_NUMERICHOST", AI_NUMERICHOST, CONST_CS | CONST_PERSISTENT); +#if HAVE_AI_V4MAPPED + REGISTER_LONG_CONSTANT("AI_V4MAPPED", AI_V4MAPPED, CONST_CS | CONST_PERSISTENT); +#endif +#if HAVE_AI_ALL + REGISTER_LONG_CONSTANT("AI_ALL", AI_ALL, CONST_CS | CONST_PERSISTENT); +#endif + REGISTER_LONG_CONSTANT("AI_ADDRCONFIG", AI_ADDRCONFIG, CONST_CS | CONST_PERSISTENT); +#if HAVE_AI_IDN + REGISTER_LONG_CONSTANT("AI_IDN", AI_IDN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("AI_CANONIDN", AI_CANONIDN, CONST_CS | CONST_PERSISTENT); + // REGISTER_LONG_CONSTANT("AI_IDN_ALLOW_UNASSIGNED", AI_IDN_ALLOW_UNASSIGNED, CONST_CS | CONST_PERSISTENT); + // REGISTER_LONG_CONSTANT("AI_IDN_USE_STD3_ASCII_RULES", AI_IDN_USE_STD3_ASCII_RULES, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef AI_NUMERICSERV + REGISTER_LONG_CONSTANT("AI_NUMERICSERV", AI_NUMERICSERV, CONST_CS | CONST_PERSISTENT); +#endif +} + +void php_swoole_socket_coro_minit(int module_number) { + 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, 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", + "Co\\Socket\\Exception", + nullptr, + swoole_exception); + + if (!zend_hash_str_find_ptr(&module_registry, ZEND_STRL("sockets"))) { + 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 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 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()); +} + +SW_API bool php_swoole_export_socket(zval *zobject, Socket *_socket) { + zend_object *object = socket_coro_create_object(swoole_socket_coro_ce); + if (!object) { + return false; + } + + SocketObject *sock = (SocketObject *) socket_coro_fetch_object(object); + sock->reference = 1; + sock->socket = _socket; + + ZVAL_OBJ(zobject, object); + socket_coro_init(zobject, sock); + return true; +} + +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) { + php_swoole_sys_error(E_WARNING, "dup(%d) failed", fd); + return nullptr; + } + return php_swoole_create_socket_from_fd(new_fd, 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 = socket_coro_create_object(swoole_socket_coro_ce); + SocketObject *sock = (SocketObject *) socket_coro_fetch_object(object); + + sock->socket = socket; + if (UNEXPECTED(sock->socket->get_fd() < 0)) { + php_swoole_sys_error(E_WARNING, "new Socket() failed"); + delete sock->socket; + sock->socket = nullptr; + OBJ_RELEASE(object); + return nullptr; + } + + ZVAL_OBJ(&zobject, object); + socket_coro_init(&zobject, sock); + return object; +} + +SW_API zend_object *php_swoole_create_socket_from_fd(int fd, enum swSocketType type) { + return create_socket_object(new Socket(fd, type)); +} + +SW_API zend_object *php_swoole_create_socket_from_fd(int fd, int _domain, int _type, int _protocol) { + return create_socket_object(new Socket(fd, _domain, _type, _protocol)); +} + +SW_API Socket *php_swoole_get_socket(zval *zobject) { + SW_ASSERT(Z_OBJCE_P(zobject) == swoole_socket_coro_ce); + SocketObject *sock = (SocketObject *) 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 = socket_coro_create_object(swoole_socket_coro_ce); + SocketObject *sock = (SocketObject *) socket_coro_fetch_object(object); + sock->socket = socket; + ZVAL_OBJ(zobject, object); + socket_coro_init(zobject, sock); +} + +SW_API bool php_swoole_socket_set_protocol(Socket *sock, zval *zset) { + HashTable *vht = Z_ARRVAL_P(zset); + zval *ztmp; + bool ret = true; + + /** + * ssl + */ +#ifdef SW_USE_OPENSSL + if (php_swoole_array_get_value(vht, "open_ssl", ztmp)) { + if (zval_is_true(ztmp)) { + sock->enable_ssl_encrypt(); + } + } + if (sock->ssl_is_enable()) { + if (!php_swoole_socket_set_ssl(sock, zset)) { + ret = false; + } + } +#endif + /** + * protocol + */ + // buffer: eof check + if (php_swoole_array_get_value(vht, "open_eof_check", ztmp)) { + sock->open_eof_check = zval_is_true(ztmp); + } + // buffer: split package with eof + if (php_swoole_array_get_value(vht, "open_eof_split", ztmp)) { + sock->protocol.split_by_eof = zval_is_true(ztmp); + if (sock->protocol.split_by_eof) { + sock->open_eof_check = 1; + } + } + // package eof + if (php_swoole_array_get_value(vht, "package_eof", ztmp)) { + zend::String str_v(ztmp); + if (str_v.len() == 0) { + php_swoole_fatal_error(E_ERROR, "package_eof cannot be an empty string"); + ret = false; + } else if (str_v.len() > SW_DATA_EOF_MAXLEN) { + php_swoole_fatal_error(E_ERROR, "package_eof max length is %d", SW_DATA_EOF_MAXLEN); + ret = false; + } else { + sock->protocol.package_eof_len = str_v.len(); + memcpy(sock->protocol.package_eof, str_v.val(), str_v.len()); + } + } + if (php_swoole_array_get_value(vht, "open_fastcgi_protocol", ztmp)) { +#define FCGI_HEADER_LEN 8 +#define FCGI_MAX_LENGTH 0xffff + sock->open_length_check = zval_is_true(ztmp); + sock->protocol.package_length_size = FCGI_HEADER_LEN; + sock->protocol.package_length_offset = 0; + sock->protocol.package_body_offset = 0; + sock->protocol.get_package_length = + [](const Protocol *protocol, swoole::network::Socket *conn, PacketLength *pl) { + const uint8_t *p = (const uint8_t *) pl->buf; + ssize_t length = 0; + if (pl->buf_size >= FCGI_HEADER_LEN) { + length = ((p[4] << 8) | p[5]) + p[6]; + if (length > FCGI_MAX_LENGTH) { + length = -1; + } else { + length += FCGI_HEADER_LEN; + } + } + return length; + }; + } + // open mqtt protocol + if (php_swoole_array_get_value(vht, "open_mqtt_protocol", ztmp)) { + sock->open_length_check = zval_is_true(ztmp); + if (zval_is_true(ztmp)) { + swoole::mqtt::set_protocol(&sock->protocol); + } + } + // open length check + if (php_swoole_array_get_value(vht, "open_length_check", ztmp)) { + sock->open_length_check = zval_is_true(ztmp); + sock->protocol.get_package_length = Protocol::default_length_func; + } + // package length size + if (php_swoole_array_get_value(vht, "package_length_type", ztmp)) { + zend::String str_v(ztmp); + sock->protocol.package_length_type = str_v.val()[0]; + sock->protocol.package_length_size = swoole_type_size(sock->protocol.package_length_type); + if (sock->protocol.package_length_size == 0) { + php_swoole_fatal_error(E_WARNING, + "Unknown package_length_type name '%c', see pack(). Link: http://php.net/pack", + sock->protocol.package_length_type); + ret = false; + } + } + // package length offset + if (php_swoole_array_get_value(vht, "package_length_offset", ztmp)) { + zend_long v = zval_get_long(ztmp); + sock->protocol.package_length_offset = SW_MAX(0, SW_MIN(v, UINT16_MAX)); + } + // package body start + if (php_swoole_array_get_value(vht, "package_body_offset", ztmp)) { + zend_long v = zval_get_long(ztmp); + sock->protocol.package_body_offset = SW_MAX(0, SW_MIN(v, UINT16_MAX)); + } + // length function + if (php_swoole_array_get_value(vht, "package_length_func", ztmp)) { + 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; + } + } + /** + * package max length + */ + if (php_swoole_array_get_value(vht, "package_max_length", 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; + } + + 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]; + + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_LONG(domain) + Z_PARAM_LONG(type) + Z_PARAM_LONG(protocol) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (0 != socketpair((int) domain, (int) type, (int) protocol, pair)) { + 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); + + zend_object *s1 = php_swoole_create_socket_from_fd(pair[0], sock_type); + if (s1 == nullptr) { + RETURN_FALSE; + } + + zend_object *s2 = php_swoole_create_socket_from_fd(pair[1], sock_type); + if (s2 == nullptr) { + OBJ_RELEASE(s1); + RETURN_FALSE; + } + + zval zobject1, zobject2; + ZVAL_OBJ(&zobject1, s1); + ZVAL_OBJ(&zobject2, s2); + + array_init(return_value); + add_next_index_zval(return_value, &zobject1); + add_next_index_zval(return_value, &zobject2); +} + +static PHP_METHOD(swoole_socket_coro, __construct) { + zend_long domain, type, protocol = IPPROTO_IP; + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 2, 3) + Z_PARAM_LONG(domain) + Z_PARAM_LONG(type) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(protocol) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + 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; + } + + 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) { + char *address; + size_t l_address; + zend_long port = 0; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STRING(address, l_address) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(port) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + swoole_get_socket_coro(sock, ZEND_THIS); + + if (!sock->socket->bind(std::string(address, l_address), port)) { + socket_coro_sync_properties(ZEND_THIS, sock); + RETURN_FALSE; + } + RETURN_TRUE; +} + +static PHP_METHOD(swoole_socket_coro, listen) { + zend_long backlog = SW_BACKLOG; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(backlog) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + swoole_get_socket_coro(sock, ZEND_THIS); + + if (!sock->socket->listen(backlog)) { + socket_coro_sync_properties(ZEND_THIS, sock); + RETURN_FALSE; + } + RETURN_TRUE; +} + +static PHP_METHOD(swoole_socket_coro, accept) { + double timeout = 0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + swoole_get_socket_coro(sock, ZEND_THIS); + + Socket *conn = sock->socket->accept(timeout); + if (conn) { + 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); + 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(); + } + } else { + socket_coro_sync_properties(ZEND_THIS, sock); + RETURN_FALSE; + } +} + +static PHP_METHOD(swoole_socket_coro, connect) { + char *host; + size_t l_host; + zend_long port = 0; + double timeout = 0; + + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_STRING(host, l_host) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(port) + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + swoole_get_socket_coro(sock, ZEND_THIS); + + 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; + } else if (port == 0 || port >= 65536) { + php_swoole_error(E_WARNING, "Invalid port argument[" ZEND_LONG_FMT "]", port); + RETURN_FALSE; + } + } + Socket::TimeoutSetter ts(sock->socket, timeout, Socket::TIMEOUT_CONNECT); + if (!sock->socket->connect(std::string(host, l_host), port)) { + socket_coro_sync_properties(ZEND_THIS, sock); + RETURN_FALSE; + } + RETURN_TRUE; +} + +static PHP_METHOD(swoole_socket_coro, checkLiveness) { + swoole_get_socket_coro(sock, ZEND_THIS); + + bool liveness = sock->socket->check_liveness(); + 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; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(length) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (UNEXPECTED(length <= 0)) { + length = SW_BUFFER_SIZE_BIG; + } + + swoole_get_socket_coro(sock, ZEND_THIS); + + zend_string *buf = zend_string_alloc(length, 0); + ssize_t bytes = sock->socket->peek(ZSTR_VAL(buf), length); + socket_coro_sync_properties(ZEND_THIS, sock); + if (UNEXPECTED(bytes < 0)) { + zend_string_free(buf); + RETURN_FALSE; + } else if (UNEXPECTED(bytes == 0)) { + zend_string_free(buf); + RETURN_EMPTY_STRING(); + } else { + RETURN_STR(sw_zend_string_recycle(buf, length, bytes)); + } +} + +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; + + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(length) + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (UNEXPECTED(length <= 0)) { + length = SW_BUFFER_SIZE_BIG; + } + + swoole_get_socket_coro(sock, ZEND_THIS); + + zend_string *buf = zend_string_alloc(length, 0); + Socket::TimeoutSetter ts(sock->socket, timeout, Socket::TIMEOUT_READ); + 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; + } else if (UNEXPECTED(bytes == 0)) { + zend_string_free(buf); + RETURN_EMPTY_STRING(); + } else { + RETURN_STR(sw_zend_string_recycle(buf, length, bytes)); + } +} + +static PHP_METHOD(swoole_socket_coro, recv) { + socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV); +} + +static PHP_METHOD(swoole_socket_coro, recvAll) { + 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) { + double timeout = 0; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + swoole_get_socket_coro(sock, ZEND_THIS); + ssize_t retval = sock->socket->recv_packet(timeout); + socket_coro_sync_properties(ZEND_THIS, sock); + if (retval < 0) { + RETURN_FALSE; + } else if (retval == 0) { + sock->socket->get_read_buffer()->clear(); + RETURN_EMPTY_STRING(); + } else { + auto strval = sock->socket->pop_packet(); + if (strval == nullptr) { + sock->socket->set_err(ENOMEM); + RETURN_FALSE; + } else { + zend::assign_zend_string_by_val(return_value, strval, retval); + } + } +} + +static sw_inline void socket_coro_send(INTERNAL_FUNCTION_PARAMETERS, const bool all) { + char *data; + size_t length; + double timeout = 0; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STRING(data, length) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + swoole_get_socket_coro(sock, ZEND_THIS); + + Socket::TimeoutSetter ts(sock->socket, timeout, Socket::TIMEOUT_WRITE); + ssize_t retval = all ? sock->socket->send_all(data, length) : sock->socket->send(data, length); + socket_coro_sync_properties(ZEND_THIS, sock); + if (UNEXPECTED(retval < 0)) { + RETURN_FALSE; + } else { + RETURN_LONG(retval); + } +} + +static PHP_METHOD(swoole_socket_coro, send) { + socket_coro_send(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); +} + +static void socket_coro_write_vector(INTERNAL_FUNCTION_PARAMETERS, const bool all) { + zval *ziov = nullptr; + zval *zelement = nullptr; + HashTable *vht; + double timeout = 0; + int iovcnt = 0; + int iov_index = 0; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ARRAY(ziov) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + swoole_get_socket_coro(sock, ZEND_THIS); + + ON_SCOPE_EXIT { + 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, 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_STRING(zelement)) { + zend_throw_exception_ex(swoole_socket_coro_exception_ce, + EINVAL, + "Item #[%d] must be of type string, %s given", + iov_index, + zend_get_type_by_const(Z_TYPE_P(zelement))); + RETURN_FALSE; + } + if (Z_STRLEN_P(zelement) == 0) { + zend_throw_exception_ex( + swoole_socket_coro_exception_ce, EINVAL, "Item #[%d] cannot be empty string", iov_index); + RETURN_FALSE; + } + iov[iov_index].iov_base = Z_STRVAL_P(zelement); + iov[iov_index].iov_len = Z_STRLEN_P(zelement); + iov_index++; + SW_HASHTABLE_FOREACH_END(); + + swoole::network::IOVector io_vector((struct iovec *) iov.get(), iovcnt); + + Socket::TimeoutSetter ts(sock->socket, timeout, Socket::TIMEOUT_WRITE); + ssize_t retval = all ? sock->socket->writev_all(&io_vector) : sock->socket->writev(&io_vector); + if (UNEXPECTED(retval < 0)) { + RETURN_FALSE; + } else { + RETURN_LONG(retval); + } +} + +static PHP_METHOD(swoole_socket_coro, writeVector) { + socket_coro_write_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); +} + +static PHP_METHOD(swoole_socket_coro, writeVectorAll) { + socket_coro_write_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} + +static void socket_coro_read_vector(INTERNAL_FUNCTION_PARAMETERS, const bool all) { + zval *ziov = nullptr; + zval *zelement = nullptr; + HashTable *vht; + double timeout = 0; + int iovcnt = 0; + int iov_index = 0; + ssize_t total_length = 0; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ARRAY(ziov) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + swoole_get_socket_coro(sock, ZEND_THIS); + + ON_SCOPE_EXIT { + 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, 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); + + 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); + + Socket::TimeoutSetter ts(sock->socket, timeout, Socket::TIMEOUT_READ); + ssize_t retval = all ? sock->socket->readv_all(&io_vector) : sock->socket->readv(&io_vector); + + auto free_func = [](const iovec *iov, int iovcnt, int iov_index) { + for (; iov_index < iovcnt; iov_index++) { + zend_string_free(zend::fetch_zend_string_by_val((char *) iov[iov_index].iov_base)); + } + }; + + if (UNEXPECTED(retval < 0)) { + free_func(iov.get(), iovcnt, 0); + RETURN_FALSE; + } else if (UNEXPECTED(retval == 0)) { + free_func(iov.get(), iovcnt, 0); + RETURN_EMPTY_ARRAY(); + } else { + array_init(return_value); + size_t real_count; + + if (retval < total_length) { + /** + * Free the extra memory. + * For example iov is [5, 5, 5], but we get ['hello', 'world'], we should free the last iov. + */ + iov_index = io_vector.get_index(); + size_t offset_bytes = io_vector.get_offset_bytes(); + + 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; + } + + SW_LOOP_N(real_count) { + ((char *) iov[i].iov_base)[iov[i].iov_len] = '\0'; + add_next_index_str(return_value, zend::fetch_zend_string_by_val((char *) iov[i].iov_base)); + } + } +} + +static PHP_METHOD(swoole_socket_coro, readVector) { + socket_coro_read_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); +} + +static PHP_METHOD(swoole_socket_coro, readVectorAll) { + socket_coro_read_vector(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} + +static PHP_METHOD(swoole_socket_coro, sendFile) { + char *file; + size_t file_len; + zend_long offset = 0; + zend_long length = 0; + + 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; + } + + swoole_get_socket_coro(sock, ZEND_THIS); + if (!sock->socket->sendfile(file, offset, length)) { + zend_update_property_long( + swoole_socket_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), sock->socket->errCode); + zend_update_property_string( + swoole_socket_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errMsg"), sock->socket->errMsg); + RETVAL_FALSE; + } else { + RETVAL_TRUE; + } +} + +static PHP_METHOD(swoole_socket_coro, sendAll) { + socket_coro_send(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} + +static PHP_METHOD(swoole_socket_coro, recvfrom) { + zval *peername; + double timeout = 0; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL_EX(peername, 0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(timeout) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + swoole_get_socket_coro(sock, ZEND_THIS); + + 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); + socket_coro_sync_properties(ZEND_THIS, sock); + if (bytes < 0) { + zend_string_free(buf); + RETURN_FALSE; + } else if (bytes == 0) { + zend_string_free(buf); + RETURN_EMPTY_STRING(); + } else { + zval_dtor(peername); + array_init(peername); + add_assoc_string(peername, "address", (char *) sock->socket->get_ip()); + add_assoc_long(peername, "port", sock->socket->get_port()); + + ZSTR_LEN(buf) = bytes; + ZSTR_VAL(buf)[bytes] = 0; + RETURN_STR(buf); + } +} + +static PHP_METHOD(swoole_socket_coro, sendto) { + char *data; + size_t l_data; + char *addr; + size_t l_addr; + zend_long port = 0; + + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_STRING(addr, l_addr) + Z_PARAM_LONG(port) + Z_PARAM_STRING(data, l_data) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + swoole_get_socket_coro(sock, ZEND_THIS); + + ssize_t retval = sock->socket->sendto(std::string(addr, l_addr), port, data, l_data); + socket_coro_sync_properties(ZEND_THIS, sock); + if (retval < 0) { + RETURN_FALSE; + } else { + RETURN_LONG(retval); + } +} + +static PHP_METHOD(swoole_socket_coro, shutdown) { + zend_long how = SHUT_RDWR; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(how) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + swoole_get_socket_coro(sock, ZEND_THIS); + + if (!sock->socket->shutdown(how)) { + socket_coro_sync_properties(ZEND_THIS, sock); + RETURN_FALSE; + } + RETURN_TRUE; +} + +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_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; +} + +static PHP_METHOD(swoole_socket_coro, getsockname) { + swoole_get_socket_coro(sock, ZEND_THIS); + + Address sa; + if (!sock->socket->getsockname(&sa)) { + socket_coro_sync_properties(ZEND_THIS, sock); + 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()); +} + +static PHP_METHOD(swoole_socket_coro, getpeername) { + swoole_get_socket_coro(sock, ZEND_THIS); + + Address sa; + if (!sock->socket->getpeername(&sa)) { + socket_coro_sync_properties(ZEND_THIS, sock); + 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()); +} + +static PHP_METHOD(swoole_socket_coro, getOption) { + struct linger linger_val; + socklen_t optlen; + int other_val; + zend_long level, optname; + + 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(); + + if (level == IPPROTO_IP) { + switch (optname) { + case IP_MULTICAST_IF: { + struct in_addr if_addr; + unsigned int if_index; + optlen = sizeof(if_addr); + if (_socket->get_option(level, optname, (char *) &if_addr, &optlen) != 0) { + php_swoole_sys_error(E_WARNING, + "getsockopt(%d, " ZEND_LONG_FMT ", " ZEND_LONG_FMT ")", + sock->socket->get_fd(), + level, + optname); + RETURN_FALSE; + } + if (php_add4_to_if_index(&if_addr, sock->socket, &if_index) == SUCCESS) { + RETURN_LONG((zend_long) if_index); + } else { + RETURN_FALSE; + } + } + } + } else if (level == IPPROTO_IPV6) { + int ret = php_do_getsockopt_ipv6_rfc3542(sock->socket, level, optname, return_value); + if (ret == SUCCESS) { + return; + } else if (ret == FAILURE) { + RETURN_FALSE; + } /* else continue */ + } + + /* sol_socket options and general case */ + switch (optname) { + case SO_LINGER: { + optlen = sizeof(linger_val); + + if (_socket->get_option(level, optname, (char *) &linger_val, &optlen) != 0) { + php_swoole_sys_error(E_WARNING, + "getsockopt(%d, " ZEND_LONG_FMT ", " ZEND_LONG_FMT ")", + sock->socket->get_fd(), + level, + optname); + RETURN_FALSE; + } + + array_init(return_value); + add_assoc_long(return_value, "l_onoff", linger_val.l_onoff); + add_assoc_long(return_value, "l_linger", linger_val.l_linger); + break; + } + case SO_RCVTIMEO: + case SO_SNDTIMEO: { + double timeout = + sock->socket->get_timeout(optname == SO_RCVTIMEO ? Socket::TIMEOUT_READ : Socket::TIMEOUT_WRITE); + array_init(return_value); + 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); + + if (_socket->get_option(level, optname, (char *) &other_val, &optlen) != 0) { + php_swoole_sys_error(E_WARNING, + "getsockopt(%d, " ZEND_LONG_FMT ", " ZEND_LONG_FMT ")", + sock->socket->get_fd(), + level, + optname); + RETURN_FALSE; + } + if (optlen == 1) { + char _val; + memcpy(&_val, &other_val, optlen); + RETURN_LONG(_val); + } else { + RETURN_LONG(other_val); + } + break; + } + } +} + +static PHP_METHOD(swoole_socket_coro, setOption) { + zval *arg4; + struct linger lv; + int ov, optlen, retval; + struct timeval tv; + zend_long level, optname; + char *opt_ptr; + HashTable *opt_ht; + zval *l_onoff, *l_linger; + zval *sec, *usec; + + 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); + +#define HANDLE_SUBCALL(res) \ + do { \ + if (res == 1) { \ + goto default_case; \ + } else if (res == SUCCESS) { \ + RETURN_TRUE; \ + } else { \ + RETURN_FALSE; \ + } \ + } while (0) + + if (level == IPPROTO_IP) { + int res = php_do_setsockopt_ip_mcast(sock->socket, level, optname, arg4); + HANDLE_SUBCALL(res); + } else if (level == IPPROTO_IPV6) { + int res = php_do_setsockopt_ipv6_mcast(sock->socket, level, optname, arg4); + if (res == 1) { + res = php_do_setsockopt_ipv6_rfc3542(sock->socket, level, optname, arg4); + } + HANDLE_SUBCALL(res); + } + + switch (optname) { + case SO_LINGER: { + const char l_onoff_key[] = "l_onoff"; + const char l_linger_key[] = "l_linger"; + + convert_to_array_ex(arg4); + opt_ht = Z_ARRVAL_P(arg4); + + if ((l_onoff = zend_hash_str_find(opt_ht, l_onoff_key, sizeof(l_onoff_key) - 1)) == nullptr) { + php_error_docref(nullptr, E_WARNING, "no key \"%s\" passed in optval", l_onoff_key); + RETURN_FALSE; + } + if ((l_linger = zend_hash_str_find(opt_ht, l_linger_key, sizeof(l_linger_key) - 1)) == nullptr) { + php_error_docref(nullptr, E_WARNING, "no key \"%s\" passed in optval", l_linger_key); + RETURN_FALSE; + } + + convert_to_long_ex(l_onoff); + convert_to_long_ex(l_linger); + + lv.l_onoff = (unsigned short) Z_LVAL_P(l_onoff); + lv.l_linger = (unsigned short) Z_LVAL_P(l_linger); + + optlen = sizeof(lv); + opt_ptr = (char *) &lv; + break; + } + + case SO_RCVTIMEO: + case SO_SNDTIMEO: { + const char sec_key[] = "sec"; + const char usec_key[] = "usec"; + + convert_to_array_ex(arg4); + opt_ht = Z_ARRVAL_P(arg4); + + if ((sec = zend_hash_str_find(opt_ht, sec_key, sizeof(sec_key) - 1)) == nullptr) { + php_error_docref(nullptr, E_WARNING, "no key \"%s\" passed in optval", sec_key); + RETURN_FALSE; + } + if ((usec = zend_hash_str_find(opt_ht, usec_key, sizeof(usec_key) - 1)) == nullptr) { + php_error_docref(nullptr, E_WARNING, "no key \"%s\" passed in optval", usec_key); + RETURN_FALSE; + } + + convert_to_long_ex(sec); + convert_to_long_ex(usec); + tv.tv_sec = Z_LVAL_P(sec); + tv.tv_usec = Z_LVAL_P(usec); + sock->socket->set_timeout( + &tv, optname == SO_RCVTIMEO ? Socket::TIMEOUT_READ : Socket::TIMEOUT_CONNECT | Socket::TIMEOUT_WRITE); + RETURN_TRUE; + break; + } +#ifdef SO_BINDTODEVICE + case SO_BINDTODEVICE: { + if (Z_TYPE_P(arg4) == IS_STRING) { + opt_ptr = Z_STRVAL_P(arg4); + optlen = Z_STRLEN_P(arg4); + } else { + opt_ptr = (char *) ""; + optlen = 0; + } + break; + } +#endif + + default: + default_case: + convert_to_long_ex(arg4); + ov = Z_LVAL_P(arg4); + + optlen = sizeof(ov); + opt_ptr = (char *) &ov; + break; + } + + retval = sock->socket->get_socket()->set_option(level, optname, opt_ptr, optlen); + if (retval != 0) { + php_swoole_sys_error(E_WARNING, "setsockopt(%d) failed", sock->socket->get_fd()); + RETURN_FALSE; + } + + RETURN_TRUE; +} + +static PHP_METHOD(swoole_socket_coro, cancel) { + swoole_get_socket_coro(sock, ZEND_THIS); + zend_long event = SW_EVENT_READ; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(event) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_BOOL(sock->socket->cancel(event == SW_EVENT_READ ? SW_EVENT_READ : SW_EVENT_WRITE)); +} + +static PHP_METHOD(swoole_socket_coro, setProtocol) { + swoole_get_socket_coro(sock, ZEND_THIS); + zval *zset; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(zset) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (php_swoole_array_length(zset) == 0) { + RETURN_FALSE; + } else { + RETURN_BOOL(php_swoole_socket_set_protocol(sock->socket, zset)); + } +} + +#ifdef SW_USE_OPENSSL +static PHP_METHOD(swoole_socket_coro, sslHandshake) { + swoole_get_socket_coro(sock, ZEND_THIS); + + 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 new file mode 100644 index 00000000000..0eab1792796 --- /dev/null +++ b/ext-src/swoole_table.cc @@ -0,0 +1,606 @@ +/* + +----------------------------------------------------------------------+ + | 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 "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); + + for (auto i = table->column_list->begin(); i != table->column_list->end(); i++) { + TableColumn *col = *i; + if (col->type == TableColumn::TYPE_STRING) { + 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) { + 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) { + long lval = 0; + row->get_value(col, &lval); + add_assoc_long_ex(return_value, col->name.c_str(), col->name.length(), lval); + } else { + abort(); + } + } +} + +static inline void php_swoole_table_get_field_value( + Table *table, TableRow *row, zval *return_value, char *field, uint16_t field_len) { + TableColumn *col = table->get_column(std::string(field, field_len)); + if (!col) { + ZVAL_FALSE(return_value); + return; + } + if (col->type == TableColumn::TYPE_STRING) { + 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) { + double dval = 0; + row->get_value(col, &dval); + ZVAL_DOUBLE(return_value, dval); + } else if (col->type == TableColumn::TYPE_INT) { + long lval = 0; + row->get_value(col, &lval); + ZVAL_LONG(return_value, lval); + } else { + abort(); + } +} + +static zend_class_entry *swoole_table_ce; +static zend_object_handlers swoole_table_handlers; + +struct TableObject { + Table *ptr; + zend_object std; +}; + +static inline TableObject *php_swoole_table_fetch_object(zend_object *obj) { + return (TableObject *) ((char *) obj - swoole_table_handlers.offset); +} + +static inline Table *php_swoole_table_get_ptr(zval *zobject) { + return php_swoole_table_fetch_object(Z_OBJ_P(zobject))->ptr; +} + +static inline Table *php_swoole_table_get_and_check_ptr(zval *zobject) { + Table *table = php_swoole_table_get_ptr(zobject); + if (UNEXPECTED(!table)) { + swoole_fatal_error(SW_ERROR_WRONG_OPERATION, "must call constructor first"); + } + return table; +} + +static inline Table *php_swoole_table_get_and_check_ptr2(zval *zobject) { + Table *table = php_swoole_table_get_and_check_ptr(zobject); + if (!table->ready()) { + php_swoole_fatal_error(E_ERROR, "table is not created or has been destroyed"); + } + return table; +} + +static void inline php_swoole_table_set_ptr(zval *zobject, Table *ptr) { + php_swoole_table_fetch_object(Z_OBJ_P(zobject))->ptr = ptr; +} + +static inline void php_swoole_table_free_object(zend_object *object) { + zend_object_std_dtor(object); +} + +static inline zend_object *php_swoole_table_create_object(zend_class_entry *ce) { + TableObject *table = (TableObject *) zend_object_alloc(sizeof(TableObject), ce); + zend_object_std_init(&table->std, ce); + object_properties_init(&table->std, ce); + table->std.handlers = &swoole_table_handlers; + return &table->std; +} + +SW_EXTERN_C_BEGIN +static PHP_METHOD(swoole_table, __construct); +static PHP_METHOD(swoole_table, column); +static PHP_METHOD(swoole_table, create); +static PHP_METHOD(swoole_table, set); +static PHP_METHOD(swoole_table, get); +static PHP_METHOD(swoole_table, del); +static PHP_METHOD(swoole_table, exists); +static PHP_METHOD(swoole_table, incr); +static PHP_METHOD(swoole_table, decr); +static PHP_METHOD(swoole_table, count); +static PHP_METHOD(swoole_table, destroy); +static PHP_METHOD(swoole_table, getSize); +static PHP_METHOD(swoole_table, getMemorySize); +static PHP_METHOD(swoole_table, stats); + +static PHP_METHOD(swoole_table, rewind); +static PHP_METHOD(swoole_table, next); +static PHP_METHOD(swoole_table, current); +static PHP_METHOD(swoole_table, key); +static PHP_METHOD(swoole_table, valid); + +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_table_methods[] = +{ + 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_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", 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, 1, zend_ce_iterator); +#ifdef SW_HAVE_COUNTABLE + zend_class_implements(swoole_table_ce, 1, zend_ce_countable); +#endif + + zend_declare_property_null(swoole_table_ce, ZEND_STRL("size"), ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_table_ce, ZEND_STRL("memorySize"), ZEND_ACC_PUBLIC); + + 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); +} + +PHP_METHOD(swoole_table, __construct) { + Table *table = php_swoole_table_get_ptr(ZEND_THIS); + if (table) { + 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; + double conflict_proportion = SW_TABLE_CONFLICT_PROPORTION; + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 2) + Z_PARAM_LONG(table_size) + Z_PARAM_OPTIONAL + Z_PARAM_DOUBLE(conflict_proportion) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + table = Table::make(table_size, conflict_proportion); + if (table == nullptr) { + zend_throw_exception(swoole_exception_ce, "global memory allocation failure", SW_ERROR_MALLOC_FAIL); + RETURN_FALSE; + } + table->set_hash_func([](const char *key, size_t len) -> uint64_t { + return zend_string_hash_val(zend::fetch_zend_string_by_val((void *) key)); + }); + php_swoole_table_set_ptr(ZEND_THIS, table); +} + +PHP_METHOD(swoole_table, column) { + Table *table = php_swoole_table_get_and_check_ptr(ZEND_THIS); + char *name; + size_t len; + long type; + long size = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl|l", &name, &len, &type, &size) == FAILURE) { + RETURN_FALSE; + } + if (type == TableColumn::TYPE_STRING) { + if (size < 1) { + php_swoole_fatal_error(E_WARNING, "the length of string type values has to be more than zero"); + RETURN_FALSE; + } + size = SW_MEM_ALIGNED_SIZE(size); + } + if (table->ready()) { + php_swoole_fatal_error(E_WARNING, "unable to add column after table has been created"); + RETURN_FALSE; + } + RETURN_BOOL(table->add_column(std::string(name, len), (enum TableColumn::Type) type, size)); +} + +static PHP_METHOD(swoole_table, create) { + Table *table = php_swoole_table_get_and_check_ptr(ZEND_THIS); + + if (!table->create()) { + php_swoole_fatal_error(E_ERROR, "unable to allocate memory"); + 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()); + RETURN_TRUE; +} + +static PHP_METHOD(swoole_table, destroy) { + Table *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); + + table->destroy(); + php_swoole_table_set_ptr(ZEND_THIS, nullptr); + RETURN_TRUE; +} + +static PHP_METHOD(swoole_table, set) { + Table *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); + zval *array; + char *key; + size_t keylen; + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 2, 2) + Z_PARAM_STRING(key, keylen) + Z_PARAM_ARRAY(array) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (!table->ready()) { + php_swoole_fatal_error(E_ERROR, "the table object does not exist"); + RETURN_FALSE; + } + + if (keylen >= SW_TABLE_KEY_SIZE) { + php_swoole_fatal_error(E_WARNING, "key[%s] is too long", key); + } + + int out_flags; + TableRow *_rowlock = nullptr; + TableRow *row = table->set(key, keylen, &_rowlock, &out_flags); + if (!row) { + _rowlock->unlock(); + php_swoole_error(E_WARNING, "failed to set('%*s'), unable to allocate memory", (int) keylen, key); + RETURN_FALSE; + } + + HashTable *ht = Z_ARRVAL_P(array); + + if (out_flags & SW_TABLE_FLAG_NEW_ROW) { + for (auto i = table->column_list->begin(); i != table->column_list->end(); i++) { + 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); + } else { + if (col->type == TableColumn::TYPE_STRING) { + zend_string *str = zval_get_string(zv); + row->set_value(col, ZSTR_VAL(str), ZSTR_LEN(str)); + zend_string_release(str); + } else if (col->type == TableColumn::TYPE_FLOAT) { + double _value = zval_get_double(zv); + row->set_value(col, &_value, 0); + } else { + long _value = zval_get_long(zv); + row->set_value(col, &_value, 0); + } + } + } + } else { + const char *k; + uint32_t klen; + int ktype; + zval *zv; + SW_HASHTABLE_FOREACH_START2(ht, k, klen, ktype, zv) { + if (k == nullptr) { + continue; + } + TableColumn *col = table->get_column(std::string(k, klen)); + if (col == nullptr) { + continue; + } else if (col->type == TableColumn::TYPE_STRING) { + zend_string *str = zval_get_string(zv); + row->set_value(col, ZSTR_VAL(str), ZSTR_LEN(str)); + zend_string_release(str); + } else if (col->type == TableColumn::TYPE_FLOAT) { + double _value = zval_get_double(zv); + row->set_value(col, &_value, 0); + } else { + long _value = zval_get_long(zv); + row->set_value(col, &_value, 0); + } + } + (void) ktype; + SW_HASHTABLE_FOREACH_END(); + } + _rowlock->unlock(); + RETURN_TRUE; +} + +static PHP_METHOD(swoole_table, incr) { + Table *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); + char *key; + size_t key_len; + char *col; + size_t col_len; + zval *incrby = nullptr; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|z", &key, &key_len, &col, &col_len, &incrby) == FAILURE) { + RETURN_FALSE; + } + + int out_flags; + TableRow *_rowlock = nullptr; + TableRow *row = table->set(key, key_len, &_rowlock, &out_flags); + if (!row) { + _rowlock->unlock(); + php_swoole_fatal_error(E_WARNING, "unable to allocate memory"); + RETURN_FALSE; + } + + TableColumn *column = table->get_column(std::string(col, col_len)); + if (column == nullptr) { + _rowlock->unlock(); + php_swoole_fatal_error(E_WARNING, "column[%s] does not exist", col); + RETURN_FALSE; + } + + if (out_flags & SW_TABLE_FLAG_NEW_ROW) { + table->clear_row(row); + } + + if (column->type == TableColumn::TYPE_STRING) { + _rowlock->unlock(); + php_swoole_fatal_error(E_WARNING, "can't execute 'incr' on a string type column"); + RETURN_FALSE; + } else if (column->type == TableColumn::TYPE_FLOAT) { + double set_value = 0; + memcpy(&set_value, row->data + column->index, sizeof(set_value)); + if (incrby) { + set_value += zval_get_double(incrby); + } else { + set_value += 1; + } + row->set_value(column, &set_value, 0); + RETVAL_DOUBLE(set_value); + } else { + long set_value = 0; + memcpy(&set_value, row->data + column->index, sizeof(set_value)); + if (incrby) { + set_value += zval_get_long(incrby); + } else { + set_value += 1; + } + row->set_value(column, &set_value, 0); + RETVAL_LONG(set_value); + } + _rowlock->unlock(); +} + +static PHP_METHOD(swoole_table, decr) { + Table *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); + char *key; + size_t key_len; + char *col; + size_t col_len; + zval *decrby = nullptr; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|z", &key, &key_len, &col, &col_len, &decrby) == FAILURE) { + RETURN_FALSE; + } + + int out_flags; + TableRow *_rowlock = nullptr; + TableRow *row = table->set(key, key_len, &_rowlock, &out_flags); + if (!row) { + _rowlock->unlock(); + php_swoole_fatal_error(E_WARNING, "unable to allocate memory"); + RETURN_FALSE; + } + + TableColumn *column = table->get_column(std::string(col, col_len)); + if (column == nullptr) { + _rowlock->unlock(); + php_swoole_fatal_error(E_WARNING, "column[%s] does not exist", col); + RETURN_FALSE; + } + + if (out_flags & SW_TABLE_FLAG_NEW_ROW) { + table->clear_row(row); + } + + if (column->type == TableColumn::TYPE_STRING) { + _rowlock->unlock(); + php_swoole_fatal_error(E_WARNING, "can't execute 'decr' on a string type column"); + RETURN_FALSE; + } else if (column->type == TableColumn::TYPE_FLOAT) { + double set_value = 0; + memcpy(&set_value, row->data + column->index, sizeof(set_value)); + if (decrby) { + set_value -= zval_get_double(decrby); + } else { + set_value -= 1; + } + row->set_value(column, &set_value, 0); + RETVAL_DOUBLE(set_value); + } else { + long set_value = 0; + memcpy(&set_value, row->data + column->index, sizeof(set_value)); + if (decrby) { + set_value -= zval_get_long(decrby); + } else { + set_value -= 1; + } + row->set_value(column, &set_value, 0); + RETVAL_LONG(set_value); + } + _rowlock->unlock(); +} + +static PHP_METHOD(swoole_table, get) { + Table *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); + char *key; + size_t keylen; + char *field = nullptr; + size_t field_len = 0; + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 2) + Z_PARAM_STRING(key, keylen) + Z_PARAM_OPTIONAL + Z_PARAM_STRING(field, field_len) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + TableRow *_rowlock = nullptr; + TableRow *row = table->get(key, keylen, &_rowlock); + if (!row) { + RETVAL_FALSE; + } else if (field && field_len > 0) { + php_swoole_table_get_field_value(table, row, return_value, field, (uint16_t) field_len); + } else { + php_swoole_table_row2array(table, row, return_value); + } + _rowlock->unlock(); +} + +static PHP_METHOD(swoole_table, exists) { + Table *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); + char *key; + size_t keylen; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &keylen) == FAILURE) { + RETURN_FALSE; + } + RETURN_BOOL(table->exists(key, keylen)); +} + +static PHP_METHOD(swoole_table, del) { + Table *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); + char *key; + size_t keylen; + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1) + Z_PARAM_STRING(key, keylen) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + RETURN_BOOL(table->del(key, keylen)); +} + +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; + } + + if (mode == COUNT_NORMAL) { + RETURN_LONG(table->count()); + } else { + RETURN_LONG(table->count() * table->column_list->size()); + } +} + +static PHP_METHOD(swoole_table, getMemorySize) { + Table *table = php_swoole_table_get_ptr(ZEND_THIS); + if (!table) { + RETURN_LONG(0); + } else { + RETURN_LONG(table->get_memory_size()); + } +} + +static PHP_METHOD(swoole_table, getSize) { + Table *table = php_swoole_table_get_ptr(ZEND_THIS); + if (!table) { + RETURN_LONG(0); + } else { + RETURN_LONG(table->get_size()); + } +} + +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); + 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); + 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(); +} 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 new file mode 100644 index 00000000000..1011cb624bd --- /dev/null +++ b/ext-src/swoole_timer.cc @@ -0,0 +1,311 @@ +/* + +----------------------------------------------------------------------+ + | 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: 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; + +zend_class_entry *swoole_timer_ce; +static zend_object_handlers swoole_timer_handlers; + +static zend_class_entry *swoole_timer_iterator_ce; + +SW_EXTERN_C_BEGIN +static PHP_FUNCTION(swoole_timer_after); +static PHP_FUNCTION(swoole_timer_tick); +static PHP_FUNCTION(swoole_timer_exists); +static PHP_FUNCTION(swoole_timer_info); +static PHP_FUNCTION(swoole_timer_stats); +static PHP_FUNCTION(swoole_timer_list); +static PHP_FUNCTION(swoole_timer_clear); +static PHP_FUNCTION(swoole_timer_clear_all); +SW_EXTERN_C_END + +// clang-format off +static const zend_function_entry swoole_timer_methods[] = +{ + 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", 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", 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); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TIMER_MAX_MS", SW_TIMER_MAX_MS); + SW_REGISTER_DOUBLE_CONSTANT("SWOOLE_TIMER_MAX_SEC", SW_TIMER_MAX_SEC); +} + +static void timer_dtor(TimerNode *tnode) { + Function *fci = (Function *) tnode->data; + sw_zend_fci_params_discard(&fci->fci); + sw_zend_fci_cache_discard(&fci->fci_cache); + efree(fci); +} + +bool php_swoole_timer_clear(TimerNode *tnode) { + return swoole_timer_del(tnode); +} + +bool php_swoole_timer_clear_all() { + if (UNEXPECTED(!SwooleTG.timer)) { + return false; + } + + size_t num = SwooleTG.timer->count(), index = 0; + TimerNode **list = (TimerNode **) emalloc(num * sizeof(TimerNode *)); + for (auto &kv : SwooleTG.timer->get_map()) { + TimerNode *tnode = kv.second; + if (tnode->type == TimerNode::TYPE_PHP) { + list[index++] = tnode; + } + } + + while (index--) { + swoole_timer_del(list[index]); + } + + efree(list); + + return true; +} + +static void timer_callback(Timer *timer, TimerNode *tnode) { + Function *fci = (Function *) tnode->data; + + if (UNEXPECTED(!fci->call(nullptr, php_swoole_is_enable_coroutine()))) { + php_swoole_error(E_WARNING, "%s->onTimeout handler error", ZSTR_VAL(swoole_timer_ce->name)); + } + if (!tnode->interval || tnode->removed) { + timer_dtor(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)); + TimerNode *tnode; + + ZEND_PARSE_PARAMETERS_START(2, -1) + Z_PARAM_LONG(ms) + Z_PARAM_FUNC(fci->fci, fci->fci_cache) + Z_PARAM_VARIADIC('*', fci->fci.params, fci->fci.param_count) + ZEND_PARSE_PARAMETERS_END_EX(goto _failed); + + if (UNEXPECTED(ms < SW_TIMER_MIN_MS)) { + php_swoole_fatal_error(E_WARNING, "Timer must be greater than or equal to " ZEND_TOSTR(SW_TIMER_MIN_MS)); + _failed: + efree(fci); + RETURN_FALSE; + } + + if (UNEXPECTED(!sw_reactor() && timer_if_use_reactor())) { + php_swoole_check_reactor(); + } + + tnode = swoole_timer_add((long) ms, persistent, timer_callback, fci); + if (UNEXPECTED(!tnode)) { + php_swoole_fatal_error(E_WARNING, "add timer failed"); + goto _failed; + } + tnode->type = TimerNode::TYPE_PHP; + tnode->destructor = timer_dtor; + if (persistent) { + if (fci->fci.param_count > 0) { + uint32_t i; + zval *params = (zval *) ecalloc(fci->fci.param_count + 1, sizeof(zval)); + for (i = 0; i < fci->fci.param_count; i++) { + ZVAL_COPY(¶ms[i + 1], &fci->fci.params[i]); + } + fci->fci.params = params; + } else { + fci->fci.params = (zval *) emalloc(sizeof(zval)); + } + fci->fci.param_count += 1; + ZVAL_LONG(fci->fci.params, tnode->id); + } else { + sw_zend_fci_params_persist(&fci->fci); + } + sw_zend_fci_cache_persist(&fci->fci_cache); + RETURN_LONG(tnode->id); +} + +static PHP_FUNCTION(swoole_timer_after) { + timer_add(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); +} + +static PHP_FUNCTION(swoole_timer_tick) { + timer_add(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); +} + +static PHP_FUNCTION(swoole_timer_exists) { + if (UNEXPECTED(!SwooleTG.timer)) { + RETURN_FALSE; + } else { + zend_long id; + TimerNode *tnode; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(id) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + tnode = swoole_timer_get(id); + RETURN_BOOL(tnode && !tnode->removed); + } +} + +static PHP_FUNCTION(swoole_timer_info) { + if (UNEXPECTED(!SwooleTG.timer)) { + RETURN_FALSE; + } else { + zend_long id; + TimerNode *tnode; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(id) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + tnode = swoole_timer_get(id); + if (UNEXPECTED(!tnode)) { + RETURN_NULL(); + } + 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); + } +} + +static PHP_FUNCTION(swoole_timer_stats) { + array_init(return_value); + if (SwooleTG.timer) { + add_assoc_bool(return_value, "initialized", 1); + add_assoc_long(return_value, "num", SwooleTG.timer->count()); + add_assoc_long(return_value, "round", SwooleTG.timer->get_round()); + } else { + add_assoc_bool(return_value, "initialized", 0); + add_assoc_long(return_value, "num", 0); + add_assoc_long(return_value, "round", 0); + } +} + +static PHP_FUNCTION(swoole_timer_list) { + zval zlist; + array_init(&zlist); + if (EXPECTED(SwooleTG.timer)) { + for (auto &kv : SwooleTG.timer->get_map()) { + TimerNode *tnode = kv.second; + if (tnode->type == TimerNode::TYPE_PHP) { + add_next_index_long(&zlist, tnode->id); + } + } + } + object_init_ex(return_value, swoole_timer_iterator_ce); + sw_zend_call_method_with_1_params( + return_value, swoole_timer_iterator_ce, &swoole_timer_iterator_ce->constructor, "__construct", nullptr, &zlist); + zval_ptr_dtor(&zlist); +} + +static PHP_FUNCTION(swoole_timer_clear) { + if (UNEXPECTED(!SwooleTG.timer)) { + RETURN_FALSE; + } else { + zend_long id; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(id) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + TimerNode *tnode = swoole_timer_get(id); + if (!tnode || tnode->type != TimerNode::TYPE_PHP) { + RETURN_FALSE; + } + RETURN_BOOL(swoole_timer_del(tnode)); + } +} + +static PHP_FUNCTION(swoole_timer_clear_all) { + RETURN_BOOL(php_swoole_timer_clear_all()); +} diff --git a/ext-src/swoole_websocket_server.cc b/ext-src/swoole_websocket_server.cc new file mode 100644 index 00000000000..1ab926a18a3 --- /dev/null +++ b/ext-src/swoole_websocket_server.cc @@ -0,0 +1,932 @@ +/* + +----------------------------------------------------------------------+ + | 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_http_server.h" + +SW_EXTERN_C_BEGIN +#include "ext/standard/sha1.h" +#include "stubs/php_swoole_websocket_arginfo.h" +SW_EXTERN_C_END + +#include "swoole_base64.h" + +using swoole::Connection; +using swoole::ListenPort; +using swoole::RecvData; +using swoole::Server; +using swoole::SessionId; +using swoole::String; +using swoole::coroutine::Socket; + +using HttpContext = swoole::http::Context; + +namespace WebSocket = swoole::websocket; + +zend_class_entry *swoole_websocket_server_ce; +static zend_object_handlers swoole_websocket_server_handlers; + +zend_class_entry *swoole_websocket_frame_ce; +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); +static PHP_METHOD(swoole_websocket_server, pack); +static PHP_METHOD(swoole_websocket_server, unpack); +static PHP_METHOD(swoole_websocket_server, disconnect); + +static PHP_METHOD(swoole_websocket_frame, __toString); +SW_EXTERN_C_END + +// clang-format off +const zend_function_entry swoole_websocket_server_methods[] = +{ + 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_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 + +#ifdef SW_HAVE_ZLIB +static bool websocket_message_compress(String *buffer, const char *data, size_t length, int level); +static bool websocket_message_uncompress(String *buffer, const char *in, size_t in_len); +#endif + +static void php_swoole_websocket_construct_frame(zval *zframe, zend_long opcode, zval *zpayload, uint8_t flags) { + 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); + if (payload_length >= SW_WEBSOCKET_CLOSE_CODE_LEN) { + // WebSocket Close code + zend_update_property_long(swoole_websocket_closeframe_ce, + SW_Z8_OBJ_P(zframe), + ZEND_STRL("code"), + (payload[0] << 8) ^ (payload[1] & 0xFF)); + if (payload_length > SW_WEBSOCKET_CLOSE_CODE_LEN) { + // WebSocket Close reason message + zend_update_property_stringl(swoole_websocket_closeframe_ce, + SW_Z8_OBJ_P(zframe), + ZEND_STRL("reason"), + payload + SW_WEBSOCKET_CLOSE_CODE_LEN, + payload_length - SW_WEBSOCKET_CLOSE_CODE_LEN); + } + } + } else { + object_init_ex(zframe, swoole_websocket_frame_ce); + zend_update_property(swoole_websocket_frame_ce, SW_Z8_OBJ_P(zframe), ZEND_STRL("data"), zpayload); + } + zend_update_property_long(swoole_websocket_frame_ce, SW_Z8_OBJ_P(zframe), ZEND_STRL("opcode"), 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 & WebSocket::FLAG_FIN); +} + +void php_swoole_websocket_frame_unpack_ex(String *data, zval *zframe, uchar uncompress) { + WebSocket::Frame frame; + zval zpayload; + uint8_t flags; + + if (data->length < sizeof(frame.header)) { + swoole_set_last_error(SW_ERROR_PROTOCOL_ERROR); + ZVAL_FALSE(zframe); + return; + } + + 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) { + 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 = zlib_buffer->str; + frame.payload_length = zlib_buffer->length; + flags ^= (WebSocket::FLAG_RSV1 | WebSocket::FLAG_COMPRESS); + } +#endif + /* TODO: optimize memory copy */ + ZVAL_STRINGL(&zpayload, frame.payload, frame.payload_length); + php_swoole_websocket_construct_frame(zframe, frame.header.OPCODE, &zpayload, flags); + zval_ptr_dtor(&zpayload); +} + +void php_swoole_websocket_frame_unpack(String *data, zval *zframe) { + return php_swoole_websocket_frame_unpack_ex(data, zframe, 0); +} + +static sw_inline int php_swoole_websocket_frame_pack_ex(String *buffer, + zval *zdata, + zend_long opcode, + zend_long code, + uint8_t flags, + zend_bool mask, + zend_bool allow_compress) { + char *data = nullptr; + size_t length = 0; + + if (sw_unlikely(opcode > SW_WEBSOCKET_OPCODE_MAX)) { + php_swoole_fatal_error(E_WARNING, "the maximum value of opcode is %d", SW_WEBSOCKET_OPCODE_MAX); + return SW_ERR; + } + + zend::String str_zdata; + if (zdata && !ZVAL_IS_NULL(zdata)) { + str_zdata = zdata; + data = str_zdata.val(); + length = str_zdata.len(); + } + + if (mask) { + flags |= WebSocket::FLAG_MASK; + } + +#ifdef SW_HAVE_ZLIB + if (flags & WebSocket::FLAG_COMPRESS) { + if (!allow_compress) { + flags ^= WebSocket::FLAG_COMPRESS; + } else if (length > 0) { + String *zlib_buffer = sw_tg_buffer(); + zlib_buffer->clear(); + if (websocket_message_compress(zlib_buffer, data, length, Z_DEFAULT_COMPRESSION)) { + data = zlib_buffer->str; + length = zlib_buffer->length; + flags |= WebSocket::FLAG_RSV1; + } + } + } +#endif + + // TODO: send data in zlib_buffer directly, do not copy to http_buffer + switch (opcode) { + case WebSocket::OPCODE_CLOSE: + return WebSocket::pack_close_frame(buffer, code, data, length, flags); + default: + return WebSocket::encode(buffer, data, length, opcode, flags) ? SW_OK : SW_ERR; + } +} + +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); +} + +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 = 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), 1))) { + opcode = zval_get_long(ztmp); + } + 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); + } + if ((ztmp = sw_zend_read_property_not_null_ex( + swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_REASON), 1))) { + zdata = ztmp; + } + } + if (!zdata && + (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), 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), 1))) { + if (zval_is_true(ztmp)) { + flags |= WebSocket::FLAG_FIN; + } else { + flags &= ~WebSocket::FLAG_FIN; + } + } + + return php_swoole_websocket_frame_pack_ex( + buffer, zdata, opcode, code, flags & WebSocket::FLAGS_ALL, mask, allow_compress); +} + +void swoole_websocket_onBeforeHandshakeResponse(Server *serv, int server_fd, HttpContext *ctx) { + auto cb = php_swoole_server_get_callback(serv, server_fd, SW_SERVER_CB_onBeforeHandshakeResponse); + if (cb) { + zval args[3]; + args[0] = *php_swoole_server_zval_ptr(serv); + args[1] = *ctx->request.zobject; + args[2] = *ctx->response.zobject; + if (UNEXPECTED(!zend::function::call(cb, 3, args, nullptr, serv->is_enable_coroutine()))) { + php_swoole_error( + E_WARNING, "%s->onBeforeHandshakeResponse handler error", ZSTR_VAL(swoole_websocket_server_ce->name)); + serv->close(ctx->fd, false); + } + } +} + +void swoole_websocket_onOpen(Server *serv, HttpContext *ctx) { + Connection *conn = serv->get_connection_by_session_id(ctx->fd); + if (!conn) { + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "session[%ld] is closed", ctx->fd); + return; + } + auto cb = php_swoole_server_get_callback(serv, conn->server_fd, SW_SERVER_CB_onOpen); + if (cb) { + zval args[2]; + args[0] = *php_swoole_server_zval_ptr(serv); + args[1] = *ctx->request.zobject; + 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); + } + } +} + +/** + * default onRequest callback + */ +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" + "Cache-Control: must-revalidate,no-cache,no-store\r\n" + "Content-Length: 83\r\n" + "Server: " SW_HTTP_SERVER_SOFTWARE "\r\n\r\n" + "

HTTP 400 Bad Request


Powered by Swoole"; + + ctx->send(ctx, (char *) bad_request, strlen(bad_request)); + ctx->end_ = 1; + ctx->close(ctx); +} + +void php_swoole_sha1(const char *str, int _len, unsigned char *digest) { + PHP_SHA1_CTX context; + PHP_SHA1Init(&context); + PHP_SHA1Update(&context, (unsigned char *) str, _len); + PHP_SHA1Final(digest, &context); +} + +bool swoole_websocket_handshake(HttpContext *ctx) { + char sec_buf[128]; + zval *header = ctx->request.zheader; + HashTable *ht = Z_ARRVAL_P(header); + zval *pData; + zval retval; + + if (!(pData = zend_hash_str_find(ht, ZEND_STRL("sec-websocket-key")))) { + _bad_request: + ctx->response.status = SW_HTTP_BAD_REQUEST; + ctx->end(nullptr, &retval); + return false; + } + + zend::String str_pData(pData); + + if (str_pData.len() != BASE64_ENCODE_OUT_SIZE(SW_WEBSOCKET_SEC_KEY_LEN)) { + goto _bad_request; + } + + char sha1_str[20]; + // sec_websocket_accept + memcpy(sec_buf, str_pData.val(), str_pData.len()); + memcpy(sec_buf + str_pData.len(), SW_WEBSOCKET_GUID, sizeof(SW_WEBSOCKET_GUID) - 1); + // sha1 sec_websocket_accept + php_swoole_sha1(sec_buf, str_pData.len() + sizeof(SW_WEBSOCKET_GUID) - 1, (unsigned char *) sha1_str); + // base64 encode + int sec_len = swoole::base64_encode((unsigned char *) sha1_str, sizeof(sha1_str), sec_buf); + + 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); + + Server *serv = nullptr; + Connection *conn = nullptr; + + if (!ctx->co_socket) { + serv = (Server *) ctx->private_data; + conn = serv->get_connection_by_session_id(ctx->fd); + if (!conn) { + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_NOT_EXIST, "session[%ld] is closed", ctx->fd); + return false; + } + } + + if (conn) { + conn->websocket_status = WebSocket::STATUS_ACTIVE; + ListenPort *port = serv->get_port_by_server_fd(conn->server_fd); + if (port && !port->websocket_subprotocol.empty()) { + ctx->set_header(ZEND_STRL("Sec-WebSocket-Protocol"), + port->websocket_subprotocol.c_str(), + port->websocket_subprotocol.length(), + false); + } + 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 = WebSocket::get_package_length; + } + + ctx->response.status = SW_HTTP_SWITCHING_PROTOCOLS; + ctx->upgrade = 1; + + ctx->end(nullptr, &retval); + return Z_TYPE(retval) == IS_TRUE; +} + +#ifdef SW_HAVE_ZLIB +static bool websocket_message_uncompress(String *buffer, const char *in, size_t in_len) { + z_stream zstream; + int status; + bool ret = false; + + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = php_zlib_alloc; + zstream.zfree = php_zlib_free; + // gzip_stream.total_out = 0; + status = inflateInit2(&zstream, SW_ZLIB_ENCODING_RAW); + if (status != Z_OK) { + swoole_warning("inflateInit2() failed by %s", zError(status)); + return false; + } + + zstream.next_in = (Bytef *) in; + zstream.avail_in = in_len; + zstream.total_in = 0; + + while (1) { + zstream.avail_out = buffer->size - buffer->length; + zstream.next_out = (Bytef *) (buffer->str + buffer->length); + status = inflate(&zstream, Z_SYNC_FLUSH); + if (status >= 0) { + buffer->length = zstream.total_out; + } + if (status == Z_STREAM_END || (status == Z_OK && zstream.avail_in == 0)) { + ret = true; + break; + } + if (status != Z_OK) { + break; + } + if (buffer->length + (SW_BUFFER_SIZE_STD / 2) >= buffer->size) { + if (!buffer->extend()) { + status = Z_MEM_ERROR; + break; + } + } + } + inflateEnd(&zstream); + + if (!ret) { + swoole_warning("inflate() failed, Error: %s[%d]", zError(status), status); + return false; + } + return true; +} + +static bool websocket_message_compress(String *buffer, const char *data, size_t length, int level) { + // ==== ZLIB ==== + if (level == Z_NO_COMPRESSION) { + level = Z_DEFAULT_COMPRESSION; + } else if (level > Z_BEST_COMPRESSION) { + level = Z_BEST_COMPRESSION; + } + + z_stream zstream = {}; + int status; + + zstream.zalloc = php_zlib_alloc; + zstream.zfree = php_zlib_free; + + status = deflateInit2(&zstream, level, Z_DEFLATED, SW_ZLIB_ENCODING_RAW, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); + if (status != Z_OK) { + swoole_warning("deflateInit2() failed, Error: [%d]", status); + return false; + } + + zstream.next_in = (Bytef *) data; + zstream.avail_in = length; + zstream.next_out = (Bytef *) buffer->str; + + size_t max_length = deflateBound(&zstream, length); + if (max_length > buffer->size) { + if (!buffer->extend(max_length)) { + return false; + } + } + + size_t bytes_written = 0; + uchar in_sync_flush; + int result; + + do { + size_t write_remaining; + + if (zstream.avail_out == 0) { + size_t write_position; + + zstream.avail_out = max_length; + write_position = buffer->length; + buffer->length = max_length; + zstream.next_out = (Bytef *) buffer->str + write_position; + + /* Use a fixed value for buffer increments */ + max_length = 4096; + } + + write_remaining = buffer->length - bytes_written; + in_sync_flush = zstream.avail_in == 0; + result = deflate(&zstream, in_sync_flush ? Z_SYNC_FLUSH : Z_NO_FLUSH); + bytes_written += write_remaining - zstream.avail_out; + } while (result == Z_OK); + + deflateEnd(&zstream); + + if (result != Z_BUF_ERROR || bytes_written < 4) { + swoole_warning("Failed to compress outgoing frame"); + return false; + } + + if (status != Z_OK) { + swoole_warning("deflate() failed, Error: [%d]", status); + return false; + } + + buffer->length = bytes_written - 4; + + return true; +} +#endif + +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); + if (!port) { + return SW_ERR; + } + + zval zdata; + char frame_header[2]; + memcpy(frame_header, &req->info.ext_flags, sizeof(frame_header)); + + php_swoole_get_recv_data(serv, &zdata, req); + + // 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) { + 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.size = sizeof(buf); + 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); + return SW_OK; + } + +#ifdef SW_HAVE_ZLIB + /** + * RFC 7692 + */ + 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, zlib_buffer->str, zlib_buffer->length); + flags ^= (WebSocket::FLAG_RSV1 | WebSocket::FLAG_COMPRESS); + } +#endif + + auto cb = php_swoole_server_get_callback(serv, req->info.server_fd, SW_SERVER_CB_onMessage); + zval args[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(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); + } + + zval_ptr_dtor(&zdata); + zval_ptr_dtor(&args[1]); + + return SW_OK; +} + +int swoole_websocket_onHandshake(Server *serv, ListenPort *port, HttpContext *ctx) { + SessionId fd = ctx->fd; + bool success = swoole_websocket_handshake(ctx); + if (success) { + swoole_websocket_onOpen(serv, ctx); + } else { + serv->close(fd, true); + } + return SW_OK; +} + +void php_swoole_websocket_server_minit(int module_number) { + SW_INIT_CLASS_ENTRY_EX(swoole_websocket_server, + "Swoole\\WebSocket\\Server", + nullptr, + swoole_websocket_server_methods, + swoole_http_server); +#ifndef SW_THREAD + SW_SET_CLASS_NOT_SERIALIZABLE(swoole_websocket_server); +#endif + SW_SET_CLASS_CLONEABLE(swoole_websocket_server, sw_zend_class_clone_deny); + SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_websocket_server, sw_zend_class_unset_property_deny); + + SW_INIT_CLASS_ENTRY(swoole_websocket_frame, "Swoole\\WebSocket\\Frame", nullptr, swoole_websocket_frame_methods); + zend_class_implements(swoole_websocket_frame_ce, 1, zend_ce_stringable); + zend_declare_property_long(swoole_websocket_frame_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC); + zend_declare_property_string(swoole_websocket_frame_ce, ZEND_STRL("data"), "", ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_websocket_frame_ce, ZEND_STRL("opcode"), WebSocket::OPCODE_TEXT, ZEND_ACC_PUBLIC); + zend_declare_property_long(swoole_websocket_frame_ce, ZEND_STRL("flags"), WebSocket::FLAG_FIN, ZEND_ACC_PUBLIC); + zend_declare_property_null(swoole_websocket_frame_ce, ZEND_STRL("finish"), ZEND_ACC_PUBLIC); + + SW_INIT_CLASS_ENTRY_EX( + swoole_websocket_closeframe, "Swoole\\WebSocket\\CloseFrame", nullptr, nullptr, swoole_websocket_frame); + zend_declare_property_long( + swoole_websocket_closeframe_ce, ZEND_STRL("opcode"), WebSocket::OPCODE_CLOSE, ZEND_ACC_PUBLIC); + zend_declare_property_long( + 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); + // 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); + // flags + 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_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); + // 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); + // 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_CLOSE_SERVICE_RESTART", WebSocket::CLOSE_SERVICE_RESTART); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_TRY_AGAIN_LATER", WebSocket::CLOSE_TRY_AGAIN_LATER); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_BAD_GATEWAY", WebSocket::CLOSE_BAD_GATEWAY); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_TLS", WebSocket::CLOSE_TLS); +} + +void php_swoole_websocket_server_rinit() { + if (swoole_websocket_buffer == nullptr) { + swoole_websocket_buffer = swoole::make_string(SW_BUFFER_SIZE_BIG); + } +} + +void php_swoole_websocket_server_rshutdown() { + if (swoole_websocket_buffer) { + delete swoole_websocket_buffer; + swoole_websocket_buffer = nullptr; + } +} + +void php_swoole_websocket_server_mshutdown() { + +} + +static sw_inline bool swoole_websocket_server_push(Server *serv, SessionId fd, String *buffer) { + if (sw_unlikely(fd <= 0)) { + php_swoole_fatal_error(E_WARNING, "fd[%ld] is invalid", fd); + return false; + } + + Connection *conn = serv->get_connection_by_session_id(fd); + 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); + return false; + } + + bool ret = serv->send(fd, buffer->str, buffer->length); + if (!ret && swoole_get_last_error() == SW_ERROR_OUTPUT_SEND_YIELD) { + zval _return_value; + zval *return_value = &_return_value; + zval _yield_data; + ZVAL_STRINGL(&_yield_data, buffer->str, buffer->length); + 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; +} + +static sw_inline bool swoole_websocket_server_close(Server *serv, SessionId fd, String *buffer, bool real_close) { + bool ret = swoole_websocket_server_push(serv, fd, buffer); + if (!ret || !real_close) { + return ret; + } + Connection *conn = serv->get_connection_by_session_id(fd); + if (conn) { + // Change status immediately to avoid double close + conn->websocket_status = WebSocket::STATUS_CLOSING; + // Server close connection immediately + return serv->close(fd, false); + } else { + return false; + } +} + +static PHP_METHOD(swoole_websocket_server, disconnect) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (sw_unlikely(!serv->is_started())) { + php_swoole_fatal_error(E_WARNING, "server is not running"); + RETURN_FALSE; + } + + zend_long fd = 0; + zend_long code = WebSocket::CLOSE_NORMAL; + char *data = nullptr; + size_t length = 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_websocket_buffer, 1)); +} + +static PHP_METHOD(swoole_websocket_server, push) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (sw_unlikely(!serv->is_started())) { + php_swoole_fatal_error(E_WARNING, "server is not running"); + RETURN_FALSE; + } + + zend_long fd = 0; + zval *zdata = nullptr; + zend_long opcode = WebSocket::OPCODE_TEXT; + zval *zflags = nullptr; + zend_long flags = WebSocket::FLAG_FIN; +#ifdef SW_HAVE_ZLIB + zend_bool allow_compress = 0; +#endif + + ZEND_PARSE_PARAMETERS_START(2, 4) + Z_PARAM_LONG(fd) + Z_PARAM_ZVAL(zdata) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(opcode) + Z_PARAM_ZVAL_EX(zflags, 1, 0) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (zflags != nullptr) { + flags = zval_get_long(zflags); + } + +#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_websocket_buffer->clear(); + if (php_swoole_websocket_frame_is_object(zdata)) { + 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_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_websocket_buffer, flags & WebSocket::FLAG_FIN)); + break; + default: + 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) { + zval *zdata; + zend_long opcode = WebSocket::OPCODE_TEXT; + zval *zflags = nullptr; + zend_long flags = WebSocket::FLAG_FIN; + + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_ZVAL(zdata) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(opcode) + Z_PARAM_ZVAL_EX(zflags, 1, 0) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (zflags != nullptr) { + flags = zval_get_long(zflags); + } + + String *buffer; + int result; + if (php_swoole_websocket_frame_is_object(zdata)) { + 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 { + 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); + } + + 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) { + String *buffer = sw_tg_buffer(); + buffer->clear(); + + if (php_swoole_websocket_frame_object_pack(buffer, ZEND_THIS, 0, 1) < 0) { + RETURN_EMPTY_STRING(); + } + RETURN_STRINGL(buffer->str, buffer->length); +} + +static PHP_METHOD(swoole_websocket_server, unpack) { + String buffer = {}; + + 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); +} + +static PHP_METHOD(swoole_websocket_server, isEstablished) { + Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); + if (sw_unlikely(!serv->is_started())) { + php_swoole_fatal_error(E_WARNING, "server is not running"); + RETURN_FALSE; + } + + zend_long session_id; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(session_id) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + Connection *conn = serv->get_connection_verify(session_id); + // not isEstablished + if (!conn || conn->closed || conn->websocket_status < WebSocket::STATUS_ACTIVE) { + RETURN_FALSE; + } else { + RETURN_TRUE; + } +} diff --git a/gdbinit b/gdbinit index d96ccb91482..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,9 +111,9 @@ end define __co_bt set $cid = (int)$arg0 - set $co = swoole_coro_get($cid) + set $co = swoole_coroutine_get($cid) if $co - set $task = (php_coro_task *) $co->task + set $task = ('swoole::PHPContext' *) $co->get_task() if $task sw_dump_bt $task->execute_data end @@ -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/asm_context.h b/include/asm_context.h deleted file mode 100644 index 53e1ac40db4..00000000000 --- a/include/asm_context.h +++ /dev/null @@ -1,38 +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: Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#ifndef _SW_ASM_CONTEXT_H_ -#define _SW_ASM_CONTEXT_H_ - -#ifdef SW_USE_ASM_CONTEXT - -SW_EXTERN_C_BEGIN - -#include -#include -#include - -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)); - -SW_EXTERN_C_END - -#endif -#endif diff --git a/include/async.h b/include/async.h deleted file mode 100644 index 59fecf42567..00000000000 --- a/include/async.h +++ /dev/null @@ -1,87 +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: Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#ifndef _SW_ASYNC_H_ -#define _SW_ASYNC_H_ - -SW_EXTERN_C_BEGIN - -#ifndef O_DIRECT -#define O_DIRECT 040000 -#endif - -enum swAioFlag -{ - SW_AIO_WRITE_FSYNC = 1u << 1, - SW_AIO_EOF = 1u << 2, -}; - -typedef struct _swAio_event -{ - int fd; - size_t task_id; - uint8_t lock; - uint8_t canceled; - /** - * input & output - */ - uint16_t flags; - off_t offset; - size_t nbytes; - void *buf; - void *req; - /** - * output - */ - int ret; - int error; - /** - * reserved by system - */ - int pipe_fd; - double timestamp; - void *object; - void (*handler)(struct _swAio_event *event); - void (*callback)(struct _swAio_event *event); -} swAio_event; - -typedef void (*swAio_handler)(swAio_event *event); - -ssize_t swAio_dispatch(const swAio_event *request); -swAio_event* swAio_dispatch2(const swAio_event *request); -int swAio_cancel(int task_id); -int swAio_callback(swReactor *reactor, swEvent *_event); -size_t swAio_thread_count(); - -#ifdef SW_DEBUG -void swAio_notify_one(); -#endif - -void swAio_handler_fread(swAio_event *event); -void swAio_handler_fwrite(swAio_event *event); -void swAio_handler_read(swAio_event *event); -void swAio_handler_write(swAio_event *event); -void swAio_handler_gethostbyname(swAio_event *event); -void swAio_handler_getaddrinfo(swAio_event *event); -void swAio_handler_fgets(swAio_event *event); -void swAio_handler_read_file(swAio_event *event); -void swAio_handler_write_file(swAio_event *event); - -SW_EXTERN_C_END - -#endif /* _SW_ASYNC_H_ */ diff --git a/include/atomic.h b/include/atomic.h deleted file mode 100644 index 95849403187..00000000000 --- a/include/atomic.h +++ /dev/null @@ -1,93 +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 | - | Twosee | - +----------------------------------------------------------------------+ -*/ - -#ifndef SW_ATOMIC_H_ -#define SW_ATOMIC_H_ - -#ifdef _WIN32 -typedef int32_t sw_atomic_int32_t; -typedef uint32_t sw_atomic_uint32_t; -typedef int64_t sw_atomic_int64_t; -typedef uint64_t sw_atomic_uint64_t; -typedef sw_atomic_uint32_t sw_atomic_t; -typedef sw_atomic_uint64_t sw_atomic_long_t; - -static inline sw_atomic_t sw_atomic_cmp_set(sw_atomic_t *lock, sw_atomic_t old, sw_atomic_t set) -{ - if (*lock == old) { - *lock = set; - return 1; - } - - return 0; -} - -static inline sw_atomic_t sw_atomic_fetch_add(sw_atomic_t *value, sw_atomic_t add) -{ - sw_atomic_t old; - - old = *value; - *value += add; - - return old; -} - -#define sw_memory_barrier() -#define sw_atomic_cpu_pause() - -#else -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_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__) -#define sw_atomic_cpu_pause() __asm__ __volatile__ ("pause") -#else -#define sw_atomic_cpu_pause() -#endif - -#if 0 -#define sw_spinlock_release(lock) __sync_lock_release(lock) -#else -#define sw_spinlock_release(lock) *(lock) = 0 -#endif -#endif - -#endif diff --git a/include/base64.h b/include/base64.h deleted file mode 100644 index daeb303377b..00000000000 --- a/include/base64.h +++ /dev/null @@ -1,35 +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 SW_BASE64_H__ -#define SW_BASE64_H__ - -#ifdef __cplusplus -extern "C" -{ -#endif - -#define BASE64_ENCODE_OUT_SIZE(s) (((s) + 2) / 3 * 4) -#define BASE64_DECODE_OUT_SIZE(s) (((s)) / 4 * 3) - -size_t swBase64_encode(const unsigned char *in, size_t inlen, char *out); -size_t swBase64_decode(const char *in, size_t inlen, char *out); - -#ifdef __cplusplus -} -#endif - -#endif /* SW_BASE64_H__ */ diff --git a/include/buffer.h b/include/buffer.h deleted file mode 100644 index 3e27a89b37f..00000000000 --- a/include/buffer.h +++ /dev/null @@ -1,74 +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: Tianfeng Han | - +----------------------------------------------------------------------+ -*/ - -#ifndef SW_BUFFER_H_ -#define SW_BUFFER_H_ - -SW_EXTERN_C_BEGIN - -enum swBufferChunk -{ - SW_CHUNK_DATA, - SW_CHUNK_SENDFILE, - SW_CHUNK_CLOSE, -}; - -typedef struct _swBuffer_chunk -{ - uint32_t type; - uint32_t length; - uint32_t offset; - union - { - void *ptr; - struct - { - uint32_t val1; - uint32_t val2; - } data; - } store; - uint32_t size; - void (*destroy)(struct _swBuffer_chunk *chunk); - struct _swBuffer_chunk *next; -} swBuffer_chunk; - -typedef struct _swBuffer -{ - int fd; - uint32_t chunk_num; - /** - * 0: donot use chunk - */ - uint32_t chunk_size; - uint32_t length; - swBuffer_chunk *head; - swBuffer_chunk *tail; -} swBuffer; - -#define swBuffer_get_chunk(buffer) (buffer->head) -#define swBuffer_empty(buffer) (buffer == NULL || buffer->head == NULL) - -swBuffer* swBuffer_new(uint32_t chunk_size); -swBuffer_chunk *swBuffer_new_chunk(swBuffer *buffer, uint32_t type, uint32_t size); -void swBuffer_pop_chunk(swBuffer *buffer, swBuffer_chunk *chunk); -int swBuffer_append(swBuffer *buffer, const void *data, uint32_t size); - -void swBuffer_debug(swBuffer *buffer, int print_data); -int swBuffer_free(swBuffer *buffer); - -SW_EXTERN_C_END - -#endif /* SW_BUFFER_H_ */ diff --git a/include/client.h b/include/client.h deleted file mode 100644 index 81610fbf7da..00000000000 --- a/include/client.h +++ /dev/null @@ -1,182 +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 SW_CLIENT_H_ -#define SW_CLIENT_H_ - -#include "swoole_api.h" -#include "ssl.h" - -SW_EXTERN_C_BEGIN - -#define SW_SOCK_ASYNC 1 -#define SW_SOCK_SYNC 0 - -#define SW_HTTPS_PROXY_HANDSHAKE_RESPONSE "HTTP/1.1 200 Connection established" - -enum swClient_pipe_flag -{ - SW_CLIENT_PIPE_TCP_SESSION = 1, -}; - -enum swHttp_proxy_state -{ - SW_HTTP_PROXY_STATE_WAIT = 0, - SW_HTTP_PROXY_STATE_HANDSHAKE, - SW_HTTP_PROXY_STATE_READY, -}; - -struct _http_proxy -{ - uint8_t state; - uint8_t dont_handshake; - int proxy_port; - const char *proxy_host; - const char *user; - const char *password; - int l_user; - int l_password; - const char *target_host; - int l_target_host; - int target_port; - char buf[512]; -}; - -typedef struct _swClient -{ - int id; - int type; - long timeout_id; //timeout node id - int _sock_type; - int _sock_domain; - int _protocol; - enum swFd_type reactor_fdtype; - - uchar active :1; - uchar async :1; - uchar keep :1; - uchar destroyed :1; - uchar http2 :1; - uchar sleep :1; - uchar wait_dns :1; - uchar shutdow_rw :1; - uchar shutdown_read :1; - uchar shutdown_write :1; - uchar remove_delay :1; - uchar closed :1; - uchar high_watermark :1; - - /** - * one package: length check - */ - uchar open_length_check :1; - uchar open_eof_check :1; - - swProtocol protocol; - struct _swSocks5 *socks5_proxy; - struct _http_proxy* http_proxy; - - uint32_t reuse_count; - - const char *server_str; - const char *server_host; - int server_port; - void *ptr; - void *params; - - uint8_t server_strlen; - double timeout; - swTimer_node *timer; - - /** - * signal interruption - */ - double interrupt_time; - - /** - * sendto, read only. - */ - swSocketAddress server_addr; - - /** - * recvfrom - */ - swSocketAddress remote_addr; - - swSocket *socket; - - void *object; - - swString *buffer; - uint32_t wait_length; - uint32_t input_buffer_size; - - uint32_t buffer_high_watermark; - uint32_t buffer_low_watermark; - -#ifdef SW_USE_OPENSSL - uchar open_ssl :1; - uchar ssl_wait_handshake :1; - SSL_CTX *ssl_context; - swSSL_option ssl_option; -#endif - - void (*onConnect)(struct _swClient *cli); - void (*onError)(struct _swClient *cli); - void (*onReceive)(struct _swClient *cli, char *data, uint32_t length); - void (*onClose)(struct _swClient *cli); - void (*onBufferFull)(struct _swClient *cli); - void (*onBufferEmpty)(struct _swClient *cli); - - int (*connect)(struct _swClient *cli, const char *host, int port, double _timeout, int sock_flag); - int (*send)(struct _swClient *cli, const char *data, int length, int flags); - int (*sendfile)(struct _swClient *cli, const char *filename, off_t offset, size_t length); - int (*recv)(struct _swClient *cli, char *data, int len, int flags); - int (*close)(struct _swClient *cli); - -} swClient; - -void swClient_init_reactor(swReactor *reactor); -int swClient_create(swClient *cli, enum swSocket_type type, int async); -int swClient_sleep(swClient *cli); -int swClient_wakeup(swClient *cli); -int swClient_shutdown(swClient *cli, int __how); -#ifdef SW_USE_OPENSSL -int swClient_enable_ssl_encrypt(swClient *cli); -int swClient_ssl_handshake(swClient *cli); -int swClient_ssl_verify(swClient *cli, int allow_self_signed); -#endif -void swClient_free(swClient *cli); -//----------------------------------------Stream--------------------------------------- -typedef struct _swStream -{ - swString *buffer; - uint8_t cancel; - void *private_data; - void (*response)(struct _swStream *stream, char *data, uint32_t length); - swClient client; -} swStream; - -swStream* swStream_new(char *dst_host, int dst_port, enum swSocket_type type); -int swStream_send(swStream *stream, char *data, size_t length); -void swStream_set_protocol(swProtocol *protocol); -void swStream_set_max_length(swStream *stream, uint32_t max_length); -int swStream_recv_blocking(swSocket *sock, void *__buf, size_t __len); -//----------------------------------------Stream End------------------------------------ - -SW_EXTERN_C_END - -#endif /* SW_CLIENT_H_ */ diff --git a/include/context.h b/include/context.h deleted file mode 100644 index b41a72f1f7a..00000000000 --- a/include/context.h +++ /dev/null @@ -1,144 +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 | - | Twosee | - +----------------------------------------------------------------------+ -*/ - -#pragma once - -#ifdef SW_USE_THREAD_CONTEXT - #include - #include -#elif !defined(SW_USE_ASM_CONTEXT) - #define USE_UCONTEXT 1 - #include -#else - #define USE_ASM_CONTEXT 1 - #include "asm_context.h" -#endif - -#if defined(HAVE_VALGRIND) && !defined(HAVE_KQUEUE) - #define USE_VALGRIND 1 - #include -#endif - -#include "swoole.h" -#include "error.h" - -#if __linux__ - #include -#endif - -#ifdef USE_UCONTEXT - typedef ucontext_t coroutine_context_t; -#elif defined(USE_ASM_CONTEXT) - typedef fcontext_t coroutine_context_t; -#endif - -typedef void (*coroutine_func_t)(void*); - -namespace swoole -{ -#ifdef SW_CONTEXT_PROTECT_STACK_PAGE -//namespace start -static uint32_t& get_protect_stack_page() -{ - static uint32_t protect_stack_page = 0; - return protect_stack_page; -} - -static bool protect_stack(void *top, size_t stack_size, uint32_t page) -{ - if (stack_size <= SwooleG.pagesize * (page + 1)) - { - swoole_error_log(SW_LOG_ERROR, SW_ERROR_CO_PROTECT_STACK_FAILED, "getpagesize() failed"); - return false; - } -#ifdef PROT_NONE - void *protect_page_addr = ((size_t) top & 0xfff) ? (void*) (((size_t) top & ~(size_t) 0xfff) + 0x1000) : top; - if (-1 == mprotect(protect_page_addr, SwooleG.pagesize * page, PROT_NONE)) - { - swSysWarn( - "mprotect() failed: origin_addr:%p, align_addr:%p, page_size:%d, protect_page:%u", - top, protect_page_addr, SwooleG.pagesize, page - ); - return false; - } - else - { - swDebug("origin_addr:%p, align_addr:%p, page_size:%d, protect_page:%u", top, protect_page_addr, page, SwooleG.pagesize); - return true; - } -#endif -} -static bool unprotect_stack(void *top, uint32_t page) -{ - void *protect_page_addr = ((size_t) top & 0xfff) ? (void*) (((size_t) top & ~(size_t) 0xfff) + 0x1000) : top; -#ifdef PROT_READ - if (-1 == mprotect(protect_page_addr, SwooleG.pagesize * page, PROT_READ | PROT_WRITE)) - { - swSysWarn( - "mprotect() failed: origin_addr:%p, align_addr:%p, page_size:%d, protect_page:%u", - top, protect_page_addr, SwooleG.pagesize, page - ); - return false; - } - else - { - swDebug("origin_addr:%p, align_addr:%p, page_size:%d, protect_page:%u", top, protect_page_addr, page, SwooleG.pagesize); - return true; - } -#endif -} -#endif - -class Context -{ -public: - Context(size_t stack_size, coroutine_func_t fn, void* private_data); - ~Context(); - bool swap_in(); - bool swap_out(); -#if defined(SW_USE_ASM_CONTEXT) && defined(SW_LOG_TRACE_OPEN) - ssize_t get_stack_usage(); -#endif - inline bool is_end() - { - return end_; - } - static void context_func(void* arg); - -protected: - coroutine_func_t fn_; -#ifdef SW_USE_THREAD_CONTEXT - std::thread thread_; - std::mutex lock_; - std::mutex *swap_lock_; -#else - coroutine_context_t ctx_; - coroutine_context_t swap_ctx_; - char* stack_; - uint32_t stack_size_; -#endif -#ifdef SW_CONTEXT_PROTECT_STACK_PAGE - uint32_t protect_page_; -#endif -#ifdef USE_VALGRIND - uint32_t valgrind_stack_id; -#endif - void *private_data_; - bool end_; -}; -//namespace end -} diff --git a/include/coroutine.h b/include/coroutine.h deleted file mode 100644 index 7c76e65e256..00000000000 --- a/include/coroutine.h +++ /dev/null @@ -1,272 +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 | - | Twosee | - +----------------------------------------------------------------------+ -*/ - -#pragma once - -#include "swoole_api.h" -#include "context.h" -#include "async.h" - -#include - -#include -#include -#include - -#define SW_CORO_STACK_ALIGNED_SIZE (4 * 1024) -#define SW_CORO_MIN_STACK_SIZE (256 * 1024) -#define SW_CORO_MAX_STACK_SIZE (16 * 1024 * 1024) -#define SW_CORO_MAX_NUM_LIMIT LONG_MAX - -// TODO: remove it -typedef enum -{ - SW_CORO_ERR_END = 0, - SW_CORO_ERR_LIMIT = -1, - SW_CORO_ERR_INVALID = -2, -} sw_coro_error; - -typedef enum -{ - SW_CORO_INIT = 0, - SW_CORO_WAITING, - SW_CORO_RUNNING, - SW_CORO_END, -} sw_coro_state; - -typedef void (*sw_coro_on_swap_t)(void*); -typedef void (*sw_coro_bailout_t)(); - -namespace swoole -{ -struct socket_poll_fd -{ - int16_t events; - int16_t revents; - void *ptr; - swSocket *socket; - - socket_poll_fd(int16_t _event, void *_ptr) - { - events = _event; - ptr = _ptr; - revents = 0; - socket = nullptr; - } -}; - -class Coroutine -{ -public: - void resume(); - void yield(); - - void resume_naked(); - void yield_naked(); - - inline sw_coro_state get_state() - { - return state; - } - - inline long get_init_msec() - { - return init_msec; - } - - inline long get_cid() - { - return cid; - } - - inline Coroutine* get_origin() - { - return origin; - } - - inline long get_origin_cid() - { - return sw_likely(origin) ? origin->get_cid() : -1; - } - - inline void* get_task() - { - return task; - } - - inline bool is_end() - { - return ctx.is_end(); - } - - inline void set_task(void *_task) - { - task = _task; - } - - static std::unordered_map coroutines; - - static void set_on_yield(sw_coro_on_swap_t func); - static void set_on_resume(sw_coro_on_swap_t func); - static void set_on_close(sw_coro_on_swap_t func); - static void bailout(sw_coro_bailout_t func); - - static inline long create(coroutine_func_t fn, void* args = nullptr) - { - return (new Coroutine(fn, args))->run(); - } - - 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"); - } - return current; - } - - static inline void* get_current_task() - { - return sw_likely(current) ? current->get_task() : nullptr; - } - - static inline long get_current_cid() - { - return sw_likely(current) ? current->get_cid() : -1; - } - - static inline Coroutine* get_by_cid(long cid) - { - auto i = coroutines.find(cid); - return sw_likely(i != coroutines.end()) ? i->second : nullptr; - } - - static inline void* get_task_by_cid(long cid) - { - Coroutine *co = get_by_cid(cid); - return sw_likely(co) ? co->get_task() : nullptr; - } - - static inline size_t get_stack_size() - { - return stack_size; - } - - static inline void set_stack_size(size_t size) - { - stack_size = SW_MEM_ALIGNED_SIZE_EX(SW_MAX(SW_CORO_MIN_STACK_SIZE, SW_MIN(size, SW_CORO_MAX_STACK_SIZE)), SW_CORO_STACK_ALIGNED_SIZE); - } - - static inline long get_last_cid() - { - return last_cid; - } - - static inline size_t count() - { - return coroutines.size(); - } - - static inline uint64_t get_peak_num() - { - return peak_num; - } - - static inline long get_elapsed(long cid) - { - Coroutine *co = cid == 0 ? get_current() : get_by_cid(cid); - return sw_likely(co) ? swTimer_get_absolute_msec() - co->get_init_msec() : -1; - } - - static void print_list(); - -protected: - static Coroutine* current; - static long last_cid; - static uint64_t peak_num; - static size_t stack_size; - static sw_coro_on_swap_t on_yield; /* before yield */ - static sw_coro_on_swap_t on_resume; /* before resume */ - static sw_coro_on_swap_t on_close; /* before close */ - static sw_coro_bailout_t on_bailout; /* when bailout */ - - sw_coro_state state = SW_CORO_INIT; - long cid; - long init_msec = swTimer_get_absolute_msec(); - void *task = nullptr; - Context ctx; - Coroutine *origin; - - Coroutine(coroutine_func_t 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(); - } - } - - inline long run() - { - long cid = this->cid; - origin = current; - current = this; - ctx.swap_in(); - check_end(); - return cid; - } - - inline 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); - } - } - - void close(); -}; -//------------------------------------------------------------------------------- -namespace coroutine -{ -bool async(swAio_handler handler, swAio_event &event, double timeout = -1); -bool async(const std::function &fn, double timeout = -1); -} -//------------------------------------------------------------------------------- -} - -/** - * 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(); diff --git a/include/coroutine_c_api.h b/include/coroutine_c_api.h deleted file mode 100644 index ca01317e2fa..00000000000 --- a/include/coroutine_c_api.h +++ /dev/null @@ -1,81 +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 SW_COROUTINE_API_H_ -#define SW_COROUTINE_API_H_ - -#ifdef __cplusplus -extern "C" -{ -#endif - -#include -#include -#include -#include -#include -#include -#include - -/** - * base - */ -uint8_t swoole_coroutine_is_in(); -long swoole_coroutine_get_current_id(); -void swoole_coroutine_sleep(int sec); -void swoole_coroutine_usleep(int usec); -/** - * file - */ -int swoole_coroutine_access(const char *pathname, int mode); -int swoole_coroutine_open(const char *pathname, int flags, mode_t mode); -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_unlink(const char *pathname); -int swoole_coroutine_mkdir(const char *pathname, mode_t mode); -int swoole_coroutine_rmdir(const char *pathname); -int swoole_coroutine_rename(const char *oldpath, const char *newpath); -int swoole_coroutine_flock(int fd, int operation); -int swoole_coroutine_flock_ex(char *filename, int fd, int operation); -int swoole_coroutine_statvfs(const char *path, struct statvfs *buf); - -/** - * socket - */ -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); -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); -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_socket_set_timeout(int fd, int which, double timeout); -int swoole_coroutine_socket_wait_event(int fd, int event, double timeout); - -/** - * wait - */ -size_t swoole_coroutine_wait_count(); -pid_t swoole_coroutine_waitpid(pid_t __pid, int *__stat_loc, int __options); -pid_t swoole_coroutine_wait(int *__stat_loc); - -#ifdef __cplusplus -} /* end extern "C" */ -#endif -#endif diff --git a/include/coroutine_channel.h b/include/coroutine_channel.h deleted file mode 100644 index 83582e14138..00000000000 --- a/include/coroutine_channel.h +++ /dev/null @@ -1,152 +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 | - | Twosee | - +----------------------------------------------------------------------+ -*/ - -#pragma once - -#include "swoole.h" -#include "coroutine.h" - -#include - -#include -#include -#include -#include - -namespace swoole { namespace coroutine { -//------------------------------------------------------------------------------- -class Channel -{ -public: - enum opcode - { - PRODUCER = 1, - CONSUMER = 2, - }; - - struct timer_msg_t - { - Channel *chan; - enum opcode type; - Coroutine *co; - bool error; - swTimer_node *timer; - }; - - void* pop(double timeout = -1); - bool push(void *data, double timeout = -1); - bool close(); - - Channel(size_t _capacity = 1) : - capacity(_capacity) - { - } - - ~Channel() - { - if (!producer_queue.empty()) - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_CO_HAS_BEEN_DISCARDED, "channel is destroyed, %zu producers will be discarded", producer_queue.size()); - } - if (!consumer_queue.empty()) - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_CO_HAS_BEEN_DISCARDED, "channel is destroyed, %zu consumers will be discarded", consumer_queue.size()); - } - } - - inline bool is_closed() - { - return closed; - } - - inline bool is_empty() - { - return data_queue.size() == 0; - } - - inline bool is_full() - { - return data_queue.size() == capacity; - } - - inline size_t length() - { - return data_queue.size(); - } - - inline size_t consumer_num() - { - return consumer_queue.size(); - } - - inline size_t producer_num() - { - return producer_queue.size(); - } - - inline void* pop_data() - { - if (data_queue.size() == 0) - { - return nullptr; - } - void *data = data_queue.front(); - data_queue.pop(); - return data; - } - -protected: - size_t capacity = 1; - bool closed = false; - std::list producer_queue; - std::list consumer_queue; - std::queue data_queue; - - static void timer_callback(swTimer *timer, swTimer_node *tnode); - - void yield(enum opcode type); - - inline void consumer_remove(Coroutine *co) - { - consumer_queue.remove(co); - } - - inline void producer_remove(Coroutine *co) - { - producer_queue.remove(co); - } - - inline 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()); - } - else // if (type == CONSUMER) - { - co = consumer_queue.front(); - consumer_queue.pop_front(); - swTraceLog(SW_TRACE_CHANNEL, "resume consumer cid=%ld", co->get_cid()); - } - return co; - } -}; -//------------------------------------------------------------------------------- -}} diff --git a/include/coroutine_cxx_api.h b/include/coroutine_cxx_api.h deleted file mode 100644 index 2ee3bf154a6..00000000000 --- a/include/coroutine_cxx_api.h +++ /dev/null @@ -1,26 +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: Xinyu Zhu | - | shiguangqi | - | Twosee | - | Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#pragma once - -#include "coroutine.h" -#include "coroutine_system.h" -#include "coroutine_socket.h" - -swoole::coroutine::Socket* swoole_coroutine_get_socket_object(int sockfd); diff --git a/include/coroutine_socket.h b/include/coroutine_socket.h deleted file mode 100644 index ed91d65cbe2..00000000000 --- a/include/coroutine_socket.h +++ /dev/null @@ -1,563 +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 | - | Twosee | - +----------------------------------------------------------------------+ -*/ - -#pragma once - -#include "coroutine.h" -#include "ssl.h" -#include "socks5.h" - -#include -#include - -#define SW_DEFAULT_SOCKET_DNS_TIMEOUT -1 -#define SW_DEFAULT_SOCKET_CONNECT_TIMEOUT 1 -#define SW_DEFAULT_SOCKET_READ_TIMEOUT -1 -#define SW_DEFAULT_SOCKET_WRITE_TIMEOUT -1 - -namespace swoole -{ -enum swTimeout_type -{ - SW_TIMEOUT_DNS = 1 << 0, - SW_TIMEOUT_CONNECT = 1 << 1, - SW_TIMEOUT_READ = 1 << 2, - SW_TIMEOUT_WRITE = 1 << 3, - SW_TIMEOUT_RDWR = SW_TIMEOUT_READ | SW_TIMEOUT_WRITE, - SW_TIMEOUT_ALL = SW_TIMEOUT_DNS | SW_TIMEOUT_CONNECT | SW_TIMEOUT_RDWR, -}; - -static constexpr enum swTimeout_type swTimeout_type_list[] = -{ - SW_TIMEOUT_DNS, SW_TIMEOUT_CONNECT, SW_TIMEOUT_READ, SW_TIMEOUT_WRITE -}; -} - -namespace swoole { namespace coroutine { -//------------------------------------------------------------------------------- -class Socket -{ -public: - static double default_dns_timeout; - static double default_connect_timeout; - static double default_read_timeout; - static double default_write_timeout; - - swSocket *socket = nullptr; - int errCode = 0; - const char *errMsg = ""; - std::string errString; - - bool open_length_check = false; - bool open_eof_check = false; - bool http2 = false; - - swProtocol protocol = {}; - struct _swSocks5 *socks5_proxy = nullptr; - struct _http_proxy* http_proxy = nullptr; - -#ifdef SW_USE_OPENSSL - bool open_ssl = false; - swSSL_option ssl_option = {}; -#endif - - 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(swSocket *sock, Socket *socket); - ~Socket(); - bool connect(std::string host, int port, 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 close(); - - inline bool is_connect() - { - return activated && !closed; - } - - bool check_liveness(); - ssize_t peek(void *__buf, size_t __n); - ssize_t recv(void *__buf, size_t __n); - ssize_t send(const void *__buf, size_t __n); - ssize_t read(void *__buf, size_t __n); - ssize_t write(const void *__buf, size_t __n); - ssize_t recvmsg(struct msghdr *msg, int flags); - ssize_t sendmsg(const struct msghdr *msg, int flags); - 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); - bool poll(enum swEvent_type type); - Socket* accept(double timeout = 0); - bool bind(std::string address, int port = 0); - bool listen(int backlog = 0); - bool sendfile(const char *filename, off_t offset, size_t length); - ssize_t sendto(const std::string &host, int port, const void *__buf, size_t __n); - ssize_t recvfrom(void *__buf, size_t __n); - ssize_t recvfrom(void *__buf, size_t __n, struct sockaddr *_addr, socklen_t *_socklen); -#ifdef SW_USE_OPENSSL - bool ssl_check_context(); - bool ssl_handshake(); - bool ssl_verify(bool allow_self_signed); - std::string ssl_get_peer_cert(); -#endif - - 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; - default: - return SW_SOCK_TCP; - } - } - - static inline enum swSocket_type 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); - return SW_SOCK_UNIX_STREAM; - } - else if (host.find(':') != std::string::npos) - { - return SW_SOCK_TCP6; - } - else - { - return SW_SOCK_TCP; - } - } - - static inline void init_reactor(swReactor *reactor) - { - swReactor_set_handler(reactor, SW_FD_CORO_SOCKET | SW_EVENT_READ, readable_event_callback); - swReactor_set_handler(reactor, SW_FD_CORO_SOCKET | SW_EVENT_WRITE, writable_event_callback); - swReactor_set_handler(reactor, SW_FD_CORO_SOCKET | SW_EVENT_ERROR, error_event_callback); - } - - inline enum swSocket_type get_type() - { - return type; - } - - inline int get_sock_domain() - { - return sock_domain; - } - - inline int get_sock_type() - { - return sock_type; - } - - inline int get_sock_protocol() - { - return sock_protocol; - } - - inline int get_fd() - { - return sock_fd; - } - - inline int get_bind_port() - { - return bind_port; - } - - bool getsockname(swSocketAddress *sa); - bool getpeername(swSocketAddress *sa); - const char* get_ip(); - int get_port(); - - inline bool has_bound(const enum swEvent_type event = SW_EVENT_RDWR) - { - return get_bound_co(event) != nullptr; - } - - inline Coroutine* get_bound_co(const enum swEvent_type event) - { - if (event & SW_EVENT_READ) - { - if (read_co) - { - return read_co; - } - } - if (event & SW_EVENT_WRITE) - { - if (write_co) - { - return write_co; - } - } - return nullptr; - } - - inline long get_bound_cid(const enum swEvent_type event = SW_EVENT_RDWR) - { - Coroutine *co = get_bound_co(event); - return co ? co->get_cid() : 0; - } - - inline void check_bound_co(const enum swEvent_type 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, - (event == SW_EVENT_READ ? "reading" : (event == SW_EVENT_WRITE ? "writing" : - (read_co && write_co ? "reading or writing" : (read_co ? "reading" : "writing")))), - Coroutine::get_current_cid() - ); - } - } - - inline void set_err(int e) - { - errCode = errno = e; - errMsg = e ? swoole_strerror(e) : ""; - } - - inline void set_err(int e, const char *s) - { - errCode = errno = e; - errMsg = s; - } - - - inline void set_err(int e, std::string s) - { - errCode = errno = e; - errString = s; - errMsg = errString.c_str(); - } - - /* set connect read write timeout */ - inline void set_timeout(double timeout, int type = SW_TIMEOUT_ALL) - { - if (timeout == 0) - { - return; - } - if (type & SW_TIMEOUT_DNS) - { - dns_timeout = timeout; - } - if (type & SW_TIMEOUT_CONNECT) - { - connect_timeout = timeout; - } - if (type & SW_TIMEOUT_READ) - { - read_timeout = timeout; - } - if (type & SW_TIMEOUT_WRITE) - { - write_timeout = timeout; - } - } - - inline void set_timeout(struct timeval *timeout, int type = SW_TIMEOUT_ALL) - { - set_timeout((double) timeout->tv_sec + ((double) timeout->tv_usec / 1000 / 1000), type); - } - - inline double get_timeout(enum swTimeout_type type = SW_TIMEOUT_ALL) - { - SW_ASSERT_1BYTE(type); - if (type == SW_TIMEOUT_DNS) - { - return dns_timeout; - } - else if (type == SW_TIMEOUT_CONNECT) - { - return connect_timeout; - } - else if (type == SW_TIMEOUT_READ) - { - return read_timeout; - } - else // if (type == SW_TIMEOUT_WRITE) - { - return write_timeout; - } - } - - inline bool set_option(int level, int optname, int optval) - { - if (setsockopt(sock_fd, level, optname, &optval, sizeof(optval)) != 0) - { - swSysWarn("setsockopt(%d, %d, %d, %d) failed", sock_fd, level, optname, optval); - return false; - } - return true; - } - - inline swString* get_read_buffer() - { - if (sw_unlikely(!read_buffer)) - { - read_buffer = swString_new(SW_BUFFER_SIZE_BIG); - } - return read_buffer; - } - - inline swString* get_write_buffer() - { - if (sw_unlikely(!write_buffer)) - { - write_buffer = swString_new(SW_BUFFER_SIZE_BIG); - } - return write_buffer; - } - -#ifdef SW_USE_OPENSSL - inline bool is_ssl_enable() - { - return socket && ssl_handshaked; - } - - bool ssl_shutdown(); -#endif - -private: - enum swSocket_type type; - int sock_domain = 0; - int sock_type = 0; - int sock_protocol = 0; - int sock_fd = -1; - - Coroutine *read_co = nullptr; - Coroutine *write_co = nullptr; -#ifdef SW_USE_OPENSSL - enum swEvent_type want_event = SW_EVENT_NULL; -#endif - - std::string connect_host; - int connect_port = 0; - - std::string bind_address; - int bind_port = 0; - int backlog = 0; - - double dns_timeout = default_dns_timeout; - double connect_timeout = default_connect_timeout; - double read_timeout = default_read_timeout; - double write_timeout = default_write_timeout; - swTimer_node *read_timer = nullptr; - swTimer_node *write_timer = nullptr; - - swString *read_buffer = nullptr; - swString *write_buffer = nullptr; - swSocketAddress bind_address_info = {}; - -#ifdef SW_USE_OPENSSL - bool ssl_is_server = false; - bool ssl_handshaked = false; - SSL_CTX *ssl_context = nullptr; - std::string ssl_host_name; - bool ssl_create(SSL_CTX *ssl_context); -#endif - - bool activated = true; - bool shutdown_read = false; - bool shutdown_write = false; - bool closed = false; - - static void timer_callback(swTimer *timer, swTimer_node *tnode); - static int readable_event_callback(swReactor *reactor, swEvent *event); - static int writable_event_callback(swReactor *reactor, swEvent *event); - static int error_event_callback(swReactor *reactor, swEvent *event); - - inline void init_sock_type(enum swSocket_type _type); - inline bool init_sock(); - bool init_reactor_socket(int fd); - - inline void init_options() - { - if (type == SW_SOCK_TCP || type == SW_SOCK_TCP6) - { - set_option(IPPROTO_TCP, TCP_NODELAY, 1); - } - protocol.package_length_type = 'N'; - protocol.package_length_size = 4; - protocol.package_length_offset = 0; - protocol.package_body_offset = 0; - 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); - - inline bool is_available(const enum swEvent_type event) - { - if (event != SW_EVENT_NULL) - { - check_bound_co(event); - } - if (sw_unlikely(closed)) - { - set_err(ECONNRESET); - return false; - } - return true; - } - - // TODO: move to client.cc - bool socks5_handshake(); - bool http_proxy_handshake(); - - class timer_controller - { - public: - timer_controller(swTimer_node **timer_pp, double timeout, Socket *sock, swTimerCallback callback) : - timer_pp(timer_pp), timeout(timeout), socket_(sock), callback(callback) - { - } - bool start() - { - if (timeout != 0 && !*timer_pp) - { - enabled = true; - if (timeout > 0) - { - *timer_pp = swoole_timer_add((long) (timeout * 1000), SW_FALSE, callback, socket_); - return *timer_pp != nullptr; - } - else // if (timeout < 0) - { - *timer_pp = (swTimer_node *) -1; - } - } - return true; - } - ~timer_controller() - { - if (enabled && *timer_pp) - { - if (*timer_pp != (swTimer_node *) -1) - { - swoole_timer_del(*timer_pp); - } - *timer_pp = nullptr; - } - } - private: - bool enabled = false; - swTimer_node** timer_pp; - double timeout; - Socket *socket_; - swTimerCallback callback; - }; - -public: - class timeout_setter - { - public: - timeout_setter(Socket *socket, double timeout, const enum swTimeout_type type) : - socket_(socket), timeout(timeout), type(type) - { - if (timeout == 0) - { - return; - } - for (uint8_t i = 0; i < SW_ARRAY_SIZE(swTimeout_type_list); i++) - { - if (type & swTimeout_type_list[i]) - { - original_timeout[i] = socket->get_timeout(swTimeout_type_list[i]); - if (timeout != original_timeout[i]) - { - socket->set_timeout(timeout, swTimeout_type_list[i]); - } - } - } - } - ~timeout_setter() - { - if (timeout == 0) - { - return; - } - for (uint8_t i = 0; i < SW_ARRAY_SIZE(swTimeout_type_list); i++) - { - if (type & swTimeout_type_list[i]) - { - if (timeout != original_timeout[i]) - { - socket_->set_timeout(original_timeout[i], swTimeout_type_list[i]); - } - } - } - } - protected: - Socket *socket_; - double timeout; - enum swTimeout_type type; - double original_timeout[sizeof(swTimeout_type_list)] = {}; - }; - - class timeout_controller: public timeout_setter - { - public: - timeout_controller(Socket *socket, double timeout, const enum swTimeout_type type) : - timeout_setter(socket, timeout, type) - { - } - inline bool has_timedout(const enum swTimeout_type type) - { - SW_ASSERT_1BYTE(type); - if (timeout > 0) - { - if (sw_unlikely(startup_time == 0)) - { - startup_time = swoole_microtime(); - } - else - { - double used_time = swoole_microtime() - startup_time; - if (sw_unlikely(timeout - used_time < SW_TIMER_MIN_SEC)) - { - socket_->set_err(ETIMEDOUT); - return true; - } - socket_->set_timeout(timeout - used_time, type); - } - } - return false; - } - protected: - double startup_time = 0; - }; -}; -std::vector dns_lookup(const char *domain, double timeout = 2.0); -//------------------------------------------------------------------------------- -}} diff --git a/include/coroutine_system.h b/include/coroutine_system.h deleted file mode 100644 index 6b324399cbc..00000000000 --- a/include/coroutine_system.h +++ /dev/null @@ -1,55 +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 | - | Twosee | - +----------------------------------------------------------------------+ -*/ - -#pragma once - -#include "coroutine.h" -#include -#include - -namespace swoole { namespace coroutine { -//------------------------------------------------------------------------------- -class System -{ -public: - static void init_reactor(swReactor *reactor); - /* sleep */ - static int sleep(double sec); - /* file */ - static swString* read_file(const char *file, bool lock = false); - static ssize_t write_file(const char *file, char *buf, size_t length, bool lock = 0, int flags = 0); - /* dns */ - static std::string gethostbyname(const std::string &hostname, int domain, double timeout = -1); - static std::vector getaddrinfo( - const std::string &hostname, int family = AF_INET, int socktype = SOCK_STREAM, int protocol = IPPROTO_TCP, - const std::string &service = "", double timeout = -1 - ); - static void set_dns_cache_expire(time_t expire); - static void set_dns_cache_capacity(size_t capacity); - static void clear_dns_cache(); - /* multiplexing */ - static bool socket_poll(std::unordered_map &fds, double timeout); - /* 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); - /* signal */ - static bool wait_signal(int signo, double timeout = -1); - /* event */ - static int wait_event(int fd, int events, double timeout); -}; -//------------------------------------------------------------------------------- -}} diff --git a/include/dtls.h b/include/dtls.h deleted file mode 100644 index d39a882075a..00000000000 --- a/include/dtls.h +++ /dev/null @@ -1,87 +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 | - +----------------------------------------------------------------------+ -*/ - -#pragma once - -#include "swoole_api.h" - -#ifdef SW_SUPPORT_DTLS -#include - -namespace swoole { namespace dtls { -//------------------------------------------------------------------------------- - -int BIO_write(BIO *b, const char *data, int dlen); -int BIO_read(BIO *b, char *data, int dlen); -long BIO_ctrl(BIO *b, int cmd, long larg, void *pargs); -int BIO_create(BIO *b); -int BIO_destroy(BIO *b); -BIO_METHOD *BIO_get_methods(void); -void BIO_meth_free(void); - -struct Buffer -{ - uint16_t length; - uchar data[0]; -}; - -struct Session -{ - SSL_CTX *ctx; - bool listened = false; - swSocket *socket; - std::deque rxqueue; - bool peek_mode = false; - - Session(swSocket *_sock, SSL_CTX *_ctx) - { - socket = _sock; - ctx = _ctx; - } - - ~Session() - { - while(!rxqueue.empty()) - { - Buffer *buffer = rxqueue.front(); - rxqueue.pop_front(); - delete buffer; - } - } - - bool init(); - bool listen(); - - void append(const char* data, ssize_t len); - - inline void append(Buffer *buffer) - { - rxqueue.push_back(buffer); - } - - inline size_t get_buffer_length() - { - size_t total_length = 0; - for (auto i : rxqueue) - { - total_length += i->length; - } - return total_length; - } -}; -//------------------------------------------------------------------------------- -}} -#endif diff --git a/include/error.h b/include/error.h deleted file mode 100644 index 45f58845b5c..00000000000 --- a/include/error.h +++ /dev/null @@ -1,159 +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 SW_ERRNO_H_ -#define SW_ERRNO_H_ - -enum swErrorCode -{ - /** - * Prevent repetition with errno [syscall error] - */ - SW_ERROR_BEGIN = 500, - - /** - * common error - */ - SW_ERROR_MALLOC_FAIL = 501, - SW_ERROR_SYSTEM_CALL_FAIL, - SW_ERROR_PHP_FATAL_ERROR, - SW_ERROR_NAME_TOO_LONG, - SW_ERROR_INVALID_PARAMS, - SW_ERROR_QUEUE_FULL, - SW_ERROR_OPERATION_NOT_SUPPORT, - SW_ERROR_PROTOCOL_ERROR, - - SW_ERROR_FILE_NOT_EXIST = 700, - SW_ERROR_FILE_TOO_LARGE, - SW_ERROR_FILE_EMPTY, - SW_ERROR_DNSLOOKUP_DUPLICATE_REQUEST, - SW_ERROR_DNSLOOKUP_RESOLVE_FAILED, - SW_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT, - SW_ERROR_BAD_IPV6_ADDRESS, - SW_ERROR_UNREGISTERED_SIGNAL, - - /** - * connection error - */ - SW_ERROR_SESSION_CLOSED_BY_SERVER = 1001, - SW_ERROR_SESSION_CLOSED_BY_CLIENT, - SW_ERROR_SESSION_CLOSING, - SW_ERROR_SESSION_CLOSED, - SW_ERROR_SESSION_NOT_EXIST, - SW_ERROR_SESSION_INVALID_ID, - SW_ERROR_SESSION_DISCARD_TIMEOUT_DATA, - SW_ERROR_SESSION_DISCARD_DATA, - SW_ERROR_OUTPUT_BUFFER_OVERFLOW, - SW_ERROR_OUTPUT_SEND_YIELD, - SW_ERROR_SSL_NOT_READY, - SW_ERROR_SSL_CANNOT_USE_SENFILE, - SW_ERROR_SSL_EMPTY_PEER_CERTIFICATE, - SW_ERROR_SSL_VERIFY_FAILED, - SW_ERROR_SSL_BAD_CLIENT, - SW_ERROR_SSL_BAD_PROTOCOL, - SW_ERROR_SSL_RESET, - - SW_ERROR_PACKAGE_LENGTH_TOO_LARGE = 1201, - SW_ERROR_PACKAGE_LENGTH_NOT_FOUND, - SW_ERROR_DATA_LENGTH_TOO_LARGE, - - /** - * task error - */ - SW_ERROR_TASK_PACKAGE_TOO_BIG = 2001, - SW_ERROR_TASK_DISPATCH_FAIL, - SW_ERROR_TASK_TIMEOUT, - - /** - * http2 protocol error - */ - SW_ERROR_HTTP2_STREAM_ID_TOO_BIG = 3001, - SW_ERROR_HTTP2_STREAM_NO_HEADER, - SW_ERROR_HTTP2_STREAM_NOT_FOUND, - - /** - * AIO - */ - SW_ERROR_AIO_BAD_REQUEST = 4001, - SW_ERROR_AIO_CANCELED, - SW_ERROR_AIO_TIMEOUT, - - /** - * Client & Socket - */ - SW_ERROR_CLIENT_NO_CONNECTION = 5001, - SW_ERROR_SOCKET_CLOSED, - - SW_ERROR_SOCKS5_UNSUPPORT_VERSION = 7001, - SW_ERROR_SOCKS5_UNSUPPORT_METHOD, - SW_ERROR_SOCKS5_AUTH_FAILED, - SW_ERROR_SOCKS5_SERVER_ERROR, - - SW_ERROR_HTTP_PROXY_HANDSHAKE_ERROR = 8001, - SW_ERROR_HTTP_INVALID_PROTOCOL, - - SW_ERROR_WEBSOCKET_BAD_CLIENT = 8501, - SW_ERROR_WEBSOCKET_BAD_OPCODE, - SW_ERROR_WEBSOCKET_UNCONNECTED, - SW_ERROR_WEBSOCKET_HANDSHAKE_FAILED, - - /** - * server global error - */ - SW_ERROR_SERVER_MUST_CREATED_BEFORE_CLIENT = 9001, - SW_ERROR_SERVER_TOO_MANY_SOCKET, - SW_ERROR_SERVER_WORKER_TERMINATED, - SW_ERROR_SERVER_INVALID_LISTEN_PORT, - SW_ERROR_SERVER_TOO_MANY_LISTEN_PORT, - SW_ERROR_SERVER_PIPE_BUFFER_FULL, - SW_ERROR_SERVER_NO_IDLE_WORKER, - SW_ERROR_SERVER_ONLY_START_ONE, - SW_ERROR_SERVER_SEND_IN_MASTER, - SW_ERROR_SERVER_INVALID_REQUEST, - SW_ERROR_SERVER_CONNECT_FAIL, - - /** - * Process exit timeout, forced to end. - */ - SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT, - - /** - * Coroutine - */ - SW_ERROR_CO_OUT_OF_COROUTINE = 10001, - SW_ERROR_CO_HAS_BEEN_BOUND, - SW_ERROR_CO_HAS_BEEN_DISCARDED, - - SW_ERROR_CO_MUTEX_DOUBLE_UNLOCK, - SW_ERROR_CO_BLOCK_OBJECT_LOCKED, - SW_ERROR_CO_BLOCK_OBJECT_WAITING, - SW_ERROR_CO_YIELD_FAILED, - SW_ERROR_CO_GETCONTEXT_FAILED, - SW_ERROR_CO_SWAPCONTEXT_FAILED, - SW_ERROR_CO_MAKECONTEXT_FAILED, - - SW_ERROR_CO_IOCPINIT_FAILED, - SW_ERROR_CO_PROTECT_STACK_FAILED, - SW_ERROR_CO_STD_THREAD_LINK_ERROR, - SW_ERROR_CO_DISABLED_MULTI_THREAD, - - SW_ERROR_END -}; - -const char* swoole_strerror(int code); -void swoole_throw_error(int code); - -#endif /* SW_ERRNO_H_ */ diff --git a/include/file_hook.h b/include/file_hook.h deleted file mode 100644 index 30bfdc5df48..00000000000 --- a/include/file_hook.h +++ /dev/null @@ -1,40 +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 SW_FILE_HOOK_H_ -#define SW_FILE_HOOK_H_ - -#include "coroutine_c_api.h" - -#define access(pathname, mode) swoole_coroutine_access(pathname, mode) -#define open(pathname, flags, mode) swoole_coroutine_open(pathname, flags, mode) -#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 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) - -#if 0 -DIR *swoole_coroutine_opendir(const char *name); -struct dirent *swoole_coroutine_readdir(DIR *dirp); -#define opendir(name) swoole_coroutine_opendir(name) -#define readdir(dir) swoole_coroutine_readdir(dir) -#endif - -#endif diff --git a/include/hash.h b/include/hash.h deleted file mode 100644 index caacefcf57e..00000000000 --- a/include/hash.h +++ /dev/null @@ -1,222 +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: Tianfeng Han | - +----------------------------------------------------------------------+ -*/ - -#ifndef SW_HASH_H_ -#define SW_HASH_H_ - -SW_EXTERN_C_BEGIN - -#include - -#define HASH_JEN_MIX(a,b,c) \ -do { \ - a -= b; a -= c; a ^= ( c >> 13 ); \ - b -= c; b -= a; b ^= ( a << 8 ); \ - c -= a; c -= b; c ^= ( b >> 13 ); \ - a -= b; a -= c; a ^= ( c >> 12 ); \ - b -= c; b -= a; b ^= ( a << 16 ); \ - c -= a; c -= b; c ^= ( b >> 5 ); \ - a -= b; a -= c; a ^= ( c >> 3 ); \ - b -= c; b -= a; b ^= ( a << 10 ); \ - c -= a; c -= b; c ^= ( b >> 15 ); \ -} while (0) - -/** - * jenkins - */ -static inline uint64_t swoole_hash_jenkins(const char *key, size_t keylen) -{ - uint64_t hashv; - - unsigned i, j, k; - hashv = 0xfeedbeef; - i = j = 0x9e3779b9; - k = (unsigned) (keylen); - - while (k >= 12) - { - i += (key[0] + ((unsigned) key[1] << 8) + ((unsigned) key[2] << 16) - + ((unsigned) key[3] << 24)); - j += (key[4] + ((unsigned) key[5] << 8) + ((unsigned) key[6] << 16) - + ((unsigned) key[7] << 24)); - hashv += (key[8] + ((unsigned) key[9] << 8) + ((unsigned) key[10] << 16) - + ((unsigned) key[11] << 24)); - - HASH_JEN_MIX(i, j, hashv); - - key += 12; - k -= 12; - } - hashv += keylen; - switch (k) - { - case 11: - hashv += ((unsigned) key[10] << 24); - /* no break */ - case 10: - hashv += ((unsigned) key[9] << 16); - /* no break */ - case 9: - hashv += ((unsigned) key[8] << 8); - /* no break */ - case 8: - j += ((unsigned) key[7] << 24); - /* no break */ - case 7: - j += ((unsigned) key[6] << 16); - /* no break */ - case 6: - j += ((unsigned) key[5] << 8); - /* no break */ - case 5: - j += key[4]; - /* no break */ - case 4: - i += ((unsigned) key[3] << 24); - /* no break */ - case 3: - i += ((unsigned) key[2] << 16); - /* no break */ - case 2: - i += ((unsigned) key[1] << 8); - /* no break */ - case 1: - i += key[0]; - } - HASH_JEN_MIX(i, j, hashv); - return hashv; -} - -/** - * MurmurHash2(Austin Appleby) - */ -static inline uint32_t swoole_hash_austin(const char *key, unsigned int keylen) -{ - unsigned int h, k; - h = 0 ^ keylen; - - while (keylen >= 4) - { - k = key[0]; - k |= key[1] << 8; - k |= key[2] << 16; - k |= key[3] << 24; - - k *= 0x5bd1e995; - k ^= k >> 24; - k *= 0x5bd1e995; - - h *= 0x5bd1e995; - h ^= k; - - key += 4; - keylen -= 4; - } - - switch (keylen) - { - case 3: - h ^= key[2] << 16; - /* no break */ - case 2: - h ^= key[1] << 8; - /* no break */ - case 1: - h ^= key[0]; - h *= 0x5bd1e995; - } - - h ^= h >> 13; - h *= 0x5bd1e995; - h ^= h >> 15; - - return h; -} - -/* {{{ DJBX33A (Daniel J. Bernstein, Times 33 with Addition) - * - * This is Daniel J. Bernstein's popular `times 33' hash function as - * posted by him years ago on comp->lang.c. It basically uses a function - * like ``hash(i) = hash(i-1) * 33 + str[i]''. This is one of the best - * known hash functions for strings. Because it is both computed very - * fast and distributes very well. - * - * The magic of number 33, i.e. why it works better than many other - * constants, prime or not, has never been adequately explained by - * anyone. So I try an explanation: if one experimentally tests all - * multipliers between 1 and 256 (as RSE did now) one detects that even - * numbers are not useable at all. The remaining 128 odd numbers - * (except for the number 1) work more or less all equally well. They - * all distribute in an acceptable way and this way fill a hash table - * with an average percent of approx. 86%. - * - * If one compares the Chi^2 values of the variants, the number 33 not - * even has the best value. But the number 33 and a few other equally - * good numbers like 17, 31, 63, 127 and 129 have nevertheless a great - * advantage to the remaining numbers in the large set of possible - * multipliers: their multiply operation can be replaced by a faster - * operation based on just one shift plus either a single addition - * or subtraction operation. And because a hash function has to both - * distribute good _and_ has to be very fast to compute, those few - * numbers should be preferred and seems to be the reason why Daniel J. - * Bernstein also preferred it. - * - * -- Ralf S. Engelschall - */ -static inline uint64_t swoole_hash_php(const char *key, size_t len) -{ - register ulong_t hash = 5381; - /* variant with the hash unrolled eight times */ - for (; len >= 8; len -= 8) - { - hash = ((hash << 5) + hash) + *key++; - hash = ((hash << 5) + hash) + *key++; - hash = ((hash << 5) + hash) + *key++; - hash = ((hash << 5) + hash) + *key++; - hash = ((hash << 5) + hash) + *key++; - hash = ((hash << 5) + hash) + *key++; - hash = ((hash << 5) + hash) + *key++; - hash = ((hash << 5) + hash) + *key++; - } - - switch (len) - { - case 7: hash = ((hash << 5) + hash) + *key++; /* fallthrough... */ - /* no break */ - case 6: hash = ((hash << 5) + hash) + *key++; /* fallthrough... */ - /* no break */ - case 5: hash = ((hash << 5) + hash) + *key++; /* fallthrough... */ - /* no break */ - case 4: hash = ((hash << 5) + hash) + *key++; /* fallthrough... */ - /* no break */ - case 3: hash = ((hash << 5) + hash) + *key++; /* fallthrough... */ - /* no break */ - case 2: hash = ((hash << 5) + hash) + *key++; /* fallthrough... */ - /* no break */ - case 1: hash = ((hash << 5) + hash) + *key++; break; - case 0: break; - default: break; - } - return hash; -} - -#define CRC_STRING_MAXLEN 256 - -uint32_t swoole_crc32(const char *data, uint32_t size); - -SW_EXTERN_C_END - -#endif /* SW_HASH_H_ */ diff --git a/include/hashmap.h b/include/hashmap.h deleted file mode 100644 index 9419e1389a6..00000000000 --- a/include/hashmap.h +++ /dev/null @@ -1,57 +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 __SW_HASHMAP_H -#define __SW_HASHMAP_H - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void (*swHashMap_dtor)(void *data); - -typedef struct -{ - struct swHashMap_node *root; - struct swHashMap_node *iterator; - swHashMap_dtor dtor; -} swHashMap; - -swHashMap* swHashMap_new(uint32_t bucket_num, swHashMap_dtor dtor); -void swHashMap_clean(swHashMap* hmap); -void swHashMap_free(swHashMap *hmap); - -int swHashMap_add(swHashMap *hmap, const char *key, uint16_t key_len, void *data); -int swHashMap_add_int(swHashMap *hmap, uint64_t key, void *data); -void* swHashMap_find(swHashMap *hmap, const char *key, uint16_t key_len); -void* swHashMap_find_int(swHashMap *hmap, uint64_t key); -void swHashMap_update_int(swHashMap *hmap, uint64_t key, void *data); -int swHashMap_update(swHashMap *hmap, const char *key, uint16_t key_len, void *data); -int swHashMap_del(swHashMap *hmap, const char *key, uint16_t key_len); -int swHashMap_del_int(swHashMap *hmap, uint64_t key); -int swHashMap_move(swHashMap *hmap, const char *old_key, uint16_t old_key_len, const char *new_key, uint16_t new_key_len); -int swHashMap_move_int(swHashMap *hmap, uint64_t old_key, uint64_t new_key); -void swHashMap_rewind(swHashMap* hmap); -void* swHashMap_each(swHashMap* hmap, char **key); -void* swHashMap_each_int(swHashMap* hmap, uint64_t *key); -#define swHashMap_each_reset(hmap) ((hmap)->iterator = NULL) -uint32_t swHashMap_count(swHashMap* hmap); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/include/heap.h b/include/heap.h deleted file mode 100644 index 7cf40150082..00000000000 --- a/include/heap.h +++ /dev/null @@ -1,59 +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: Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#ifndef SW_MINHEAP_H_ -#define SW_MINHEAP_H_ - -enum swHeap_type -{ - SW_MIN_HEAP, SW_MAX_HEAP, -}; - -typedef struct swHeap_node -{ - uint64_t priority; - uint32_t position; - void *data; -} swHeap_node; - -typedef struct _swHeap -{ - uint32_t num; - uint32_t size; - uint8_t type; - swHeap_node **nodes; -} swHeap; - -swHeap *swHeap_new(size_t n, uint8_t type); -void swHeap_free(swHeap *heap); -uint32_t swHeap_size(swHeap *heap); -swHeap_node* swHeap_push(swHeap *heap, uint64_t priority, void *data); -void *swHeap_pop(swHeap *heap); -void swHeap_change_priority(swHeap *heap, uint64_t new_priority, void* ptr); -void swHeap_remove(swHeap *heap, swHeap_node *node); -void *swHeap_peek(swHeap *heap); -void swHeap_print(swHeap *heap); - -static inline swHeap_node *swHeap_top(swHeap *heap) -{ - if (heap->num == 1) - { - return NULL; - } - return heap->nodes[1]; -} - -#endif /* SW_MINHEAP_H_ */ 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/http.h b/include/http.h deleted file mode 100644 index 8f5c03a3d90..00000000000 --- a/include/http.h +++ /dev/null @@ -1,146 +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: Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#ifndef SW_HTTP_H_ -#define SW_HTTP_H_ - -#include "swoole.h" - -SW_EXTERN_C_BEGIN - -enum swHttp_version -{ - SW_HTTP_VERSION_10 = 1, - SW_HTTP_VERSION_11, -}; - -enum swHttp_method -{ - SW_HTTP_DELETE = 1, SW_HTTP_GET, SW_HTTP_HEAD, SW_HTTP_POST, SW_HTTP_PUT, SW_HTTP_PATCH, - /* pathological */ - SW_HTTP_CONNECT, SW_HTTP_OPTIONS, SW_HTTP_TRACE, - /* webdav */ - SW_HTTP_COPY, SW_HTTP_LOCK, SW_HTTP_MKCOL, SW_HTTP_MOVE, SW_HTTP_PROPFIND, SW_HTTP_PROPPATCH, SW_HTTP_UNLOCK, - /* subversion */ - SW_HTTP_REPORT, SW_HTTP_MKACTIVITY, SW_HTTP_CHECKOUT, SW_HTTP_MERGE, - /* upnp */ - SW_HTTP_MSEARCH, SW_HTTP_NOTIFY, SW_HTTP_SUBSCRIBE, SW_HTTP_UNSUBSCRIBE, - /* proxy */ - SW_HTTP_PURGE, - /* Http2 */ - SW_HTTP_PRI, -}; - -enum swHttp_status_code -{ - SW_HTTP_CONTINUE = 100, - SW_HTTP_SWITCHING_PROTOCOLS = 101, - SW_HTTP_PROCESSING = 102, - - SW_HTTP_OK = 200, - SW_HTTP_CREATED = 201, - SW_HTTP_ACCEPTED = 202, - SW_HTTP_NO_CONTENT = 204, - SW_HTTP_PARTIAL_CONTENT = 206, - - SW_HTTP_SPECIAL_RESPONSE = 300, - SW_HTTP_MOVED_PERMANENTLY = 301, - SW_HTTP_MOVED_TEMPORARILY = 302, - SW_HTTP_SEE_OTHER = 303, - SW_HTTP_NOT_MODIFIED = 304, - SW_HTTP_TEMPORARY_REDIRECT = 307, - SW_HTTP_PERMANENT_REDIRECT = 308, - - SW_HTTP_BAD_REQUEST = 400, - SW_HTTP_UNAUTHORIZED = 401, - SW_HTTP_FORBIDDEN = 403, - SW_HTTP_NOT_FOUND = 404, - SW_HTTP_NOT_ALLOWED = 405, - SW_HTTP_REQUEST_TIME_OUT = 408, - SW_HTTP_CONFLICT = 409, - SW_HTTP_LENGTH_REQUIRED = 411, - SW_HTTP_PRECONDITION_FAILED = 412, - SW_HTTP_REQUEST_ENTITY_TOO_LARGE = 413, - SW_HTTP_REQUEST_URI_TOO_LARGE = 414, - SW_HTTP_UNSUPPORTED_MEDIA_TYPE = 415, - SW_HTTP_RANGE_NOT_SATISFIABLE = 416, - SW_HTTP_MISDIRECTED_REQUEST = 421, - SW_HTTP_TOO_MANY_REQUESTS = 429, - - SW_HTTP_INTERNAL_SERVER_ERROR = 500, - SW_HTTP_NOT_IMPLEMENTED = 501, - SW_HTTP_BAD_GATEWAY = 502, - SW_HTTP_SERVICE_UNAVAILABLE = 503, - SW_HTTP_GATEWAY_TIME_OUT = 504, - SW_HTTP_VERSION_NOT_SUPPORTED = 505, - SW_HTTP_INSUFFICIENT_STORAGE = 507 -}; - -typedef struct _swHttpRequest -{ - uint8_t method; - uint8_t version; - uchar excepted :1; - - uchar header_parsed :1; - uchar tried_to_dispatch :1; - - uchar known_length :1; - uchar keep_alive :1; - uchar chunked :1; - uchar nobody_chunked :1; - - uint32_t url_offset; - uint32_t url_length; - - uint32_t request_line_length; /* without \r\n */ - uint32_t header_length; /* include request_line_length + \r\n */ - uint32_t content_length; - - swString *buffer; -} swHttpRequest; - -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); - -int swHttpRequest_get_protocol(swHttpRequest *request); -int swHttpRequest_get_header_length(swHttpRequest *request); -int swHttpRequest_get_chunked_body_length(swHttpRequest *request); -void swHttpRequest_parse_header_info(swHttpRequest *request); -void swHttpRequest_free(swConnection *conn); - -static inline void swHttpRequest_clean(swHttpRequest *request) -{ - memset(request, 0, offsetof(swHttpRequest, buffer)); -} - -#ifdef SW_HTTP_100_CONTINUE -int swHttpRequest_has_expect_header(swHttpRequest *request); -#endif - -#ifdef SW_USE_HTTP2 -ssize_t swHttpMix_get_package_length(swProtocol *protocol, swSocket *conn, char *data, uint32_t length); -uint8_t swHttpMix_get_package_length_size(swSocket *conn); -int swHttpMix_dispatch_frame(swProtocol *protocol, swSocket *conn, char *data, uint32_t length); -#endif - -SW_EXTERN_C_END - -#endif /* SW_HTTP_H_ */ diff --git a/include/http2.h b/include/http2.h deleted file mode 100644 index e3eddf0c2ac..00000000000 --- a/include/http2.h +++ /dev/null @@ -1,185 +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: Tianfeng Han | - | Twosee | - +----------------------------------------------------------------------+ -*/ - -#ifndef SW_HTTP2_H_ -#define SW_HTTP2_H_ - -SW_EXTERN_C_BEGIN - -#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 swHttp2_frame_type -{ - SW_HTTP2_TYPE_DATA = 0, - SW_HTTP2_TYPE_HEADERS = 1, - SW_HTTP2_TYPE_PRIORITY = 2, - SW_HTTP2_TYPE_RST_STREAM = 3, - SW_HTTP2_TYPE_SETTINGS = 4, - SW_HTTP2_TYPE_PUSH_PROMISE = 5, - SW_HTTP2_TYPE_PING = 6, - SW_HTTP2_TYPE_GOAWAY = 7, - SW_HTTP2_TYPE_WINDOW_UPDATE = 8, - SW_HTTP2_TYPE_CONTINUATION = 9, -}; - -enum swHttp2_frame_flag -{ - SW_HTTP2_FLAG_NONE = 0x00, - SW_HTTP2_FLAG_ACK = 0x01, - SW_HTTP2_FLAG_END_STREAM = 0x01, - SW_HTTP2_FLAG_END_HEADERS = 0x04, - SW_HTTP2_FLAG_PADDED = 0x08, - SW_HTTP2_FLAG_PRIORITY = 0x20, -}; - -enum swHttp2_setting_id -{ - SW_HTTP2_SETTING_HEADER_TABLE_SIZE = 0x1, - SW_HTTP2_SETTINGS_ENABLE_PUSH = 0x2, - SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 0x3, - SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE = 0x4, - SW_HTTP2_SETTINGS_MAX_FRAME_SIZE = 0x5, - SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x6, -}; - -enum swHttp2_stream_flag -{ - SW_HTTP2_STREAM_NORMAL = 0, - SW_HTTP2_STREAM_REQUEST_END = 1 << 0, - SW_HTTP2_STREAM_PIPELINE_REQUEST = 1 << 1, - SW_HTTP2_STREAM_PIPELINE_RESPONSE = 1 << 2, -}; - -#define SW_HTTP2_FRAME_HEADER_SIZE 9 -#define SW_HTTP2_SETTING_OPTION_SIZE 6 -#define SW_HTTP2_FRAME_PING_PAYLOAD_SIZE 8 - -#define SW_HTTP2_RST_STREAM_SIZE 4 -#define SW_HTTP2_PRIORITY_SIZE 5 -#define SW_HTTP2_PING_SIZE 8 -#define SW_HTTP2_RST_STREAM_SIZE 4 -#define SW_HTTP2_GOAWAY_SIZE 8 -#define SW_HTTP2_WINDOW_UPDATE_SIZE 4 -#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 \ - ); - -typedef struct _swHttp2_settings -{ - uint32_t header_table_size; - uint32_t window_size; - uint32_t max_concurrent_streams; - uint32_t max_frame_size; - uint32_t max_header_list_size; -} swHttp2_settings; - -/** - +-----------------------------------------------+ - | Length (24) | - +---------------+---------------+---------------+ - | Type (8) | Flags (8) | - +-+-------------+---------------+-------------------------------+ - |R| Stream Identifier (31) | - +=+=============================================================+ - | Frame Payload (0...) ... - +---------------------------------------------------------------+ - */ -typedef struct _swHttp2_frame -{ - uint32_t length :24; - uint32_t type :8; - uint32_t flags :8; - uint32_t rsv1 :1; - uint32_t identifier :31; - char data[0]; -} swHttp2_frame; - -static sw_inline ssize_t swHttp2_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, 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; -} - -/** - +-----------------------------------------------+ - | Length (24) | - +---------------+---------------+---------------+ - | Type (8) | Flags (8) | - +-+-------------+---------------+-------------------------------+ - |R| Stream Identifier (31) | - +=+=============================================================+ - | 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) -{ - buffer[0] = length >> 16; - buffer[1] = length >> 8; - buffer[2] = length; - buffer[3] = type; - buffer[4] = flags; - *(uint32_t *) (buffer + 5) = htonl(stream_id); -} - -SW_EXTERN_C_END - -#endif /* SW_HTTP2_H_ */ diff --git a/include/list.h b/include/list.h deleted file mode 100644 index c5c5345ad7f..00000000000 --- a/include/list.h +++ /dev/null @@ -1,753 +0,0 @@ -/* -Copyright (c) 2007-2014, Troy D. Hanson http://troydhanson.github.com/uthash/ -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. -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 UTLIST_H -#define UTLIST_H - -#define UTLIST_VERSION 1.9.9 - -#include - -/* - * This file contains macros to manipulate singly and doubly-linked lists. - * - * 1. LL_ macros: singly-linked lists. - * 2. DL_ macros: doubly-linked lists. - * 3. CDL_ macros: circular doubly-linked lists. - * - * To use singly-linked lists, your structure must have a "next" pointer. - * To use doubly-linked lists, your structure must "prev" and "next" pointers. - * Either way, the pointer to the head of the list must be initialized to NULL. - * - * ----------------.EXAMPLE ------------------------- - * struct item { - * int id; - * struct item *prev, *next; - * } - * - * struct item *list = NULL: - * - * int main() { - * struct item *item; - * ... allocate and populate item ... - * DL_APPEND(list, item); - * } - * -------------------------------------------------- - * - * For doubly-linked lists, the append and delete macros are O(1) - * For singly-linked lists, append and delete are O(n) but prepend is O(1) - * The sort macro is O(n log(n)) for all types of single/double/circular lists. - */ - -/* These macros use decltype or the earlier __typeof GNU extension. - As decltype is only available in newer compilers (VS2010 or gcc 4.3+ - when compiling c++ code), this code uses whatever method is needed - or, for VS2008 where neither is available, uses casting workarounds. */ -#ifdef _MSC_VER /* MS compiler */ -#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ -#define LDECLTYPE(x) decltype(x) -#else /* VS2008 or older (or VS2010 in C mode) */ -#define NO_DECLTYPE -#define LDECLTYPE(x) char* -#endif -#elif defined(__ICCARM__) -#define NO_DECLTYPE -#define LDECLTYPE(x) char* -#else /* GNU, Sun and other compilers */ -#define LDECLTYPE(x) __typeof(x) -#endif - -/* for VS2008 we use some workarounds to get around the lack of decltype, - * namely, we always reassign our tmp variable to the list head if we need - * to dereference its prev/next pointers, and save/restore the real head.*/ -#ifdef NO_DECLTYPE -#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } -#define _NEXT(elt,list,next) ((char*)((list)->next)) -#define _NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } -/* #define _PREV(elt,list,prev) ((char*)((list)->prev)) */ -#define _PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } -#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } -#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } -#else -#define _SV(elt,list) -#define _NEXT(elt,list,next) ((elt)->next) -#define _NEXTASGN(elt,list,to,next) ((elt)->next)=(to) -/* #define _PREV(elt,list,prev) ((elt)->prev) */ -#define _PREVASGN(elt,list,to,prev) ((elt)->prev)=(to) -#define _RS(list) -#define _CASTASGN(a,b) (a)=(b) -#endif - -/****************************************************************************** - * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * - * Unwieldy variable names used here to avoid shadowing passed-in variables. * - *****************************************************************************/ -#define LL_SORT(list, cmp) \ - LL_SORT2(list, cmp, next) - -#define LL_SORT2(list, cmp, next) \ -do { \ - LDECLTYPE(list) _ls_p; \ - LDECLTYPE(list) _ls_q; \ - LDECLTYPE(list) _ls_e; \ - LDECLTYPE(list) _ls_tail; \ - int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ - if (list) { \ - _ls_insize = 1; \ - _ls_looping = 1; \ - while (_ls_looping) { \ - _CASTASGN(_ls_p,list); \ - list = NULL; \ - _ls_tail = NULL; \ - _ls_nmerges = 0; \ - while (_ls_p) { \ - _ls_nmerges++; \ - _ls_q = _ls_p; \ - _ls_psize = 0; \ - for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ - _ls_psize++; \ - _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \ - if (!_ls_q) break; \ - } \ - _ls_qsize = _ls_insize; \ - while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ - if (_ls_psize == 0) { \ - _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ - _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ - } else if (_ls_qsize == 0 || !_ls_q) { \ - _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ - _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ - } else if (cmp(_ls_p,_ls_q) <= 0) { \ - _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ - _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ - } else { \ - _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ - _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ - } \ - if (_ls_tail) { \ - _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ - } else { \ - _CASTASGN(list,_ls_e); \ - } \ - _ls_tail = _ls_e; \ - } \ - _ls_p = _ls_q; \ - } \ - if (_ls_tail) { \ - _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \ - } \ - if (_ls_nmerges <= 1) { \ - _ls_looping=0; \ - } \ - _ls_insize *= 2; \ - } \ - } \ -} while (0) - - -#define DL_SORT(list, cmp) \ - DL_SORT2(list, cmp, prev, next) - -#define DL_SORT2(list, cmp, prev, next) \ -do { \ - LDECLTYPE(list) _ls_p; \ - LDECLTYPE(list) _ls_q; \ - LDECLTYPE(list) _ls_e; \ - LDECLTYPE(list) _ls_tail; \ - int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ - if (list) { \ - _ls_insize = 1; \ - _ls_looping = 1; \ - while (_ls_looping) { \ - _CASTASGN(_ls_p,list); \ - list = NULL; \ - _ls_tail = NULL; \ - _ls_nmerges = 0; \ - while (_ls_p) { \ - _ls_nmerges++; \ - _ls_q = _ls_p; \ - _ls_psize = 0; \ - for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ - _ls_psize++; \ - _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \ - if (!_ls_q) break; \ - } \ - _ls_qsize = _ls_insize; \ - while ((_ls_psize > 0) || ((_ls_qsize > 0) && _ls_q)) { \ - if (_ls_psize == 0) { \ - _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ - _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ - } else if ((_ls_qsize == 0) || (!_ls_q)) { \ - _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ - _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ - } else if (cmp(_ls_p,_ls_q) <= 0) { \ - _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ - _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ - } else { \ - _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ - _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ - } \ - if (_ls_tail) { \ - _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ - } else { \ - _CASTASGN(list,_ls_e); \ - } \ - _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \ - _ls_tail = _ls_e; \ - } \ - _ls_p = _ls_q; \ - } \ - _CASTASGN(list->prev, _ls_tail); \ - _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \ - if (_ls_nmerges <= 1) { \ - _ls_looping=0; \ - } \ - _ls_insize *= 2; \ - } \ - } \ -} while (0) - -#define CDL_SORT(list, cmp) \ - CDL_SORT2(list, cmp, prev, next) - -#define CDL_SORT2(list, cmp, prev, next) \ -do { \ - LDECLTYPE(list) _ls_p; \ - LDECLTYPE(list) _ls_q; \ - LDECLTYPE(list) _ls_e; \ - LDECLTYPE(list) _ls_tail; \ - LDECLTYPE(list) _ls_oldhead; \ - LDECLTYPE(list) _tmp; \ - int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ - if (list) { \ - _ls_insize = 1; \ - _ls_looping = 1; \ - while (_ls_looping) { \ - _CASTASGN(_ls_p,list); \ - _CASTASGN(_ls_oldhead,list); \ - list = NULL; \ - _ls_tail = NULL; \ - _ls_nmerges = 0; \ - while (_ls_p) { \ - _ls_nmerges++; \ - _ls_q = _ls_p; \ - _ls_psize = 0; \ - for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ - _ls_psize++; \ - _SV(_ls_q,list); \ - if (_NEXT(_ls_q,list,next) == _ls_oldhead) { \ - _ls_q = NULL; \ - } else { \ - _ls_q = _NEXT(_ls_q,list,next); \ - } \ - _RS(list); \ - if (!_ls_q) break; \ - } \ - _ls_qsize = _ls_insize; \ - while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ - if (_ls_psize == 0) { \ - _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ - _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ - if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ - } else if (_ls_qsize == 0 || !_ls_q) { \ - _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ - _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ - if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ - } else if (cmp(_ls_p,_ls_q) <= 0) { \ - _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ - _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ - if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ - } else { \ - _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ - _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ - if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ - } \ - if (_ls_tail) { \ - _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ - } else { \ - _CASTASGN(list,_ls_e); \ - } \ - _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \ - _ls_tail = _ls_e; \ - } \ - _ls_p = _ls_q; \ - } \ - _CASTASGN(list->prev,_ls_tail); \ - _CASTASGN(_tmp,list); \ - _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp,next); _RS(list); \ - if (_ls_nmerges <= 1) { \ - _ls_looping=0; \ - } \ - _ls_insize *= 2; \ - } \ - } \ -} while (0) - -/****************************************************************************** - * singly linked list macros (non-circular) * - *****************************************************************************/ -#define LL_PREPEND(head,add) \ - LL_PREPEND2(head,add,next) - -#define LL_PREPEND2(head,add,next) \ -do { \ - (add)->next = head; \ - head = add; \ -} while (0) - -#define LL_CONCAT(head1,head2) \ - LL_CONCAT2(head1,head2,next) - -#define LL_CONCAT2(head1,head2,next) \ -do { \ - LDECLTYPE(head1) _tmp; \ - if (head1) { \ - _tmp = head1; \ - while (_tmp->next) { _tmp = _tmp->next; } \ - _tmp->next=(head2); \ - } else { \ - (head1)=(head2); \ - } \ -} while (0) - -#define LL_APPEND(head,add) \ - LL_APPEND2(head,add,next) - -#define LL_APPEND2(head,add,next) \ -do { \ - LDECLTYPE(head) _tmp; \ - (add)->next=NULL; \ - if (head) { \ - _tmp = head; \ - while (_tmp->next) { _tmp = _tmp->next; } \ - _tmp->next=(add); \ - } else { \ - (head)=(add); \ - } \ -} while (0) - -#define LL_DELETE(head,del) \ - LL_DELETE2(head,del,next) - -#define LL_DELETE2(head,del,next) \ -do { \ - LDECLTYPE(head) _tmp; \ - if ((head) == (del)) { \ - (head)=(head)->next; \ - } else { \ - _tmp = head; \ - while (_tmp->next && (_tmp->next != (del))) { \ - _tmp = _tmp->next; \ - } \ - if (_tmp->next) { \ - _tmp->next = ((del)->next); \ - } \ - } \ -} while (0) - -/* Here are VS2008 replacements for LL_APPEND and LL_DELETE */ -#define LL_APPEND_VS2008(head,add) \ - LL_APPEND2_VS2008(head,add,next) - -#define LL_APPEND2_VS2008(head,add,next) \ -do { \ - if (head) { \ - (add)->next = head; /* use add->next as a temp variable */ \ - while ((add)->next->next) { (add)->next = (add)->next->next; } \ - (add)->next->next=(add); \ - } else { \ - (head)=(add); \ - } \ - (add)->next=NULL; \ -} while (0) - -#define LL_DELETE_VS2008(head,del) \ - LL_DELETE2_VS2008(head,del,next) - -#define LL_DELETE2_VS2008(head,del,next) \ -do { \ - if ((head) == (del)) { \ - (head)=(head)->next; \ - } else { \ - char *_tmp = (char*)(head); \ - while ((head)->next && ((head)->next != (del))) { \ - head = (head)->next; \ - } \ - if ((head)->next) { \ - (head)->next = ((del)->next); \ - } \ - { \ - char **_head_alias = (char**)&(head); \ - *_head_alias = _tmp; \ - } \ - } \ -} while (0) -#ifdef NO_DECLTYPE -#undef LL_APPEND -#define LL_APPEND LL_APPEND_VS2008 -#undef LL_DELETE -#define LL_DELETE LL_DELETE_VS2008 -#undef LL_DELETE2 -#define LL_DELETE2 LL_DELETE2_VS2008 -#undef LL_APPEND2 -#define LL_APPEND2 LL_APPEND2_VS2008 -#undef LL_CONCAT /* no LL_CONCAT_VS2008 */ -#undef DL_CONCAT /* no DL_CONCAT_VS2008 */ -#endif -/* end VS2008 replacements */ - -#define LL_COUNT(head,el,counter) \ - LL_COUNT2(head,el,counter,next) \ - -#define LL_COUNT2(head,el,counter,next) \ -{ \ - counter = 0; \ - LL_FOREACH2(head,el,next){ ++counter; } \ -} - -#define LL_FOREACH(head,el) \ - LL_FOREACH2(head,el,next) - -#define LL_FOREACH2(head,el,next) \ - for(el=head;el;el=(el)->next) - -#define LL_FOREACH_SAFE(head,el,tmp) \ - LL_FOREACH_SAFE2(head,el,tmp,next) - -#define LL_FOREACH_SAFE2(head,el,tmp,next) \ - for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) - -#define LL_SEARCH_SCALAR(head,out,field,val) \ - LL_SEARCH_SCALAR2(head,out,field,val,next) - -#define LL_SEARCH_SCALAR2(head,out,field,val,next) \ -do { \ - LL_FOREACH2(head,out,next) { \ - if ((out)->field == (val)) break; \ - } \ -} while(0) - -#define LL_SEARCH(head,out,elt,cmp) \ - LL_SEARCH2(head,out,elt,cmp,next) - -#define LL_SEARCH2(head,out,elt,cmp,next) \ -do { \ - LL_FOREACH2(head,out,next) { \ - if ((cmp(out,elt))==0) break; \ - } \ -} while(0) - -#define LL_REPLACE_ELEM(head, el, add) \ -do { \ - LDECLTYPE(head) _tmp; \ - assert(head != NULL); \ - assert(el != NULL); \ - assert(add != NULL); \ - (add)->next = (el)->next; \ - if ((head) == (el)) { \ - (head) = (add); \ - } else { \ - _tmp = head; \ - while (_tmp->next && (_tmp->next != (el))) { \ - _tmp = _tmp->next; \ - } \ - if (_tmp->next) { \ - _tmp->next = (add); \ - } \ - } \ -} while (0) - -#define LL_PREPEND_ELEM(head, el, add) \ -do { \ - LDECLTYPE(head) _tmp; \ - assert(head != NULL); \ - assert(el != NULL); \ - assert(add != NULL); \ - (add)->next = (el); \ - if ((head) == (el)) { \ - (head) = (add); \ - } else { \ - _tmp = head; \ - while (_tmp->next && (_tmp->next != (el))) { \ - _tmp = _tmp->next; \ - } \ - if (_tmp->next) { \ - _tmp->next = (add); \ - } \ - } \ -} while (0) \ - - -/****************************************************************************** - * doubly linked list macros (non-circular) * - *****************************************************************************/ -#define DL_PREPEND(head,add) \ - DL_PREPEND2(head,add,prev,next) - -#define DL_PREPEND2(head,add,prev,next) \ -do { \ - (add)->next = head; \ - if (head) { \ - (add)->prev = (head)->prev; \ - (head)->prev = (add); \ - } else { \ - (add)->prev = (add); \ - } \ - (head) = (add); \ -} while (0) - -#define DL_APPEND(head,add) \ - DL_APPEND2(head,add,prev,next) - -#define DL_APPEND2(head,add,prev,next) \ -do { \ - if (head) { \ - (add)->prev = (head)->prev; \ - (head)->prev->next = (add); \ - (head)->prev = (add); \ - (add)->next = NULL; \ - } else { \ - (head)=(add); \ - (head)->prev = (head); \ - (head)->next = NULL; \ - } \ -} while (0) - -#define DL_CONCAT(head1,head2) \ - DL_CONCAT2(head1,head2,prev,next) - -#define DL_CONCAT2(head1,head2,prev,next) \ -do { \ - LDECLTYPE(head1) _tmp; \ - if (head2) { \ - if (head1) { \ - _tmp = (head2)->prev; \ - (head2)->prev = (head1)->prev; \ - (head1)->prev->next = (head2); \ - (head1)->prev = _tmp; \ - } else { \ - (head1)=(head2); \ - } \ - } \ -} while (0) - -#define DL_DELETE(head,del) \ - DL_DELETE2(head,del,prev,next) - -#define DL_DELETE2(head,del,prev,next) \ -do { \ - assert((del)->prev != NULL); \ - if ((del)->prev == (del)) { \ - (head)=NULL; \ - } else if ((del)==(head)) { \ - (del)->next->prev = (del)->prev; \ - (head) = (del)->next; \ - } else { \ - (del)->prev->next = (del)->next; \ - if ((del)->next) { \ - (del)->next->prev = (del)->prev; \ - } else { \ - (head)->prev = (del)->prev; \ - } \ - } \ -} while (0) - -#define DL_COUNT(head,el,counter) \ - DL_COUNT2(head,el,counter,next) \ - -#define DL_COUNT2(head,el,counter,next) \ -{ \ - counter = 0; \ - DL_FOREACH2(head,el,next){ ++counter; } \ -} - -#define DL_FOREACH(head,el) \ - DL_FOREACH2(head,el,next) - -#define DL_FOREACH2(head,el,next) \ - for(el=head;el;el=(el)->next) - -/* this version is safe for deleting the elements during iteration */ -#define DL_FOREACH_SAFE(head,el,tmp) \ - DL_FOREACH_SAFE2(head,el,tmp,next) - -#define DL_FOREACH_SAFE2(head,el,tmp,next) \ - for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) - -/* these are identical to their singly-linked list counterparts */ -#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR -#define DL_SEARCH LL_SEARCH -#define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2 -#define DL_SEARCH2 LL_SEARCH2 - -#define DL_REPLACE_ELEM(head, el, add) \ -do { \ - assert(head != NULL); \ - assert(el != NULL); \ - assert(add != NULL); \ - if ((head) == (el)) { \ - (head) = (add); \ - (add)->next = (el)->next; \ - if ((el)->next == NULL) { \ - (add)->prev = (add); \ - } else { \ - (add)->prev = (el)->prev; \ - (add)->next->prev = (add); \ - } \ - } else { \ - (add)->next = (el)->next; \ - (add)->prev = (el)->prev; \ - (add)->prev->next = (add); \ - if ((el)->next == NULL) { \ - (head)->prev = (add); \ - } else { \ - (add)->next->prev = (add); \ - } \ - } \ -} while (0) - -#define DL_PREPEND_ELEM(head, el, add) \ -do { \ - assert(head != NULL); \ - assert(el != NULL); \ - assert(add != NULL); \ - (add)->next = (el); \ - (add)->prev = (el)->prev; \ - (el)->prev = (add); \ - if ((head) == (el)) { \ - (head) = (add); \ - } else { \ - (add)->prev->next = (add); \ - } \ -} while (0) \ - - -/****************************************************************************** - * circular doubly linked list macros * - *****************************************************************************/ -#define CDL_PREPEND(head,add) \ - CDL_PREPEND2(head,add,prev,next) - -#define CDL_PREPEND2(head,add,prev,next) \ -do { \ - if (head) { \ - (add)->prev = (head)->prev; \ - (add)->next = (head); \ - (head)->prev = (add); \ - (add)->prev->next = (add); \ - } else { \ - (add)->prev = (add); \ - (add)->next = (add); \ - } \ -(head)=(add); \ -} while (0) - -#define CDL_DELETE(head,del) \ - CDL_DELETE2(head,del,prev,next) - -#define CDL_DELETE2(head,del,prev,next) \ -do { \ - if ( ((head)==(del)) && ((head)->next == (head))) { \ - (head) = NULL; \ - } else { \ - (del)->next->prev = (del)->prev; \ - (del)->prev->next = (del)->next; \ - if ((del) == (head)) (head)=(del)->next; \ - } \ -} while (0) - -#define CDL_COUNT(head,el,counter) \ - CDL_COUNT2(head,el,counter,next) \ - -#define CDL_COUNT2(head, el, counter,next) \ -{ \ - counter = 0; \ - CDL_FOREACH2(head,el,next){ ++counter; } \ -} - -#define CDL_FOREACH(head,el) \ - CDL_FOREACH2(head,el,next) - -#define CDL_FOREACH2(head,el,next) \ - for(el=head;el;el=(((el)->next==head) ? 0L : (el)->next)) - -#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ - CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) - -#define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \ - for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \ - (el) && ((tmp2)=(el)->next, 1); \ - ((el) = (((el)==(tmp1)) ? 0L : (tmp2)))) - -#define CDL_SEARCH_SCALAR(head,out,field,val) \ - CDL_SEARCH_SCALAR2(head,out,field,val,next) - -#define CDL_SEARCH_SCALAR2(head,out,field,val,next) \ -do { \ - CDL_FOREACH2(head,out,next) { \ - if ((out)->field == (val)) break; \ - } \ -} while(0) - -#define CDL_SEARCH(head,out,elt,cmp) \ - CDL_SEARCH2(head,out,elt,cmp,next) - -#define CDL_SEARCH2(head,out,elt,cmp,next) \ -do { \ - CDL_FOREACH2(head,out,next) { \ - if ((cmp(out,elt))==0) break; \ - } \ -} while(0) - -#define CDL_REPLACE_ELEM(head, el, add) \ -do { \ - assert(head != NULL); \ - assert(el != NULL); \ - assert(add != NULL); \ - if ((el)->next == (el)) { \ - (add)->next = (add); \ - (add)->prev = (add); \ - (head) = (add); \ - } else { \ - (add)->next = (el)->next; \ - (add)->prev = (el)->prev; \ - (add)->next->prev = (add); \ - (add)->prev->next = (add); \ - if ((head) == (el)) { \ - (head) = (add); \ - } \ - } \ -} while (0) - -#define CDL_PREPEND_ELEM(head, el, add) \ -do { \ - assert(head != NULL); \ - assert(el != NULL); \ - assert(add != NULL); \ - (add)->next = (el); \ - (add)->prev = (el)->prev; \ - (el)->prev = (add); \ - (add)->prev->next = (add); \ - if ((head) == (el)) { \ - (head) = (add); \ - } \ -} while (0) \ - -#endif /* UTLIST_H */ diff --git a/include/lru_cache.h b/include/lru_cache.h deleted file mode 100644 index ded8aa50418..00000000000 --- a/include/lru_cache.h +++ /dev/null @@ -1,115 +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 | - +----------------------------------------------------------------------+ -*/ - -#pragma once - -#include -#include -#include -#include -#include - -namespace swoole -{ -/** - * This cache isn't thread safe - */ -class LRUCache -{ -private: - typedef std::pair> cache_node_t; - typedef std::list> cache_list_t; - - std::unordered_map cache_map; - cache_list_t cache_list; - size_t cache_capacity; - -public: - explicit LRUCache(size_t capacity) - { - cache_capacity = capacity; - } - - inline std::shared_ptr get(const std::string &key) - { - auto iter = cache_map.find(key); - if (iter == cache_map.end()) - { - return nullptr; - } - - if (iter->second->second.first < time(nullptr) && iter->second->second.first > 0) - { - return nullptr; - } - - cache_list.splice(cache_list.begin(), cache_list, iter->second); - 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) - { - time_t expire_time; - - if (expire <= 0) - { - expire_time = 0; - } - else - { - expire_time = time(nullptr) + expire; - } - - auto iter = cache_map.find(key); - if (iter != cache_map.end()) - { - iter->second->second.first = expire_time; - iter->second->second.second = val; - cache_list.splice(cache_list.begin(), cache_list, iter->second); - return; - } - - size_t size = cache_list.size(); - if (size == cache_capacity && size > 0) - { - auto del = cache_list.back(); - cache_map.erase(del.first); - cache_list.pop_back(); - } - - cache_list.emplace_front(key, cache_node_t{expire_time, val}); - cache_map[key] = cache_list.begin(); - } - - inline void del(const std::string &key) - { - auto iter = cache_map.find(key); - if (iter == cache_map.end()) - { - return; - } - - cache_list.erase(iter->second); - cache_map.erase(iter); - } - - inline void clear() - { - cache_list.clear(); - cache_map.clear(); - } -}; -} diff --git a/include/mime_type.h b/include/mime_type.h deleted file mode 100644 index ea419486772..00000000000 --- a/include/mime_type.h +++ /dev/null @@ -1,30 +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 | - +----------------------------------------------------------------------+ -*/ - -#pragma once - -#include -#include -#include - -namespace swoole { namespace mime_type { - const std::unordered_map& list(); - bool add(const std::string &suffix, const std::string &mime_type); - void set(const std::string &suffix, const std::string &mime_type); - bool del(const std::string &suffix); - const std::string& get(const std::string &filename); - bool exists(const std::string &filename); -}} diff --git a/include/mqtt.h b/include/mqtt.h deleted file mode 100644 index c58613ea53b..00000000000 --- a/include/mqtt.h +++ /dev/null @@ -1,68 +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: Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#ifndef SW_MQTT_H_ -#define SW_MQTT_H_ - -#include "swoole.h" - -SW_EXTERN_C_BEGIN - -#define SW_MQTT_MIN_LENGTH 2 -#define SW_MQTT_MAX_PAYLOAD_SIZE 268435455 - -enum swMqtt_opcode -{ - SW_MQTT_CONNECT = 0x10, - SW_MQTT_CONNACK = 0x20, - SW_MQTT_PUBLISH = 0x30, - SW_MQTT_PUBACK = 0x40, - SW_MQTT_PUBREC = 0x50, - SW_MQTT_PUBREL = 0x60, - SW_MQTT_PUBCOMP = 0x70, - SW_MQTT_SUBSCRIBE = 0x80, - SW_MQTT_SUBACK = 0x90, - SW_MQTT_UNSUBSCRIBE = 0xA0, - SW_MQTT_UNSUBACK = 0xB0, - SW_MQTT_PINGREQ = 0xC0, - SW_MQTT_PINGRESP = 0xD0, - SW_MQTT_DISCONNECT = 0xE0, -}; - -typedef struct -{ - uint8_t type :4; - uint8_t dup :1; - uint8_t qos :2; - uint8_t retain :1; - - uint32_t length; - - char protocol_name[8]; - -} swMqtt_packet; - -#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, char *data, uint32_t size); - -SW_EXTERN_C_END - -#endif /* SW_MQTT_H_ */ diff --git a/include/readme b/include/readme deleted file mode 100644 index 5d6dc845a17..00000000000 --- a/include/readme +++ /dev/null @@ -1 +0,0 @@ -Swoole used Troy D. Hanson's Uhash and Ulist. diff --git a/include/redis.h b/include/redis.h deleted file mode 100644 index 78f85928475..00000000000 --- a/include/redis.h +++ /dev/null @@ -1,72 +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: Tianfeng Han | - +----------------------------------------------------------------------+ -*/ - -#ifndef SW_REDIS_H_ -#define SW_REDIS_H_ - -#ifdef __cplusplus -extern "C" -{ -#endif - -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 char* swRedis_get_number(char *p, int *_ret) -{ - char *endptr; - p++; - int ret = strtol(p, &endptr, 10); - if (strncmp(SW_CRLF, endptr, SW_CRLF_LEN) == 0) - { - p += (endptr - p) + SW_CRLF_LEN; - *_ret = ret; - return p; - } - else - { - return NULL; - } -} - -int swRedis_recv(swProtocol *protocol, swConnection *conn, swString *buffer); - -#ifdef __cplusplus -} -#endif - -#endif /* SW_REDIS_H_ */ diff --git a/include/ring_queue.h b/include/ring_queue.h deleted file mode 100644 index 7db11f01ce9..00000000000 --- a/include/ring_queue.h +++ /dev/null @@ -1,55 +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 _SW_RINGQUEUE_H_ -#define _SW_RINGQUEUE_H_ - -typedef struct _swRingQueue -{ - int head; - int tail; - /** - * empty or full - */ - int tag; - int size; - void **data; -} swRingQueue; - -int swRingQueue_init(swRingQueue *queue, int buffer_size); -int swRingQueue_push(swRingQueue *queue, void *); -int swRingQueue_pop(swRingQueue *queue, void **); -void swRingQueue_free(swRingQueue *queue); - -static inline int swRingQueue_count(swRingQueue *queue) -{ - if (queue->tail > queue->head) - { - return queue->tail - queue->head; - } - else if (queue->head == queue->tail) - { - return queue->tag == 1 ? queue->size : 0; - } - else - { - return queue->tail + queue->size - queue->head; - } -} - -#define swRingQueue_empty(q) ( (q->head == q->tail) && (q->tag == 0)) -#define swRingQueue_full(q) ( (q->head == q->tail) && (q->tag == 1)) -#endif diff --git a/include/server.h b/include/server.h deleted file mode 100644 index 011d1e396d0..00000000000 --- a/include/server.h +++ /dev/null @@ -1,1072 +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: Tianfeng Han | - +----------------------------------------------------------------------+ -*/ - -#pragma once - -#include "swoole_api.h" -#include "ssl.h" -#include "http.h" - -#ifdef SW_USE_OPENSSL -#include "dtls.h" -#endif - -#include -#include -#include -#include - -#define SW_REACTOR_NUM SW_CPU_NUM -#define SW_WORKER_NUM (SW_CPU_NUM*2) - -enum swServer_event_type -{ - //data payload - SW_SERVER_EVENT_SEND_DATA, - SW_SERVER_EVENT_SEND_FILE, - SW_SERVER_EVENT_SNED_DGRAM, - //connection event - SW_SERVER_EVENT_CLOSE, - SW_SERVER_EVENT_CONNECT, - //task - SW_SERVER_EVENT_TASK, - 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, - //buffer event - SW_SERVER_EVENT_BUFFER_FULL, - SW_SERVER_EVENT_BUFFER_EMPTY, - //process message - SW_SERVER_EVENT_INCOMING, - SW_SERVER_EVENT_SHUTDOWN, -}; - -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, -}; - -struct swReactorThread -{ - pthread_t thread_id; - swReactor reactor; - swSocket *notify_pipe; - uint32_t pipe_num; - swSocket *pipe_sockets; - std::unordered_map *send_buffers; -}; - -struct swListenPort -{ - /** - * tcp socket listen backlog - */ - uint16_t backlog; - /** - * open tcp_defer_accept option - */ - int tcp_defer_accept; - /** - * TCP_FASTOPEN - */ - int tcp_fastopen; - /** - * TCP KeepAlive - */ - int tcp_keepidle; - int tcp_keepinterval; - int tcp_keepcount; - - int socket_buffer_size; - uint32_t buffer_high_watermark; - uint32_t buffer_low_watermark; - - enum swSocket_type type; - uint8_t ssl; - int port; - int socket_fd; - swSocket *socket; - pthread_t thread_id; - char host[SW_HOST_MAXSIZE]; - - /** - * check data eof - */ - uchar open_eof_check :1; - /** - * built-in http protocol - */ - uchar open_http_protocol :1; - /** - * built-in http2.0 protocol - */ - uchar open_http2_protocol :1; - /** - * built-in websocket protocol - */ - uchar open_websocket_protocol :1; - /** - * open websocket close frame - */ - uchar open_websocket_close_frame :1; - /** - * one package: length check - */ - uchar open_length_check :1; - /** - * for mqtt protocol - */ - uchar open_mqtt_protocol :1; - /** - * redis protocol - */ - uchar open_redis_protocol :1; - /** - * open tcp nodelay option - */ - uchar open_tcp_nodelay :1; - /** - * open tcp nopush option(for sendfile) - */ - uchar open_tcp_nopush :1; - /** - * open tcp keepalive - */ - uchar open_tcp_keepalive :1; - /** - * open tcp keepalive - */ - uchar open_ssl_encrypt :1; - /** - * Sec-WebSocket-Protocol - */ - char *websocket_subprotocol; - uint16_t websocket_subprotocol_length; - /** - * set socket option - */ - int kernel_socket_recv_buffer_size; - int kernel_socket_send_buffer_size; - -#ifdef SW_USE_OPENSSL - SSL_CTX *ssl_context; - swSSL_config ssl_config; - swSSL_option ssl_option; -#ifdef SW_SUPPORT_DTLS - std::unordered_map *dtls_sessions; -#endif -#endif - - sw_atomic_t connection_num; - - swProtocol protocol; - void *ptr; - int (*onRead)(swReactor *reactor, swListenPort *port, swEvent *event); -}; - -struct swWorkerStopMessage -{ - pid_t pid; - uint16_t worker_id; -}; - -//------------------------------------Packet------------------------------------------- -struct swPacket_task -{ - size_t length; - char tmpfile[SW_TASK_TMPDIR_SIZE + sizeof(SW_TASK_TMP_FILE)]; -}; - -struct swPacket_response -{ - int length; - int worker_id; -}; - -struct swPacket_ptr -{ - swDataHead info; - swString data; -}; - -//-----------------------------------Factory-------------------------------------------- -struct swFactory -{ - void *object; - void *ptr; //server object - - int (*start)(swFactory *); - int (*shutdown)(swFactory *); - int (*dispatch)(swFactory *, swSendData *); - /** - * success returns SW_OK, failure returns SW_ERR. - */ - int (*finish)(swFactory *, swSendData *); - int (*notify)(swFactory *, swDataHead *); //send a event notify - int (*end)(swFactory *, int fd); - void (*free)(swFactory *); -}; - -int swFactory_create(swFactory *factory); -int swFactory_finish(swFactory *factory, swSendData *_send); -int swFactory_check_callback(swFactory *factory); - -int swFactoryProcess_create(swFactory *factory, uint32_t worker_num); - -//------------------------------------Server------------------------------------------- -enum swServer_hook_type -{ - SW_SERVER_HOOK_MASTER_START, - SW_SERVER_HOOK_MASTER_TIMER, - SW_SERVER_HOOK_REACTOR_START, - SW_SERVER_HOOK_WORKER_START, - SW_SERVER_HOOK_TASK_WORKER_START, - SW_SERVER_HOOK_MASTER_CONNECT, - SW_SERVER_HOOK_REACTOR_CONNECT, - SW_SERVER_HOOK_WORKER_CONNECT, - SW_SERVER_HOOK_REACTOR_RECEIVE, - SW_SERVER_HOOK_WORKER_RECEIVE, - SW_SERVER_HOOK_REACTOR_CLOSE, - SW_SERVER_HOOK_WORKER_CLOSE, - SW_SERVER_HOOK_MANAGER_START, - SW_SERVER_HOOK_MANAGER_TIMER, - SW_SERVER_HOOK_PROCESS_TIMER, -}; - -struct swServerStats -{ - time_t start_time; - sw_atomic_t connection_num; - sw_atomic_t tasking_num; - sw_atomic_long_t accept_count; - sw_atomic_long_t close_count; - sw_atomic_long_t request_count; -}; - -struct swServerGS -{ - pid_t master_pid; - pid_t manager_pid; - - uint32_t session_round :24; - sw_atomic_t start; - sw_atomic_t shutdown; - - sw_atomic_t spinlock; - - swProcessPool task_workers; - swProcessPool event_workers; -}; - -struct swServer -{ - /** - * reactor thread/process num - */ - uint16_t reactor_num; - /** - * worker process num - */ - uint32_t worker_num; - /** - * The number of pipe per reactor maintenance - */ - uint16_t reactor_pipe_num; - - uint8_t factory_mode; - - uint8_t dgram_port_num; - - /** - * package dispatch mode - */ - uint8_t dispatch_mode; - - /** - * No idle work process is available. - */ - uint8_t scheduler_warning; - - int worker_uid; - int worker_groupid; - - /** - * max connection num - */ - uint32_t max_connection; - - /** - * worker process max request - */ - uint32_t max_request; - uint32_t max_request_grace; - - int udp_socket_ipv4; - int udp_socket_ipv6; - - uint32_t max_wait_time; - - /*----------------------------Reactor schedule--------------------------------*/ - uint16_t reactor_round_i; - uint16_t reactor_next_i; - uint16_t reactor_schedule_count; - - sw_atomic_t worker_round_id; - - /** - * run as a daemon process - */ - uchar daemonize :1; - /** - * have dgram socket - */ - uchar have_dgram_sock :1; - /** - * have stream socket - */ - uchar have_stream_sock :1; - /** - * open cpu affinity setting - */ - uchar open_cpu_affinity :1; - /** - * disable notice when use SW_DISPATCH_ROUND and SW_DISPATCH_QUEUE - */ - uchar disable_notify :1; - /** - * discard the timeout request - */ - uchar discard_timeout_request :1; - /** - * parse cookie header - */ - uchar http_parse_cookie :1; - /** - * parse x-www-form-urlencoded data - */ - uchar http_parse_post :1; - /** - * parse multipart/form-data files to match $_FILES - */ - uchar http_parse_files :1; - /** - * http content compression - */ - uchar http_compression :1; - /** - * RFC-7692 - */ - uchar websocket_compression :1; - /** - * handle static files - */ - uchar enable_static_handler :1; - /** - * show file list in the current directory - */ - uchar http_autoindex :1; - /** - * enable onConnect/onClose event when use dispatch_mode=1/3 - */ - uchar enable_unsafe_event :1; - /** - * waiting for worker onConnect callback function to return - */ - uchar enable_delay_receive :1; - /** - * reuse port - */ - uchar enable_reuse_port :1; - /** - * asynchronous reloading - */ - uchar reload_async :1; - /** - * use task object - */ - uchar task_use_object :1; - /** - * enable coroutine in task worker - */ - uchar task_enable_coroutine :1; - /** - * yield coroutine when the output buffer is full - */ - uchar send_yield :1; - /** - * enable coroutine - */ - uchar enable_coroutine :1; - /** - * disable multi-threads - */ - uchar single_thread :1; - /** - * server status - */ - uchar running :1; - - /** - * heartbeat check time - */ - uint16_t heartbeat_idle_time; - uint16_t heartbeat_check_interval; - - int *cpu_affinity_available; - int cpu_affinity_available_num; - - swPipeBuffer **pipe_buffers; - double send_timeout; - - uint16_t listen_port_num; - time_t reload_time; - time_t warning_time; - long timezone; - swTimer_node *master_timer; - swTimer_node *heartbeat_timer; - swTimer_node *enable_accept_timer; - - /* buffer output/input setting*/ - uint32_t output_buffer_size; - uint32_t input_buffer_size; - uint32_t max_queued_bytes; - - /** - * the master process and worker process communicate using unix socket dgram. - * ipc_max_size represents the maximum size of each datagram, - * which is obtained from the kernel send buffer of unix socket in swServer_set_ipc_max_size function. - */ - uint32_t ipc_max_size; - - void *ptr2; - void *private_data_3; - - swFactory factory; - std::vector *listen_list; - pthread_t heartbeat_pidt; - - /** - * task process - */ - uint32_t task_worker_num; - uint8_t task_ipc_mode; - uint32_t task_max_request; - uint32_t task_max_request_grace; - swPipe *task_notify; - swEventData *task_result; - - /** - * user process - */ - uint32_t user_worker_num; - std::vector *user_worker_list; - swHashMap *user_worker_map; - swWorker *user_workers; - - swReactorThread *reactor_threads; - swWorker *workers; - - swLock lock; - swChannel *message_box; - - swServerStats *stats; - swServerGS *gs; - - std::unordered_set *types; - std::unordered_set *locations; - std::vector *http_index_files; - -#ifdef HAVE_PTHREAD_BARRIER - pthread_barrier_t barrier; -#endif - - swConnection *connection_list; - swSession *session_list; - - /** - * temporary directory for HTTP uploaded file. - */ - char *upload_tmp_dir; - /** - * http compression level for gzip/br - */ -#ifdef SW_HAVE_COMPRESSION - uint8_t http_compression_level; -#endif - /** - * http static file directory - */ - char *document_root; - uint16_t document_root_len; - /** - * master process pid - */ - char *pid_file; - /** - * stream - */ - char *stream_socket_file; - swSocket *stream_socket; - swProtocol stream_protocol; - swSocket *last_stream_socket; - std::queue *buffer_pool; - -#ifdef SW_BUFFER_RECV_TIME - double last_receive_usec; -#endif - - int manager_alarm; - - /** - * message queue key - */ - uint64_t message_queue_key; - - void *hooks[SW_MAX_HOOK_TYPE]; - - void (*onStart)(swServer *serv); - void (*onManagerStart)(swServer *serv); - void (*onManagerStop)(swServer *serv); - void (*onShutdown)(swServer *serv); - void (*onPipeMessage)(swServer *, swEventData *); - void (*onWorkerStart)(swServer *serv, int worker_id); - void (*onBeforeReload)(swServer *serv); - void (*onAfterReload)(swServer *serv); - void (*onWorkerStop)(swServer *serv, int worker_id); - void (*onWorkerExit)(swServer *serv, int worker_id); - void (*onWorkerError)(swServer *serv, int worker_id, pid_t worker_pid, int exit_code, int signo); - void (*onUserWorkerStart)(swServer *serv, swWorker *worker); - /** - * Client - */ - int (*onReceive)(swServer *, swEventData *); - int (*onPacket)(swServer *, swEventData *); - void (*onClose)(swServer *serv, swDataHead *); - void (*onConnect)(swServer *serv, swDataHead *); - void (*onBufferFull)(swServer *serv, swDataHead *); - void (*onBufferEmpty)(swServer *serv, swDataHead *); - /** - * Task Worker - */ - int (*onTask)(swServer *serv, swEventData *data); - int (*onFinish)(swServer *serv, swEventData *data); - /** - * Server method - */ - int (*send)(swServer *serv, int session_id, void *data, uint32_t length); - int (*sendfile)(swServer *serv, int session_id, const char *file, uint32_t l_file, off_t offset, size_t length); - int (*sendwait)(swServer *serv, int session_id, void *data, uint32_t length); - int (*close)(swServer *serv, int session_id, int reset); - int (*notify)(swServer *serv, swConnection *conn, int event); - int (*feedback)(swServer *serv, int session_id, int event); - /** - * Chunk control - */ - void** (*create_buffers)(swServer *serv, uint buffer_num); - void* (*get_buffer)(swServer *serv, swDataHead *info); - size_t (*get_buffer_len)(swServer *serv, swDataHead *info); - void (*add_buffer_len)(swServer *serv, swDataHead *info, size_t len); - void (*move_buffer)(swServer *serv, swPipeBuffer *buffer); - size_t (*get_packet)(swServer *serv, swEventData *req, char **data_ptr); - /** - * Hook - */ - int (*dispatch_func)(swServer *, swConnection *, swSendData *); -}; - -typedef int (*swServer_dispatch_function)(swServer *, swConnection *, swSendData *); - -int swServer_master_onAccept(swReactor *reactor, swEvent *event); -void swServer_master_onTimer(swTimer *timer, swTimer_node *tnode); -int swServer_master_send(swServer *serv, swSendData *_send); - -int swServer_onFinish(swFactory *factory, swSendData *resp); -int swServer_onFinish2(swFactory *factory, swSendData *resp); - -void swServer_init(swServer *serv); -void swServer_signal_init(swServer *serv); -int swServer_start(swServer *serv); -swListenPort* swServer_add_port(swServer *serv, enum swSocket_type type, const char *host, int port); -void swServer_close_port(swServer *serv, enum swBool_type only_stream_port); -int swServer_add_worker(swServer *serv, swWorker *worker); -int swServer_add_systemd_socket(swServer *serv); -int swServer_add_hook(swServer *serv, enum swServer_hook_type type, swCallback func, int push_back); -void swServer_call_hook(swServer *serv, enum swServer_hook_type type, void *arg); -void swServer_clear_timer(swServer *serv); -int swServer_create(swServer *serv); - -static inline bool swServer_if_require_receive_callback(swServer *serv, swListenPort *port, bool isset) -{ -#ifdef SW_USE_OPENSSL - return (((swSocket_is_dgram(port->type) && port->ssl) || swSocket_is_stream(port->type)) && !isset); -#else - return (swSocket_is_stream(port->type) && !isset); -#endif -} - -static inline bool swServer_if_require_packet_callback(swServer *serv, swListenPort *port, bool isset) -{ -#ifdef SW_USE_OPENSSL - return (swSocket_is_dgram(port->type) && !port->ssl && !isset); -#else - return (swSocket_is_dgram(port->type) && !isset); -#endif -} - -#ifdef SW_SUPPORT_DTLS -swoole::dtls::Session* swServer_dtls_accept(swServer *serv, swListenPort *ls, swSocketAddress *sa); -#endif -int swServer_shutdown(swServer *serv); - -void swServer_set_ipc_max_size(swServer *serv); -int swServer_create_pipe_buffers(swServer *serv); - -static sw_inline swListenPort* swServer_get_port(swServer *serv, int fd) -{ - sw_atomic_t server_fd = serv->connection_list[fd].server_fd; - return (swListenPort*) serv->connection_list[server_fd].object; -} - -static sw_inline void swServer_lock(swServer *serv) -{ - if (serv->single_thread) - { - return; - } - serv->lock.lock(&serv->lock); -} - -static sw_inline void swServer_unlock(swServer *serv) -{ - if (serv->single_thread) - { - return; - } - serv->lock.unlock(&serv->lock); -} - -#define SW_MAX_SESSION_ID 0x1000000 - -static sw_inline int swEventData_is_dgram(uint8_t type) -{ - switch (type) - { - case SW_SERVER_EVENT_SNED_DGRAM: - return SW_TRUE; - default: - return SW_FALSE; - } -} - -static sw_inline int swEventData_is_stream(uint8_t type) -{ - switch (type) - { - case SW_SERVER_EVENT_SEND_DATA: - case SW_SERVER_EVENT_CONNECT: - case SW_SERVER_EVENT_CLOSE: - case SW_SERVER_EVENT_PAUSE_RECV: - case SW_SERVER_EVENT_RESUME_RECV: - case SW_SERVER_EVENT_BUFFER_FULL: - case SW_SERVER_EVENT_BUFFER_EMPTY: - return SW_TRUE; - default: - return SW_FALSE; - } -} - -swPipe * swServer_get_pipe_object(swServer *serv, int pipe_fd); -void swServer_store_pipe_fd(swServer *serv, swPipe *p); -void swServer_store_listen_socket(swServer *serv); - -int swServer_get_socket(swServer *serv, int port); -int swServer_worker_create(swServer *serv, swWorker *worker); -int swServer_worker_init(swServer *serv, swWorker *worker); -void swServer_worker_start(swServer *serv, swWorker *worker); - -int swServer_create_task_workers(swServer *serv); -int swServer_create_user_workers(swServer *serv); -void swServer_reopen_log_file(swServer *serv); - -void swTaskWorker_init(swServer *serv); -int swTaskWorker_onTask(swProcessPool *pool, swEventData *task); -int swTaskWorker_onFinish(swReactor *reactor, swEvent *event); -void swTaskWorker_onStart(swProcessPool *pool, int worker_id); -void swTaskWorker_onStop(swProcessPool *pool, int worker_id); -int swTaskWorker_large_pack(swEventData *task, const void *data, size_t data_len); -int swTaskWorker_finish(swServer *serv, const char *data, size_t data_len, int flags, swEventData *current_task); - -static sw_inline swString* swTaskWorker_large_unpack(swEventData *task_result) -{ - swPacket_task _pkg; - memcpy(&_pkg, task_result->data, sizeof(_pkg)); - - int tmp_file_fd = open(_pkg.tmpfile, O_RDONLY); - if (tmp_file_fd < 0) - { - swSysWarn("open(%s) failed", _pkg.tmpfile); - return NULL; - } - if (SwooleTG.buffer_stack->size < _pkg.length && swString_extend_align(SwooleTG.buffer_stack, _pkg.length) < 0) - { - close(tmp_file_fd); - return NULL; - } - if (swoole_sync_readfile(tmp_file_fd, SwooleTG.buffer_stack->str, _pkg.length) != _pkg.length) - { - close(tmp_file_fd); - return NULL; - } - close(tmp_file_fd); - if (!(swTask_type(task_result) & SW_TASK_PEEK)) - { - unlink(_pkg.tmpfile); - } - SwooleTG.buffer_stack->length = _pkg.length; - return SwooleTG.buffer_stack; -} - -#define SW_SERVER_MAX_FD_INDEX 0 //max connection socket -#define SW_SERVER_MIN_FD_INDEX 1 //min listen socket - -// connection_list[0] => the largest fd -#define swServer_set_maxfd(serv,maxfd) (serv->connection_list[SW_SERVER_MAX_FD_INDEX].fd=maxfd) -#define swServer_get_maxfd(serv) (serv->connection_list[SW_SERVER_MAX_FD_INDEX].fd) -// connection_list[1] => the smallest fd -#define swServer_set_minfd(serv,maxfd) (serv->connection_list[SW_SERVER_MIN_FD_INDEX].fd=maxfd) -#define swServer_get_minfd(serv) (serv->connection_list[SW_SERVER_MIN_FD_INDEX].fd) - -#define swServer_get_thread(serv, reactor_id) (&(serv->reactor_threads[reactor_id])) - -static sw_inline swConnection* swServer_connection_get(swServer *serv, int fd) -{ - if ((uint32_t) fd > serv->max_connection) - { - return NULL; - } - return &serv->connection_list[fd]; -} - -static sw_inline int swServer_connection_valid(swServer *serv, swConnection *conn) -{ - return (conn && conn->socket && conn->active == 1 && conn->closed == 0 - && conn->socket->fdtype == SW_FD_SESSION); -} - -static sw_inline swSession* swServer_get_session(swServer *serv, uint32_t session_id) -{ - return &serv->session_list[session_id % SW_SESSION_LIST_SIZE]; -} - -static sw_inline int swServer_get_fd(swServer *serv, uint32_t session_id) -{ - return serv->session_list[session_id % SW_SESSION_LIST_SIZE].fd; -} - -static sw_inline swWorker* swServer_get_worker(swServer *serv, uint16_t worker_id) -{ - //Event Worker - if (worker_id < serv->worker_num) - { - return &(serv->gs->event_workers.workers[worker_id]); - } - - //Task Worker - uint32_t task_worker_max = serv->task_worker_num + serv->worker_num; - if (worker_id < task_worker_max) - { - return &(serv->gs->task_workers.workers[worker_id - serv->worker_num]); - } - - //User Worker - uint32_t user_worker_max = task_worker_max + serv->user_worker_num; - if (worker_id < user_worker_max) - { - return &(serv->user_workers[worker_id - task_worker_max]); - } - - return NULL; -} - -static sw_inline int swServer_worker_schedule(swServer *serv, int fd, swSendData *data) -{ - uint32_t key = 0; - - if (serv->dispatch_func) - { - int id = serv->dispatch_func(serv, swServer_connection_get(serv, fd), data); - if (id != SW_DISPATCH_RESULT_USERFUNC_FALLBACK) - { - return id; - } - } - - //polling mode - if (serv->dispatch_mode == SW_DISPATCH_ROUND) - { - key = sw_atomic_fetch_add(&serv->worker_round_id, 1); - } - //Using the FD touch access to hash - else if (serv->dispatch_mode == SW_DISPATCH_FDMOD) - { - key = fd; - } - //Using the IP touch access to hash - else if (serv->dispatch_mode == SW_DISPATCH_IPMOD) - { - swConnection *conn = swServer_connection_get(serv, fd); - //UDP - if (conn == NULL) - { - key = fd; - } - //IPv4 - else if (conn->socket_type == SW_SOCK_TCP) - { - key = conn->info.addr.inet_v4.sin_addr.s_addr; - } - //IPv6 - else - { -#ifdef HAVE_KQUEUE - 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]; -#else - key = conn->info.addr.inet_v6.sin6_addr.s6_addr32[3]; -#endif - } - } - else if (serv->dispatch_mode == SW_DISPATCH_UIDMOD) - { - swConnection *conn = swServer_connection_get(serv, fd); - if (conn == NULL || conn->uid == 0) - { - key = fd; - } - else - { - key = conn->uid; - } - } - //Preemptive distribution - else - { - uint32_t i; - uint8_t found = 0; - for (i = 0; i < serv->worker_num + 1; i++) - { - key = sw_atomic_fetch_add(&serv->worker_round_id, 1) % serv->worker_num; - if (serv->workers[key].status == SW_WORKER_IDLE) - { - found = 1; - break; - } - } - if (sw_unlikely(found == 0)) - { - serv->scheduler_warning = 1; - } - swTraceLog(SW_TRACE_SERVER, "schedule=%d, round=%d", key, serv->worker_round_id); - return key; - } - return key % serv->worker_num; -} - -void swServer_worker_onStart(swServer *serv); -void swServer_worker_onStop(swServer *serv); - -int swServer_http_static_handler_hit(swServer *serv, swHttpRequest *request, swConnection *conn); -int swServer_http_static_handler_add_location(swServer *serv, const char *location, size_t length); -int swServer_http_static_handler_add_http_index_files(swServer *serv, const char *filename, size_t length); - -int swWorker_onTask(swFactory *factory, swEventData *task); -void swWorker_stop(swWorker *worker); - -static sw_inline swConnection *swWorker_get_connection(swServer *serv, int session_id) -{ - int real_fd = swServer_get_fd(serv, session_id); - swConnection *conn = swServer_connection_get(serv, real_fd); - return conn; -} - -static sw_inline swConnection *swServer_connection_verify_no_ssl(swServer *serv, uint32_t session_id) -{ - swSession *session = swServer_get_session(serv, session_id); - int fd = session->fd; - swConnection *conn = swServer_connection_get(serv, fd); - if (!conn || conn->active == 0) - { - return NULL; - } - if (session->id != session_id || conn->session_id != session_id) - { - return NULL; - } - return conn; -} - -static sw_inline swConnection *swServer_connection_verify(swServer *serv, int session_id) -{ - swConnection *conn = swServer_connection_verify_no_ssl(serv, 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 NULL; - } -#endif - return conn; -} - -static sw_inline int swServer_connection_incoming(swServer *serv, swReactor *reactor, swConnection *conn) -{ -#ifdef SW_USE_OPENSSL - if (conn->socket->ssl) - { - return reactor->add(reactor, conn->socket, SW_EVENT_READ); - } -#endif - //delay receive, wait resume command - if (!serv->enable_delay_receive) - { - if (reactor->add(reactor, conn->socket, SW_EVENT_READ) < 0) - { - return SW_ERR; - } - } - //notify worker process - if (serv->onConnect) - { - if (serv->notify(serv, conn, SW_SERVER_EVENT_CONNECT) < 0) - { - return SW_ERR; - } - } - - return SW_OK; -} - -void swServer_connection_each(swServer *serv, void (*callback)(swConnection *conn)); - -/** - * reactor_id: The fd in which the reactor. - */ -static sw_inline swSocket* swServer_get_send_pipe(swServer *serv, int session_id, int reactor_id) -{ - int pipe_index = session_id % serv->reactor_pipe_num; - /** - * pipe_worker_id: The pipe in which worker. - */ - int pipe_worker_id = reactor_id + (pipe_index * serv->reactor_num); - swWorker *worker = swServer_get_worker(serv, pipe_worker_id); - return worker->pipe_worker; -} - -static sw_inline uint8_t swServer_support_unsafe_events(swServer *serv) -{ - if (serv->dispatch_mode != SW_DISPATCH_ROUND && serv->dispatch_mode != SW_DISPATCH_QUEUE - && serv->dispatch_mode != SW_DISPATCH_STREAM) - { - return 1; - } - else - { - return serv->enable_unsafe_event; - } -} - -static sw_inline uint8_t swServer_dispatch_mode_is_mod(swServer *serv) -{ - return serv->dispatch_mode == SW_DISPATCH_FDMOD || serv->dispatch_mode == SW_DISPATCH_IPMOD; -} - -static sw_inline swServer* sw_server() -{ - return (swServer *) SwooleG.serv; -} - -#define swServer_support_send_yield swServer_dispatch_mode_is_mod - -//------------------------------------Listen Port------------------------------------------- -void swPort_init(swListenPort *port); -void swPort_free(swListenPort *port); -int swPort_listen(swListenPort *ls); -void swPort_set_protocol(swServer *serv, swListenPort *ls); -int swPort_set_address(swListenPort *ls, int sock); -#ifdef SW_USE_OPENSSL -int swPort_enable_ssl_encrypt(swListenPort *ls); -#endif -void swPort_clear_protocol(swListenPort *ls); -//------------------------------------Worker Process------------------------------------------- -void swWorker_onStart(swServer *serv); -void swWorker_onStop(swServer *serv); -int swWorker_loop(swServer *serv, swWorker *worker); -void swWorker_clean_pipe_buffer(swServer *serv); -int swWorker_send2reactor(swServer *serv, swEventData *ev_data, size_t sendn, int session_id); -int swWorker_send2worker(swWorker *dst_worker, const void *buf, int n, int flag); -void swWorker_signal_handler(int signo); -void swWorker_signal_init(void); - -int swReactorThread_create(swServer *serv); -int swReactorThread_start(swServer *serv); -void swReactorThread_set_protocol(swServer *serv, swReactor *reactor); -void swReactorThread_join(swServer *serv); -void swReactorThread_free(swServer *serv); -int swReactorThread_close(swReactor *reactor, swSocket *_socket); -int swReactorThread_dispatch(swProtocol *proto, swSocket *_socket, char *data, uint32_t length); -int swReactorThread_send2worker(swServer *serv, swWorker *worker, void *data, size_t len); - -int swReactorProcess_create(swServer *serv); -int swReactorProcess_start(swServer *serv); -void swReactorProcess_free(swServer *serv); - -int swManager_start(swServer *serv); -pid_t swManager_spawn_worker(swServer *serv, swWorker *worker); -pid_t swManager_spawn_user_worker(swServer *serv, swWorker* worker); -pid_t swManager_spawn_task_worker(swServer *serv, swWorker* worker); -pid_t swManager_spawn_worker_by_type(swServer *serv, swWorker *worker, int worker_type); -int swManager_wait_other_worker(swProcessPool *pool, pid_t pid, int status); -void swManager_kill_workers(swServer *serv); -void swManager_kill_task_workers(swServer *serv); -void swManager_kill_user_workers(swServer *serv); diff --git a/include/sha1.h b/include/sha1.h deleted file mode 100644 index 51f300f2d77..00000000000 --- a/include/sha1.h +++ /dev/null @@ -1,63 +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: Tianfeng Han | - +----------------------------------------------------------------------+ -*/ - - -#ifndef _WEBSOCKET_SHA1_H__ -#define _WEBSOCKET_SHA1_H__ - -/* Define this if your machine is LITTLE_ENDIAN, otherwise #undef it: */ -#ifdef WORDS_BIGENDIAN -# undef LITTLE_ENDIAN -#else -# ifndef LITTLE_ENDIAN -# define LITTLE_ENDIAN -# endif -#endif - -/* Make sure you define these types for your architecture: */ -typedef unsigned int sha1_quadbyte; /* 4 byte type */ -typedef unsigned char sha1_byte; /* single byte type */ - -/* - * Be sure to get the above definitions right. For instance, on my - * x86 based FreeBSD box, I define LITTLE_ENDIAN and use the type - * "unsigned long" for the quadbyte. On FreeBSD on the Alpha, however, - * while I still use LITTLE_ENDIAN, I must define the quadbyte type - * as "unsigned int" instead. - */ - -#define SHA1_BLOCK_LENGTH 64 -#define SHA1_DIGEST_LENGTH 20 - -/* The SHA1 structure: */ -typedef struct _SHA_CTX { - sha1_quadbyte state[5]; - sha1_quadbyte count[2]; - sha1_byte buffer[SHA1_BLOCK_LENGTH]; -} SHA_CTX; - -#ifndef NOPROTO -void swSha1_init(SHA_CTX *context); -void swSha1_update(SHA_CTX *context, sha1_byte *data, unsigned int len); -void swSha1_final(sha1_byte digest[SHA1_DIGEST_LENGTH], - SHA_CTX* context); -#else -void swSha1_init(); -void swSha1_update(); -void swSha1_final(); -#endif - -#endif diff --git a/include/socket_hook.h b/include/socket_hook.h deleted file mode 100644 index 4bc52b99c6a..00000000000 --- a/include/socket_hook.h +++ /dev/null @@ -1,47 +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 SW_SOCKET_HOOK_H_ -#define SW_SOCKET_HOOK_H_ - -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#include "coroutine_c_api.h" - -#define socket(domain, type, protocol) swoole_coroutine_socket(domain, type, protocol) -#define send(sockfd, buf, len, flags) swoole_coroutine_send(sockfd, buf, len, flags) -#define read(sockfd, buf, len) swoole_coroutine_read(sockfd, buf, len) -#define write(sockfd, buf, len) swoole_coroutine_write(sockfd, buf, len) -#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) -#define poll(fds, nfds, timeout) swoole_coroutine_poll(fds, nfds, timeout) -#define sendmsg(sockfd, msg, flags) swoole_coroutine_sendmsg(sockfd, msg, flags) -#define recvmsg(sockfd, msg, flags) swoole_coroutine_recvmsg(sockfd, msg, flags) - - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/include/socks5.h b/include/socks5.h deleted file mode 100644 index f83d89d9b20..00000000000 --- a/include/socks5.h +++ /dev/null @@ -1,79 +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 SW_SOCKS5_H_ -#define SW_SOCKS5_H_ - -#ifdef __cplusplus -extern "C" -{ -#endif - -#include "client.h" - -#define SW_SOCKS5_VERSION_CODE 0x05 - -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, -}; - -typedef struct _swSocks5 -{ - const char *host; - int port; - - uint8_t state; - uint8_t version; - uint8_t method; - uint8_t dns_tunnel; - - const char *username; - const char *password; - uint16_t l_username; - uint16_t l_password; - - const char *target_host; - int target_port; - uint16_t l_target_host; - - char buf[600]; -} swSocks5; - -static sw_inline void swSocks5_pack(char *buf, int method) -{ - buf[0] = SW_SOCKS5_VERSION_CODE; - buf[1] = 0x01; - buf[2] = method; -} - -const char* swSocks5_strerror(int code); -int swSocks5_connect(swClient *cli, char *recv_data, int length); - -#ifdef __cplusplus -} -#endif - -#endif /* SW_SOCKS5_H_ */ diff --git a/include/ssl.h b/include/ssl.h deleted file mode 100644 index 7303666d981..00000000000 --- a/include/ssl.h +++ /dev/null @@ -1,141 +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: Tianfeng Han | - +----------------------------------------------------------------------+ -*/ - -#ifndef SW_CONNECTION_H_ -#define SW_CONNECTION_H_ - -#include "swoole.h" - -SW_EXTERN_C_BEGIN - -#ifdef SW_USE_OPENSSL - -#include -#include -#include -#include -#include - -enum swSSL_create_flag -{ - SW_SSL_SERVER = 1, - SW_SSL_CLIENT = 2, -}; - -typedef struct _swSSL_option -{ - char *cert_file; - char *key_file; - char *passphrase; - char *client_cert_file; -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - uchar disable_tls_host_name :1; - char *tls_host_name; -#endif - char *cafile; - char *capath; - uint8_t verify_depth; - uint8_t method; -#ifdef SW_SUPPORT_DTLS - uint8_t dtls; -#endif - uchar disable_compress :1; - uchar verify_peer :1; - uchar allow_self_signed :1; - uint32_t disable_protocols; -} swSSL_option; - -enum swSSL_state -{ - SW_SSL_STATE_HANDSHAKE = 0, - SW_SSL_STATE_READY = 1, - SW_SSL_STATE_WAIT_STREAM = 2, -}; - -enum swSSL_version -{ - SW_SSL_SSLv2 = 0x0002, - SW_SSL_SSLv3 = 0x0004, - SW_SSL_TLSv1 = 0x0008, - SW_SSL_TLSv1_1 = 0x0010, - SW_SSL_TLSv1_2 = 0x0020, -}; - -enum swSSL_method -{ - SW_SSLv23_METHOD = 0, - SW_SSLv3_METHOD, - SW_SSLv3_SERVER_METHOD, - SW_SSLv3_CLIENT_METHOD, - SW_SSLv23_SERVER_METHOD, - SW_SSLv23_CLIENT_METHOD, - SW_TLSv1_METHOD, - SW_TLSv1_SERVER_METHOD, - SW_TLSv1_CLIENT_METHOD, -#ifdef TLS1_1_VERSION - SW_TLSv1_1_METHOD, - SW_TLSv1_1_SERVER_METHOD, - SW_TLSv1_1_CLIENT_METHOD, -#endif -#ifdef TLS1_2_VERSION - SW_TLSv1_2_METHOD, - SW_TLSv1_2_SERVER_METHOD, - SW_TLSv1_2_CLIENT_METHOD, -#endif -#ifdef SW_SUPPORT_DTLS - SW_DTLS_CLIENT_METHOD, - SW_DTLS_SERVER_METHOD, -#endif -}; - -typedef struct -{ - uchar http :1; - uchar http_v2 :1; - uchar prefer_server_ciphers :1; - uchar session_tickets :1; - uchar stapling :1; - uchar stapling_verify :1; - char *ciphers; - char *ecdh_curve; - char *session_cache; - char *dhparam; -} swSSL_config; - -void swSSL_init(void); -void swSSL_init_thread_safety(); -int swSSL_server_set_cipher(SSL_CTX* ssl_context, swSSL_config *cfg); -void swSSL_server_http_advise(SSL_CTX* ssl_context, swSSL_config *cfg); -SSL_CTX* swSSL_get_context(swSSL_option *option); -void swSSL_free_context(SSL_CTX* ssl_context); -int swSSL_create(swSocket *conn, SSL_CTX* ssl_context, int flags); -int swSSL_set_client_certificate(SSL_CTX *ctx, char *cert_file, int depth); -int swSSL_set_capath(swSSL_option *cfg, SSL_CTX *ctx); -int swSSL_check_host(swSocket *conn, char *tls_host_name); -int swSSL_get_peer_cert(SSL *ssl, char *buffer, size_t length); -const char* swSSL_get_error(); -int swSSL_verify(swSocket *conn, int allow_self_signed); -enum swReturn_code swSSL_accept(swSocket *conn); -int swSSL_connect(swSocket *conn); -void swSSL_close(swSocket *conn); -ssize_t swSSL_recv(swSocket *conn, void *__buf, size_t __n); -ssize_t swSSL_send(swSocket *conn, const void *__buf, size_t __n); -int swSSL_sendfile(swSocket *conn, int fd, off_t *offset, size_t size); -#endif - -SW_EXTERN_C_END - -#endif /* SW_CONNECTION_H_ */ diff --git a/include/static_handler.h b/include/static_handler.h deleted file mode 100644 index 1c09fe634ca..00000000000 --- a/include/static_handler.h +++ /dev/null @@ -1,108 +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 | - +----------------------------------------------------------------------+ -*/ - -#pragma once - -#include "server.h" -#include "http.h" -#include "mime_type.h" - -#include -#include - -namespace swoole { namespace http { - -class StaticHandler -{ -private: - swServer *serv; - std::string request_url; - std::string dir_path; - struct - { - off_t offset; - size_t length; - char filename[PATH_MAX]; - } task; - - size_t l_filename; - struct stat file_stat; - bool last; - -public: - int status_code; - StaticHandler(swServer *_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 = ""; - } - bool hit(); - 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); - - std::string get_date(); - - inline time_t get_file_mtime() - { -#ifdef __MACH__ - return file_stat.st_mtimespec.tv_sec; -#else - return file_stat.st_mtim.tv_sec; -#endif - } - - std::string get_date_last_modified(); - - inline const char* get_filename() - { - return task.filename; - } - - inline 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); - } - - inline const size_t get_filesize() - { - return file_stat.st_size; - } - - inline const swSendFile_request* get_task() - { - return (const swSendFile_request*) &task; - } - - inline const bool is_dir() - { - return S_ISDIR(file_stat.st_mode); - } -}; - -};}; diff --git a/include/swoole.h b/include/swoole.h index e6facb100b2..4f19d095cff 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -10,99 +10,63 @@ | to obtain it through the world-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 | +----------------------------------------------------------------------+ */ -#ifndef SWOOLE_H_ -#define SWOOLE_H_ +#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 #ifdef __cplusplus #define SW_EXTERN_C_BEGIN extern "C" { -#define SW_EXTERN_C_END } +#define SW_EXTERN_C_END } #else #define SW_EXTERN_C_BEGIN #define SW_EXTERN_C_END #endif -SW_EXTERN_C_BEGIN - #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif +#ifndef _PTHREAD_PSHARED +#define _PTHREAD_PSHARED +#endif + /*--- C standard library ---*/ #include #include #include -#include -#include -#include +#include #include #include #include #include -#include -#include -#include - -#include +#include #include +#include /* sched_yield() */ #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include - -#if defined(HAVE_CPU_AFFINITY) -#ifdef __FreeBSD__ -#include -#include -#include -typedef cpuset_t cpu_set_t; -#else -#include -#endif -#endif - -#ifdef __MACH__ -#include -#include -#include +#include -#define ORWL_NANO (+1.0E-9) -#define ORWL_GIGA UINT64_C(1000000000) +#include +#include +#include +#include +#include -static double orwl_timebase = 0.0; -static uint64_t orwl_timestart = 0; -#ifndef HAVE_CLOCK_GETTIME -int clock_gettime(clock_id_t which_clock, struct timespec *t); -#endif -#endif +typedef unsigned long ulong_t; -#ifndef ulong -#define ulong unsigned long +#ifndef PRId64 +#define PRId64 "lld" #endif -typedef unsigned long ulong_t; #ifndef PRIu64 #define PRIu64 "llu" @@ -125,237 +89,353 @@ typedef unsigned long ulong_t; #endif #if defined(__GNUC__) && __GNUC__ >= 4 -#define SW_API __attribute__ ((visibility("default"))) +#define SW_API __attribute__((visibility("default"))) #else #define SW_API #endif -#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS) -#define MAP_ANONYMOUS MAP_ANON -#endif - -#if defined(MAP_HUGETLB) || defined(MAP_ALIGNED_SUPER) -#define MAP_HUGE_PAGE 1 -#endif - -#ifndef SOCK_NONBLOCK -#define SOCK_NONBLOCK O_NONBLOCK -#endif - -#ifndef CLOCK_REALTIME -#define CLOCK_REALTIME 0 -#endif - #if !defined(__GNUC__) || __GNUC__ < 3 #define __builtin_expect(x, expected_value) (x) #endif -#define sw_likely(x) __builtin_expect(!!(x), 1) -#define sw_unlikely(x) __builtin_expect(!!(x), 0) - -#define SW_START_LINE "-------------------------START----------------------------" -#define SW_END_LINE "--------------------------END-----------------------------" -#define SW_ECHO_RED "\e[31m%s\e[0m" -#define SW_ECHO_GREEN "\e[32m%s\e[0m" -#define SW_ECHO_YELLOW "\e[33m%s\e[0m" -#define SW_ECHO_BLUE "\e[34m%s\e[0m" -#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_COLOR_RED 1 -#define SW_COLOR_GREEN 2 -#define SW_COLOR_YELLOW 3 -#define SW_COLOR_BLUE 4 -#define SW_COLOR_MAGENTA 5 -#define SW_COLOR_CYAN 6 -#define SW_COLOR_WHITE 7 - -#define SW_SPACE ' ' -#define SW_CRLF "\r\n" -#define SW_CRLF_LEN 2 -#define SW_ASCII_CODE_0 64 -#define SW_ASCII_CODE_Z 106 +#define sw_likely(x) __builtin_expect(!!(x), 1) +#define sw_unlikely(x) __builtin_expect(!!(x), 0) + +#define SW_START_LINE "-------------------------START----------------------------" +#define SW_END_LINE "--------------------------END-----------------------------" +#define SW_ECHO_RED "\e[31m%s\e[0m" +#define SW_ECHO_GREEN "\e[32m%s\e[0m" +#define SW_ECHO_YELLOW "\e[33m%s\e[0m" +#define SW_ECHO_BLUE "\e[34m%s\e[0m" +#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 +#define SW_COLOR_BLUE 4 +#define SW_COLOR_MAGENTA 5 +#define SW_COLOR_CYAN 6 +#define SW_COLOR_WHITE 7 + +#define SW_SPACE ' ' +#define SW_CRLF "\r\n" +#define SW_CRLF_LEN 2 +#define SW_ASCII_CODE_0 64 +#define SW_ASCII_CODE_Z 106 /*----------------------------------------------------------------------------*/ #include "swoole_config.h" #include "swoole_version.h" -#include "atomic.h" -#include "buffer.h" -#include "hashmap.h" -#include "heap.h" -#include "ring_queue.h" -#include "error.h" +#include "swoole_log.h" +#include "swoole_atomic.h" +#include "swoole_error.h" -#define SW_MAX(A, B) ((A) > (B) ? (A) : (B)) -#define SW_MIN(A, B) ((A) < (B) ? (A) : (B)) +#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) +#define MAX(A, B) SW_MAX(A, B) #endif #ifndef MIN -#define MIN(A, B) SW_MIN(A, B) +#define MIN(A, B) SW_MIN(A, B) #endif +#define SW_NUM_BILLION (1000 * 1000 * 1000) +#define SW_NUM_MILLION (1000 * 1000) + #ifdef SW_DEBUG -#define SW_ASSERT(e) assert(e) -#define SW_ASSERT_1BYTE(v) do { \ - size_t i = 0, n = 0; \ - for (; i < sizeof(v); i++) { \ - n += ((v >> i) & 1) ? 1 : 0; \ - } \ - assert(n == 1); \ -} while (0) +#define SW_ASSERT(e) assert(e) +#define SW_ASSERT_1BYTE(v) \ + do { \ + size_t i = 0, n = 0; \ + for (; i < sizeof(v); i++) { \ + n += ((v >> i) & 1) ? 1 : 0; \ + } \ + assert(n == 1); \ + } while (0) #else #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); +void sw_free(void *ptr); +void *sw_calloc(size_t nmemb, size_t size); +void *sw_realloc(void *ptr, size_t size); // Evaluates to the number of elements in 'array' #define SW_ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) -#define SW_DEFAULT_ALIGNMENT sizeof(unsigned long) -#define SW_MEM_ALIGNED_SIZE(size) \ - SW_MEM_ALIGNED_SIZE_EX(size, SW_DEFAULT_ALIGNMENT) -#define SW_MEM_ALIGNED_SIZE_EX(size, alignment) \ - (((size) + ((alignment) - 1LL)) & ~((alignment) - 1LL)) - -#ifdef SW_USE_EMALLOC -#define sw_malloc emalloc -#define sw_free efree -#define sw_calloc ecalloc -#define sw_realloc erealloc -#else -#ifdef SW_USE_JEMALLOC -#include -#define sw_malloc je_malloc -#define sw_free je_free -#define sw_calloc je_calloc -#define sw_realloc je_realloc -#else -#define sw_malloc malloc -#define sw_free free -#define sw_calloc calloc -#define sw_realloc realloc -#endif -#endif - -static sw_inline int sw_mem_equal(const void *v1, size_t s1, const void *v2, size_t s2) -{ - return s1 == s2 && memcmp(v1, v2, s2) == 0; -} +#define SW_DEFAULT_ALIGNMENT sizeof(unsigned long) +#define SW_MEM_ALIGNED_SIZE(size) SW_MEM_ALIGNED_SIZE_EX(size, SW_DEFAULT_ALIGNMENT) +#define SW_MEM_ALIGNED_SIZE_EX(size, alignment) (((size) + ((alignment) -1LL)) & ~((alignment) -1LL)) + +/*-------------------------------Declare Struct--------------------------------*/ +namespace swoole { +class MemoryPool; +class Reactor; +class String; +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; +typedef swoole::String swString; +typedef swoole::Timer swTimer; +typedef swoole::network::Socket swSocket; +typedef swoole::Protocol swProtocol; +typedef swoole::EventData swEventData; +typedef swoole::DataHead swDataHead; +typedef swoole::Event swEvent; /*----------------------------------String-------------------------------------*/ -#define SW_STRS(s) s, sizeof(s) -#define SW_STRL(s) s, sizeof(s)-1 +#define SW_STRS(s) s, sizeof(s) +#define SW_STRL(s) s, sizeof(s) - 1 + +#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)) + +#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 -#define sw_strndup swoole_strndup +#define sw_strdup swoole_strdup +#define sw_strndup swoole_strndup #else -#define sw_strdup strdup -#define sw_strndup strndup +#define sw_strdup strdup +#define sw_strndup strndup #endif -#define SW_Z_BEST_SPEED 1 - /** always return less than size, zero termination */ -size_t sw_snprintf(char *buf, size_t size, const char *format, ...); +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); -static sw_inline char* swoole_strdup(const char *s) -{ +#define sw_memset_zero(s, n) memset(s, '\0', n) + +static sw_inline int sw_mem_equal(const void *v1, size_t s1, const void *v2, size_t s2) { + return s1 == s2 && memcmp(v1, v2, s2) == 0; +} + +static inline size_t swoole_strlcpy(char *dest, const char *src, size_t size) { + const size_t len = strlen(src); + if (size != 0) { + const size_t n = std::min(len, size - 1); + memcpy(dest, src, n); + dest[n] = '\0'; + } + return len; +} + +static inline char *swoole_strdup(const char *s) { size_t l = strlen(s) + 1; char *p = (char *) sw_malloc(l); - if (sw_likely(p)) - { + if (sw_likely(p)) { memcpy(p, s, l); } return p; } -static sw_inline char* swoole_strndup(const char *s, size_t n) -{ +static inline char *swoole_strndup(const char *s, size_t n) { char *p = (char *) sw_malloc(n + 1); - if (sw_likely(p)) - { + if (sw_likely(p)) { strncpy(p, s, n)[n] = '\0'; } return p; } /* string equal */ -static sw_inline unsigned int swoole_streq(const char *str1, size_t len1, const char *str2, size_t len2) -{ +static inline unsigned int swoole_streq(const char *str1, size_t len1, const char *str2, size_t len2) { return (len1 == len2) && (strncmp(str1, str2, len1) == 0); } -static sw_inline unsigned int swoole_strcaseeq(const char *str1, size_t len1, const char *str2, size_t len2) -{ +static inline unsigned int swoole_strcaseeq(const char *str1, size_t len1, const char *str2, size_t len2) { return (len1 == len2) && (strncasecmp(str1, str2, len1) == 0); } -static sw_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 sw_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); } -#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)) +static inline const char *swoole_strnstr(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 == memcmp(haystack, needle, needle_length))) { + return haystack; + } + 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, + uint32_t needle_length) { + assert(needle_length > 0); + const char *pos; + + pos = swoole_strnstr(haystack, haystack_length, needle, needle_length); + return pos == NULL ? -1 : pos - haystack; +} + +static inline ssize_t swoole_strrnpos(const char *haystack, const char *needle, uint32_t length) { + uint32_t needle_length = strlen(needle); + assert(needle_length > 0); + uint32_t i; + haystack += (length - needle_length); + + for (i = length - needle_length; i > 0; i--) { + if ((haystack[0] == needle[0]) && (0 == memcmp(haystack, needle, needle_length))) { + return i; + } + haystack--; + } + + return -1; +} + +static inline void swoole_strtolower(char *str, int length) { + char *c, *e; + + c = str; + e = c + length; -/* 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)) + while (c < e) { + *c = tolower(*c); + c++; + } +} /*--------------------------------Constants------------------------------------*/ -enum swResult_code -{ +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 -{ - SW_FD_SESSION, //server stream session - SW_FD_STREAM_SERVER, //server stream port - SW_FD_DGRAM_SERVER, //server dgram port +enum swFdType { + SW_FD_SESSION, // server stream session + SW_FD_STREAM_SERVER, // server stream port + SW_FD_DGRAM_SERVER, // server dgram port SW_FD_PIPE, SW_FD_STREAM, SW_FD_AIO, /** * 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 */ @@ -364,2284 +444,482 @@ enum swFd_type SW_FD_DGRAM_CLIENT, }; -enum swBool_type -{ - SW_TRUE = 1, - SW_FALSE = 0, +enum swSocketFlag { + SW_SOCK_NONBLOCK = 1 << 2, + SW_SOCK_CLOEXEC = 1 << 3, + SW_SOCK_SSL = (1u << 9), }; -enum swEvent_type -{ - 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, +enum swSocketType { + SW_SOCK_TCP = 1, + SW_SOCK_UDP = 2, + SW_SOCK_TCP6 = 3, + SW_SOCK_UDP6 = 4, + SW_SOCK_UNIX_STREAM = 5, // unix sock stream + SW_SOCK_UNIX_DGRAM = 6, // unix sock dgram + SW_SOCK_RAW = 7, }; -enum swGlobal_hook_type -{ - SW_GLOBAL_HOOK_BEFORE_SERVER_START, - SW_GLOBAL_HOOK_BEFORE_CLIENT_START, - SW_GLOBAL_HOOK_BEFORE_WORKER_START, - SW_GLOBAL_HOOK_ON_CORO_START, - SW_GLOBAL_HOOK_ON_CORO_STOP, - SW_GLOBAL_HOOK_ON_REACTOR_CREATE, +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, }; -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 swServer_mode -{ - SW_MODE_BASE = 1, - SW_MODE_PROCESS = 2, -}; -//------------------------------------------------------------------------------- -enum swSocket_type -{ - 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 -}; -#define SW_SOCK_SSL (1u << 9) -//------------------------------------------------------------------------------- -enum swLog_level -{ - SW_LOG_DEBUG = 0, - SW_LOG_TRACE, - SW_LOG_INFO, - SW_LOG_NOTICE, - SW_LOG_WARNING, - SW_LOG_ERROR, - SW_LOG_NONE, -}; -//------------------------------------------------------------------------------- -enum swWorker_status -{ - SW_WORKER_BUSY = 1, - SW_WORKER_IDLE = 2, +enum swTraverseOperation { + SW_TRAVERSE_KEEP = 0, + SW_TRAVERSE_REMOVE = 1, + SW_TRAVERSE_STOP = 2, }; + //------------------------------------------------------------------------------- +#define sw_yield() sched_yield() -#define swInfo(str,...) \ - if (SW_LOG_INFO >= SwooleG.log_level) { \ - size_t _sw_error_len = sw_snprintf(sw_error,SW_ERROR_MSG_SIZE,str,##__VA_ARGS__); \ - SwooleG.write_log(SW_LOG_INFO, sw_error, _sw_error_len); \ - } +//------------------------------Base-------------------------------- +#ifndef uchar +typedef unsigned char uchar; +#endif -#define swNotice(str,...) \ - if (SW_LOG_NOTICE >= SwooleG.log_level) { \ - size_t _sw_error_len = sw_snprintf(sw_error,SW_ERROR_MSG_SIZE,str,##__VA_ARGS__); \ - SwooleG.write_log(SW_LOG_NOTICE, sw_error, _sw_error_len); \ +#define swoole_tolower(c) (uchar)((c >= 'A' && c <= 'Z') ? (c | 0x20) : c) +#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) { + while (p < last) { + if (*p == c) { + return p; + } + p++; } + return NULL; +} -#define swSysNotice(str,...) \ - do{ \ - SwooleG.error = errno; \ - if (SW_LOG_ERROR >= SwooleG.log_level) { \ - size_t _sw_error_len = sw_snprintf(sw_error,SW_ERROR_MSG_SIZE,"%s(:%d): " str ", Error: %s[%d]",__func__,__LINE__,##__VA_ARGS__,swoole_strerror(errno),errno); \ - SwooleG.write_log(SW_LOG_NOTICE, sw_error, _sw_error_len); \ - } \ - } while(0) - -#define swWarn(str,...) \ - do{ \ - if (SW_LOG_WARNING >= SwooleG.log_level) { \ - size_t _sw_error_len = sw_snprintf(sw_error,SW_ERROR_MSG_SIZE,"%s: " str,__func__,##__VA_ARGS__); \ - SwooleG.write_log(SW_LOG_WARNING, sw_error, _sw_error_len); \ - } \ - } while(0) - -#define swSysWarn(str,...) \ - do{ \ - SwooleG.error = errno; \ - if (SW_LOG_ERROR >= SwooleG.log_level) { \ - size_t _sw_error_len = sw_snprintf(sw_error,SW_ERROR_MSG_SIZE,"%s(:%d): " str ", Error: %s[%d]",__func__,__LINE__,##__VA_ARGS__,swoole_strerror(errno),errno); \ - SwooleG.write_log(SW_LOG_WARNING, sw_error, _sw_error_len); \ - } \ - } while(0) - -#define swError(str,...) \ - do{ \ - size_t _sw_error_len = sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, str, ##__VA_ARGS__); \ - SwooleG.write_log(SW_LOG_ERROR, sw_error, _sw_error_len); \ - exit(1); \ - } while(0) - -#define swSysError(str,...) \ - do{ \ - size_t _sw_error_len = sw_snprintf(sw_error,SW_ERROR_MSG_SIZE,"%s(:%d): " str ", Error: %s[%d]",__func__,__LINE__,##__VA_ARGS__,swoole_strerror(errno),errno); \ - SwooleG.write_log(SW_LOG_ERROR, sw_error, _sw_error_len); \ - exit(1); \ - } while(0) - -#define swFatalError(code, str,...) \ - do { \ - SwooleG.fatal_error(code, str, ##__VA_ARGS__); \ - abort(); \ - } while (0) +static sw_inline size_t swoole_size_align(size_t size, int pagesize) { + return size + (pagesize - (size % pagesize)); +} -#define swoole_error_log(level, __errno, str, ...) \ - do{ \ - SwooleG.error = __errno; \ - if (level >= SwooleG.log_level){ \ - size_t _sw_error_len = sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, "%s (ERRNO %d): " str,__func__,__errno,##__VA_ARGS__); \ - SwooleG.write_log(level, sw_error, _sw_error_len); \ - } \ - } while(0) +//------------------------------Base-------------------------------- +enum swEventDataFlag { + SW_EVENT_DATA_NORMAL, + SW_EVENT_DATA_PTR = 1u << 1, + SW_EVENT_DATA_CHUNK = 1u << 2, + SW_EVENT_DATA_BEGIN = 1u << 3, + SW_EVENT_DATA_END = 1u << 4, + SW_EVENT_DATA_OBJ_PTR = 1u << 5, + SW_EVENT_DATA_POP_PTR = 1u << 6, +}; -#ifdef SW_DEBUG -#define swDebug(str,...) \ - if (SW_LOG_DEBUG >= SwooleG.log_level) { \ - size_t _sw_error_len = sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, "%s(:%d): " str, __func__, __LINE__, ##__VA_ARGS__); \ - SwooleG.write_log(SW_LOG_DEBUG, sw_error, _sw_error_len); \ - } +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, +}; -#define swHexDump(data, length) \ - do { \ - const char *__data = (data); \ - size_t __length = (length); \ - swDebug("+----------+------------+-----------+-----------+------------+------------------+"); \ - for (size_t of = 0; of < __length; of += 16) \ - { \ - char hex[16 * 3 + 1]; \ - char str[16 + 1]; \ - size_t i, hof = 0, sof = 0; \ - for (i = of; i < of + 16 && i < __length; i++) \ - { \ - 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); \ - } \ - swDebug("+----------+------------+-----------+-----------+------------+------------------+"); \ - } while (0) -#else -#define swDebug(str,...) -#define swHexDump(data, length) -#endif +enum swDNSLookupFlag { + SW_DNS_LOOKUP_RANDOM = (1u << 11), +}; -enum swTrace_type -{ - /** - * Server - */ - SW_TRACE_SERVER = 1u << 1, - SW_TRACE_CLIENT = 1u << 2, - SW_TRACE_BUFFER = 1u << 3, - SW_TRACE_CONN = 1u << 4, - SW_TRACE_EVENT = 1u << 5, - SW_TRACE_WORKER = 1u << 6, - SW_TRACE_MEMORY = 1u << 7, - SW_TRACE_REACTOR = 1u << 8, - SW_TRACE_PHP = 1u << 9, - SW_TRACE_HTTP = 1u << 10, - SW_TRACE_HTTP2 = 1u << 11, - SW_TRACE_EOF_PROTOCOL = 1u << 12, - SW_TRACE_LENGTH_PROTOCOL = 1u << 13, - SW_TRACE_CLOSE = 1u << 14, - SW_TRACE_WEBSOCEKT = 1u << 15, - /** - * Client - */ - SW_TRACE_REDIS_CLIENT = 1u << 16, - SW_TRACE_MYSQL_CLIENT = 1u << 17, - SW_TRACE_HTTP_CLIENT = 1u << 18, - SW_TRACE_AIO = 1u << 19, - SW_TRACE_SSL = 1u << 20, - SW_TRACE_NORMAL = 1u << 21, - /** - * Coroutine - */ - SW_TRACE_CHANNEL = 1u << 22, - SW_TRACE_TIMER = 1u << 23, - SW_TRACE_SOCKET = 1u << 24, - SW_TRACE_COROUTINE = 1u << 25, - SW_TRACE_CONTEXT = 1u << 26, - SW_TRACE_CO_HTTP_SERVER = 1u << 27, +extern thread_local char sw_error[SW_ERROR_MSG_SIZE]; +enum swProcessType { + SW_PROCESS_MASTER = 1, + SW_PROCESS_WORKER = 2, + SW_PROCESS_MANAGER = 3, + SW_PROCESS_EVENTWORKER = 2, + SW_PROCESS_TASKWORKER = 4, + SW_PROCESS_USERWORKER = 5, +}; - SW_TRACE_ALL = 0xffffffff +enum swPipeType { + SW_PIPE_WORKER = 0, + SW_PIPE_MASTER = 1, + SW_PIPE_READ = 0, + SW_PIPE_WRITE = 1, + SW_PIPE_NONBLOCK = 2, }; -#ifdef SW_LOG_TRACE_OPEN -#define swTraceLog(what,str,...) \ - if (SW_LOG_TRACE >= SwooleG.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__);\ - SwooleG.write_log(SW_LOG_TRACE, sw_error, _sw_error_len);\ - } -#else -#define swTraceLog(what,str,...) -#endif +//----------------------Tool Function--------------------- +uint32_t swoole_common_multiple(uint32_t u, uint32_t v); +uint32_t swoole_common_divisor(uint32_t u, uint32_t v); + +int swoole_itoa(char *buf, long value); +bool swoole_mkdir_recursive(const std::string &dir); -#define swTrace(str,...) swTraceLog(SW_TRACE_NORMAL, str, ##__VA_ARGS__) +int swoole_rand(int min, int max); +int swoole_system_random(int min, int max); -#define swYield() sched_yield() //or usleep(1) -#define SW_MAX_FDTYPE 32 //32 kinds of event +int swoole_version_compare(const char *version1, const char *version2); +void swoole_print_backtrace(void); +char *swoole_string_format(size_t n, const char *format, ...); +bool swoole_get_env(const char *name, int *value); +int swoole_get_systemd_listen_fds(); -//------------------------------Base-------------------------------- -#ifndef uchar -typedef unsigned char uchar; -#endif +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); +bool swoole_set_task_tmpdir(const std::string &dir); +int swoole_tmpfile(char *filename); -#ifdef SW_USE_OPENSSL -#include -#if OPENSSL_VERSION_NUMBER >= 0x10100000L -#define SW_SUPPORT_DTLS +#ifdef HAVE_CPU_AFFINITY +#ifdef __FreeBSD__ +#include +#include +#include +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 -typedef void (*swDestructor)(void *data); -typedef void (*swCallback)(void *data); - -typedef struct -{ - uint32_t id; - uint32_t fd :24; - uint32_t reactor_id :8; -} swSession; - -typedef struct _swString -{ - size_t length; - size_t size; - off_t offset; - char *str; -} swString; - -typedef struct -{ - union - { - struct sockaddr ss; - struct sockaddr_in inet_v4; - struct sockaddr_in6 inet_v6; - struct sockaddr_un un; - } addr; - socklen_t len; -} swSocketAddress; - -typedef struct _swSocket -{ - int fd; - enum swFd_type fdtype; - enum swSocket_type socket_type; - int events; - - uchar removed :1; - uchar nonblock :1; - uchar cloexec :1; - uchar direct_send :1; -#ifdef SW_USE_OPENSSL - uchar ssl_send :1; - uchar ssl_want_read :1; - uchar ssl_want_write :1; - uchar ssl_renegotiation :1; - uchar ssl_handshake_buffer_set :1; - uchar ssl_quiet_shutdown :1; -#ifdef SW_SUPPORT_DTLS - uchar dtls :1; +#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 - uchar dontwait :1; - uchar close_wait :1; - uchar send_wait :1; - uchar tcp_nopush :1; - uchar tcp_nodelay :1; - uchar skip_recv :1; - uchar recv_wait :1; - uchar event_hup :1; - - /** - * memory buffer size; - */ - uint32_t buffer_size; - uint32_t chunk_size; - void *object; +static inline struct timespec swoole_time_until(int milliseconds) { + struct timespec t; + swoole_clock_realtime(&t); -#ifdef SW_USE_OPENSSL - SSL *ssl; - uint32_t ssl_state; -#endif + int sec = milliseconds / 1000; + int msec = milliseconds - (sec * 1000); - swSocketAddress info; + t.tv_sec += sec; + t.tv_nsec += msec * 1000 * 1000; - struct _swBuffer *out_buffer; - struct _swBuffer *in_buffer; - swString *recv_buffer; + if (t.tv_nsec > SW_NUM_BILLION) { + int _sec = t.tv_nsec / SW_NUM_BILLION; + t.tv_sec += _sec; + t.tv_nsec -= _sec * SW_NUM_BILLION; + } -#ifdef SW_DEBUG - size_t total_recv_bytes; - size_t total_send_bytes; -#endif + return t; +} -} swSocket; +namespace swoole { -typedef struct _swTask_sendfile -{ - char *filename; - uint16_t name_len; - int fd; - size_t length; - off_t offset; -} swTask_sendfile; +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; -typedef struct _swConnection -{ - /** - * file descript - */ +struct Event { int fd; - /** - * session id - */ - uint32_t 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 - */ - uint8_t close_actively; - uint8_t closed; - uint8_t close_queued; - uint8_t closing; - uint8_t close_reset; - uint8_t peer_closed; - /** - * protected connection, cannot be closed by heartbeat thread. - */ - uint8_t protect; - //-------------------------------------------------------------- - uint8_t close_notify; - uint8_t close_force; - //-------------------------------------------------------------- - /** - * ReactorThread id - */ - uint16_t reactor_id; - /** - * close error code - */ - uint16_t close_errno; - /** - * from which socket fd - */ - sw_atomic_t server_fd; - sw_atomic_t queued_bytes; - uint16_t waiting_time; - struct _swTimer_node *timer; - /** - * socket address - */ - swSocketAddress info; - /** - * link any thing, for kernel, do not use with application. - */ - void *object; - /** - * socket info - */ - swSocket *socket; - /** - * connect time(seconds) - */ - time_t connect_time; + int16_t reactor_id; + FdType type; + network::Socket *socket; +}; - /** - * received time with last data - */ - time_t last_time; +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(); +}; -#ifdef SW_BUFFER_RECV_TIME - /** - * received time(microseconds) with last data - */ - double last_time_usec; -#endif - /** - * bind uid - */ - uint32_t uid; - /** - * upgarde websocket - */ - uint8_t websocket_status; - /** - * unfinished data frame - */ - swString *websocket_buffer; +struct EventData { + DataHead info; + char data[SW_IPC_BUFFER_SIZE]; -#ifdef SW_USE_OPENSSL - swString *ssl_client_cert; - uint16_t ssl_client_cert_pid; -#endif - sw_atomic_t lock; + uint32_t size() { + return sizeof(info) + len(); + } -} swConnection; + uint32_t len() { + return info.len; + } +}; -typedef struct _swProtocol -{ - /* one package: eof check */ - uint8_t split_by_eof; - uint8_t package_eof_len; - char package_eof[SW_DATA_EOF_MAXLEN]; +struct SendData { + DataHead info; + const char *data; +}; - char package_length_type; - uint8_t package_length_size; - uint16_t package_length_offset; - uint16_t package_body_offset; - uint32_t package_max_length; +struct RecvData { + DataHead info; + const char *data; +}; - void *private_data; - void *private_data_2; - uint16_t real_header_length; - uint16_t ext_flags; - - int (*onPackage)(struct _swProtocol *, swSocket *, char *, uint32_t); - ssize_t (*get_package_length)(struct _swProtocol *, swSocket *, char *, uint32_t); - uint8_t (*get_package_length_size)(swSocket *); -} swProtocol; - -typedef ssize_t (*swProtocol_length_function)(struct _swProtocol *, swSocket *, char *, uint32_t); -//------------------------------String-------------------------------- -#define swoole_tolower(c) (uchar) ((c >= 'A' && c <= 'Z') ? (c | 0x20) : c) -#define swoole_toupper(c) (uchar) ((c >= 'a' && c <= 'z') ? (c & ~0x20) : c) - -uint32_t swoole_utf8_decode(uchar **p, size_t n); -size_t swoole_utf8_length(uchar *p, size_t n); -void swoole_random_string(char *buf, size_t size); - -static sw_inline char *swoole_strlchr(char *p, char *last, char c) -{ - while (p < last) - { - if (*p == c) - { - return p; - } - p++; - } - return NULL; -} - -static sw_inline size_t swoole_size_align(size_t size, int pagesize) -{ - return size + (pagesize - (size % pagesize)); -} - -#define SW_STRINGL(s) s->str, s->length -#define SW_STRINGS(s) s->str, s->size -#define SW_STRINGCVL(s) s->str + s->offset, s->length - s->offset - -swString *swString_new(size_t size); -swString *swString_dup(const char *src_str, size_t length); -swString *swString_dup2(swString *src); -int swString_repeat(swString *src, const char *data, size_t len, size_t n); -void swString_print(swString *str); -int swString_append(swString *str, swString *append_str); -int swString_append_ptr(swString *str, const char *append_str, size_t length); -int swString_write(swString *str, off_t offset, swString *write_str); -int swString_write_ptr(swString *str, off_t offset, char *write_str, size_t length); -int swString_extend(swString *str, size_t new_size); -char* swString_alloc(swString *str, size_t __size); - -static sw_inline void swString_clear(swString *str) -{ - str->length = 0; - str->offset = 0; -} - -static sw_inline void swString_free(swString *str) -{ - sw_free(str->str); - sw_free(str); -} - -static sw_inline int swString_extend_align(swString *str, size_t _new_size) -{ - size_t align_size = SW_MEM_ALIGNED_SIZE(str->size * 2); - while (align_size < _new_size) - { - align_size *= 2; - } - return swString_extend(str, align_size); -} - -static sw_inline int swString_grow(swString *str, size_t incr_value) -{ - str->length += incr_value; - if (str->length == str->size && swString_extend(str, str->size * 2) < 0) - { - return SW_ERR; - } - else - { - return SW_OK; - } -} - -/** - * migrate data to head, [offset, length - offset] -> [0, length - offset] - */ -static sw_inline void swString_pop_front(swString *str, off_t offset) -{ - assert(offset >= 0 && (size_t ) offset <= str->length); - if (sw_unlikely(offset == 0)) return; - str->length = str->length - offset; - str->offset = 0; - if (str->length == 0) return; - memmove(str->str, str->str + offset, str->length); -} - -static sw_inline void swString_sub(swString *str, off_t start, size_t length) -{ - char *from = str->str + start + (start >= 0 ? 0 : str->length); - str->length = length != 0 ? length : str->length - start; - str->offset = 0; - if (sw_likely(str->length > 0)) - { - memmove(str->str, from, str->length); - } -} - -//------------------------------Base-------------------------------- -enum _swEventData_flag -{ - 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, -}; - -typedef struct _swDataHead -{ - int fd; - uint32_t len; - int16_t reactor_id; +struct ThreadGlobal { + uint16_t id; uint8_t type; - uint8_t flags; - uint16_t server_fd; - uint16_t ext_flags; -#ifdef SW_BUFFER_RECV_TIME - double time; -#endif -} swDataHead; - -void swDataHead_dump(const swDataHead *data); - -#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 -}; - -typedef struct _swEvent -{ - int fd; - int16_t reactor_id; - enum swFd_type type; - swSocket *socket; -} swEvent; - -typedef struct -{ - swDataHead info; - char data[SW_IPC_BUFFER_SIZE]; -} swEventData; - -typedef struct -{ - swDataHead info; - char data[0]; -} swPipeBuffer; - -typedef struct _swDgramPacket -{ - int socket_type; - swSocketAddress socket_addr; - uint32_t length; - char data[0]; -} swDgramPacket; - -typedef struct _swSendData -{ - swDataHead info; - char *data; -} swSendData; - -typedef struct -{ - off_t offset; - size_t length; - char filename[0]; -} swSendFile_request; - -typedef void (*swSignalHandler)(int); -typedef struct _swReactor swReactor; - -typedef int (*swReactor_handler)(swReactor *reactor, swEvent *event); -//------------------Pipe-------------------- -typedef struct _swPipe -{ - void *object; - int blocking; - double timeout; - - swSocket *master_socket; - swSocket *worker_socket; - - int (*read)(struct _swPipe *, void *recv, int length); - int (*write)(struct _swPipe *, void *send, int length); - swSocket* (*getSocket)(struct _swPipe *, int master); - int (*close)(struct _swPipe *); -} swPipe; - -enum swPipe_close_which -{ - SW_PIPE_CLOSE_MASTER = 1, - SW_PIPE_CLOSE_WORKER = 2, - SW_PIPE_CLOSE_READ = 3, - SW_PIPE_CLOSE_WRITE = 4, - SW_PIPE_CLOSE_BOTH = 0, -}; - -int swPipeBase_create(swPipe *p, int blocking); -int swPipeEventfd_create(swPipe *p, int blocking, int semaphore, int timeout); -int swPipeUnsock_create(swPipe *p, int blocking, int protocol); -int swPipeUnsock_close_ext(swPipe *p, int which); -int swPipe_init_socket(swPipe *p, int master_fd, int worker_fd, int blocking); -swSocket* swPipe_getSocket(swPipe *p, int master); - -static inline int swPipeNotify_auto(swPipe *p, int blocking, int semaphore) -{ -#ifdef HAVE_EVENTFD - return swPipeEventfd_create(p, blocking, semaphore, 0); -#else - return swPipeBase_create(p, blocking); -#endif -} - -//------------------Queue-------------------- -typedef struct _swQueue_Data -{ - long mtype; /* type of received/sent message */ - char mdata[sizeof(swEventData)]; /* text of the message */ -} swQueue_data; - -typedef struct _swMsgQueue -{ - int blocking; - int msg_id; - int flags; - int perms; -} swMsgQueue; - -int swMsgQueue_create(swMsgQueue *q, int blocking, key_t msg_key, int perms); -void swMsgQueue_set_blocking(swMsgQueue *q, uint8_t blocking); -int swMsgQueue_set_capacity(swMsgQueue *q, int queue_bytes); -int swMsgQueue_push(swMsgQueue *q, swQueue_data *in, int data_length); -int swMsgQueue_pop(swMsgQueue *q, swQueue_data *out, int buffer_length); -int swMsgQueue_stat(swMsgQueue *q, int *queue_num, int *queue_bytes); -int swMsgQueue_free(swMsgQueue *q); -//------------------Lock-------------------------------------- -enum SW_LOCKS -{ - SW_RWLOCK = 1, - SW_FILELOCK = 2, - SW_MUTEX = 3, - SW_SEM = 4, - SW_SPINLOCK = 5, - SW_ATOMLOCK = 6, -}; - -enum swDNSLookup_cache_type -{ - SW_DNS_LOOKUP_RANDOM = (1u << 11), -}; - -typedef struct -{ - const char *hostname; - const char *service; - int family; - int socktype; - int protocol; +#ifdef SW_THREAD + uint8_t process_type; + uint32_t process_id; +#endif + String *buffer_stack; + Reactor *reactor; + Timer *timer; + TimerScheduler timer_scheduler; + MessageBus *message_bus; + AsyncThreads *async_threads; +#ifdef SW_USE_IOURING + Iouring *iouring; +#endif + uint32_t signal_listener_num; + uint32_t co_signal_listener_num; int error; - void *result; - int count; -} swRequest_getaddrinfo; - -typedef struct _swMutex -{ - pthread_mutex_t _lock; - pthread_mutexattr_t attr; -} swMutex; - -typedef struct _swFileLock -{ - struct flock lock_t; - int fd; -} swFileLock; - -#ifdef HAVE_RWLOCK -typedef struct _swRWLock -{ - pthread_rwlock_t _lock; - pthread_rwlockattr_t attr; - -} swRWLock; - -#ifdef HAVE_SPINLOCK -typedef struct _swSpinLock -{ - pthread_spinlock_t lock_t; -} swSpinLock; -#endif - -typedef struct _swAtomicLock -{ - sw_atomic_t lock_t; - uint32_t spin; -} swAtomicLock; - -typedef struct _swSem -{ - key_t key; - int semid; -} swSem; -#endif - -typedef struct _swLock -{ - int type; - union - { - swMutex mutex; -#ifdef HAVE_RWLOCK - swRWLock rwlock; -#endif -#ifdef HAVE_SPINLOCK - swSpinLock spinlock; -#endif - swFileLock filelock; - swSem sem; - swAtomicLock atomlock; - } object; - - int (*lock_rd)(struct _swLock *); - int (*lock)(struct _swLock *); - int (*unlock)(struct _swLock *); - int (*trylock_rd)(struct _swLock *); - int (*trylock)(struct _swLock *); - int (*free)(struct _swLock *); -} swLock; - - -//Thread Condition -typedef struct _swCond -{ - swLock _lock; - pthread_cond_t _cond; - - int (*wait)(struct _swCond *object); - int (*timewait)(struct _swCond *object, long, long); - int (*notify)(struct _swCond *object); - int (*broadcast)(struct _swCond *object); - void (*free)(struct _swCond *object); - int (*lock)(struct _swCond *object); - int (*unlock)(struct _swCond *object); -} swCond; - -#define SW_SHM_MMAP_FILE_LEN 64 - -typedef struct _swShareMemory_mmap -{ - size_t size; - char mapfile[SW_SHM_MMAP_FILE_LEN]; - int tmpfd; - int key; - int shmid; - void *mem; -} swShareMemory; - -void *swShareMemory_mmap_create(swShareMemory *object, size_t size, char *mapfile); -void *swShareMemory_sysv_create(swShareMemory *object, size_t size, int key); -int swShareMemory_sysv_free(swShareMemory *object, int rm); -int swShareMemory_mmap_free(swShareMemory *object); - -//-------------------memory manager------------------------- -typedef struct _swMemoryPool -{ - void *object; - void* (*alloc)(struct _swMemoryPool *pool, uint32_t size); - void (*free)(struct _swMemoryPool *pool, void *ptr); - void (*destroy)(struct _swMemoryPool *pool); -} swMemoryPool; - -typedef struct _swFixedPool_slice -{ - uint8_t lock; - struct _swFixedPool_slice *next; - struct _swFixedPool_slice *pre; - char data[0]; - -} swFixedPool_slice; - -typedef struct _swFixedPool -{ - void *memory; - size_t size; - - swFixedPool_slice *head; - swFixedPool_slice *tail; - - /** - * total memory size - */ - uint32_t slice_num; - - /** - * memory usage - */ - uint32_t slice_use; - - /** - * Fixed slice size, not include the memory used by swFixedPool_slice - */ - uint32_t slice_size; - - /** - * use shared memory - */ - uint8_t shared; - -} swFixedPool; -/** - * FixedPool, random alloc/free fixed size memory - */ -swMemoryPool* swFixedPool_new(uint32_t slice_num, uint32_t slice_size, uint8_t shared); -swMemoryPool* swFixedPool_new2(uint32_t slice_size, void *memory, size_t size); -swMemoryPool* swMalloc_new(); - -/** - * RingBuffer, In order for malloc / free - */ -swMemoryPool *swRingBuffer_new(uint32_t size, uint8_t shared); - -/** - * Global memory, the program life cycle only malloc / free one time - */ -swMemoryPool* swMemoryGlobal_new(uint32_t pagesize, uint8_t shared); - -void swFixedPool_debug(swMemoryPool *pool); - -/** - * alloc shared memory - */ -void* sw_shm_malloc(size_t size); -void sw_shm_free(void *ptr); -void* sw_shm_calloc(size_t num, size_t _size); -int sw_shm_protect(void *addr, int flags); -void* sw_shm_realloc(void *ptr, size_t new_size); - -#ifdef HAVE_RWLOCK -int swRWLock_create(swLock *lock, int use_in_process); -#endif -#ifdef SEM_UNDO -int swSem_create(swLock *lock, key_t key); -#endif -int swFileLock_create(swLock *lock, int fd); -#ifdef HAVE_SPINLOCK -int swSpinLock_create(swLock *object, int spin); -#endif -int swAtomicLock_create(swLock *object, int spin); - -int swMutex_create(swLock *lock, int use_in_process); -int swMutex_lockwait(swLock *lock, int timeout_msec); -int swCond_create(swCond *cond); - -typedef struct _swThreadParam -{ - void *object; - int pti; -} swThreadParam; - - -#ifdef __MACH__ -char* sw_error_(); -#define sw_error sw_error_() -#else -extern __thread char sw_error[SW_ERROR_MSG_SIZE]; -#endif - -enum swProcess_type -{ - SW_PROCESS_MASTER = 1, - SW_PROCESS_WORKER = 2, - SW_PROCESS_MANAGER = 3, - SW_PROCESS_TASKWORKER = 4, - SW_PROCESS_USERWORKER = 5, }; -enum swIPC_type -{ - SW_IPC_NONE = 0, - SW_IPC_UNIXSOCK = 1, - SW_IPC_MSGQUEUE = 2, - SW_IPC_SOCKET = 3, +struct Allocator { + void *(*malloc)(size_t size); + void *(*calloc)(size_t nmemb, size_t size); + void *(*realloc)(void *ptr, size_t size); + void (*free)(void *ptr); }; -enum swPipe_type -{ - SW_PIPE_WORKER = 0, - SW_PIPE_MASTER = 1, - SW_PIPE_READ = 0, - SW_PIPE_WRITE = 1, - SW_PIPE_NONBLOCK = 2, -}; - -#define swIsMaster() (SwooleG.process_type==SW_PROCESS_MASTER) -#define swIsWorker() (SwooleG.process_type==SW_PROCESS_WORKER) -#define swIsTaskWorker() (SwooleG.process_type==SW_PROCESS_TASKWORKER) -#define swIsManager() (SwooleG.process_type==SW_PROCESS_MANAGER) -#define swIsUserWorker() (SwooleG.process_type==SW_PROCESS_USERWORKER) - -//----------------------Logger--------------------- -int swLog_init(char *logfile); -void swLog_put(int level, char *content, size_t length); -void swLog_reopen(enum swBool_type redirect); -void swLog_free(void); - -//----------------------Tool Function--------------------- -uint64_t swoole_hash_key(char *str, int str_len); -uint32_t swoole_common_multiple(uint32_t u, uint32_t v); -uint32_t swoole_common_divisor(uint32_t u, uint32_t v); - -extern void swoole_sha1(const char *str, int _len, unsigned char *digest); -extern void swoole_sha256(const char *str, int _len, unsigned char *digest); - -static sw_inline uint16_t swoole_swap_endian16(uint16_t x) -{ - return (((x & 0xff) << 8) | ((x & 0xff00) >> 8)); -} - -static sw_inline uint32_t swoole_swap_endian32(uint32_t x) -{ - return (((x & 0xff) << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | ((x & 0xff000000) >> 24)); -} - -static sw_inline int32_t swoole_unpack(char type, const void *data) -{ - switch(type) - { - /*-------------------------16bit-----------------------------*/ - case 'c': - return *((int8_t *) data); - case 'C': - return *((uint8_t *) data); - /*-------------------------16bit-----------------------------*/ - /** - * signed short (always 16 bit, machine byte order) - */ - case 's': - return *((int16_t *) data); - /** - * unsigned short (always 16 bit, machine byte order) - */ - case 'S': - return *((uint16_t *) data); - /** - * unsigned short (always 16 bit, big endian byte order) - */ - case 'n': - return ntohs(*((uint16_t *) data)); - /** - * unsigned short (always 32 bit, little endian byte order) - */ - case 'v': - return swoole_swap_endian16(ntohs(*((uint16_t *) data))); - - /*-------------------------32bit-----------------------------*/ - /** - * unsigned long (always 32 bit, machine byte order) - */ - case 'L': - return *((uint32_t *) data); - /** - * signed long (always 32 bit, machine byte order) - */ - case 'l': - return *((int *) data); - /** - * unsigned long (always 32 bit, big endian byte order) - */ - case 'N': - return ntohl(*((uint32_t *) data)); - /** - * unsigned short (always 32 bit, little endian byte order) - */ - case 'V': - return swoole_swap_endian32(ntohl(*((uint32_t *) data))); - - default: - return *((uint32_t *) data); - } -} - -static inline const char *swoole_strnstr(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 == memcmp(haystack, needle, needle_length))) - { - return haystack; +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); } - haystack++; } - } - - return NULL; -} - -static inline int swoole_strnpos(const char *haystack, uint32_t haystack_length, const char *needle, uint32_t needle_length) -{ - assert(needle_length > 0); - const char *pos; - - pos = swoole_strnstr(haystack, haystack_length, needle, needle_length); - return pos == NULL ? -1 : pos - haystack; -} - -static inline int swoole_strrnpos(const char *haystack, const char *needle, uint32_t length) -{ - uint32_t needle_length = strlen(needle); - assert(needle_length > 0); - uint32_t i; - haystack += (length - needle_length); - - for (i = length - needle_length; i > 0; i--) - { - if ((haystack[0] == needle[0]) && (0 == memcmp(haystack, needle, needle_length))) - { - return i; - } - haystack--; - } + }; + std::function resolve; + void *private_data; + enum Type type; +}; - return -1; -} +struct Global { + uchar init : 1; + uchar running : 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; + + uint8_t process_type; + uint32_t process_id; + TaskId current_task_id; + pid_t pid; -static inline void swoole_strtolower(char *str, int length) -{ - char *c, *e; + int signal_fd; + bool signal_alarm; - c = str; - e = c + length; + long trace_flags; - while (c < e) - { - *c = tolower(*c); - c++; - } -} + void (*fatal_error)(int code, const char *str, ...); -int swoole_itoa(char *buf, long value); -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); -int swoole_type_size(char type); -int swoole_mkdir_recursive(const char *dir); -char* swoole_dirname(char *file); -size_t swoole_sync_writefile(int fd, const void *data, size_t len); -size_t swoole_sync_readfile(int fd, void *buf, size_t len); -swString* swoole_sync_readfile_eof(int fd); -int swoole_rand(int min, int max); -int swoole_system_random(int min, int max); -long swoole_file_get_size(FILE *fp); -int swoole_tmpfile(char *filename); -swString* swoole_file_get_contents(const char *filename); -int swoole_file_put_contents(const char *filename, const char *content, size_t length); -long swoole_file_size(const char *filename); -char *swoole_dec2hex(int value, int base); -size_t swoole_hex2dec(char** hex); -int swoole_version_compare(const char *version1, const char *version2); -#ifdef HAVE_EXECINFO -void swoole_print_trace(void); + //-----------------------[System]-------------------------- + uint16_t cpu_num; + uint32_t pagesize; + struct utsname uname; + uint32_t max_sockets; + //-----------------------[Memory]-------------------------- + MemoryPool *memory_pool; + Allocator std_allocator; + std::string task_tmpfile; + //-----------------------[DNS]-------------------------- + 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 -int swoole_ioctl_set_block(int sock, int nonblock); -int swoole_fcntl_set_option(int sock, int nonblock, int cloexec); -int swoole_gethostbyname(int type, const char *name, char *addr); -int swoole_getaddrinfo(swRequest_getaddrinfo *req); -char* swoole_string_format(size_t n, const char *format, ...); -int swoole_get_systemd_listen_fds(); -//----------------------core function--------------------- -int swSocket_set_timeout(swSocket *sock, double timeout); -swSocket* swSocket_create_server(enum swSocket_type type, const char *address, int port, int backlog); -//----------------------------------------Socket--------------------------------------- -static sw_inline int swSocket_is_dgram(uint8_t type) -{ - return (type == SW_SOCK_UDP || type == SW_SOCK_UDP6 || type == SW_SOCK_UNIX_DGRAM); -} - -static sw_inline int swSocket_is_stream(uint8_t type) -{ - return (type == SW_SOCK_TCP || type == SW_SOCK_TCP6 || type == SW_SOCK_UNIX_STREAM); -} - -void swoole_init(void); -void swoole_clean(void); -pid_t swoole_fork(int flags); -double swoole_microtime(void); -void swoole_rtrim(char *str, int len); -void swoole_redirect_stdout(int new_fd); -int swoole_shell_exec(const char *command, pid_t *pid, uint8_t get_error_stream); -int swoole_daemon(int nochdir, int noclose); - -SW_API const char* swoole_version(void); -SW_API int swoole_version_id(void); - -SW_API int swoole_add_function(const char *name, void* func); -SW_API void* swoole_get_function(const char *name, uint32_t length); -SW_API int swoole_add_hook(enum swGlobal_hook_type type, swCallback func, int push_back); -SW_API void swoole_call_hook(enum swGlobal_hook_type type, void *arg); - -static sw_inline uint64_t swoole_hton64(uint64_t host) -{ - uint64_t ret = 0; - uint32_t high, low; - - low = host & 0xFFFFFFFF; - high = (host >> 32) & 0xFFFFFFFF; - low = htonl(low); - high = htonl(high); + double aio_max_wait_time; + double aio_max_idle_time; + network::Socket *aio_default_socket; + //-----------------------[Hook]-------------------------- + void *hooks[SW_MAX_HOOK_TYPE]; + std::function user_exit_condition; + // bug report message + std::string bug_report_message; +}; - ret = low; - ret <<= 32; - ret |= high; - return ret; -} +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 -static sw_inline uint64_t swoole_ntoh64(uint64_t net) -{ - uint64_t ret = 0; - uint32_t high, low; +extern swoole::Global SwooleG; // Local Global Variable +extern thread_local swoole::ThreadGlobal SwooleTG; // Thread Global Variable - low = net & 0xFFFFFFFF; - high = net >> 32; - low = ntohl(low); - high = ntohl(high); +#define SW_CPU_NUM (SwooleG.cpu_num) - ret = low; - ret <<= 32; - ret |= high; - return ret; +static inline void swoole_set_last_error(int error) { + SwooleTG.error = error; } -//------------------------------Socket-------------------------------- -swSocket* swSocket_new(int fd, enum swFd_type type); -void swSocket_free(swSocket *sock); -int swSocket_create(enum swSocket_type type, uchar nonblock, uchar cloexec); -int swSocket_bind(swSocket *sock, const char *host, int *port); -swSocket* swSocket_accept(swSocket *server_socket, swSocketAddress *sa); -int swSocket_wait(int fd, int timeout_ms, int events); -int swSocket_wait_multi(int *list_of_fd, int n_fd, int timeout_ms, int events); -void swSocket_clean(int fd); -ssize_t swSocket_sendto_blocking(int fd, const void *buf, size_t n, int flag, struct sockaddr *addr, socklen_t addr_len); -int swSocket_set_buffer_size(swSocket *sock, uint32_t buffer_size); -ssize_t swSocket_udp_sendto(int server_sock, const char *dst_ip, int dst_port, const char *data, uint32_t len); -ssize_t swSocket_udp_sendto6(int server_sock, const char *dst_ip, int dst_port, const char *data, uint32_t len); -ssize_t swSocket_unix_sendto(int server_sock, const char *dst_path, const char *data, uint32_t len); -int swSocket_sendfile_sync(int sock, const char *filename, off_t offset, size_t length, double timeout); -ssize_t swSocket_write_blocking(swSocket *sock, const void *__data, size_t __len); -ssize_t swSocket_recv_blocking(swSocket *sock, void *__data, size_t __len, int flags); - - -static sw_inline int swSocket_error(int err) -{ - switch (err) - { - case EFAULT: - abort(); - return SW_ERROR; - case EBADF: - case ECONNRESET: -#ifdef __CYGWIN__ - case ECONNABORTED: -#endif - case EPIPE: - case ENOTCONN: - case ETIMEDOUT: - case ECONNREFUSED: - case ENETDOWN: - case ENETUNREACH: - case EHOSTDOWN: - case EHOSTUNREACH: - case SW_ERROR_SSL_BAD_CLIENT: - case SW_ERROR_SSL_RESET: - return SW_CLOSE; - case EAGAIN: -#ifdef HAVE_KQUEUE - case ENOBUFS: -#endif - case 0: - return SW_WAIT; - default: - return SW_ERROR; - } +static inline int swoole_get_last_error(void) { + return SwooleTG.error; } -ssize_t swSocket_recv(swSocket *conn, void *__buf, size_t __n, int __flags); -ssize_t swSocket_send(swSocket *conn, const void *__buf, size_t __n, int __flags); -ssize_t swSocket_peek(swSocket *conn, void *__buf, size_t __n, int __flags); - -static sw_inline int swSocket_set_nonblock(swSocket *sock) -{ - if (swoole_fcntl_set_option(sock->fd, 1, -1) < 0) - { - return SW_ERR; - } - else - { - sock->nonblock = 1; - return SW_OK; - } +static inline void swoole_clear_last_error(void) { + SwooleTG.error = 0; } -static sw_inline int swSocket_get_domain_and_type(enum swSocket_type type, int *sock_domain, int *sock_type) -{ - switch (type) - { - case SW_SOCK_TCP6: - *sock_domain = AF_INET6; - *sock_type = SOCK_STREAM; - break; - case SW_SOCK_UNIX_STREAM: - *sock_domain = AF_UNIX; - *sock_type = SOCK_STREAM; - break; - case SW_SOCK_UDP: - *sock_domain = AF_INET; - *sock_type = SOCK_DGRAM; - break; - case SW_SOCK_UDP6: - *sock_domain = AF_INET6; - *sock_type = SOCK_DGRAM; - break; - case SW_SOCK_UNIX_DGRAM: - *sock_domain = AF_UNIX; - *sock_type = SOCK_DGRAM; - break; - case SW_SOCK_TCP: - *sock_domain = AF_INET; - *sock_type = SOCK_STREAM; - break; - default: - return SW_ERR; - } +void swoole_clear_last_error_msg(void); +const char *swoole_get_last_error_msg(void); - return SW_OK; +static inline int swoole_get_thread_id(void) { + return SwooleTG.id; } -static sw_inline int swSocket_set_block(swSocket *sock) -{ - if (swoole_fcntl_set_option(sock->fd, 0, -1) < 0) - { - return SW_ERR; - } - else - { - sock->nonblock = 0; - return SW_OK; - } +static inline int swoole_get_thread_type(void) { + return SwooleTG.type; } -int swSocket_buffer_send(swSocket *conn); - -int swSocket_sendfile(swSocket *conn, const char *filename, off_t offset, size_t length); -int swSocket_onSendfile(swSocket *conn, swBuffer_chunk *chunk); -void swSocket_sendfile_destructor(swBuffer_chunk *chunk); -const char* swSocket_get_ip(enum swSocket_type socket_type, swSocketAddress *info); -int swSocket_get_port(enum swSocket_type socket_type, swSocketAddress *info); - -static sw_inline swString *swSocket_get_buffer(swSocket *_socket) -{ - swString *buffer = _socket->recv_buffer; - if (buffer == NULL) - { - buffer = swString_new(SW_BUFFER_SIZE_BIG); - //alloc memory failed. - if (!buffer) - { - return NULL; - } - _socket->recv_buffer = buffer; - } - return buffer; +static inline void swoole_set_thread_id(uint16_t id) { + SwooleTG.id = id; } -static sw_inline void swSocket_free_buffer(swSocket *conn) -{ - if (conn->recv_buffer) - { - swString_free(conn->recv_buffer); - conn->recv_buffer = NULL; - } +static inline void swoole_set_thread_type(uint8_t type) { + SwooleTG.type = type; } -#ifdef TCP_CORK -#define HAVE_TCP_NOPUSH -static sw_inline int swSocket_tcp_nopush(int sock, int nopush) -{ - return setsockopt(sock, IPPROTO_TCP, TCP_CORK, (const void *) &nopush, sizeof(int)); -} +static inline swoole::WorkerId swoole_get_process_id(void) { +#ifdef SW_THREAD + return SwooleTG.process_id; #else -#define swSocket_tcp_nopush(sock, nopush) + return SwooleG.process_id; #endif - -//------------------------------Process-------------------------------- -static sw_inline int swoole_waitpid(pid_t __pid, int *__stat_loc, int __options) -{ - int ret; - do - { - ret = waitpid(__pid, __stat_loc, __options); - } while (ret < 0 && errno == EINTR); - return ret; } -static sw_inline int swoole_kill(pid_t __pid, int __sig) -{ - if (__pid <= 0) - { - return -1; - } - return kill(__pid, __sig); -} - -swSignalHandler swSignal_set(int sig, swSignalHandler func, int restart, int mask); -void swSignal_add(int signo, swSignalHandler func); -void swSignal_callback(int signo); -swSignalHandler swSignal_get_handler(int signo); -void swSignal_clear(void); -void swSignal_none(void); -char* swSignal_str(int sig); - -#ifdef HAVE_SIGNALFD -void swSignalfd_init(); -int swSignalfd_setup(swReactor *reactor); -#endif - -typedef struct _swDefer_callback -{ - struct _swDefer_callback *next, *prev; - swCallback callback; - void *data; -} swDefer_callback; - -struct _swReactor -{ - void *object; - void *ptr; //reserve - - /** - * last signal number - */ - int singal_no; - - uint32_t event_num; - uint32_t max_event_num; - uint16_t signal_listener_num; - uint16_t co_signal_listener_num; - - uchar check_timer :1; - uchar running :1; - uchar start :1; - uchar once :1; - uchar wait_exit :1; - /** - * callback signal - */ - uchar check_signalfd :1; - /** - * reactor->wait timeout (millisecond) or -1 - */ - int32_t timeout_msec; - - uint16_t id; //Reactor ID - uint16_t flag; //flag - - uint32_t max_socket; - -#ifdef SW_USE_MALLOC_TRIM - time_t last_malloc_trim_time; +static inline void swoole_set_process_id(swoole::WorkerId id) { +#ifdef SW_THREAD + SwooleTG.process_id = id; +#else + SwooleG.process_id = id; #endif - - swReactor_handler read_handler[SW_MAX_FDTYPE]; - swReactor_handler write_handler[SW_MAX_FDTYPE]; - swReactor_handler error_handler[SW_MAX_FDTYPE]; - - swReactor_handler default_write_handler; - swReactor_handler default_error_handler; - - struct _swTimer *timer; - - int (*add)(swReactor *reactor, swSocket *socket, int events); - int (*set)(swReactor *reactor, swSocket *socket, int events); - int (*del)(swReactor *reactor, swSocket *socket); - int (*wait)(swReactor *reactor, struct timeval *); - void (*free)(swReactor *); - - void *defer_tasks; - void *destroy_callbacks; - - swDefer_callback idle_task; - swDefer_callback future_task; - - void (*onTimeout)(swReactor *reactor); - void (*onFinish)(swReactor *reactor); - void (*onBegin)(swReactor *reactor); - - int (*is_empty)(swReactor *reactor); - int (*can_exit)(swReactor *reactor); - - int (*write)(swReactor *reactor, swSocket *socket, const void *buf, int n); - int (*close)(swReactor *reactor, swSocket *socket); - void (*defer)(swReactor *reactor, swCallback callback, void *data); -}; - -typedef struct _swWorker swWorker; -typedef struct _swThread swThread; -typedef struct _swProcessPool swProcessPool; - -struct _swWorker -{ - /** - * worker process - */ - pid_t pid; - - /** - * worker thread - */ - pthread_t tid; - - swProcessPool *pool; - - swMemoryPool *pool_output; - - swMsgQueue *queue; - - /** - * redirect stdout to pipe_master - */ - uchar redirect_stdout :1; - - /** - * redirect stdin to pipe_worker - */ - uchar redirect_stdin :1; - - /** - * redirect stderr to pipe_worker - */ - uchar redirect_stderr :1; - - /** - * worker status, IDLE or BUSY - */ - uint8_t status; - uint8_t type; - uint8_t ipc_mode; - uint8_t child_process; - - /** - * tasking num - */ - sw_atomic_t tasking_num; - - time_t start_time; - - long dispatch_count; - long request_count; - - /** - * worker id - */ - uint32_t id; - - swLock lock; - - swPipe *pipe_object; - - swSocket *pipe_master; - swSocket *pipe_worker; - swSocket *pipe_current; - - void *ptr; - void *ptr2; -}; - -typedef struct -{ - swSocket *socket; - swSocket *last_connection; - char *socket_file; - swString *response_buffer; -} swStreamInfo; - -struct _swProcessPool -{ - /** - * reloading - */ - uint8_t reloading; - uint8_t reload_init; - uint8_t dispatch_mode; - uint8_t ipc_mode; - uint8_t started; - uint32_t reload_worker_i; - uint32_t max_wait_time; - swWorker *reload_workers; - - /** - * process type - */ - uint8_t type; - - /** - * worker->id = start_id + i - */ - uint16_t start_id; - - /** - * use message queue IPC - */ - uint8_t use_msgqueue; - - /** - * use stream socket IPC - */ - uint8_t use_socket; - - char *packet_buffer; - uint32_t max_packet_size; - - /** - * message queue key - */ - key_t msgqueue_key; - - - uint32_t worker_num; - uint32_t max_request; - uint32_t max_request_grace; - - int (*onTask)(struct _swProcessPool *pool, swEventData *task); - - void (*onWorkerStart)(struct _swProcessPool *pool, int worker_id); - void (*onMessage)(struct _swProcessPool *pool, char *data, uint32_t length); - void (*onWorkerStop)(struct _swProcessPool *pool, int worker_id); - - int (*main_loop)(struct _swProcessPool *pool, swWorker *worker); - int (*onWorkerNotFound)(struct _swProcessPool *pool, pid_t pid, int status); - - sw_atomic_t round_id; - - swWorker *workers; - swPipe *pipes; - swHashMap *map; - swReactor *reactor; - swMsgQueue *queue; - swStreamInfo *stream; - - void *ptr; - void *ptr2; -}; - -//----------------------------------------Reactor--------------------------------------- -static sw_inline int swReactor_error(swReactor *reactor) -{ - switch (errno) - { - case EINTR: - return SW_OK; - } - return SW_ERR; -} - -static sw_inline int swReactor_event_read(int fdtype) -{ - return (fdtype < SW_EVENT_DEAULT) || (fdtype & SW_EVENT_READ); -} - -static sw_inline int swReactor_event_write(int fdtype) -{ - return fdtype & SW_EVENT_WRITE; -} - -static sw_inline int swReactor_event_error(int fdtype) -{ - return fdtype & SW_EVENT_ERROR; -} - -static sw_inline enum swFd_type swReactor_fdtype(int flags) -{ - return (enum swFd_type) (flags & (~SW_EVENT_READ) & (~SW_EVENT_WRITE) & (~SW_EVENT_ERROR) & (~SW_EVENT_ONCE)); -} - -static sw_inline int swReactor_events(int flags) -{ - int events = 0; - if (swReactor_event_read(flags)) - { - events |= SW_EVENT_READ; - } - if (swReactor_event_write(flags)) - { - events |= SW_EVENT_WRITE; - } - if (swReactor_event_error(flags)) - { - events |= SW_EVENT_ERROR; - } - if (flags & SW_EVENT_ONCE) - { - events |= SW_EVENT_ONCE; - } - return events; -} - -int swReactor_create(swReactor *reactor, int max_event); -void swReactor_destroy(swReactor *reactor); -void swReactor_add_destroy_callback(swReactor *reactor, swCallback cb, void *data); - -static inline void swReactor_before_wait(swReactor *reactor) -{ - reactor->running = 1; - reactor->start = 1; -} - -#define SW_REACTOR_CONTINUE if (reactor->once) {break;} else {continue;} - -int swReactor_empty(swReactor *reactor); - -static sw_inline int swReactor_isset_handler(swReactor *reactor, int fdtype) -{ - return reactor->read_handler[fdtype] != NULL; -} - -static sw_inline void swReactor_add(swReactor *reactor, swSocket *_socket, int events) -{ - _socket->events = events; - _socket->removed = 0; - reactor->event_num++; -} - -static sw_inline void swReactor_set(swReactor *reactor, swSocket *_socket, int events) -{ - _socket->events = events; -} - -static sw_inline void swReactor_del(swReactor *reactor, swSocket *_socket) -{ - _socket->events = 0; - _socket->removed = 1; - reactor->event_num--; -} - -static sw_inline int swReactor_exists(swReactor *reactor, swSocket *_socket) -{ - return !_socket->removed && _socket->events; -} - -static sw_inline int swReactor_get_timeout_msec(swReactor *reactor) -{ - return reactor->defer_tasks ? 0 : reactor->timeout_msec; -} - -int swReactor_onWrite(swReactor *reactor, swEvent *ev); -int swReactor_close(swReactor *reactor, swSocket *socket); -int swReactor_write(swReactor *reactor, swSocket *socket, const void *buf, int n); -int swReactor_wait_write_buffer(swReactor *reactor, swSocket *socket); -void swReactor_activate_future_task(swReactor *reactor); - -static sw_inline int swReactor_add_event(swReactor *reactor, swSocket *_socket, enum swEvent_type event_type) -{ - if (!(_socket->events & event_type)) - { - return reactor->set(reactor, _socket, _socket->events | event_type); - } - return SW_OK; -} - -static sw_inline int swReactor_del_event(swReactor *reactor, swSocket *_socket, enum swEvent_type event_type) -{ - if (_socket->events & event_type) - { - return reactor->set(reactor, _socket, _socket->events & (~event_type)); - } - return SW_OK; -} - -static sw_inline int swReactor_remove_read_event(swReactor *reactor, swSocket *_socket) -{ - if (_socket->events & SW_EVENT_WRITE) - { - _socket->events &= (~SW_EVENT_READ); - return reactor->set(reactor, _socket, _socket->events); - } - else - { - return reactor->del(reactor, _socket); - } -} - -static sw_inline int swReactor_remove_write_event(swReactor *reactor, swSocket *_socket) -{ - if (_socket->events & SW_EVENT_READ) - { - _socket->events &= (~SW_EVENT_WRITE); - return reactor->set(reactor, _socket, _socket->events); - } - else - { - return reactor->del(reactor, _socket); - } -} - -static sw_inline int swReactor_add_read_event(swReactor *reactor, swSocket *_socket) -{ - if (_socket->events & SW_EVENT_WRITE) - { - _socket->events |= SW_EVENT_READ; - return reactor->set(reactor, _socket, _socket->events); - } - else - { - return reactor->add(reactor, _socket, SW_EVENT_READ); - } -} - -static sw_inline int swReactor_add_write_event(swReactor *reactor, swSocket *_socket) -{ - if (_socket->events & SW_EVENT_READ) - { - _socket->events |= SW_EVENT_WRITE; - return reactor->set(reactor, _socket, _socket->events); - } - else - { - return reactor->add(reactor, _socket, SW_EVENT_WRITE);; - } } -static sw_inline swReactor_handler swReactor_get_handler(swReactor *reactor, enum swEvent_type event_type, enum swFd_type fdtype) -{ - switch(event_type) - { - case SW_EVENT_READ: - return reactor->read_handler[fdtype]; - case SW_EVENT_WRITE: - return (reactor->write_handler[fdtype] != NULL) ? reactor->write_handler[fdtype] : reactor->default_write_handler; - case SW_EVENT_ERROR: - return (reactor->error_handler[fdtype] != NULL) ? reactor->error_handler[fdtype] : reactor->default_error_handler; - default: - abort(); - break; - } - return NULL; -} - -int swReactor_set_handler(swReactor *reactor, int fdtype, swReactor_handler); - -static sw_inline int swReactor_trigger_close_event(swReactor *reactor, swEvent *event) -{ - return reactor->default_error_handler(reactor, event); -} - -int swReactorEpoll_create(swReactor *reactor, int max_event_num); -int swReactorPoll_create(swReactor *reactor, int max_event_num); -int swReactorKqueue_create(swReactor *reactor, int max_event_num); -int swReactorSelect_create(swReactor *reactor); - -/*----------------------------Process Pool-------------------------------*/ -int swProcessPool_create(swProcessPool *pool, uint32_t worker_num, key_t msgqueue_key, int ipc_mode); -int swProcessPool_create_unix_socket(swProcessPool *pool, char *socket_file, int blacklog); -int swProcessPool_create_tcp_socket(swProcessPool *pool, char *host, int port, int blacklog); -int swProcessPool_set_protocol(swProcessPool *pool, int task_protocol, uint32_t max_packet_size); -void swProcessPool_set_max_request(swProcessPool *pool, uint32_t max_request, uint32_t max_request_grace); -int swProcessPool_wait(swProcessPool *pool); -int swProcessPool_start(swProcessPool *pool); -void swProcessPool_shutdown(swProcessPool *pool); -pid_t swProcessPool_spawn(swProcessPool *pool, swWorker *worker); -int swProcessPool_dispatch(swProcessPool *pool, swEventData *data, int *worker_id); -int swProcessPool_response(swProcessPool *pool, char *data, int length); -int swProcessPool_dispatch_blocking(swProcessPool *pool, swEventData *data, int *dst_worker_id); -int swProcessPool_add_worker(swProcessPool *pool, swWorker *worker); -int swProcessPool_del_worker(swProcessPool *pool, swWorker *worker); -int swProcessPool_get_max_request(swProcessPool *pool); - -static sw_inline void swProcessPool_set_start_id(swProcessPool *pool, int start_id) -{ - uint32_t i; - pool->start_id = start_id; - for (i = 0; i < pool->worker_num; i++) - { - pool->workers[i].id = pool->start_id + i; - } -} - -static sw_inline void swProcessPool_set_type(swProcessPool *pool, int type) -{ - uint32_t i; - pool->type = type; - for (i = 0; i < pool->worker_num; i++) - { - pool->workers[i].type = type; - } -} - -static sw_inline swWorker* swProcessPool_get_worker(swProcessPool *pool, int worker_id) -{ - return &(pool->workers[worker_id - pool->start_id]); +static inline void swoole_set_process_type(int type) { +#ifdef SW_THREAD + SwooleTG.process_type = type; +#else + SwooleG.process_type = type; +#endif } -//-----------------------------Channel--------------------------- -enum swChannel_flag -{ - SW_CHAN_LOCK = 1u << 1, - SW_CHAN_NOTIFY = 1u << 2, - SW_CHAN_SHM = 1u << 3, -}; - -typedef struct _swChannel -{ - off_t head; - off_t tail; - size_t size; - char head_tag; - char tail_tag; - int num; - int max_num; - /** - * Data length, excluding structure - */ - size_t bytes; - int flag; - int maxlen; - /** - * memory point - */ - void *mem; - swLock lock; - swPipe notify_fd; -} swChannel; - -swChannel* swChannel_new(size_t size, size_t maxlen, int flag); -#define swChannel_empty(ch) (ch->num == 0) -#define swChannel_full(ch) ((ch->head == ch->tail && ch->tail_tag != ch->head_tag) || (ch->bytes + sizeof(int) * ch->num == ch->size)) -int swChannel_pop(swChannel *object, void *out, int buffer_length); -int swChannel_push(swChannel *object, void *in, int data_length); -int swChannel_out(swChannel *object, void *out, int buffer_length); -int swChannel_in(swChannel *object, void *in, int data_length); -int swChannel_peek(swChannel *object, void *out, int buffer_length); -int swChannel_wait(swChannel *object); -int swChannel_notify(swChannel *object); -void swChannel_free(swChannel *object); -void swChannel_print(swChannel *); - -/*----------------------------Thread Pool-------------------------------*/ -enum swThread_type -{ - SW_THREAD_MASTER = 1, - SW_THREAD_REACTOR = 2, - SW_THREAD_WORKER = 3, - SW_THREAD_UDP = 4, - SW_THREAD_UNIX_DGRAM = 5, - SW_THREAD_HEARTBEAT = 6, -}; - -typedef struct _swThreadPool -{ - swCond cond; - - swThread *threads; - swThreadParam *params; - - void *ptr1; - void *ptr2; - -#ifdef SW_THREADPOOL_USE_CHANNEL - swChannel *chan; +static inline int swoole_get_process_type(void) { +#ifdef SW_THREAD + return SwooleTG.process_type; #else - swRingQueue queue; + return SwooleG.process_type; #endif - - int thread_num; - int shutdown; - sw_atomic_t task_num; - - void (*onStart)(struct _swThreadPool *pool, int id); - void (*onStop)(struct _swThreadPool *pool, int id); - int (*onTask)(struct _swThreadPool *pool, void *task, int task_len); - -} swThreadPool; - -struct _swThread -{ - pthread_t tid; - int id; - swThreadPool *pool; -}; - -int swThreadPool_dispatch(swThreadPool *pool, void *task, int task_len); -int swThreadPool_create(swThreadPool *pool, int max_num); -int swThreadPool_run(swThreadPool *pool); -int swThreadPool_free(swThreadPool *pool); - -//--------------------------------protocol------------------------------ -ssize_t swProtocol_get_package_length(swProtocol *protocol, swSocket *conn, char *data, uint32_t size); -int swProtocol_recv_check_length(swProtocol *protocol, swSocket *conn, swString *buffer); -int swProtocol_recv_check_eof(swProtocol *protocol, swSocket *conn, swString *buffer); - -//--------------------------------timer------------------------------ -#define SW_TIMER_MIN_MS 1 -#define SW_TIMER_MIN_SEC 0.001 -#define SW_TIMER_MAX_MS LONG_MAX -#define SW_TIMER_MAX_SEC ((double) (LONG_MAX / 1000)) - -typedef struct _swTimer swTimer; -typedef struct _swTimer_node swTimer_node; - -typedef void (*swTimerCallback)(swTimer *, swTimer_node *); -typedef void (*swTimerDtor)(swTimer_node *); - -enum swTimer_type -{ - SW_TIMER_TYPE_KERNEL, - SW_TIMER_TYPE_PHP, -}; - -struct _swTimer_node -{ - /*----------------properties--------------*/ - long id; - enum swTimer_type type; - int64_t exec_msec; - int64_t interval; - uint64_t round; - uint8_t removed; - swHeap_node *heap_node; - /*-----------------callback---------------*/ - swTimerCallback callback; - void *data; - /*-----------------destructor-------------*/ - swTimerDtor dtor; -}; - -struct _swTimer -{ - /*--------------signal timer--------------*/ - swReactor *reactor; - swHeap *heap; - swHashMap *map; - uint32_t num; - uint64_t round; - long _next_id; - long _current_id; - long _next_msec; - /*---------------event timer--------------*/ - struct timeval basetime; - /*---------------system timer-------------*/ - long lasttime; - /*----------------------------------------*/ - int (*set)(swTimer *timer, long exec_msec); - void (*close)(swTimer *timer); -}; - -int swTimer_init(swTimer *timer, long msec); -void swTimer_reinit(swTimer *timer, swReactor *reactor); -swTimer_node* swTimer_add(swTimer *timer, long _msec, int interval, void *data, swTimerCallback callback); -enum swBool_type swTimer_del(swTimer *timer, swTimer_node *node); -void swTimer_free(swTimer *timer); -int swTimer_select(swTimer *timer); -int swTimer_now(struct timeval *time); - -static sw_inline swTimer_node* swTimer_get(swTimer *timer, long id) -{ - return (swTimer_node*) swHashMap_find_int(timer->map, id); } -static sw_inline swTimer_node* swTimer_get_ex(swTimer *timer, long id, const enum swTimer_type type) -{ - swTimer_node* tnode = swTimer_get(timer, id); - return (tnode && tnode->type == type) ? tnode : NULL; +static inline uint32_t swoole_pagesize() { + return SwooleG.pagesize; } -int swSystemTimer_init(swTimer *timer, long msec); -void swSystemTimer_signal_handler(int sig); -//-------------------------------------------------------------- - -//Worker process global Variable -typedef struct -{ - /** - * Always run - */ - uint8_t run_always; - - /** - * for timer with block io - */ - uint8_t signal_alarm; - - /** - * Current Proccess Worker's id - */ - uint32_t id; - - /** - * pipe_worker - */ - int pipe_used; - - uchar shutdown :1; - - uint32_t max_request; - - void **input_buffers; - swString **output_buffer; - swWorker *worker; - time_t exit_time; - -} swWorkerGlobal_t; - -typedef struct -{ - uint16_t id; - uint8_t type; - uint8_t update_time; - swString *buffer_stack; - swReactor *reactor; - swTimer *timer; - uint8_t aio_init; - uint8_t aio_schedule; - uint32_t aio_task_num; - swPipe aio_pipe; - swSocket *aio_read_socket; - swSocket *aio_write_socket; -#ifdef SW_AIO_WRITE_LOCK - swLock aio_lock; -#endif -} swThreadGlobal_t; - -typedef struct -{ - union - { - char v4[INET_ADDRSTRLEN]; - char v6[INET6_ADDRSTRLEN]; - } address; -} swDNS_server; - -typedef struct -{ - uchar init :1; - uchar running :1; - uchar enable_coroutine :1; - uchar use_signalfd :1; - uchar enable_signalfd :1; - uchar socket_dontwait :1; - uchar dns_lookup_random :1; - uchar use_async_resolver :1; - - int error; - int process_type; - pid_t pid; - - int signal_fd; - int log_fd; - int null_fd; - - /** - * worker(worker and task_worker) process chroot / user / group - */ - char *chroot; - char *user; - char *group; - - uint32_t log_level; - char *log_file; - uint32_t trace_flags; - - void (*write_log)(int level, char *content, size_t len); - void (*fatal_error)(int code, const char *str, ...); - - //-----------------------[System]-------------------------- - uint16_t cpu_num; - uint32_t pagesize; - struct utsname uname; - - //-----------------------[Socket]-------------------------- - uint32_t max_sockets; - /** - * tcp socket default buffer size - */ - uint32_t socket_buffer_size; - double socket_send_timeout; - - void *serv; - - swMemoryPool *memory_pool; - swLock lock; - - char *task_tmpdir; - uint16_t task_tmpdir_len; - - char *dns_server_v4; - char *dns_server_v6; - double dns_cache_refresh_time; - - /** - * aio-threads - */ - uint32_t aio_core_worker_num; - uint32_t aio_worker_num; - double aio_max_wait_time; - double aio_max_idle_time; - swSocket *aio_default_socket; - - swHashMap *functions; - void *hooks[SW_MAX_HOOK_TYPE]; - - int (*reactor_can_exit)(swReactor *); -} swGlobal_t; - -extern swGlobal_t SwooleG; //Local Global Variable -extern swWorkerGlobal_t SwooleWG; //Worker Global Variable -extern __thread swThreadGlobal_t SwooleTG; //Thread Global Variable - -#define SW_CPU_NUM (SwooleG.cpu_num) +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(); //----------------------------------------------- -//OS Feature -#if defined(HAVE_KQUEUE) || !defined(HAVE_SENDFILE) -int 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 - -static sw_inline void sw_spinlock(sw_atomic_t *lock) -{ +static sw_inline void sw_spinlock(sw_atomic_t *lock) { uint32_t i, n; - while (1) - { - if (*lock == 0 && sw_atomic_cmp_set(lock, 0, 1)) - { + while (1) { + if (*lock == 0 && sw_atomic_cmp_set(lock, 0, 1)) { return; } - if (SW_CPU_NUM > 1) - { - for (n = 1; n < SW_SPINLOCK_LOOP_N; n <<= 1) - { - for (i = 0; i < n; i++) - { + if (SW_CPU_NUM > 1) { + for (n = 1; n < SW_SPINLOCK_LOOP_N; n <<= 1) { + for (i = 0; i < n; i++) { sw_atomic_cpu_pause(); } - if (*lock == 0 && sw_atomic_cmp_set(lock, 0, 1)) - { + if (*lock == 0 && sw_atomic_cmp_set(lock, 0, 1)) { return; } } } - swYield(); + sw_yield(); } } -static sw_inline int64_t swTimer_get_relative_msec() -{ - struct timeval now; - if (!SwooleTG.timer) - { - return SW_ERR; - } - if (swTimer_now(&now) < 0) - { - return SW_ERR; - } - int64_t msec1 = (now.tv_sec - SwooleTG.timer->basetime.tv_sec) * 1000; - int64_t msec2 = (now.tv_usec - SwooleTG.timer->basetime.tv_usec) / 1000; - return msec1 + msec2; +static sw_inline swoole::String *sw_tg_buffer() { + return SwooleTG.buffer_stack; } -static sw_inline int64_t swTimer_get_absolute_msec() -{ - struct timeval now; - if (swTimer_now(&now) < 0) - { - return SW_ERR; - } - int64_t msec1 = (now.tv_sec) * 1000; - int64_t msec2 = (now.tv_usec) / 1000; - return msec1 + msec2; +static sw_inline swoole::MemoryPool *sw_mem_pool() { + return SwooleG.memory_pool; } -SW_EXTERN_C_END - -#endif /* SWOOLE_H_ */ +static sw_inline const swoole::Allocator *sw_std_allocator() { + return &SwooleG.std_allocator; +} diff --git a/include/swoole_api.h b/include/swoole_api.h index 642db1f196e..deacc484ddf 100644 --- a/include/swoole_api.h +++ b/include/swoole_api.h @@ -12,45 +12,58 @@ | to obtain it through the world-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 _SW_API_H_ -#define _SW_API_H_ +#pragma once #include "swoole.h" -#include "coroutine_c_api.h" +#include "swoole_coroutine_c_api.h" -SW_EXTERN_C_BEGIN +enum swEventInitFlag { + SW_EVENTLOOP_WAIT_EXIT = 1, +}; -SW_API long swoole_timer_after(long ms, swTimerCallback callback, void *private_data); -SW_API long swoole_timer_tick(long ms, swTimerCallback callback, void *private_data); -SW_API swTimer_node* swoole_timer_add(long ms, uchar persistent, swTimerCallback callback, void *private_data); -SW_API uchar swoole_timer_del(swTimer_node* tnode); -SW_API uchar swoole_timer_exists(long timer_id); -SW_API swTimer_node* swoole_timer_get(long timer_id); -SW_API uchar swoole_timer_clear(long timer_id); +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(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); +SW_API void swoole_timer_delay(swoole::TimerNode *tnode, long delay_ms); +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(); -SW_API int swoole_event_add(swSocket *socket, int events); -SW_API int swoole_event_set(swSocket *socket, int events); -SW_API int swoole_event_del(swSocket *socket); -SW_API void swoole_event_defer(swCallback cb, void *private_data); -SW_API int swoole_event_write(swSocket *socket, const void *data, size_t len); +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 int swoole_event_set_handler(int fdtype, swReactor_handler handler); +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__ -swReactor* sw_reactor(); -swTimer* sw_timer(); +swoole::Reactor *sw_reactor(); +swoole::Timer *sw_timer(); #else -#define sw_reactor() (SwooleTG.reactor) -#define sw_timer() (SwooleTG.timer) +#define sw_reactor() (SwooleTG.reactor) +#define sw_timer() (SwooleTG.timer) #endif - -SW_EXTERN_C_END - -#endif /* _SW_API_H_ */ diff --git a/include/swoole_asm_context.h b/include/swoole_asm_context.h new file mode 100644 index 00000000000..7db0fbd9452 --- /dev/null +++ b/include/swoole_asm_context.h @@ -0,0 +1,66 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + +----------------------------------------------------------------------+ + */ + +#ifndef _SW_ASM_CONTEXT_H_ +#define _SW_ASM_CONTEXT_H_ + +#ifdef SW_USE_ASM_CONTEXT + +SW_EXTERN_C_BEGIN + +#include +#include +#include + +typedef void *fcontext_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 + +#endif +#endif diff --git a/include/swoole_async.h b/include/swoole_async.h new file mode 100644 index 00000000000..c419708e948 --- /dev/null +++ b/include/swoole_async.h @@ -0,0 +1,138 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + +----------------------------------------------------------------------+ + */ + +#pragma once + +#include +#include + +#ifndef O_DIRECT +#define O_DIRECT 040000 +#endif + +namespace swoole { + +enum AsyncFlag { + SW_AIO_WRITE_FSYNC = 1u << 1, + SW_AIO_EOF = 1u << 2, +}; + +struct AsyncRequest { + virtual ~AsyncRequest() = default; +}; + +struct AsyncEvent { + size_t task_id; + uint8_t canceled; + int error; + /** + * input & output + */ + std::shared_ptr data; + /** + * output + */ + ssize_t retval; + /** + * internal use only + */ + network::Socket *pipe_socket; + double timestamp; + 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 { + +typedef void (*Handler)(AsyncEvent *event); + +AsyncEvent *dispatch(const AsyncEvent *request); + +void handler_gethostbyname(AsyncEvent *event); +void handler_getaddrinfo(AsyncEvent *event); + +} // namespace async +}; // namespace swoole diff --git a/include/swoole_atomic.h b/include/swoole_atomic.h new file mode 100644 index 00000000000..abf49880182 --- /dev/null +++ b/include/swoole_atomic.h @@ -0,0 +1,107 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Twosee | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +typedef volatile int32_t sw_atomic_int32_t; +typedef volatile uint32_t sw_atomic_uint32_t; +typedef volatile int64_t sw_atomic_int64_t; +typedef volatile uint64_t sw_atomic_uint64_t; + +typedef sw_atomic_int64_t sw_atomic_long_t; +typedef sw_atomic_uint64_t sw_atomic_ulong_t; +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) + +#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 + +#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 +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 new file mode 100644 index 00000000000..eadc13e06d5 --- /dev/null +++ b/include/swoole_base64.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: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include +#include +#include + +#define BASE64_ENCODE_OUT_SIZE(s) (((s) + 2) / 3 * 4) +#define BASE64_DECODE_OUT_SIZE(s) (((s)) / 4 * 3) + +namespace swoole { + size_t base64_encode(const unsigned char *in, size_t inlen, char *out); + size_t base64_decode(const char *in, size_t inlen, char *out); +} diff --git a/include/swoole_buffer.h b/include/swoole_buffer.h new file mode 100644 index 00000000000..f8339228dc6 --- /dev/null +++ b/include/swoole_buffer.h @@ -0,0 +1,86 @@ +/* + +----------------------------------------------------------------------+ + | 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: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include +#include + +namespace swoole { + +struct BufferChunk { + enum Type { + TYPE_DATA, + TYPE_SENDFILE, + TYPE_CLOSE, + }; + + Type type; + uint32_t length = 0; + uint32_t offset = 0; + union { + 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 { + private: + // 0: donot use chunk + uint32_t chunk_size; + uint32_t total_length = 0; + std::queue queue_; + + public: + explicit Buffer(uint32_t chunk_size); + ~Buffer(); + + BufferChunk *alloc(BufferChunk::Type type, uint32_t size); + + BufferChunk *front() { + return queue_.front(); + } + + void pop(); + void append(const void *data, uint32_t size); + void append(const struct iovec *iov, size_t iovcnt, off_t offset); + + uint32_t length() const { + return total_length; + } + + size_t count() const { + return queue_.size(); + } + + bool empty() const { + return queue_.empty(); + } + + static bool empty(Buffer *buffer) { + return buffer == nullptr || buffer->queue_.empty(); + } +}; + +} // namespace swoole diff --git a/include/swoole_c_api.h b/include/swoole_c_api.h new file mode 100644 index 00000000000..565e8b2d16f --- /dev/null +++ b/include/swoole_c_api.h @@ -0,0 +1,60 @@ +/* + +----------------------------------------------------------------------+ + | 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 SW_C_API_H_ +#define SW_C_API_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "swoole_config.h" + +enum swGlobalHookType { + SW_GLOBAL_HOOK_BEFORE_SERVER_START, + SW_GLOBAL_HOOK_BEFORE_CLIENT_START, + SW_GLOBAL_HOOK_BEFORE_WORKER_START, + SW_GLOBAL_HOOK_ON_CORO_START, + 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_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_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); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif +#endif diff --git a/include/swoole_channel.h b/include/swoole_channel.h new file mode 100644 index 00000000000..17a11a6b9aa --- /dev/null +++ b/include/swoole_channel.h @@ -0,0 +1,77 @@ +/* + +----------------------------------------------------------------------+ + | 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: Xinyu Zhu | + | shiguangqi | + | Twosee | + | Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#pragma once + +#include "swoole.h" +#include "swoole_lock.h" + +namespace swoole { + +enum ChannelFlag { + SW_CHAN_LOCK = 1u << 1, + SW_CHAN_NOTIFY = 1u << 2, + SW_CHAN_SHM = 1u << 3, +}; + +struct Channel { + off_t head; + off_t tail; + size_t size; + char head_tag; + char tail_tag; + int num; + int max_num; + /** + * Data length, excluding structure + */ + size_t bytes; + int flags; + int maxlen; + /** + * memory point + */ + void *mem; + Lock *lock; + Pipe *notify_pipe; + + bool empty() { + return num == 0; + } + bool full() { + return ((head == tail && tail_tag != head_tag) || (bytes + sizeof(int) * num == size)); + } + int pop(void *out_buf, int buffer_length); + int push(const void *in_data, int data_length); + int out(void *out_buf, int buffer_length); + int in(const void *in_data, int data_length); + int peek(void *out, int buffer_length); + int wait(); + int notify(); + void destroy(); + void print(); + int count() { + return num; + } + int get_bytes() { + return bytes; + } + static Channel *make(size_t size, size_t maxlen, int flags); +}; +} // namespace swoole diff --git a/include/swoole_client.h b/include/swoole_client.h new file mode 100644 index 00000000000..3031dea6071 --- /dev/null +++ b/include/swoole_client.h @@ -0,0 +1,307 @@ +/* + +----------------------------------------------------------------------+ + | 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 "swoole_reactor.h" +#include "swoole_protocol.h" +#include "swoole_proxy.h" + +namespace swoole { +namespace network { + +class Client { + public: + int id = 0; + long timeout_id = 0; // timeout node id + int _sock_type = 0; + int _sock_domain = 0; + int _protocol = 0; + FdType fd_type; + bool active = false; + bool async = false; + bool keep = false; + bool destroyed = false; + bool http2 = false; + bool sleep_ = false; + bool wait_dns = false; + bool shutdow_rw = false; + bool shutdown_read = false; + bool shutdown_write = false; + bool remove_delay = false; + bool closed = false; + bool high_watermark = false; + bool async_connect = false; + + /** + * one package: length check + */ + bool open_length_check = false; + bool open_eof_check = false; + + Protocol protocol = {}; + Socks5Proxy *socks5_proxy = nullptr; + HttpProxy *http_proxy = nullptr; + + uint32_t reuse_count = 0; + + const char *server_str = nullptr; + const char *server_host = nullptr; + int server_port = 0; + void *ptr = nullptr; + void *params = nullptr; + + uint8_t server_strlen = 0; + double timeout = 0; + TimerNode *timer = nullptr; + + /** + * signal interruption + */ + double interrupt_time = 0; + + /** + * sendto, read only. + */ + Address server_addr = {}; + + /** + * recvfrom + */ + Address remote_addr = {}; + + Socket *socket; + + void *object = nullptr; + + String *buffer = nullptr; + uint32_t wait_length = 0; + uint32_t input_buffer_size = 0; + + uint32_t buffer_high_watermark = 0; + uint32_t buffer_low_watermark = 0; + +#ifdef SW_USE_OPENSSL + bool open_ssl = false; + bool ssl_wait_handshake = false; + std::shared_ptr ssl_context = nullptr; +#endif + + std::function onConnect = nullptr; + std::function onError = nullptr; + std::function onReceive = nullptr; + std::function onClose = nullptr; + std::function onBufferFull = nullptr; + std::function onBufferEmpty = nullptr; + + 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; + int (*sendfile)(Client *cli, const char *filename, off_t offset, size_t length) = nullptr; + ssize_t (*recv)(Client *cli, char *data, size_t length, int flags) = nullptr; + + static void init_reactor(Reactor *reactor); + 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); + int close(); + void destroy(); + 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 +}; + +//----------------------------------------Stream--------------------------------------- +class Stream { + public: + String *buffer = nullptr; + Client client; + bool connected = false; + bool cancel = false; + int errCode = 0; + void *private_data = nullptr; + void *private_data_2 = nullptr; + long private_data_fd = -1; + + std::function response = nullptr; + + int send(const char *data, size_t length); + void set_max_length(uint32_t max_length); + + 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; + return nullptr; + } else { + return stream; + } + } + ~Stream(); + 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, SocketType type); +}; +//----------------------------------------Stream End------------------------------------ + +class SyncClient { + protected: + Client client; + bool connected = false; + bool created; + bool async = false; + SocketType type; + + public: + SyncClient(SocketType _type, bool _async = false) : client(_type, _async), async(_async), type(_type) { + created = client.socket != nullptr; + } + + bool connect(const char *host, int port, double timeout = -1) { + if (connected || !created) { + return false; + } + 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); + } + + ssize_t send(const char *buf, size_t len) { + return client.send(&client, buf, len, 0); + } + + ssize_t recv(char *buf, size_t len) { + return client.recv(&client, buf, len, 0); + } + + bool close() { + if (!created || client.closed) { + return false; + } + client.close(); + created = false; + return true; + } + + virtual ~SyncClient() { + if (created) { + close(); + } + } +}; + +class AsyncClient : public SyncClient { + protected: + std::function _onConnect = nullptr; + std::function _onError = nullptr; + std::function _onClose = nullptr; + std::function _onReceive = nullptr; + + public: + AsyncClient(SocketType _type) : SyncClient(_type, true) {} + + bool connect(const char *host, int port, double timeout = -1) { + client.object = this; + client.onConnect = [](Client *cli) { + AsyncClient *ac = (AsyncClient *) cli->object; + ac->_onConnect(ac); + }; + client.onError = [](Client *cli) { + AsyncClient *ac = (AsyncClient *) cli->object; + ac->_onError(ac); + }; + client.onClose = [](Client *cli) { + AsyncClient *ac = (AsyncClient *) cli->object; + ac->_onClose(ac); + }; + client.onReceive = [](Client *cli, const char *data, size_t length) { + AsyncClient *ac = (AsyncClient *) cli->object; + ac->_onReceive(ac, data, length); + }; + return SyncClient::connect(host, port, timeout); + } + + void on_connect(std::function fn) { + _onConnect = fn; + } + + void on_error(std::function fn) { + _onError = fn; + } + + void on_close(std::function fn) { + _onClose = fn; + } + + void on_receive(std::function fn) { + _onReceive = fn; + } +}; + +} // namespace network +} // namespace swoole diff --git a/include/swoole_config.h b/include/swoole_config.h index 3dc569a0328..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_ @@ -23,248 +23,242 @@ #endif #endif -#define SW_MAX_FDTYPE 32 // 32 kinds of event -#define SW_MAX_HOOK_TYPE 32 -#define SW_ERROR_MSG_SIZE 16384 -#define SW_MAX_FILE_CONTENT (64*1024*1024) // for swoole_file_get_contents -#define SW_MAX_LISTEN_PORT 60000 -#define SW_MAX_CONNECTION 100000 -#define SW_MAX_CONCURRENT_TASK 1024 -#define SW_STACK_BUFFER_SIZE 65536 +#define SW_MAX_FDTYPE 32 // 32 kinds of event +#define SW_MAX_HOOK_TYPE 32 +#define SW_ERROR_MSG_SIZE 16384 +#define SW_MAX_FILE_CONTENT (64 * 1024 * 1024) // for swoole_file_get_contents +#define SW_MAX_LISTEN_PORT 60000 +#define SW_MAX_CONNECTION 100000 +#define SW_MAX_CONCURRENT_TASK 1024 +#define SW_STACK_BUFFER_SIZE 65536 #ifdef HAVE_MALLOC_TRIM -#define SW_USE_MALLOC_TRIM 1 +#define SW_USE_MALLOC_TRIM 1 #endif -#define SW_MALLOC_TRIM_INTERVAL 60 -#define SW_MALLOC_TRIM_PAD 0 -#define SW_USE_MONOTONIC_TIME 1 +#define SW_MALLOC_TRIM_INTERVAL 60 +#define SW_MALLOC_TRIM_PAD 0 +#define SW_USE_MONOTONIC_TIME 1 -#define SW_MAX_SOCKETS_DEFAULT 1024 +#define SW_MAX_SOCKETS_DEFAULT 1024 -#define SW_SOCKET_OVERFLOW_WAIT 100 -#define SW_SOCKET_MAX_DEFAULT 65536 +#define SW_SOCKET_OVERFLOW_WAIT 100 #if defined(__MACH__) || defined(__FreeBSD__) -#define SW_SOCKET_BUFFER_SIZE 262144 +#define SW_SOCKET_BUFFER_SIZE 262144 #else -#define SW_SOCKET_BUFFER_SIZE 8388608 +#define SW_SOCKET_BUFFER_SIZE 8388608 #endif -#define SW_SOCKET_SEND_TIMEOUT 1.0 -#define SW_SOCKET_ARRAY_INIT_SIZE 1024 -#define SW_SOCKET_SYNC_SEND_RETRY_COUNT 10 +#define SW_SOCKET_RETRY_COUNT 10 -#define SW_SYSTEMD_FDS_START 3 +#define SW_SOCKET_DEFAULT_DNS_TIMEOUT 60 +#define SW_SOCKET_DEFAULT_CONNECT_TIMEOUT 2 +#define SW_SOCKET_DEFAULT_READ_TIMEOUT 60 +#define SW_SOCKET_DEFAULT_WRITE_TIMEOUT 60 -#define SW_GLOBAL_MEMORY_PAGESIZE (2*1024*1024) // global memory page -// #define SW_USE_HUGEPAGE +#define SW_SYSTEMD_FDS_START 3 -#define SW_MAX_THREAD_NCPU 4 // n * cpu_num -#define SW_MAX_WORKER_NCPU 1000 // n * cpu_num +#define SW_GLOBAL_MEMORY_PAGESIZE (2 * 1024 * 1024) // global memory page -#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_MAX_THREAD_NCPU 4 // n * cpu_num +#define SW_MAX_WORKER_NCPU 1000 // n * cpu_num -#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 +#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_CLIENT_BUFFER_SIZE 65536 +#define SW_CLIENT_CONNECT_TIMEOUT 0.5 +#define SW_CLIENT_MAX_PORT 65535 // !!!Don't modify.---------------------------------------------------------- #ifdef __MACH__ -#define SW_IPC_MAX_SIZE 2048 // MacOS +#define SW_IPC_MAX_SIZE 2048 // MacOS #else -#define SW_IPC_MAX_SIZE 8192 // for IPC, dgram and message-queue max size +#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(struct _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 -#define SW_BUFFER_SIZE_BIG 65536 -#define SW_BUFFER_SIZE_UDP 65536 -// #define SW_BUFFER_RECV_TIME +#define SW_BUFFER_SIZE_STD 8192 +#define SW_BUFFER_SIZE_BIG 65536 +#define SW_BUFFER_SIZE_UDP 65536 -#define SW_SENDFILE_CHUNK_SIZE 65536 -#define SW_SENDFILE_MAXLEN 4194304 +#define SW_SENDFILE_CHUNK_SIZE 65536 -#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 -#define SW_DATA_EOF "\r\n\r\n" -#define SW_DATA_EOF_MAXLEN 8 +#define SW_TASKWAIT_TIMEOUT 0.5 -#define SW_TASKWAIT_TIMEOUT 0.5 +#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_EVENT_NUM 128 -#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_WAIT_TIMEOUT 1000 +#define SW_WORKER_MAX_WAIT_TIME 3 +#define SW_WORKER_MIN_REQUEST 10 +#define SW_WORKER_MAX_RECV_CHUNK_COUNT 32 -#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 +#define SW_REACTOR_MAXEVENTS 4096 -#define SW_REACTOR_MAXEVENTS 4096 -#define SW_SESSION_LIST_SIZE (1*1024*1024) +#define SW_SESSION_LIST_SIZE (1 * 1024 * 1024) -#define SW_MSGMAX 65536 -#define SW_UNIXSOCK_MAX_BUF_SIZE (2*1024*1024) +#define SW_MSGMAX 65536 +#define SW_MESSAGE_BOX_SIZE 65536 -#define SW_DGRAM_HEADER_SIZE 32 +#define SW_DGRAM_HEADER_SIZE 32 /** * The maximum number of Reactor threads * the number of the CPU cores threads will be started by default * number 8 is the maximum */ -#define SW_REACTOR_MAX_THREAD 8 +#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 +#define SW_INPUT_BUFFER_SIZE (2 * 1024 * 1024) +#define SW_BUFFER_MIN_SIZE 65536 +#define SW_SEND_BUFFER_SIZE 65536 -/** - * RINGBUFFER - */ -#define SW_RINGQUEUE_LEN 1024 -#define SW_RINGBUFFER_FREE_N_MAX 4 // when free_n > MAX, execute collect -#define SW_RINGBUFFER_WARNING 100 +#define SW_BACKLOG 512 /** - * ringbuffer memory pool size + * max accept times for single time */ -#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 +#define SW_ACCEPT_MAX_COUNT 64 +#define SW_ACCEPT_RETRY_TIME 1.0 -#define SW_BACKLOG 512 +#define SW_TCP_KEEPCOUNT 5 +#define SW_TCP_KEEPIDLE 3600 // 1 hour +#define SW_TCP_KEEPINTERVAL 60 -/** - * max accept times for single time - */ -#define SW_ACCEPT_MAX_COUNT 64 -#define SW_ACCEPT_RETRY_TIME 1.0 +#define SW_TASK_TMP_PATH_SIZE 256 +#define SW_TASK_TMP_DIR "/tmp" +#define SW_TASK_TMP_FILE "swoole.task.XXXXXX" -#define SW_TCP_KEEPCOUNT 5 -#define SW_TCP_KEEPIDLE 3600 // 1 hour -#define SW_TCP_KEEPINTERVAL 60 +#define SW_FILE_CHUNK_SIZE 65536 -#define SW_USE_EVENTFD 1 // Whether to use eventfd for message notification, Linux 2.6.22 or later is required to support +#define SW_TABLE_CONFLICT_PROPORTION 0.2 // 20% +#define SW_TABLE_KEY_SIZE 64 -#define SW_TASK_TMP_FILE "/tmp/swoole.task.XXXXXX" -#define SW_TASK_TMPDIR_SIZE 128 +#define SW_SSL_BUFFER_SIZE 16384 +#define SW_SSL_CIPHER_LIST "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH" +#define SW_SSL_ECDH_CURVE "auto" -#define SW_FILE_CHUNK_SIZE 65536 +#define SW_SPINLOCK_LOOP_N 1024 -#define SW_TABLE_CONFLICT_PROPORTION 0.2 // 20% -#define SW_TABLE_KEY_SIZE 64 +#define SW_SIGNO_MAX 128 +#define SW_UNREGISTERED_SIGNAL_FMT "Unable to find callback function for signal %s" -#define SW_SSL_BUFFER_SIZE 16384 -#define SW_SSL_CIPHER_LIST "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH" -#define SW_SSL_ECDH_CURVE "secp384r1" -#define SW_SSL_NPN_ADVERTISE "\x08http/1.1" -#define SW_SSL_HTTP2_NPN_ADVERTISE "\x02h2" +#define SW_DNS_HOST_BUFFER_SIZE 16 +#define SW_DNS_SERVER_PORT 53 +#define SW_DNS_RESOLV_CONF "/etc/resolv.conf" -#define SW_SPINLOCK_LOOP_N 1024 +#define SW_Z_BEST_SPEED 1 +#define SW_COMPRESSION_MIN_LENGTH_DEFAULT 20 -#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 +#ifndef IOV_MAX +#define IOV_MAX 16 +#endif -#define SW_SIGNO_MAX 128 -#define SW_UNREGISTERED_SIGNAL_FMT "Unable to find callback function for signal %s" +#define SW_IOV_MAX_ERROR_MSG "The maximum of iov count is %d" -#define SW_DNS_HOST_BUFFER_SIZE 16 -#define SW_DNS_SERVER_PORT 53 -#define SW_DNS_DEFAULT_SERVER "8.8.8.8" +#define SW_IOURING_CQES_SIZE 8192 /** * HTTP Protocol */ -#define SW_HTTP_SERVER_SOFTWARE "swoole-http-server" -#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 -#define SW_HTTP_UPLOAD_TMPDIR_SIZE 256 -#define SW_HTTP_DATE_FORMAT "D, d M Y H:i:s T" -#define SW_HTTP_RFC1123_DATE_GMT "%a, %d %b %Y %T GMT" -#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_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_RESPONSE_INIT_SIZE 65536 +#define SW_HTTP_HEADER_MAX_SIZE 65536 +#define SW_HTTP_HEADER_KEY_SIZE 128 +#define SW_HTTP_UPLOAD_TMPDIR_SIZE SW_TASK_TMP_PATH_SIZE +#define SW_HTTP_DATE_FORMAT "D, d M Y H:i:s T" +#define SW_HTTP_RFC1123_DATE_GMT "%a, %d %b %Y %T GMT" +#define SW_HTTP_RFC1123_DATE_UTC "%a, %d %b %Y %T UTC" +#define SW_HTTP_RFC850_DATE "%A, %d-%b-%y %T GMT" +#define SW_HTTP_ASCTIME_DATE "%a %b %e %T %Y" +#define SW_HTTP_UPLOAD_FILE "Swoole-Upload-File" +#define SW_HTTP_CHUNK_EOF "0\r\n\r\n" +#define SW_HTTP_DEFAULT_CONTENT_TYPE "text/html" +#define SW_HTTP_MAX_APPEND_DATA 16384 // #define SW_HTTP_100_CONTINUE -#define SW_HTTP_100_CONTINUE_PACKET "HTTP/1.1 100 Continue\r\n\r\n" -#define SW_HTTP_BAD_REQUEST_PACKET "HTTP/1.1 400 Bad Request\r\n\r\n" -#define SW_HTTP_REQUEST_ENTITY_TOO_LARGE_PACKET "HTTP/1.1 413 Request Entity Too Large\r\n\r\n" -#define SW_HTTP_SERVICE_UNAVAILABLE_PACKET "HTTP/1.1 503 Service Unavailable\r\n\r\n" -#define SW_HTTP_PAGE_400 "

HTTP 400 Bad Request


Powered by Swoole" -#define SW_HTTP_PAGE_404 "

HTTP 404 Not Found


Powered by Swoole" +#define SW_HTTP_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_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_HTTP_CLIENT_USERAGENT "swoole-http-client" -#define SW_HTTP_CLIENT_BOUNDARY_PREKEY "----SwooleBoundary" -#define SW_HTTP_CLIENT_BOUNDARY_TOTAL_SIZE 39 -#define SW_HTTP_FORM_RAW_DATA_FMT "--%.*s\r\nContent-Disposition: form-data; name=\"%.*s\"\r\n\r\n" -#define SW_HTTP_FORM_RAW_DATA_FMT_LEN 8 -#define SW_HTTP_FORM_FILE_DATA_FMT "--%.*s\r\nContent-Disposition: form-data; name=\"%.*s\"; filename=\"%.*s\"\r\nContent-Type: %.*s\r\n\r\n" -#define SW_HTTP_FORM_FILE_DATA_FMT_LEN 16 - -#define SW_WEBSOCKET_VERSION "13" -#define SW_WEBSOCKET_KEY_LENGTH 16 -#define SW_WEBSOCKET_QUEUE_SIZE 16 -#define SW_WEBSOCKET_EXTENSION_DEFLATE "permessage-deflate; client_no_context_takeover; server_no_context_takeover" +#define SW_HTTP2_DATA_BUFFER_SIZE 8192 +#define SW_HTTP2_DEFAULT_HEADER_TABLE_SIZE (1 << 12) +#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" +#define SW_HTTP_CLIENT_BOUNDARY_TOTAL_SIZE 39 +#define SW_HTTP_FORM_RAW_DATA_FMT "--%.*s\r\nContent-Disposition: form-data; name=\"%.*s\"\r\n\r\n" +#define SW_HTTP_FORM_RAW_DATA_FMT_LEN 8 +#define SW_HTTP_FORM_FILE_DATA_FMT \ + "--%.*s\r\nContent-Disposition: form-data; name=\"%.*s\"; filename=\"%.*s\"\r\nContent-Type: %.*s\r\n\r\n" +#define SW_HTTP_FORM_FILE_DATA_FMT_LEN 16 + +#define SW_WEBSOCKET_VERSION "13" +#define SW_WEBSOCKET_KEY_LENGTH 16 +#define SW_WEBSOCKET_QUEUE_SIZE 16 +#define SW_WEBSOCKET_EXTENSION_DEFLATE "permessage-deflate; client_no_context_takeover; server_no_context_takeover" /** * MySQL Client */ -#define SW_MYSQL_DEFAULT_HOST "127.0.0.1" -#define SW_MYSQL_DEFAULT_PORT 3306 -#define SW_MYSQL_DEFAULT_CHARSET 33 // 0x21, utf8_general_ci +#define SW_MYSQL_DEFAULT_HOST "127.0.0.1" +#define SW_MYSQL_DEFAULT_PORT 3306 +#define SW_MYSQL_DEFAULT_CHARSET 33 // 0x21, utf8_general_ci /** * PGSQL Client */ -#define SW_PGSQL_CONNECT_TIMEOUT 3.0 +#define SW_PGSQL_CONNECT_TIMEOUT 3.0 /** * Coroutine */ -#define SW_DEFAULT_C_STACK_SIZE (2 *1024 * 1024) -#define SW_CORO_SUPPORT_BAILOUT 1 -#define SW_CORO_SWAP_BAILOUT 1 +#define SW_DEFAULT_C_STACK_SIZE (2 * 1024 * 1024) +#define SW_CORO_BAILOUT_EXIT_CODE 1 +//#define SW_CONTEXT_PROTECT_STACK_PAGE 1 +//#define SW_CONTEXT_DETECT_STACK_USAGE 1 #ifdef SW_DEBUG #ifndef SW_LOG_TRACE_OPEN -#define SW_LOG_TRACE_OPEN 1 +#define SW_LOG_TRACE_OPEN 1 #endif #endif diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h new file mode 100644 index 00000000000..3749fd65bb9 --- /dev/null +++ b/include/swoole_coroutine.h @@ -0,0 +1,334 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Twosee | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole_api.h" +#include "swoole_string.h" +#include "swoole_socket.h" +#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 +#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: + 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); + constexpr static long MAX_NUM_LIMIT = LONG_MAX; + + enum State { + STATE_INIT = 0, + STATE_WAITING, + STATE_RUNNING, + STATE_END, + }; + + enum Error { + ERR_END = 0, + ERR_LIMIT = -1, + ERR_INVALID = -2, + }; + + enum ResumeCode { + RC_OK = 0, + RC_TIMEDOUT = -1, + RC_CANCELED = -2, + }; + + typedef void (*SwapCallback)(void *); + typedef std::function BailoutCallback; + typedef std::function CancelFunc; + + void resume(); + void yield(); + void yield(CancelFunc *cancel_fn); + bool cancel(); + + bool yield_ex(double timeout = -1); + + enum State get_state() const { + return state; + } + + long get_init_msec() const { + return init_msec; + } + + long get_cid() const { + return cid; + } + + Coroutine *get_origin() { + return origin; + } + + long get_origin_cid() { + return sw_likely(origin) ? origin->get_cid() : -1; + } + + void *get_task() { + return task; + } + + bool is_end() { + return ctx.is_end(); + } + + 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; + } + + 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 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)) { + swoole_fatal_error(SW_ERROR_CO_OUT_OF_COROUTINE, "API must be called in the coroutine"); + } + return current; + } + + static inline void *get_current_task() { + return sw_likely(current) ? current->get_task() : nullptr; + } + + static inline long get_current_cid() { + return sw_likely(current) ? current->get_cid() : -1; + } + + static inline Coroutine *get_by_cid(long cid) { + auto i = coroutines.find(cid); + return sw_likely(i != coroutines.end()) ? i->second : nullptr; + } + + static inline void *get_task_by_cid(long cid) { + Coroutine *co = get_by_cid(cid); + return sw_likely(co) ? co->get_task() : nullptr; + } + + static inline size_t get_stack_size() { + return stack_size; + } + + static inline void set_stack_size(size_t size) { + stack_size = SW_MEM_ALIGNED_SIZE_EX(SW_MAX(MIN_STACK_SIZE, SW_MIN(size, MAX_STACK_SIZE)), STACK_ALIGNED_SIZE); + } + + static inline long get_last_cid() { + return last_cid; + } + + static inline size_t count() { + return coroutines.size(); + } + + static inline uint64_t get_peak_num() { + return peak_num; + } + + static inline long get_elapsed(long cid) { + Coroutine *co = cid == 0 ? get_current() : get_by_cid(cid); + 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 SW_THREAD_LOCAL Coroutine *current; + static SW_THREAD_LOCAL long last_cid; + static SW_THREAD_LOCAL uint64_t peak_num; + static SW_THREAD_LOCAL size_t stack_size; + static SW_THREAD_LOCAL SwapCallback on_yield; /* before yield */ + static SW_THREAD_LOCAL SwapCallback on_resume; /* before resume */ + static SW_THREAD_LOCAL SwapCallback on_close; /* before close */ + static SW_THREAD_LOCAL BailoutCallback on_bailout; /* when bailout */ + static SW_THREAD_LOCAL bool activated; + + enum State state = STATE_INIT; + enum ResumeCode resume_code_ = RC_OK; + long cid; + long init_msec = Timer::get_absolute_msec(); + long switch_usec = time(true); + long execute_usec = 0; + void *task = nullptr; + coroutine::Context ctx; + Coroutine *origin = nullptr; + CancelFunc *cancel_fn_ = nullptr; + + Coroutine(const CoroutineFunc &fn, void *private_data) : ctx(stack_size, fn, private_data) { + cid = ++last_cid; + coroutines[cid] = this; + if (sw_unlikely(count() > peak_num)) { + peak_num = count(); + } + if (!activated) { + activate(); + } + } + + Coroutine(long _cid, const CoroutineFunc &fn, void *private_data) : ctx(stack_size, fn, private_data) { + cid = _cid; + } + + 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; + } + + void check_end() { + if (ctx.is_end()) { + close(); + } else if (sw_unlikely(on_bailout)) { + SW_ASSERT(current == nullptr); + on_bailout(); + } + } + + void close(); +}; +//------------------------------------------------------------------------------- +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); +/** + * 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 + +/** + * for gdb + */ +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 new file mode 100644 index 00000000000..10711d273b3 --- /dev/null +++ b/include/swoole_coroutine_c_api.h @@ -0,0 +1,139 @@ +/* + +----------------------------------------------------------------------+ + | 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 SW_COROUTINE_API_H_ +#define SW_COROUTINE_API_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +extern int fdatasync(int); +#endif + +/** + * base + */ +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); +/** + * file + */ +int swoole_coroutine_access(const char *pathname, int mode); +int swoole_coroutine_open(const char *pathname, int flags, mode_t mode); +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); +int swoole_coroutine_rmdir(const char *pathname); +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 + */ +DIR *swoole_coroutine_opendir(const char *name); +struct dirent *swoole_coroutine_readdir(DIR *dirp); +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); +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, + const struct addrinfo *req, + struct addrinfo **pai); +struct hostent *swoole_coroutine_gethostbyname(const char *name); +/** + * wait + */ +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); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif +#endif diff --git a/include/swoole_coroutine_channel.h b/include/swoole_coroutine_channel.h new file mode 100644 index 00000000000..0624e9aac13 --- /dev/null +++ b/include/swoole_coroutine_channel.h @@ -0,0 +1,150 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Twosee | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole.h" +#include "swoole_coroutine.h" + +#include + +#include +#include +#include +#include + +namespace swoole { +namespace coroutine { +//------------------------------------------------------------------------------- +class Channel { + public: + enum Opcode { + PRODUCER = 1, + CONSUMER = 2, + }; + + enum ErrorCode { + ERROR_OK = 0, + ERROR_TIMEOUT = -1, + ERROR_CLOSED = -2, + ERROR_CANCELED = -3, + }; + + struct TimeoutMessage { + Channel *chan; + Opcode type; + Coroutine *co; + bool error; + TimerNode *timer; + }; + + void *pop(double timeout = -1); + bool push(void *data, double timeout = -1); + bool close(); + + Channel(size_t _capacity = 1) : capacity(_capacity) {} + + ~Channel() { + if (!producer_queue.empty()) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_CO_HAS_BEEN_DISCARDED, + "channel is destroyed, %zu producers will be discarded", + producer_queue.size()); + } + if (!consumer_queue.empty()) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_CO_HAS_BEEN_DISCARDED, + "channel is destroyed, %zu consumers will be discarded", + consumer_queue.size()); + } + } + + bool is_closed() { + return closed; + } + + bool is_empty() { + return data_queue.size() == 0; + } + + bool is_full() { + return data_queue.size() == capacity; + } + + size_t length() { + return data_queue.size(); + } + + size_t consumer_num() { + return consumer_queue.size(); + } + + size_t producer_num() { + return producer_queue.size(); + } + + void *pop_data() { + if (data_queue.size() == 0) { + return nullptr; + } + void *data = data_queue.front(); + data_queue.pop(); + 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 consumer_remove(Coroutine *co) { + consumer_queue.remove(co); + } + + void producer_remove(Coroutine *co) { + producer_queue.remove(co); + } + + Coroutine *pop_coroutine(enum Opcode type) { + Coroutine *co; + if (type == PRODUCER) { + co = producer_queue.front(); + producer_queue.pop_front(); + 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(); + swoole_trace_log(SW_TRACE_CHANNEL, "resume consumer cid=%ld", co->get_cid()); + } + return co; + } +}; +//------------------------------------------------------------------------------- +} // namespace coroutine +} // namespace swoole diff --git a/include/swoole_coroutine_context.h b/include/swoole_coroutine_context.h new file mode 100644 index 00000000000..0dffd6f326c --- /dev/null +++ b/include/swoole_coroutine_context.h @@ -0,0 +1,101 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Twosee | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole.h" + +#ifdef SW_USE_THREAD_CONTEXT +#include +#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 +#include "swoole_asm_context.h" +#endif + +#if defined(HAVE_VALGRIND) && !defined(HAVE_KQUEUE) +#define USE_VALGRIND 1 +#include +#endif + +#ifdef USE_UCONTEXT +typedef ucontext_t coroutine_context_t; +#elif defined(USE_ASM_CONTEXT) +typedef fcontext_t coroutine_context_t; +typedef transfer_t coroutine_transfer_t; +#endif + +#if defined(USE_UCONTEXT) || defined(SW_USE_THREAD_CONTEXT) +typedef void * coroutine_transfer_t; +#endif + +typedef std::function CoroutineFunc; + +namespace swoole { +namespace coroutine { + +class Context { + public: + 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 +#ifndef SW_USE_THREAD_CONTEXT + char *get_stack() const { + return stack_; + } + + size_t get_stack_size() const { + return stack_size_; + } +#endif + bool is_end() const { + return end_; + } + + protected: + CoroutineFunc fn_; +#ifdef SW_USE_THREAD_CONTEXT + std::thread thread_; + std::mutex lock_; + std::mutex *swap_lock_; +#else + coroutine_context_t ctx_; + coroutine_context_t swap_ctx_; + char *stack_; + uint32_t stack_size_; +#endif +#ifdef USE_VALGRIND + uint32_t valgrind_stack_id; +#endif + void *private_data_; + bool end_; + + static void context_func(coroutine_transfer_t arg); +}; + +} // namespace coroutine +} // namespace swoole diff --git a/include/swoole_coroutine_socket.h b/include/swoole_coroutine_socket.h new file mode 100644 index 00000000000..f5eff4a307a --- /dev/null +++ b/include/swoole_coroutine_socket.h @@ -0,0 +1,639 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | 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" + +#include + +namespace swoole { +namespace coroutine { +//------------------------------------------------------------------------------- +/** + * @return true: continue to wait for events + * @return false: stop event waiting and resume coroutine + */ +using EventBarrier = std::function; + +class Socket { + public: + int errCode = 0; + const char *errMsg = ""; + std::string errString; + + bool open_length_check = false; + bool open_eof_check = false; + bool http2 = false; + + Protocol protocol = {}; + Socks5Proxy *socks5_proxy = nullptr; + HttpProxy *http_proxy = nullptr; + + enum TimeoutType { + TIMEOUT_DNS = 1 << 0, + TIMEOUT_CONNECT = 1 << 1, + TIMEOUT_READ = 1 << 2, + TIMEOUT_WRITE = 1 << 3, + TIMEOUT_RDWR = TIMEOUT_READ | TIMEOUT_WRITE, + TIMEOUT_ALL = TIMEOUT_DNS | TIMEOUT_CONNECT | TIMEOUT_RDWR, + }; + + static enum TimeoutType timeout_type_list[4]; + + Socket(int domain, int type, int protocol); + Socket(int _fd, int _domain, int _type, int _protocol); + Socket(SocketType type = SW_SOCK_TCP); + Socket(int _fd, SocketType _type); + ~Socket(); + bool connect(std::string host, int port = 0, int flags = 0); + bool connect(const struct sockaddr *addr, socklen_t addrlen); + bool shutdown(int how = SHUT_RDWR); + bool cancel(const EventType event); + bool close(); + + 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(); + ssize_t peek(void *__buf, size_t __n); + ssize_t recv(void *__buf, size_t __n); + ssize_t send(const void *__buf, size_t __n); + + ssize_t send(const std::string &buf) { + return send(buf.c_str(), buf.length()); + } + + ssize_t read(void *__buf, size_t __n); + ssize_t write(const void *__buf, size_t __n); + ssize_t readv(network::IOVector *io_vector); + ssize_t readv_all(network::IOVector *io_vector); + ssize_t writev(network::IOVector *io_vector); + ssize_t writev_all(network::IOVector *io_vector); + ssize_t recvmsg(struct msghdr *msg, int flags); + ssize_t sendmsg(const struct msghdr *msg, int flags); + 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); + + char *pop_packet() { + if (read_buffer->offset == 0) { + return nullptr; + } else { + return read_buffer->pop(buffer_init_size); + } + } + + 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); + bool listen(int backlog = 0); + bool sendfile(const char *filename, off_t offset, size_t length); + ssize_t sendto(const std::string &host, int port, const void *__buf, size_t __n); + ssize_t recvfrom(void *__buf, size_t __n); + ssize_t recvfrom(void *__buf, size_t __n, struct sockaddr *_addr, socklen_t *_socklen); + +#ifdef SW_USE_OPENSSL + /** + * Operation sequence: + * 1. enable_ssl_encrypt() + * 2. Set SSL parameters, such as certificate file, key file + * 3. ssl_handshake(), to be executed after connect or accept + */ + bool enable_ssl_encrypt() { + if (ssl_context.get()) { + return false; + } + ssl_context.reset(new SSLContext()); + return true; + } + + bool ssl_is_enable() { + return get_ssl_context() != nullptr; + } + + SSLContext *get_ssl_context() { + return ssl_context.get(); + } + + 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_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); + } + + SocketType get_type() { + return type; + } + + FdType get_fd_type() { + return socket->fd_type; + } + + int get_sock_domain() { + return sock_domain; + } + + int get_sock_type() { + return sock_type; + } + + int get_sock_protocol() { + return sock_protocol; + } + + int get_fd() { + return sock_fd; + } + + int get_bind_port() { + return bind_port; + } + + network::Socket *get_socket() { + return socket; + } + + bool getsockname(network::Address *sa); + bool getpeername(network::Address *sa); + + const char *get_ip() { + return socket->info.get_ip(); + } + + int get_port() { + return socket->info.get_port(); + } + + bool has_bound(const EventType event = SW_EVENT_RDWR) { + return get_bound_co(event) != nullptr; + } + + Coroutine *get_bound_co(const EventType event) { + if (event & SW_EVENT_READ) { + if (read_co) { + return read_co; + } + } + if (event & SW_EVENT_WRITE) { + if (write_co) { + return write_co; + } + } + return nullptr; + } + + 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 EventType event) { + if (event == SW_EVENT_READ) { + return "reading"; + } else if (event == SW_EVENT_WRITE) { + return "writing"; + } else { + return read_co && write_co ? "reading or writing" : (read_co ? "reading" : "writing"); + } + } + + void check_bound_co(const EventType event) { + long cid = get_bound_cid(event); + if (sw_unlikely(cid)) { + swoole_fatal_error(SW_ERROR_CO_HAS_BEEN_BOUND, + "Socket#%d has already been bound to another coroutine#%ld, " + "%s of the same socket in coroutine#%ld at the same time is not allowed", + sock_fd, + cid, + get_event_str(event), + Coroutine::get_current_cid()); + } + } + + void set_err(int e) { + errCode = errno = e; + swoole_set_last_error(errCode); + errMsg = e ? swoole_strerror(e) : ""; + } + + void set_err(int e, const char *s) { + errCode = errno = e; + swoole_set_last_error(errCode); + errMsg = s; + } + + void set_err(int e, std::string s) { + errCode = errno = e; + swoole_set_last_error(errCode); + errString = s; + errMsg = errString.c_str(); + } + + /* set connect read write timeout */ + void set_timeout(double timeout, int type = TIMEOUT_ALL) { + if (timeout == 0) { + return; + } + if (type & TIMEOUT_DNS) { + dns_timeout = timeout; + } + if (type & TIMEOUT_CONNECT) { + connect_timeout = timeout; + } + if (type & TIMEOUT_READ) { + read_timeout = timeout; + } + if (type & TIMEOUT_WRITE) { + write_timeout = timeout; + } + } + + void set_timeout(struct timeval *timeout, int type = TIMEOUT_ALL) { + set_timeout((double) timeout->tv_sec + ((double) timeout->tv_usec / 1000 / 1000), type); + } + + double get_timeout(enum TimeoutType type = TIMEOUT_ALL) { + SW_ASSERT_1BYTE(type); + if (type == TIMEOUT_DNS) { + return dns_timeout; + } else if (type == TIMEOUT_CONNECT) { + return connect_timeout; + } else if (type == TIMEOUT_READ) { + return read_timeout; + } else if (type == TIMEOUT_WRITE) { + return write_timeout; + } else { + assert(0); + return -1; + } + } + + bool set_option(int level, int optname, int optval) { + if (socket->set_option(level, optname, optval) < 0) { + swoole_sys_warning("setsockopt(%d, %d, %d, %d) failed", sock_fd, level, optname, optval); + return false; + } + return true; + } + + String *get_read_buffer() { + if (sw_unlikely(!read_buffer)) { + read_buffer = make_string(SW_BUFFER_SIZE_BIG, buffer_allocator); + if (!read_buffer) { + throw std::bad_alloc(); + } + } + return read_buffer; + } + + String *get_write_buffer() { + if (sw_unlikely(!write_buffer)) { + write_buffer = make_string(SW_BUFFER_SIZE_BIG, buffer_allocator); + if (!write_buffer) { + throw std::bad_alloc(); + } + } + return write_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; + } + auto tmp = read_buffer; + read_buffer = nullptr; + return tmp; + } + + String *pop_write_buffer() { + if (sw_unlikely(!write_buffer)) { + return nullptr; + } + auto tmp = write_buffer; + write_buffer = nullptr; + return tmp; + } + + void set_zero_copy(bool enable) { + zero_copy = enable; + } + + void set_buffer_allocator(const Allocator *allocator) { + buffer_allocator = allocator; + } + + 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 + bool ssl_is_available() { + return socket && ssl_handshaked; + } + + SSL *get_ssl() { + return socket->ssl; + } + + bool ssl_shutdown(); +#endif + + private: + SocketType type; + network::Socket *socket = nullptr; + int sock_domain = 0; + int sock_type = 0; + int sock_protocol = 0; + int sock_fd = -1; + + Coroutine *read_co = nullptr; + Coroutine *write_co = nullptr; +#ifdef SW_USE_OPENSSL + EventType want_event = SW_EVENT_NULL; +#endif + + std::string connect_host; + int connect_port = 0; + + std::string bind_address; + int bind_port = 0; + int backlog = 0; + + double dns_timeout = network::Socket::default_dns_timeout; + double connect_timeout = network::Socket::default_connect_timeout; + double read_timeout = network::Socket::default_read_timeout; + double write_timeout = network::Socket::default_write_timeout; + TimerNode *read_timer = nullptr; + TimerNode *write_timer = nullptr; + + const Allocator *buffer_allocator = nullptr; + size_t buffer_init_size = SW_BUFFER_SIZE_BIG; + String *read_buffer = nullptr; + String *write_buffer = nullptr; + network::Address bind_address_info = {}; + + EventBarrier *recv_barrier = nullptr; + EventBarrier *send_barrier = nullptr; + +#ifdef SW_USE_OPENSSL + bool ssl_is_server = false; + 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 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); + static int readable_event_callback(Reactor *reactor, Event *event); + static int writable_event_callback(Reactor *reactor, Event *event); + static int error_event_callback(Reactor *reactor, Event *event); + + void init_sock_type(SocketType _type); + bool init_sock(); + bool init_reactor_socket(int fd); + + 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); + } + protocol.package_length_type = 'N'; + protocol.package_length_size = 4; + protocol.package_length_offset = 0; + protocol.package_body_offset = 0; + protocol.package_max_length = SW_INPUT_BUFFER_SIZE; + } + + 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(); + + bool is_available(const EventType event) { + if (event != SW_EVENT_NULL) { + check_bound_co(event); + } + 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; + } + + bool socks5_handshake(); + bool http_proxy_handshake(); + + class TimerController { + public: + 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(timeout, false, callback, socket_); + return *timer_pp != nullptr; + } + *timer_pp = (TimerNode *) -1; + } + return true; + } + ~TimerController() { + if (enabled && *timer_pp) { + if (*timer_pp != (TimerNode *) -1) { + swoole_timer_del(*timer_pp); + } + *timer_pp = nullptr; + } + } + + private: + bool enabled = false; + TimerNode **timer_pp; + double timeout; + Socket *socket_; + TimerCallback callback; + }; + + public: + class TimeoutSetter { + public: + 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]) { + original_timeout[i] = socket->get_timeout(timeout_type_list[i]); + if (_timeout != original_timeout[i]) { + socket->set_timeout(_timeout, timeout_type_list[i]); + } + } + } + } + ~TimeoutSetter() { + if (timeout == 0) { + return; + } + for (uint8_t i = 0; i < SW_ARRAY_SIZE(timeout_type_list); i++) { + if (type & timeout_type_list[i]) { + if (timeout != original_timeout[i]) { + socket_->set_timeout(original_timeout[i], timeout_type_list[i]); + } + } + } + } + + protected: + Socket *socket_; + double timeout; + enum TimeoutType type; + double original_timeout[sizeof(timeout_type_list)] = {}; + }; + + class TimeoutController : public TimeoutSetter { + public: + 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(); + } else { + double used_time = microtime() - startup_time; + if (sw_unlikely(timeout - used_time < SW_TIMER_MIN_SEC)) { + socket_->set_err(ETIMEDOUT); + return true; + } + socket_->set_timeout(timeout - used_time, _type); + } + } + return false; + } + + protected: + double startup_time = 0; + }; +}; + +class ProtocolSwitch { + private: + bool ori_open_eof_check; + bool ori_open_length_check; + Protocol ori_protocol; + Socket *socket_; + + public: + ProtocolSwitch(Socket *socket) { + ori_open_eof_check = socket->open_eof_check; + ori_open_length_check = socket->open_length_check; + ori_protocol = socket->protocol; + socket_ = socket; + } + + ~ProtocolSwitch() { + /* revert protocol settings */ + socket_->open_eof_check = ori_open_eof_check; + socket_->open_length_check = ori_open_length_check; + socket_->protocol = ori_protocol; + } +}; + +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 + +std::shared_ptr swoole_coroutine_get_socket_object(int sockfd); diff --git a/include/swoole_coroutine_system.h b/include/swoole_coroutine_system.h new file mode 100644 index 00000000000..e55d9913ec3 --- /dev/null +++ b/include/swoole_coroutine_system.h @@ -0,0 +1,95 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Twosee | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole_coroutine.h" +#include "swoole_file.h" + +#include + +namespace swoole { +namespace coroutine { +//------------------------------------------------------------------------------- +struct PollSocket { + int16_t events; + int16_t revents; + void *ptr; + network::Socket *socket; + + PollSocket(int16_t _event, void *_ptr) { + events = _event; + ptr = _ptr; + revents = 0; + socket = nullptr; + } +}; + +int translate_events_to_poll(int events); +int translate_events_from_poll(int events); + +class System { + public: + static void init_reactor(Reactor *reactor); + /* sleep */ + static int sleep(double sec); + /* file */ + static std::shared_ptr read_file(const char *file, bool lock = false); + static ssize_t write_file(const char *file, char *buf, size_t length, bool lock = 0, int flags = 0); + /* dns */ + static std::string gethostbyname(const std::string &hostname, int domain, double timeout = -1); + static std::vector getaddrinfo(const std::string &hostname, + int family = AF_INET, + int socktype = SOCK_STREAM, + int protocol = IPPROTO_TCP, + const std::string &service = "", + double timeout = -1); + static void set_dns_cache_expire(time_t expire); + static void set_dns_cache_capacity(size_t capacity); + static void clear_dns_cache(); + /* multiplexing */ + static bool socket_poll(std::unordered_map &fds, double timeout); + /* 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 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_cxx.h b/include/swoole_cxx.h deleted file mode 100644 index a55f363dbf9..00000000000 --- a/include/swoole_cxx.h +++ /dev/null @@ -1,126 +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 | - | Twosee | - +----------------------------------------------------------------------+ -*/ - -#pragma once - -#include "swoole.h" - -#include -#include -#include -#include -#include -#include -#include - -namespace swoole { -//------------------------------------------------------------------------------- -namespace cpp_string -{ -template -inline std::string format(const char *format, Args ...args) -{ - size_t size = snprintf(nullptr, 0, format, args...) + 1; // Extra space for '\0' - std::unique_ptr buf(new char[size]); - snprintf(buf.get(), size, format, args...); - return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside -} - -inline std::string vformat(const char *format, va_list args) -{ - va_list _args; - va_copy(_args, args); - size_t size = vsnprintf(nullptr, 0, format, _args) + 1; // Extra space for '\0' - va_end(_args); - std::unique_ptr buf(new char[size]); - vsnprintf(buf.get(), size, format, args); - return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside -} -} - -struct Callback -{ - swCallback callback; - void *private_data; - - Callback(swCallback cb, void *_private_data) - { - callback = cb; - private_data = _private_data; - } -}; - -class CallbackManager -{ -public: - inline void append(swCallback cb, void *private_data) - { - list_.push_back(new Callback(cb, private_data)); - } - inline void prepend(swCallback cb, void *private_data) - { - list_.push_front(new Callback(cb, private_data)); - } - inline void execute() - { - while (!list_.empty()) - { - Callback *task = list_.front(); - list_.pop_front(); - task->callback(task->private_data); - delete task; - } - } -protected: - std::list list_; -}; - -static inline int hook_add(void **hooks, int type, swCallback func, int push_back) -{ - if (hooks[type] == NULL) - { - hooks[type] = new std::list; - } - - std::list *l = static_cast*>(hooks[type]); - if (push_back) - { - l->push_back(func); - } - else - { - l->push_front(func); - } - - return SW_OK; -} - -static inline void hook_call(void **hooks, int type, void *arg) -{ - std::list *l = static_cast*>(hooks[type]); - for (auto i = l->begin(); i != l->end(); i++) - { - (*i)(arg); - } -} - -typedef std::function StringExplodeHandler; -size_t string_split(swString *str, const char *delimiter, size_t delimiter_length, const StringExplodeHandler &handler); -std::string intersection(std::vector &vec1, std::set &vec2); - -//------------------------------------------------------------------------------- -} diff --git a/include/swoole_dtls.h b/include/swoole_dtls.h new file mode 100644 index 00000000000..4a7046babd5 --- /dev/null +++ b/include/swoole_dtls.h @@ -0,0 +1,83 @@ +/* + +----------------------------------------------------------------------+ + | 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_ssl.h" + +#ifdef SW_SUPPORT_DTLS +#include + +namespace swoole { +namespace dtls { +//------------------------------------------------------------------------------- +using Socket = network::Socket; + +int BIO_write(BIO *b, const char *data, int dlen); +int BIO_read(BIO *b, char *data, int dlen); +long BIO_ctrl(BIO *b, int cmd, long larg, void *pargs); +int BIO_create(BIO *b); +int BIO_destroy(BIO *b); +BIO_METHOD *BIO_get_methods(void); +void BIO_meth_free(void); + +struct Buffer { + uint16_t length; + uchar data[0]; +}; + +struct Session { + SSLContext *ctx; + bool listened = false; + Socket *socket; + std::deque rxqueue; + bool peek_mode = false; + + Session(Socket *_sock, SSLContext *_ctx) { + socket = _sock; + ctx = _ctx; + } + + ~Session() { + while (!rxqueue.empty()) { + Buffer *buffer = rxqueue.front(); + rxqueue.pop_front(); + sw_free(buffer); + } + } + + bool init(); + bool listen(); + + void append(const char *data, ssize_t len); + + void append(Buffer *buffer) { + rxqueue.push_back(buffer); + } + + size_t get_buffer_length() { + size_t total_length = 0; + for (auto i : rxqueue) { + total_length += i->length; + } + return total_length; + } +}; +//------------------------------------------------------------------------------- +} // namespace dtls +} // namespace swoole +#endif diff --git a/include/swoole_error.h b/include/swoole_error.h new file mode 100644 index 00000000000..48862ebf295 --- /dev/null +++ b/include/swoole_error.h @@ -0,0 +1,216 @@ +/* + +----------------------------------------------------------------------+ + | 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 + +enum swErrorCode { + /** + * Prevent repetition with errno [syscall error] + */ + SW_ERROR_BEGIN = 500, + + /** + * common error + */ + SW_ERROR_MALLOC_FAIL = 501, + SW_ERROR_SYSTEM_CALL_FAIL, + SW_ERROR_PHP_FATAL_ERROR, + SW_ERROR_NAME_TOO_LONG, + SW_ERROR_INVALID_PARAMS, + SW_ERROR_QUEUE_FULL, + 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 = 710, + SW_ERROR_DNSLOOKUP_RESOLVE_FAILED, + SW_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT, + 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, + + /** + * connection error + */ + SW_ERROR_SESSION_CLOSED_BY_SERVER = 1001, + SW_ERROR_SESSION_CLOSED_BY_CLIENT, + SW_ERROR_SESSION_CLOSING, + SW_ERROR_SESSION_CLOSED, + SW_ERROR_SESSION_NOT_EXIST, + SW_ERROR_SESSION_INVALID_ID, + SW_ERROR_SESSION_DISCARD_TIMEOUT_DATA, + SW_ERROR_SESSION_DISCARD_DATA, + SW_ERROR_OUTPUT_BUFFER_OVERFLOW, + SW_ERROR_OUTPUT_SEND_YIELD, + SW_ERROR_SSL_NOT_READY, + SW_ERROR_SSL_CANNOT_USE_SENFILE, + SW_ERROR_SSL_EMPTY_PEER_CERTIFICATE, + SW_ERROR_SSL_VERIFY_FAILED, + SW_ERROR_SSL_BAD_CLIENT, + 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 + */ + SW_ERROR_TASK_PACKAGE_TOO_BIG = 2001, + SW_ERROR_TASK_DISPATCH_FAIL, + SW_ERROR_TASK_TIMEOUT, + + /** + * http2 protocol error + */ + SW_ERROR_HTTP2_STREAM_ID_TOO_BIG = 3001, + 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 + */ + SW_ERROR_AIO_BAD_REQUEST = 4001, + SW_ERROR_AIO_CANCELED, + SW_ERROR_AIO_TIMEOUT, + + /** + * Client + */ + SW_ERROR_CLIENT_NO_CONNECTION = 5001, + + /** + * Socket + */ + SW_ERROR_SOCKET_CLOSED = 6001, + SW_ERROR_SOCKET_POLL_TIMEOUT, + + /** + * Proxy + */ + SW_ERROR_SOCKS5_UNSUPPORT_VERSION = 7001, + SW_ERROR_SOCKS5_UNSUPPORT_METHOD, + SW_ERROR_SOCKS5_AUTH_FAILED, + SW_ERROR_SOCKS5_SERVER_ERROR, + SW_ERROR_SOCKS5_HANDSHAKE_FAILED, + + SW_ERROR_HTTP_PROXY_HANDSHAKE_ERROR = 7101, + 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 + */ + SW_ERROR_SERVER_MUST_CREATED_BEFORE_CLIENT = 9001, + SW_ERROR_SERVER_TOO_MANY_SOCKET, + SW_ERROR_SERVER_WORKER_TERMINATED, + SW_ERROR_SERVER_INVALID_LISTEN_PORT, + SW_ERROR_SERVER_TOO_MANY_LISTEN_PORT, + SW_ERROR_SERVER_PIPE_BUFFER_FULL, + SW_ERROR_SERVER_NO_IDLE_WORKER, + SW_ERROR_SERVER_ONLY_START_ONE, + 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 = 9101, + SW_ERROR_SERVER_WORKER_ABNORMAL_PIPE_DATA, + SW_ERROR_SERVER_WORKER_UNPROCESSED_DATA, + + /** + * Coroutine + */ + SW_ERROR_CO_OUT_OF_COROUTINE = 10001, + SW_ERROR_CO_HAS_BEEN_BOUND, + SW_ERROR_CO_HAS_BEEN_DISCARDED, + + SW_ERROR_CO_MUTEX_DOUBLE_UNLOCK, + SW_ERROR_CO_BLOCK_OBJECT_LOCKED, + SW_ERROR_CO_BLOCK_OBJECT_WAITING, + SW_ERROR_CO_YIELD_FAILED, + SW_ERROR_CO_GETCONTEXT_FAILED, + SW_ERROR_CO_SWAPCONTEXT_FAILED, + SW_ERROR_CO_MAKECONTEXT_FAILED, + + SW_ERROR_CO_IOCPINIT_FAILED, + SW_ERROR_CO_PROTECT_STACK_FAILED, + 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 new file mode 100644 index 00000000000..bca2711f0d3 --- /dev/null +++ b/include/swoole_file.h @@ -0,0 +1,180 @@ +/* + +----------------------------------------------------------------------+ + | 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_string.h" + +#include +#include +#include + +namespace swoole { + +ssize_t file_get_size(FILE *fp); +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; + +class File { + private: + int fd_; + int flags_; + std::string path_; + + public: + enum Flag { + READ = O_RDONLY, + WRITE = O_WRONLY, + RW = O_RDWR, + CREATE = O_CREAT, + EXCL = O_EXCL, + APPEND = O_APPEND, + }; + + explicit File(int fd) { + fd_ = fd; + flags_ = 0; + } + + File(int fd, const std::string &path) { + fd_ = fd; + path_ = path; + flags_ = 0; + } + + File(const std::string &path, int oflags) { + fd_ = ::open(path.c_str(), oflags); + path_ = path; + flags_ = oflags; + } + + File(const std::string &path, int oflags, int mode) { + fd_ = ::open(path.c_str(), oflags, mode); + path_ = path; + flags_ = oflags; + } + + ~File() { + if (fd_ >= 0) { + ::close(fd_); + } + } + + bool ready() { + return fd_ != -1; + } + + ssize_t write(const void *__buf, size_t __n) const { + return ::write(fd_, __buf, __n); + } + + ssize_t read(void *__buf, size_t __n) const { + return ::read(fd_, __buf, __n); + } + + ssize_t pwrite(const void *__buf, size_t __n, off_t __offset) const { + return ::pwrite(fd_, __buf, __n, __offset); + } + + ssize_t pread(void *__buf, size_t __n, off_t __offset) const { + return ::pread(fd_, __buf, __n, __offset); + } + + 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) { + swoole_sys_warning("fstat() failed"); + return false; + } else { + return true; + } + } + + bool sync() { + return ::fsync(fd_) == 0; + } + + bool truncate(size_t size) { + return ::ftruncate(fd_, size) == 0; + } + + off_t set_offset(off_t offset) { + return lseek(fd_, offset, SEEK_SET); + } + + off_t get_offset() { + return lseek(fd_, 0, SEEK_CUR); + } + + bool lock(int operation) { + return ::flock(fd_, operation) == 0; + } + + bool unlock() { + return ::flock(fd_, LOCK_UN) == 0; + } + + ssize_t get_size() { + return file_get_size(fd_); + } + + bool close() { + if (fd_ == -1) { + return false; + } + int tmp_fd = fd_; + fd_ = -1; + return ::close(tmp_fd) == 0; + } + + void release() { + fd_ = -1; + } + + int get_fd() { + return fd_; + } + + const std::string &get_path() { + return path_; + } + + static bool exists(const std::string &file) { + return access(file.c_str(), R_OK) == 0; + } +}; + +File make_tmpfile(); + +} // namespace swoole diff --git a/include/swoole_file_hook.h b/include/swoole_file_hook.h new file mode 100644 index 00000000000..26f2ab57892 --- /dev/null +++ b/include/swoole_file_hook.h @@ -0,0 +1,73 @@ +/* + +----------------------------------------------------------------------+ + | 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 SW_FILE_HOOK_H_ +#define SW_FILE_HOOK_H_ + +#include "swoole_coroutine_c_api.h" + +#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 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) +#define closedir(dir) swoole_coroutine_closedir(dir) + +#endif diff --git a/include/swoole_hash.h b/include/swoole_hash.h new file mode 100644 index 00000000000..321317e4fcc --- /dev/null +++ b/include/swoole_hash.h @@ -0,0 +1,238 @@ +/* + +----------------------------------------------------------------------+ + | 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: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#ifndef SW_HASH_H_ +#define SW_HASH_H_ + +SW_EXTERN_C_BEGIN + +#include + +#define HASH_JEN_MIX(a, b, c) \ + do { \ + a -= b; \ + a -= c; \ + a ^= (c >> 13); \ + b -= c; \ + b -= a; \ + b ^= (a << 8); \ + c -= a; \ + c -= b; \ + c ^= (b >> 13); \ + a -= b; \ + a -= c; \ + a ^= (c >> 12); \ + b -= c; \ + b -= a; \ + b ^= (a << 16); \ + c -= a; \ + c -= b; \ + c ^= (b >> 5); \ + a -= b; \ + a -= c; \ + a ^= (c >> 3); \ + b -= c; \ + b -= a; \ + b ^= (a << 10); \ + c -= a; \ + c -= b; \ + c ^= (b >> 15); \ + } while (0) + +/** + * jenkins + */ +static inline uint64_t swoole_hash_jenkins(const char *key, size_t keylen) { + uint64_t hashv; + + unsigned i, j, k; + hashv = 0xfeedbeef; + i = j = 0x9e3779b9; + k = (unsigned) (keylen); + + while (k >= 12) { + i += (key[0] + ((unsigned) key[1] << 8) + ((unsigned) key[2] << 16) + ((unsigned) key[3] << 24)); + j += (key[4] + ((unsigned) key[5] << 8) + ((unsigned) key[6] << 16) + ((unsigned) key[7] << 24)); + hashv += (key[8] + ((unsigned) key[9] << 8) + ((unsigned) key[10] << 16) + ((unsigned) key[11] << 24)); + + HASH_JEN_MIX(i, j, hashv); + + key += 12; + k -= 12; + } + hashv += keylen; + switch (k) { + case 11: + hashv += ((unsigned) key[10] << 24); + /* no break */ + case 10: + hashv += ((unsigned) key[9] << 16); + /* no break */ + case 9: + hashv += ((unsigned) key[8] << 8); + /* no break */ + case 8: + j += ((unsigned) key[7] << 24); + /* no break */ + case 7: + j += ((unsigned) key[6] << 16); + /* no break */ + case 6: + j += ((unsigned) key[5] << 8); + /* no break */ + case 5: + j += key[4]; + /* no break */ + case 4: + i += ((unsigned) key[3] << 24); + /* no break */ + case 3: + i += ((unsigned) key[2] << 16); + /* no break */ + case 2: + i += ((unsigned) key[1] << 8); + /* no break */ + case 1: + i += key[0]; + } + HASH_JEN_MIX(i, j, hashv); + return hashv; +} + +/** + * MurmurHash2(Austin Appleby) + */ +static inline uint32_t swoole_hash_austin(const char *key, unsigned int keylen) { + unsigned int h, k; + h = 0 ^ keylen; + + while (keylen >= 4) { + k = key[0]; + k |= key[1] << 8; + k |= key[2] << 16; + k |= key[3] << 24; + + k *= 0x5bd1e995; + k ^= k >> 24; + k *= 0x5bd1e995; + + h *= 0x5bd1e995; + h ^= k; + + key += 4; + keylen -= 4; + } + + switch (keylen) { + case 3: + h ^= key[2] << 16; + /* no break */ + case 2: + h ^= key[1] << 8; + /* no break */ + case 1: + h ^= key[0]; + h *= 0x5bd1e995; + } + + h ^= h >> 13; + h *= 0x5bd1e995; + h ^= h >> 15; + + return h; +} + +/* {{{ DJBX33A (Daniel J. Bernstein, Times 33 with Addition) + * + * This is Daniel J. Bernstein's popular `times 33' hash function as + * posted by him years ago on comp->lang.c. It basically uses a function + * like ``hash(i) = hash(i-1) * 33 + str[i]''. This is one of the best + * known hash functions for strings. Because it is both computed very + * fast and distributes very well. + * + * The magic of number 33, i.e. why it works better than many other + * constants, prime or not, has never been adequately explained by + * anyone. So I try an explanation: if one experimentally tests all + * multipliers between 1 and 256 (as RSE did now) one detects that even + * numbers are not useable at all. The remaining 128 odd numbers + * (except for the number 1) work more or less all equally well. They + * all distribute in an acceptable way and this way fill a hash table + * with an average percent of approx. 86%. + * + * If one compares the Chi^2 values of the variants, the number 33 not + * even has the best value. But the number 33 and a few other equally + * good numbers like 17, 31, 63, 127 and 129 have nevertheless a great + * advantage to the remaining numbers in the large set of possible + * multipliers: their multiply operation can be replaced by a faster + * operation based on just one shift plus either a single addition + * or subtraction operation. And because a hash function has to both + * distribute good _and_ has to be very fast to compute, those few + * numbers should be preferred and seems to be the reason why Daniel J. + * Bernstein also preferred it. + * + * -- Ralf S. Engelschall + */ +static inline uint64_t swoole_hash_php(const char *key, size_t len) { + ulong_t hash = 5381; + /* variant with the hash unrolled eight times */ + for (; len >= 8; len -= 8) { + hash = ((hash << 5) + hash) + *key++; + hash = ((hash << 5) + hash) + *key++; + hash = ((hash << 5) + hash) + *key++; + hash = ((hash << 5) + hash) + *key++; + hash = ((hash << 5) + hash) + *key++; + hash = ((hash << 5) + hash) + *key++; + hash = ((hash << 5) + hash) + *key++; + hash = ((hash << 5) + hash) + *key++; + } + + switch (len) { + case 7: + hash = ((hash << 5) + hash) + *key++; /* fallthrough... */ + /* no break */ + case 6: + hash = ((hash << 5) + hash) + *key++; /* fallthrough... */ + /* no break */ + case 5: + hash = ((hash << 5) + hash) + *key++; /* fallthrough... */ + /* no break */ + case 4: + hash = ((hash << 5) + hash) + *key++; /* fallthrough... */ + /* no break */ + case 3: + hash = ((hash << 5) + hash) + *key++; /* fallthrough... */ + /* no break */ + case 2: + hash = ((hash << 5) + hash) + *key++; /* fallthrough... */ + /* no break */ + case 1: + hash = ((hash << 5) + hash) + *key++; + break; + case 0: + break; + default: + break; + } + return hash; +} + +#define CRC_STRING_MAXLEN 256 + +uint32_t swoole_crc32(const char *data, uint32_t size); + +SW_EXTERN_C_END + +#endif /* SW_HASH_H_ */ diff --git a/include/swoole_heap.h b/include/swoole_heap.h new file mode 100644 index 00000000000..b5c08fd083f --- /dev/null +++ b/include/swoole_heap.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@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#pragma once + +namespace swoole { + +struct HeapNode { + uint64_t priority; + uint32_t position; + void *data; +}; + +class Heap { + public: + enum Type { + MIN_HEAP, + MAX_HEAP, + }; + + Heap(size_t _n, Type _type); + ~Heap(); + + size_t count() { + return num - 1; + } + + HeapNode *push(uint64_t priority, void *data); + void *pop(); + void change_priority(uint64_t new_priority, HeapNode *ptr); + void remove(HeapNode *node); + void *peek(); + void print(); + int compare(uint64_t a, uint64_t b); + + HeapNode *top() { + if (num == 1) { + return nullptr; + } + return nodes[1]; + } + + private: + uint32_t num; + uint32_t size; + enum Type type; + HeapNode **nodes; + + void bubble_up(uint32_t i); + uint32_t maxchild(uint32_t i); + void percolate_down(uint32_t i); +}; +} + diff --git a/include/swoole_http.h b/include/swoole_http.h new file mode 100644 index 00000000000..94a3dbdabdd --- /dev/null +++ b/include/swoole_http.h @@ -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@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ + */ +#pragma once + +#include "swoole.h" +#include "swoole_protocol.h" + +#include + +enum swHttpVersion { + SW_HTTP_VERSION_10 = 1, + SW_HTTP_VERSION_11, + SW_HTTP_VERSION_2, + SW_HTTP_VERSION_3, +}; + +enum swHttpMethod { + SW_HTTP_DELETE = 1, + SW_HTTP_GET, + SW_HTTP_HEAD, + SW_HTTP_POST, + SW_HTTP_PUT, + SW_HTTP_PATCH, + /* pathological */ + SW_HTTP_CONNECT, + SW_HTTP_OPTIONS, + SW_HTTP_TRACE, + /* webdav */ + SW_HTTP_COPY, + SW_HTTP_LOCK, + SW_HTTP_MKCOL, + SW_HTTP_MOVE, + SW_HTTP_PROPFIND, + SW_HTTP_PROPPATCH, + SW_HTTP_UNLOCK, + /* subversion */ + SW_HTTP_REPORT, + SW_HTTP_MKACTIVITY, + SW_HTTP_CHECKOUT, + SW_HTTP_MERGE, + /* upnp */ + SW_HTTP_MSEARCH, + SW_HTTP_NOTIFY, + SW_HTTP_SUBSCRIBE, + SW_HTTP_UNSUBSCRIBE, + /* proxy */ + SW_HTTP_PURGE, + /* Http2 */ + SW_HTTP_PRI, +}; + +enum swHttpStatusCode { + SW_HTTP_CONTINUE = 100, + SW_HTTP_SWITCHING_PROTOCOLS = 101, + SW_HTTP_PROCESSING = 102, + + SW_HTTP_OK = 200, + SW_HTTP_CREATED = 201, + SW_HTTP_ACCEPTED = 202, + SW_HTTP_NO_CONTENT = 204, + SW_HTTP_PARTIAL_CONTENT = 206, + + SW_HTTP_SPECIAL_RESPONSE = 300, + SW_HTTP_MOVED_PERMANENTLY = 301, + SW_HTTP_MOVED_TEMPORARILY = 302, + SW_HTTP_SEE_OTHER = 303, + SW_HTTP_NOT_MODIFIED = 304, + SW_HTTP_TEMPORARY_REDIRECT = 307, + SW_HTTP_PERMANENT_REDIRECT = 308, + + SW_HTTP_BAD_REQUEST = 400, + SW_HTTP_UNAUTHORIZED = 401, + SW_HTTP_FORBIDDEN = 403, + SW_HTTP_NOT_FOUND = 404, + SW_HTTP_NOT_ALLOWED = 405, + SW_HTTP_REQUEST_TIME_OUT = 408, + SW_HTTP_CONFLICT = 409, + SW_HTTP_LENGTH_REQUIRED = 411, + SW_HTTP_PRECONDITION_FAILED = 412, + SW_HTTP_REQUEST_ENTITY_TOO_LARGE = 413, + SW_HTTP_REQUEST_URI_TOO_LARGE = 414, + SW_HTTP_UNSUPPORTED_MEDIA_TYPE = 415, + 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, + SW_HTTP_BAD_GATEWAY = 502, + SW_HTTP_SERVICE_UNAVAILABLE = 503, + SW_HTTP_GATEWAY_TIME_OUT = 504, + SW_HTTP_VERSION_NOT_SUPPORTED = 505, + 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; + uchar chunked : 1; + uchar nobody_chunked : 1; + + 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 */ + uint64_t content_length_; + + FormData *form_data_; + + String *buffer_; + + public: + Request() { + clean(); + buffer_ = nullptr; + } + ~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(); + 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(); +}; + +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_server +} // namespace swoole diff --git a/include/swoole_http2.h b/include/swoole_http2.h new file mode 100644 index 00000000000..9f3b6e5c395 --- /dev/null +++ b/include/swoole_http2.h @@ -0,0 +1,209 @@ +/* + +----------------------------------------------------------------------+ + | 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: Tianfeng Han | + | Twosee | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole_protocol.h" + +#define SW_HTTP2_PRI_STRING "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" + +enum swHttp2ErrorCode { + SW_HTTP2_ERROR_NO_ERROR = 0x0, + SW_HTTP2_ERROR_PROTOCOL_ERROR = 0x1, + SW_HTTP2_ERROR_INTERNAL_ERROR = 0x2, + SW_HTTP2_ERROR_FLOW_CONTROL_ERROR = 0x3, + SW_HTTP2_ERROR_SETTINGS_TIMEOUT = 0x4, + SW_HTTP2_ERROR_STREAM_CLOSED = 0x5, + SW_HTTP2_ERROR_FRAME_SIZE_ERROR = 0x6, + SW_HTTP2_ERROR_REFUSED_STREAM = 0x7, + SW_HTTP2_ERROR_CANCEL = 0x8, + SW_HTTP2_ERROR_COMPRESSION_ERROR = 0x9, + SW_HTTP2_ERROR_CONNECT_ERROR = 0xa, + SW_HTTP2_ERROR_ENHANCE_YOUR_CALM = 0xb, + SW_HTTP2_ERROR_INADEQUATE_SECURITY = 0xc, + SW_HTTP2_ERROR_HTTP_1_1_REQUIRED = 0xd, +}; + +enum swHttp2FrameType { + SW_HTTP2_TYPE_DATA = 0, + SW_HTTP2_TYPE_HEADERS = 1, + SW_HTTP2_TYPE_PRIORITY = 2, + SW_HTTP2_TYPE_RST_STREAM = 3, + SW_HTTP2_TYPE_SETTINGS = 4, + SW_HTTP2_TYPE_PUSH_PROMISE = 5, + SW_HTTP2_TYPE_PING = 6, + SW_HTTP2_TYPE_GOAWAY = 7, + SW_HTTP2_TYPE_WINDOW_UPDATE = 8, + SW_HTTP2_TYPE_CONTINUATION = 9, +}; + +enum swHttp2FrameFlag { + SW_HTTP2_FLAG_NONE = 0x00, + SW_HTTP2_FLAG_ACK = 0x01, + SW_HTTP2_FLAG_END_STREAM = 0x01, + SW_HTTP2_FLAG_END_HEADERS = 0x04, + SW_HTTP2_FLAG_PADDED = 0x08, + SW_HTTP2_FLAG_PRIORITY = 0x20, +}; + +enum swHttp2SettingId { + SW_HTTP2_SETTING_HEADER_TABLE_SIZE = 0x1, + SW_HTTP2_SETTINGS_ENABLE_PUSH = 0x2, + SW_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 0x3, + SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE = 0x4, + SW_HTTP2_SETTINGS_MAX_FRAME_SIZE = 0x5, + SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x6, +}; + +enum swHttp2StreamFlag { + SW_HTTP2_STREAM_NORMAL = 0, + SW_HTTP2_STREAM_REQUEST_END = 1 << 0, + SW_HTTP2_STREAM_PIPELINE_REQUEST = 1 << 1, + SW_HTTP2_STREAM_PIPELINE_RESPONSE = 1 << 2, + SW_HTTP2_STREAM_USE_PIPELINE_READ = 1 << 3, +}; + +#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 +#define SW_HTTP2_PRIORITY_SIZE 5 +#define SW_HTTP2_PING_SIZE 8 +#define SW_HTTP2_RST_STREAM_SIZE 4 +#define SW_HTTP2_GOAWAY_SIZE 8 +#define SW_HTTP2_WINDOW_UPDATE_SIZE 4 +#define SW_HTTP2_STREAM_ID_SIZE 4 +#define SW_HTTP2_SETTINGS_PARAM_SIZE 6 + +#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 enable_push; + uint32_t max_concurrent_streams; + uint32_t init_window_size; + uint32_t max_frame_size; + uint32_t max_header_list_size; +}; + +/** + +-----------------------------------------------+ + | Length (24) | + +---------------+---------------+---------------+ + | Type (8) | Flags (8) | + +-+-------------+---------------+-------------------------------+ + |R| Stream Identifier (31) | + +=+=============================================================+ + | Frame Payload (0...) ... + +---------------------------------------------------------------+ + */ +struct Frame { + uint32_t length : 24; + uint32_t type : 8; + uint32_t flags : 8; + uint32_t rsv1 : 1; + uint32_t identifier : 31; + char data[0]; +}; + +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]; +} + +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"; + } +} + +/** + +-----------------------------------------------+ + | Length (24) | + +---------------+---------------+---------------+ + | Type (8) | Flags (8) | + +-+-------------+---------------+-------------------------------+ + |R| Stream Identifier (31) | + +=+=============================================================+ + | Frame Payload (0...) ... + +---------------------------------------------------------------+ + */ +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; + buffer[3] = type; + 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 new file mode 100644 index 00000000000..a88f9789348 --- /dev/null +++ b/include/swoole_lock.h @@ -0,0 +1,145 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Twosee | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole.h" +#include "swoole_memory.h" + +#include + +namespace swoole { + +class Lock { + public: + enum Type { + NONE, + RW_LOCK = 1, + MUTEX = 3, + SPIN_LOCK = 5, + COROUTINE_LOCK = 6, + }; + Type get_type() { + return type_; + } + virtual ~Lock(){}; + virtual int lock_rd() = 0; + virtual int lock() = 0; + virtual int unlock() = 0; + virtual int trylock_rd() = 0; + virtual int trylock() = 0; + + protected: + Lock() { + type_ = NONE; + shared_ = false; + } + enum Type type_; + bool shared_; +}; + +struct MutexImpl; + +class Mutex : public Lock { + MutexImpl *impl; + int flags_; + + public: + enum Flag { + PROCESS_SHARED = 1, + ROBUST = 2, + }; + + Mutex(int flags); + ~Mutex(); + int lock_rd() override; + int lock() override; + int unlock() override; + int trylock_rd() override; + int trylock() override; + int lock_wait(int timeout_msec); +}; + +#ifdef HAVE_RWLOCK +struct RWLockImpl; + +class RWLock : public Lock { + RWLockImpl *impl; + + public: + RWLock(int use_in_process); + ~RWLock(); + int lock_rd() override; + int lock() override; + int unlock() override; + int trylock_rd() override; + int trylock() override; +}; +#endif + +#ifdef HAVE_SPINLOCK +class SpinLock : public Lock { + pthread_spinlock_t *impl; + + public: + SpinLock(int use_in_process); + ~SpinLock(); + int lock_rd() override; + int lock() override; + int unlock() override; + int trylock_rd() override; + 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 new file mode 100644 index 00000000000..2eb2175804a --- /dev/null +++ b/include/swoole_log.h @@ -0,0 +1,264 @@ +/* + +----------------------------------------------------------------------+ + | 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 +#include +#include +#include + +#define SW_LOG_BUFFER_SIZE (SW_ERROR_MSG_SIZE + 256) +#define SW_LOG_DATE_STRLEN 128 +#define SW_LOG_DEFAULT_DATE_FORMAT "%F %T" + +enum swLogLevel { + SW_LOG_DEBUG = 0, + SW_LOG_TRACE, + SW_LOG_INFO, + SW_LOG_NOTICE, + SW_LOG_WARNING, + SW_LOG_ERROR, + SW_LOG_NONE, +}; + +enum swLogRotationType { + SW_LOG_ROTATION_SINGLE = 0, + SW_LOG_ROTATION_MONTHLY, + SW_LOG_ROTATION_DAILY, + SW_LOG_ROTATION_HOURLY, + SW_LOG_ROTATION_EVERY_MINUTE, +}; + +namespace swoole { +class Logger { + private: + bool opened = false; + // Redirect stdin and stdout to log_fd + bool redirected = false; + bool display_backtrace_ = false; + int stdout_fd = -1; + int stderr_fd = -1; + int log_fd = STDOUT_FILENO; + int log_level = SW_LOG_INFO; + bool date_with_microseconds = false; + std::string date_format = SW_LOG_DEFAULT_DATE_FORMAT; + std::string log_file = ""; + std::string log_real_file; + int log_rotation = SW_LOG_ROTATION_SINGLE; + + public: + bool open(const char *logfile); + void put(int level, const char *content, size_t length); + void reopen(); + void close(void); + void reset(); + void set_level(int lv); + int get_level(); + bool set_date_format(const char *format); + void set_rotation(int rotation); + const char *get_real_file(); + const char *get_file(); + bool is_opened(); + 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; + } +}; +} // namespace swoole + +swoole::Logger *sw_logger(); +#define __SW_FUNC__ (swoole::Logger::get_pretty_name(__PRETTY_FUNCTION__).c_str()) + +#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 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 swoole_sys_notice(str, ...) \ + do { \ + swoole_set_last_error(errno); \ + 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]", \ + __SW_FUNC__, \ + __LINE__, \ + ##__VA_ARGS__, \ + swoole_strerror(errno), \ + errno); \ + sw_logger()->put(SW_LOG_NOTICE, sw_error, _sw_error_len); \ + } \ + } while (0) + +#define swoole_warning(str, ...) \ + do { \ + 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 swoole_sys_warning(str, ...) \ + do { \ + swoole_set_last_error(errno); \ + if (SW_LOG_ERROR >= swoole_get_log_level()) { \ + size_t _sw_error_len = sw_snprintf(sw_error, \ + SW_ERROR_MSG_SIZE, \ + "%s(): " str ", Error: %s[%d]", \ + __SW_FUNC__, \ + ##__VA_ARGS__, \ + swoole_strerror(errno), \ + errno); \ + sw_logger()->put(SW_LOG_WARNING, sw_error, _sw_error_len); \ + } \ + } while (0) + +#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); \ + swoole_exit(1); \ + } while (0) + +#define swoole_sys_error(str, ...) \ + do { \ + size_t _sw_error_len = sw_snprintf(sw_error, \ + SW_ERROR_MSG_SIZE, \ + "%s(): " str ", Error: %s[%d]", \ + __SW_FUNC__, \ + ##__VA_ARGS__, \ + swoole_strerror(errno), \ + errno); \ + sw_logger()->put(SW_LOG_ERROR, sw_error, _sw_error_len); \ + swoole_exit(1); \ + } while (0) + +#define swoole_fatal_error(code, str, ...) SwooleG.fatal_error(code, str, ##__VA_ARGS__) + +#define swoole_error_log(level, error, str, ...) \ + do { \ + swoole_set_last_error(error); \ + if (level >= swoole_get_log_level() && !swoole_is_ignored_error(error)) { \ + size_t _sw_error_len = \ + sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, "%s() (ERRNO %d): " str, __SW_FUNC__, error, ##__VA_ARGS__); \ + sw_logger()->put(level, sw_error, _sw_error_len); \ + } \ + } while (0) + +#ifdef SW_DEBUG +#define swoole_debug(str, ...) \ + if (SW_LOG_DEBUG >= swoole_get_log_level()) { \ + size_t _sw_error_len = \ + sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, "%s(:%d): " str, __SW_FUNC__, __LINE__, ##__VA_ARGS__); \ + sw_logger()->put(SW_LOG_DEBUG, sw_error, _sw_error_len); \ + } + +#define swoole_hex_dump(data, length) \ + do { \ + const char *__data = (data); \ + size_t __length = (length); \ + swoole_debug("+----------+------------+-----------+-----------+------------+------------------+"); \ + for (size_t of = 0; of < __length; of += 16) { \ + char hex[16 * 3 + 1]; \ + char str[16 + 1]; \ + size_t i, hof = 0, sof = 0; \ + for (i = of; i < of + 16 && i < __length; i++) { \ + hof += sprintf(hex + hof, "%02x ", (__data)[i] & 0xff); \ + sof += sprintf(str + sof, "%c", isprint((int) (__data)[i]) ? (__data)[i] : '.'); \ + } \ + swoole_debug("| %08zx | %-48s| %-16s |", of, hex, str); \ + } \ + swoole_debug("+----------+------------+-----------+-----------+------------+------------------+"); \ + } while (0) +#else +#define swoole_debug(str, ...) +#define swoole_hex_dump(data, length) +#endif + +enum swTraceWhat : long { + /** + * Server + */ + SW_TRACE_SERVER = 1u << 1, + SW_TRACE_CLIENT = 1u << 2, + SW_TRACE_BUFFER = 1u << 3, + SW_TRACE_CONN = 1u << 4, + SW_TRACE_EVENT = 1u << 5, + SW_TRACE_WORKER = 1u << 6, + SW_TRACE_MEMORY = 1u << 7, + SW_TRACE_REACTOR = 1u << 8, + SW_TRACE_PHP = 1u << 9, + SW_TRACE_HTTP = 1u << 10, + SW_TRACE_HTTP2 = 1u << 11, + SW_TRACE_EOF_PROTOCOL = 1u << 12, + SW_TRACE_LENGTH_PROTOCOL = 1u << 13, + SW_TRACE_CLOSE = 1u << 14, + SW_TRACE_WEBSOCKET = 1u << 15, + /** + * Client + */ + SW_TRACE_REDIS_CLIENT = 1u << 16, + SW_TRACE_MYSQL_CLIENT = 1u << 17, + SW_TRACE_HTTP_CLIENT = 1u << 18, + SW_TRACE_AIO = 1u << 19, + SW_TRACE_SSL = 1u << 20, + SW_TRACE_NORMAL = 1u << 21, + /** + * Coroutine + */ + SW_TRACE_CHANNEL = 1u << 22, + SW_TRACE_TIMER = 1u << 23, + SW_TRACE_SOCKET = 1u << 24, + SW_TRACE_COROUTINE = 1u << 25, + 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 = 0x7fffffffffffffff +}; + +#ifdef SW_LOG_TRACE_OPEN +#define swoole_trace_log(what, str, ...) \ + if (SW_LOG_TRACE >= swoole_get_log_level() && (what & SwooleG.trace_flags)) { \ + size_t _sw_error_len = \ + sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, "%s(:%d): " str, __SW_FUNC__, __LINE__, ##__VA_ARGS__); \ + sw_logger()->put(SW_LOG_TRACE, sw_error, _sw_error_len); \ + } +#else +#define swoole_trace_log(what, str, ...) +#endif + +#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 new file mode 100644 index 00000000000..000421507bd --- /dev/null +++ b/include/swoole_lru_cache.h @@ -0,0 +1,100 @@ +/* + +----------------------------------------------------------------------+ + | 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 +#include +#include +#include +#include + +namespace swoole { +/** + * This cache isn't thread safe + */ +class LRUCache { + private: + typedef std::pair> cache_node_t; + typedef std::list> cache_list_t; + + std::unordered_map cache_map; + cache_list_t cache_list; + size_t cache_capacity; + + public: + explicit LRUCache(size_t capacity) { + cache_capacity = capacity; + } + + std::shared_ptr get(const std::string &key) { + auto iter = cache_map.find(key); + if (iter == cache_map.end()) { + return nullptr; + } + + if (iter->second->second.first < ::time(nullptr) && iter->second->second.first > 0) { + return nullptr; + } + + cache_list.splice(cache_list.begin(), cache_list, iter->second); + return iter->second->second.second; // iter -> list::iter -> cache_node_t -> value + } + + void set(const std::string &key, const std::shared_ptr &val, time_t expire = 0) { + time_t expire_time; + + if (expire <= 0) { + expire_time = 0; + } else { + expire_time = ::time(nullptr) + expire; + } + + auto iter = cache_map.find(key); + if (iter != cache_map.end()) { + iter->second->second.first = expire_time; + iter->second->second.second = val; + cache_list.splice(cache_list.begin(), cache_list, iter->second); + return; + } + + size_t size = cache_list.size(); + if (size == cache_capacity && size > 0) { + auto del = cache_list.back(); + cache_map.erase(del.first); + cache_list.pop_back(); + } + + cache_list.emplace_front(key, cache_node_t{expire_time, val}); + cache_map[key] = cache_list.begin(); + } + + void del(const std::string &key) { + auto iter = cache_map.find(key); + if (iter == cache_map.end()) { + return; + } + + cache_list.erase(iter->second); + cache_map.erase(iter); + } + + void clear() { + cache_list.clear(); + cache_map.clear(); + } +}; +} // namespace swoole diff --git a/include/swoole_memory.h b/include/swoole_memory.h new file mode 100644 index 00000000000..022fda3625b --- /dev/null +++ b/include/swoole_memory.h @@ -0,0 +1,91 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Twosee | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole.h" + +//-------------------memory manager------------------------- +namespace swoole { + +class MemoryPool { + public: + virtual ~MemoryPool(){}; + virtual void *alloc(uint32_t size) = 0; + virtual void free(void *ptr) = 0; + + protected: + MemoryPool(){}; +}; + +struct FixedPoolImpl; + +class FixedPool : public MemoryPool { + private: + FixedPoolImpl *impl; + + public: + FixedPool(uint32_t slice_num, uint32_t slice_size, bool shared); + FixedPool(uint32_t slice_size, void *memory, size_t size, bool shared); + ~FixedPool(); + void *alloc(uint32_t size); + void free(void *ptr); + 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(); +}; + +struct RingBufferImpl; + +// RingBuffer, In order for malloc / free +class RingBuffer : public MemoryPool { + private: + RingBufferImpl *impl; + + public: + RingBuffer(uint32_t size, bool shared); + ~RingBuffer(); + void *alloc(uint32_t size); + void free(void *ptr); +}; + +struct GlobalMemoryImpl; + +// Global memory, the program life cycle only malloc / free one time +class GlobalMemory : public MemoryPool { + private: + GlobalMemoryImpl *impl; + + public: + GlobalMemory(uint32_t page_size, bool shared); + ~GlobalMemory(); + void *alloc(uint32_t size); + void free(void *ptr); + void destroy(); + size_t capacity(); + size_t get_memory_size(); +}; +} // namespace swoole + +void *sw_shm_malloc(size_t size); +void sw_shm_free(void *ptr); +void *sw_shm_calloc(size_t num, size_t _size); +int sw_shm_protect(void *ptr, int flags); +void *sw_shm_realloc(void *ptr, size_t new_size); diff --git a/include/swoole_message_bus.h b/include/swoole_message_bus.h new file mode 100644 index 00000000000..faae0d1d69d --- /dev/null +++ b/include/swoole_message_bus.h @@ -0,0 +1,203 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole_api.h" +#include "swoole_string.h" +#include "swoole_socket.h" + +#include + +namespace swoole { + +struct PipeBuffer { + DataHead info; + char data[0]; + + bool is_begin() { + return info.flags & SW_EVENT_DATA_BEGIN; + } + + bool is_chunked() { + return info.flags & SW_EVENT_DATA_CHUNK; + } + + bool is_end() { + return info.flags & SW_EVENT_DATA_END; + } +}; + +struct PacketPtr { + size_t length; + char *data; +}; + +struct DgramPacket { + SocketType socket_type; + network::Address socket_addr; + uint32_t length; + char data[0]; +}; + +struct PacketTask { + size_t length; + char tmpfile[SW_TASK_TMP_PATH_SIZE]; +}; + +class MessageBus { + private: + const Allocator *allocator_; + std::unordered_map> packet_pool_; + std::vector pipe_sockets_; + std::function id_generator_; + size_t buffer_size_; + PipeBuffer *buffer_ = nullptr; + bool always_chunked_transfer_ = false; + + String *get_packet_buffer(); + ReturnCode prepare_packet(uint16_t &recv_chunk_count, String *packet_buffer); + + public: + MessageBus() { + allocator_ = sw_std_allocator(); + buffer_size_ = SW_BUFFER_SIZE_STD; + } + + ~MessageBus(); + + bool empty() { + return packet_pool_.empty(); + } + + size_t count() { + return packet_pool_.size(); + } + + void clear() { + packet_pool_.clear(); + } + + void set_allocator(const Allocator *allocator) { + allocator_ = allocator; + } + + void set_id_generator(const std::function &id_generator) { + id_generator_ = id_generator; + } + + void set_buffer_size(size_t buffer_size) { + buffer_size_ = buffer_size; + } + + void set_always_chunked_transfer() { + always_chunked_transfer_ = true; + } + + size_t get_buffer_size() { + return buffer_size_; + } + + size_t get_memory_size(); + + bool alloc_buffer() { + void *_ptr = allocator_->malloc(sizeof(*buffer_) + buffer_size_); + if (_ptr) { + buffer_ = (PipeBuffer *) _ptr; + sw_memset_zero(&buffer_->info, sizeof(buffer_->info)); + return true; + } else { + return false; + } + } + + /** + * If use the zend_string_allocator, must manually call this function to release the memory, + * otherwise coredump will occur when php shutdown, because zend_string has been released + */ + void free_buffer() { + allocator_->free(buffer_); + buffer_ = nullptr; + } + + void pass(SendData *task) { + memcpy(&buffer_->info, &task->info, sizeof(buffer_->info)); + if (task->info.len > 0) { + buffer_->info.flags = SW_EVENT_DATA_PTR; + PacketPtr pkt{task->info.len, (char *) task->data}; + buffer_->info.len = sizeof(pkt); + memcpy(buffer_->data, &pkt, sizeof(pkt)); + } + } + + /** + * Send data to socket. If the data sent is larger than Server::ipc_max_size, then it is sent in chunks. + * Otherwise send it directly. + * When sending data in multi-thread environment, must use get_pipe_socket() to separate socket memory. + * @return: send success returns true, send failure returns false. + */ + bool write(network::Socket *sock, SendData *packet); + /** + * Receive data from socket, if only one chunk is received, packet will be saved in packet_pool. + * Then continue to listen to readable events, waiting for more chunks. + * @return: >0: receive a complete packet, 0: continue to wait for data, -1: an error occurred + */ + ssize_t read(network::Socket *sock); + /** + * Receive data from pipeline, and store data to buffer + * @return: >0: receive a complete packet, 0: continue to wait for data, -1: an error occurred + */ + ssize_t read_with_buffer(network::Socket *sock); + /** + * The last chunk of data has been received, return address and length, start processing this packet. + */ + PacketPtr get_packet() const; + PipeBuffer *get_buffer() { + return buffer_; + } + /** + * Pop the data memory address to the outer layer, no longer managed by MessageBus + */ + char *move_packet() { + uint64_t msg_id = buffer_->info.msg_id; + auto iter = packet_pool_.find(msg_id); + if (iter != packet_pool_.end()) { + auto str = iter->second.get(); + char *val = str->str; + str->str = nullptr; + return val; + } else { + return nullptr; + } + } + /** + * The processing of this data packet has been completed, and the relevant memory has been released + */ + void pop() { + if (buffer_->is_end()) { + packet_pool_.erase(buffer_->info.msg_id); + } + } + /** + * It is possible to operate the same pipe in multiple threads. + * Each thread must have a unique buffer and the socket memory must be separated. + */ + network::Socket *get_pipe_socket(network::Socket *sock) { + return pipe_sockets_[sock->get_fd()]; + } + void init_pipe_socket(network::Socket *sock); +}; +} // namespace swoole diff --git a/include/swoole_mime_type.h b/include/swoole_mime_type.h new file mode 100644 index 00000000000..27a7122c627 --- /dev/null +++ b/include/swoole_mime_type.h @@ -0,0 +1,32 @@ +/* + +----------------------------------------------------------------------+ + | 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 +#include +#include + +namespace swoole { +namespace mime_type { +const std::unordered_map &list(); +bool add(const std::string &suffix, const std::string &mime_type); +void set(const std::string &suffix, const std::string &mime_type); +bool del(const std::string &suffix); +const std::string &get(const std::string &filename); +bool exists(const std::string &filename); +} // namespace mime_type +} // namespace swoole diff --git a/include/swoole_mqtt.h b/include/swoole_mqtt.h new file mode 100644 index 00000000000..1c0ee7f10c2 --- /dev/null +++ b/include/swoole_mqtt.h @@ -0,0 +1,63 @@ +/* + +----------------------------------------------------------------------+ + | 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: Tianfeng Han | + +----------------------------------------------------------------------+ + */ +#pragma once + +#include "swoole.h" +#include "swoole_protocol.h" + +#define SW_MQTT_MIN_LENGTH_SIZE 1 +#define SW_MQTT_MAX_LENGTH_SIZE 4 +#define SW_MQTT_MAX_PAYLOAD_SIZE 268435455 + +#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, + SW_MQTT_PUBACK = 0x40, + SW_MQTT_PUBREC = 0x50, + SW_MQTT_PUBREL = 0x60, + SW_MQTT_PUBCOMP = 0x70, + SW_MQTT_SUBSCRIBE = 0x80, + SW_MQTT_SUBACK = 0x90, + SW_MQTT_UNSUBSCRIBE = 0xA0, + SW_MQTT_UNSUBACK = 0xB0, + SW_MQTT_PINGREQ = 0xC0, + SW_MQTT_PINGRESP = 0xD0, + SW_MQTT_DISCONNECT = 0xE0, +}; + +struct Packet { + uint8_t type : 4; + uint8_t dup : 1; + uint8_t qos : 2; + uint8_t retain : 1; + uint32_t length; + char protocol_name[8]; +}; + +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 new file mode 100644 index 00000000000..da8c021ba6f --- /dev/null +++ b/include/swoole_msg_queue.h @@ -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 | + | Twosee | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole.h" + +#include + +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 */ +}; + +class MsgQueue { + private: + bool blocking_; + int msg_id_; + key_t msg_key_; + int flags_; + int perms_; + + public: + explicit MsgQueue(key_t msg_key, bool blocking = true, int perms = 0); + ~MsgQueue(); + + bool ready() { + return msg_id_ >= 0; + } + + int get_id() { + return msg_id_; + } + + void set_blocking(bool blocking); + bool set_capacity(size_t queue_bytes); + bool push(QueueNode *in, size_t mdata_length); + ssize_t pop(QueueNode *out, size_t mdata_size); + 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 new file mode 100644 index 00000000000..df7ced681c6 --- /dev/null +++ b/include/swoole_pipe.h @@ -0,0 +1,94 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Twosee | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole.h" +#include "swoole_socket.h" + +enum swPipe_close_which { + SW_PIPE_CLOSE_MASTER = 1, + SW_PIPE_CLOSE_WORKER = 2, + SW_PIPE_CLOSE_READ = 3, + SW_PIPE_CLOSE_WRITE = 4, + SW_PIPE_CLOSE_BOTH = 0, +}; + +namespace swoole { +class SocketPair { + protected: + bool blocking; + double timeout; + + /** + * master : socks[1] + * worker : socks[0] + */ + int socks[2]; + + network::Socket *master_socket = nullptr; + network::Socket *worker_socket = nullptr; + + bool init_socket(int master_fd, int worker_fd); + + public: + SocketPair(bool _blocking) { + blocking = _blocking; + timeout = network::Socket::default_read_timeout; + } + ~SocketPair(); + + ssize_t read(void *_buf, size_t length); + ssize_t write(const void *_buf, size_t length); + bool close(int which = 0); + + network::Socket *get_socket(bool _master) { + return _master ? master_socket : worker_socket; + } + + bool ready() { + return master_socket != nullptr && worker_socket != nullptr; + } + + void set_timeout(double _timeout) { + timeout = _timeout; + } + + void set_blocking(bool blocking) { + if (blocking) { + worker_socket->set_block(); + master_socket->set_block(); + } else { + worker_socket->set_nonblock(); + master_socket->set_nonblock(); + } + } +}; + +class Pipe : public SocketPair { + public: + Pipe(bool blocking); +}; + +class UnixSocket : public SocketPair { + int protocol_; + public: + UnixSocket(bool blocking, int _protocol); + bool set_buffer_size(size_t _size); +}; + +} // namespace swoole diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h new file mode 100644 index 00000000000..9418a19d644 --- /dev/null +++ b/include/swoole_process_pool.h @@ -0,0 +1,397 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Twosee | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#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 swWorkerStatus { + SW_WORKER_BUSY = 1, + SW_WORKER_IDLE = 2, + SW_WORKER_EXIT = 3, +}; + +enum swIPCMode { + SW_IPC_NONE = 0, + SW_IPC_UNIXSOCK = 1, + SW_IPC_MSGQUEUE = 2, + SW_IPC_SOCKET = 3, +}; + +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 { + bool shutdown; + bool running; + uint32_t max_request; + /** + * worker is shared memory, visible in other work processes. + * When a worker process restarts, it may be held by both the old and new processes simultaneously, + * necessitating careful handling of the state. + */ + Worker *worker; + /** + * worker_copy is a copy of worker, + * but it must be local memory and only used within the current process or thread. + * It is not visible to other worker processes. + */ + Worker *worker_copy; + time_t exit_time; +}; + +struct Worker { + pid_t pid; + WorkerId id; + ProcessPool *pool; + MsgQueue *queue; + bool shared; + + bool redirect_stdout; + bool redirect_stdin; + bool redirect_stderr; + + /** + * worker status, IDLE or BUSY + */ + uint8_t status; + uint8_t type; + uint8_t msgqueue_mode; + uint8_t child_process; + + uint32_t concurrency; + time_t start_time; + + 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; + network::Socket *pipe_worker; + network::Socket *pipe_current; + + void *ptr; + 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; +}; + +struct ProcessPool { + /** + * reloading + */ + bool reloading; + bool running; + bool reload_init; + bool read_message; + bool started; + bool schedule_by_sysvmsg; + bool async; + + uint8_t ipc_mode; + enum ProtocolType protocol_type_; + pid_t master_pid; + uint32_t reload_worker_i; + uint32_t max_wait_time; + uint64_t reload_count; + time_t reload_last_time; + Worker *reload_workers; + + /** + * process type + */ + uint8_t type; + + /** + * worker->id = start_id + i + */ + uint16_t start_id; + + /** + * use message queue IPC + */ + uint8_t use_msgqueue; + + /** + * use stream socket IPC + */ + uint8_t use_socket; + + char *packet_buffer; + uint32_t max_packet_size_; + + /** + * message queue key + */ + key_t msgqueue_key; + + uint32_t worker_num; + uint32_t max_request; + uint32_t max_request_grace; + + /** + * No idle task work process is available. + */ + uint8_t scheduler_warning; + time_t warning_time; + + 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); + + sw_atomic_t round_id; + + Worker *workers; + std::vector> *pipes; + std::unordered_map *map_; + MsgQueue *queue; + StreamInfo *stream_info_; + Channel *message_box = nullptr; + MessageBus *message_bus = nullptr; + + void *ptr; + + void set_type(int _type) { + uint32_t i; + type = _type; + for (i = 0; i < worker_num; i++) { + workers[i].type = type; + } + } + + void set_start_id(int _start_id) { + uint32_t i; + start_id = _start_id; + for (i = 0; i < worker_num; i++) { + workers[i].id = start_id + i; + } + } + + 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(); + bool detach(); + int wait(); + int start_check(); + int start(); + void shutdown(); + bool reload(); + pid_t spawn(Worker *worker); + 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); + swResultCode dispatch_blocking(EventData *data, int *dst_worker_id); + swResultCode dispatch_blocking(const char *data, uint32_t len); + void add_worker(Worker *worker); + int del_worker(Worker *worker); + void destroy(); + int create(uint32_t worker_num, key_t msgqueue_key = 0, swIPCMode ipc_mode = SW_IPC_NONE); + int create_message_box(size_t memory_size); + int create_message_bus(); + int push_message(uint8_t type, const void *data, size_t length); + int push_message(EventData *msg); + int pop_message(void *data, size_t size); + int listen(const char *socket_file, int blacklog); + int listen(const char *host, int port, int blacklog); + int schedule(); + bool is_worker_running(Worker *worker); + + static void kill_timeout_worker(Timer *timer, TimerNode *tnode); + + private: + static int run_with_task_protocol(ProcessPool *pool, Worker *worker); + static int run_with_stream_protocol(ProcessPool *pool, Worker *worker); + static int run_with_message_protocol(ProcessPool *pool, Worker *worker); + static int run_async(ProcessPool *pool, Worker *worker); +}; +}; // namespace swoole + +static sw_inline int swoole_waitpid(pid_t __pid, int *__stat_loc, int __options) { + int ret; + do { + ret = waitpid(__pid, __stat_loc, __options); + } while (ret < 0 && errno == EINTR); + return ret; +} + +static sw_inline int swoole_kill(pid_t __pid, int __sig) { + return kill(__pid, __sig); +} + +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 new file mode 100644 index 00000000000..179b4db9c32 --- /dev/null +++ b/include/swoole_protocol.h @@ -0,0 +1,173 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Twosee | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole.h" +#include "swoole_c_api.h" +#include "swoole_socket.h" + +#include +#include + +namespace swoole { + +struct PacketLength { + const char *buf; + uint32_t buf_size; + uint32_t header_len; +}; + +struct Protocol { + typedef ssize_t (*LengthFunc)(const Protocol *, network::Socket *, PacketLength *pl); + /* one package: eof check */ + bool split_by_eof; + + char package_eof[SW_DATA_EOF_MAXLEN]; + uint8_t package_eof_len; + + char package_length_type; + uint8_t package_length_size; + uint16_t package_length_offset; + uint16_t package_body_offset; + uint32_t package_max_length; + + void *private_data_1; + void *private_data_2; + + /** + * 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 *); + + int recv_with_eof_protocol(network::Socket *socket, String *buffer); + 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(const Protocol *protocol, network::Socket *socket, PacketLength *pl); +}; +} // namespace swoole + +static sw_inline uint16_t swoole_swap_endian16(uint16_t x) { + return (((x & 0xff) << 8) | ((x & 0xff00) >> 8)); +} + +static sw_inline uint32_t swoole_swap_endian32(uint32_t x) { + return (((x & 0xff) << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | ((x & 0xff000000) >> 24)); +} + +static sw_inline int32_t swoole_unpack(char type, const void *data) { + switch (type) { + /*-------------------------16bit-----------------------------*/ + case 'c': + return *((int8_t *) data); + case 'C': + return *((uint8_t *) data); + /*-------------------------16bit-----------------------------*/ + /** + * signed short (always 16 bit, machine byte order) + */ + case 's': + return *((int16_t *) data); + /** + * unsigned short (always 16 bit, machine byte order) + */ + case 'S': + return *((uint16_t *) data); + /** + * unsigned short (always 16 bit, big endian byte order) + */ + case 'n': + return ntohs(*((uint16_t *) data)); + /** + * unsigned short (always 32 bit, little endian byte order) + */ + case 'v': + return swoole_swap_endian16(ntohs(*((uint16_t *) data))); + + /*-------------------------32bit-----------------------------*/ + /** + * unsigned long (always 32 bit, machine byte order) + */ + case 'L': + return *((uint32_t *) data); + /** + * signed long (always 32 bit, machine byte order) + */ + case 'l': + return *((int *) data); + /** + * unsigned long (always 32 bit, big endian byte order) + */ + case 'N': + return ntohl(*((uint32_t *) data)); + /** + * unsigned short (always 32 bit, little endian byte order) + */ + case 'V': + return swoole_swap_endian32(ntohl(*((uint32_t *) data))); + + default: + return *((uint32_t *) data); + } +} + +static sw_inline uint64_t swoole_hton64(uint64_t host) { + uint64_t ret = 0; + uint32_t high, low; + + low = host & 0xFFFFFFFF; + high = (host >> 32) & 0xFFFFFFFF; + low = htonl(low); + high = htonl(high); + + ret = low; + ret <<= 32; + ret |= high; + return ret; +} + +static sw_inline uint64_t swoole_ntoh64(uint64_t net) { + uint64_t ret = 0; + uint32_t high, low; + + low = net & 0xFFFFFFFF; + high = net >> 32; + low = ntohl(low); + high = ntohl(high); + + ret = low; + ret <<= 32; + ret |= high; + return ret; +} + +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); + +char *swoole_dec2hex(ulong_t value, int base); +ulong_t swoole_hex2dec(const char *hex, size_t *parsed_bytes); +int swoole_type_size(char type); diff --git a/include/swoole_proxy.h b/include/swoole_proxy.h new file mode 100644 index 00000000000..987624228a7 --- /dev/null +++ b/include/swoole_proxy.h @@ -0,0 +1,89 @@ +/* + +----------------------------------------------------------------------+ + | 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 +#include + +#define SW_SOCKS5_VERSION_CODE 0x05 +#define SW_HTTP_PROXY_CHECK_MESSAGE 0 +#define SW_HTTP_PROXY_HANDSHAKE_RESPONSE "HTTP/1.1 200 Connection established\r\n" + +#define SW_HTTP_PROXY_FMT \ + "CONNECT %.*s:%d HTTP/1.1\r\n" \ + "Host: %.*s:%d\r\n" \ + "User-Agent: Swoole/" SWOOLE_VERSION "\r\n" \ + "Proxy-Connection: Keep-Alive\r\n" + +enum swHttpProxyState { + SW_HTTP_PROXY_STATE_WAIT = 0, + 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; + int proxy_port; + std::string proxy_host; + std::string username; + std::string password; + std::string target_host; + int target_port; + + std::string get_auth_str(); + size_t pack(String *send_buffer, const std::string *host_name); + bool handshake(String *recv_buffer); +}; + +struct Socks5Proxy { + std::string host; + int port; + uint8_t state; + uint8_t version; + uint8_t method; + uint8_t dns_tunnel; + std::string username; + std::string password; + std::string target_host; + int target_port; + char buf[600]; + + static const char *strerror(int code); + + static void pack(char *buf, int method) { + buf[0] = SW_SOCKS5_VERSION_CODE; + buf[1] = 0x01; + buf[2] = method; + } +}; +} // namespace swoole diff --git a/include/swoole_reactor.h b/include/swoole_reactor.h new file mode 100644 index 00000000000..ff67809afd1 --- /dev/null +++ b/include/swoole_reactor.h @@ -0,0 +1,382 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Twosee | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole.h" +#include "swoole_socket.h" + +#include +#include +#include + +namespace swoole { + +struct DeferCallback { + Callback callback; + void *data; +}; + +class Reactor; + +class ReactorImpl { + protected: + Reactor *reactor_; + + public: + ReactorImpl(Reactor *_reactor) { + reactor_ = _reactor; + } + void after_removal_failure(network::Socket *_socket); + virtual ~ReactorImpl(){}; + virtual bool ready() = 0; + virtual int add(network::Socket *socket, int events) = 0; + virtual int set(network::Socket *socket, int events) = 0; + virtual int del(network::Socket *socket) = 0; + virtual int wait(struct timeval *) = 0; +}; + +class CallbackManager { + public: + typedef std::list> TaskList; + void append(Callback fn, void *private_data) { + list_.emplace_back(fn, private_data); + } + void prepend(Callback fn, void *private_data) { + list_.emplace_front(fn, private_data); + auto t = list_.back(); + } + void remove(TaskList::iterator iter) { + list_.erase(iter); + } + void execute() { + while (!list_.empty()) { + std::pair task = list_.front(); + list_.pop_front(); + task.first(task.second); + } + } + + protected: + TaskList list_; +}; + +class Reactor { + public: + enum Type { + TYPE_AUTO, + TYPE_EPOLL, + TYPE_KQUEUE, + TYPE_POLL, + TYPE_SELECT, + }; + + enum EndCallback { + PRIORITY_TIMER = 0, + PRIORITY_DEFER_TASK, + PRIORITY_IDLE_TASK, + PRIORITY_SIGNAL_CALLBACK, + PRIORITY_TRY_EXIT, + PRIORITY_MALLOC_TRIM, + PRIORITY_WORKER_CALLBACK, + }; + + enum ExitCondition { + EXIT_CONDITION_TIMER = 0, + EXIT_CONDITION_DEFER_TASK, + EXIT_CONDITION_WAIT_PID, + EXIT_CONDITION_CO_SIGNAL_LISTENER, + EXIT_CONDITION_SIGNAL_LISTENER, + 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, + }; + + Type type_; + void *ptr = nullptr; + int native_handle = -1; + + /** + * last signal number + */ + int singal_no = 0; + + uint32_t max_event_num = 0; + + bool running = false; + bool start = false; + bool once = false; + bool wait_exit = false; + bool destroyed = false; + bool bailout = false; + + /** + * reactor->wait timeout (millisecond) or -1 + */ + int32_t timeout_msec = 0; + + uint16_t id = 0; + + uint32_t max_socket = 0; + +#ifdef SW_USE_MALLOC_TRIM + time_t last_malloc_trim_time = 0; +#endif + + ReactorHandler read_handler[SW_MAX_FDTYPE] = {}; + ReactorHandler write_handler[SW_MAX_FDTYPE] = {}; + ReactorHandler error_handler[SW_MAX_FDTYPE] = {}; + + ReactorHandler default_write_handler = nullptr; + ReactorHandler default_error_handler = nullptr; + + int add(network::Socket *socket, int events) { + return impl->add(socket, events); + } + + int set(network::Socket *socket, int events) { + return impl->set(socket, events); + } + + int del(network::Socket *socket) { + return impl->del(socket); + } + + int wait(struct timeval *timeout) { + return impl->wait(timeout); + } + + CallbackManager *defer_tasks = nullptr; + CallbackManager destroy_callbacks; + + DeferCallback idle_task; + DeferCallback future_task; + + std::function onBegin; + + 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::unordered_map sockets_; + + public: + Reactor(int max_event = SW_REACTOR_MAXEVENTS, Type _type = TYPE_AUTO); + ~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); + bool set_handler(int _fdtype, ReactorHandler handler); + void add_destroy_callback(Callback cb, void *data = nullptr); + void execute_end_callbacks(bool timedout = false); + void drain_write_buffer(network::Socket *socket); + + bool ready() { + return running; + } + + size_t remove_exit_condition(enum ExitCondition id) { + return exit_conditions.erase(id); + } + + bool isset_exit_condition(enum ExitCondition id) { + return exit_conditions.find(id) != exit_conditions.end(); + } + + bool isset_handler(int fdtype) { + return read_handler[fdtype] != nullptr; + } + + int add_event(network::Socket *_socket, EventType event_type) { + if (!(_socket->events & event_type)) { + return set(_socket, _socket->events | event_type); + } + return SW_OK; + } + + int del_event(network::Socket *_socket, EventType event_type) { + if (_socket->events & event_type) { + return set(_socket, _socket->events & (~event_type)); + } + return SW_OK; + } + + int remove_read_event(network::Socket *_socket) { + if (_socket->events & SW_EVENT_WRITE) { + _socket->events &= (~SW_EVENT_READ); + return set(_socket, _socket->events); + } else { + return del(_socket); + } + } + + int remove_write_event(network::Socket *_socket) { + if (_socket->events & SW_EVENT_READ) { + _socket->events &= (~SW_EVENT_WRITE); + return set(_socket, _socket->events); + } else { + return del(_socket); + } + } + + int add_read_event(network::Socket *_socket) { + if (_socket->events & SW_EVENT_WRITE) { + _socket->events |= SW_EVENT_READ; + return set(_socket, _socket->events); + } else { + return add(_socket, SW_EVENT_READ); + } + } + + int add_write_event(network::Socket *_socket) { + if (_socket->events & SW_EVENT_READ) { + _socket->events |= SW_EVENT_WRITE; + return set(_socket, _socket->events); + } else { + return add(_socket, SW_EVENT_WRITE); + } + } + + bool exists(network::Socket *_socket) { + return !_socket->removed && _socket->events; + } + + int get_timeout_msec() { + return defer_tasks == nullptr ? timeout_msec : 0; + } + + 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]; + case SW_EVENT_WRITE: + return write_handler[fd_type] ? write_handler[fd_type] : default_write_handler; + case SW_EVENT_ERROR: + return error_handler[fd_type] ? error_handler[fd_type] : default_error_handler; + default: + abort(); + break; + } + return nullptr; + } + + 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) { + handler = get_handler(SW_EVENT_READ, fd_type); + if (handler == nullptr) { + handler = get_handler(SW_EVENT_WRITE, fd_type); + } + } + return handler; + } + + void before_wait() { + start = running = true; + } + + int trigger_close_event(Event *event) { + return default_error_handler(this, event); + } + + void set_wait_exit(bool enable) { + wait_exit = enable; + } + + void _add(network::Socket *_socket, int events) { + _socket->events = events; + _socket->removed = 0; + sockets_[_socket->fd] = _socket; + } + + void _set(network::Socket *_socket, int events) { + _socket->events = events; + } + + void _del(network::Socket *_socket) { + _socket->events = 0; + _socket->removed = 1; + sockets_.erase(_socket->fd); + } + + bool catch_error() { + switch (errno) { + case EINTR: + return true; + } + return false; + } + + 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 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) { + return (events < SW_EVENT_DEAULT) || (events & SW_EVENT_READ); + } + + static bool isset_write_event(int events) { + return events & SW_EVENT_WRITE; + } + + static bool isset_error_event(int events) { + return events & SW_EVENT_ERROR; + } +}; +} // namespace swoole + +#define SW_REACTOR_CONTINUE \ + if (reactor_->once) { \ + break; \ + } else { \ + continue; \ + } diff --git a/include/swoole_redis.h b/include/swoole_redis.h new file mode 100644 index 00000000000..f7718adb6e9 --- /dev/null +++ b/include/swoole_redis.h @@ -0,0 +1,69 @@ +/* + +----------------------------------------------------------------------+ + | 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: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole_server.h" + +#include +#include + +#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 + +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); + if (strncmp(SW_CRLF, endptr, SW_CRLF_LEN) == 0) { + p += (endptr - p) + SW_CRLF_LEN; + *_ret = ret; + return p; + } else { + return nullptr; + } +} + +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 new file mode 100644 index 00000000000..d8393edcfa8 --- /dev/null +++ b/include/swoole_server.h @@ -0,0 +1,1613 @@ +/* + +----------------------------------------------------------------------+ + | 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: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole_api.h" +#include "swoole_string.h" +#include "swoole_socket.h" +#include "swoole_timer.h" +#include "swoole_reactor.h" +#include "swoole_signal.h" +#include "swoole_protocol.h" +#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" +#endif + +#ifdef __MACH__ +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------Server------------------------------------------- +namespace swoole { + +namespace http_server { +struct Request; +} + +class Server; +struct Manager; + +typedef std::function WorkerFn; + +struct Session { + SessionId id; + int fd; + uint32_t reactor_id : 8; + uint32_t reserve_ : 24; +}; + +struct Connection { + /** + * 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; + int worker_id; + SessionId session_id; + //-------------------------------------------------------------- +#ifdef SW_USE_OPENSSL + uint8_t ssl; + uint8_t ssl_ready; +#endif + uint8_t overflow; + uint8_t high_watermark; + uint8_t http_upgrade; + uint8_t http2_stream; +#ifdef SW_HAVE_ZLIB + uint8_t websocket_compression; +#endif + // 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 + uint8_t protect; + uint8_t close_notify; + uint8_t close_force; + ReactorId reactor_id; + uint16_t close_errno; + int server_fd; + sw_atomic_t recv_queued_bytes; + uint32_t send_queued_bytes; + uint16_t waiting_time; + TimerNode *timer; + /** + * socket address + */ + network::Address info; + /** + * link any thing, for kernel, do not use with application. + */ + void *object; + /** + * socket, only operated in the main process + */ + network::Socket *socket; + /** + * connect/recv/send/close time + */ + double connect_time; + double last_recv_time; + double last_send_time; + double last_dispatch_time; + /** + * bind uid + */ + uint32_t uid; + /** + * upgarde websocket + */ + uint8_t websocket_status; + /** + * unfinished data frame + */ + String *websocket_buffer; + +#ifdef SW_USE_OPENSSL + String *ssl_client_cert; + pid_t ssl_client_cert_pid; +#endif + sw_atomic_t lock; +}; + +//------------------------------------ReactorThread------------------------------------------- +struct ReactorThread { + int id; + std::thread thread; + network::Socket *notify_pipe = nullptr; + 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 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 { + /** + * tcp socket listen backlog + */ + uint16_t backlog = SW_BACKLOG; + bool listening = false; + /** + * open tcp_defer_accept option + */ + int tcp_defer_accept = 0; + /** + * TCP_FASTOPEN + */ + int tcp_fastopen = 0; + /** + * TCP KeepAlive + */ + int tcp_keepidle = SW_TCP_KEEPIDLE; + int tcp_keepinterval = SW_TCP_KEEPINTERVAL; + int tcp_keepcount = SW_TCP_KEEPCOUNT; + + int tcp_user_timeout = 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; + + SocketType type = SW_SOCK_TCP; + uint8_t ssl = 0; + std::string host; + int port = 0; + network::Socket *socket = nullptr; + pthread_t thread_id = 0; + + uint16_t heartbeat_idle_time = 0; + + /** + * check data eof + */ + bool open_eof_check = false; + /** + * built-in http protocol + */ + bool open_http_protocol = false; + /** + * built-in http2.0 protocol + */ + bool open_http2_protocol = false; + /** + * built-in websocket protocol + */ + bool open_websocket_protocol = false; + /** + * open websocket close frame + */ + bool open_websocket_close_frame = false; + /** + * open websocket ping frame + */ + bool open_websocket_ping_frame = false; + /** + * open websocket pong frame + */ + bool open_websocket_pong_frame = false; + /** + * one package: length check + */ + bool open_length_check = false; + /** + * for mqtt protocol + */ + bool open_mqtt_protocol = false; + /** + * redis protocol + */ + bool open_redis_protocol = false; + /** + * open tcp nodelay option + */ + bool open_tcp_nodelay = false; + /** + * open tcp nopush option(for sendfile) + */ + bool open_tcp_nopush = true; + /** + * open tcp keepalive + */ + bool open_tcp_keepalive = false; + /** + * Sec-WebSocket-Protocol + */ + std::string websocket_subprotocol; + /** + * set socket option + */ + int kernel_socket_recv_buffer_size = 0; + int kernel_socket_send_buffer_size = 0; + +#ifdef SW_USE_OPENSSL + SSLContext *ssl_context = nullptr; + std::unordered_map> sni_contexts; +#ifdef SW_SUPPORT_DTLS + std::unordered_map *dtls_sessions = nullptr; + bool is_dtls() { + return ssl_context && (ssl_context->protocols & SW_SSL_DTLS); + } +#endif +#endif + + ServerPortGS *gs = nullptr; + + Protocol protocol = {}; + void *ptr = nullptr; + + int (*onRead)(Reactor *reactor, ListenPort *port, Event *event) = nullptr; + + bool is_dgram() { + return network::Socket::is_dgram(type); + } + + bool is_stream() { + return network::Socket::is_stream(type); + } + + 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); + } + + 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_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(); + 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 { + pid_t master_pid; + pid_t manager_pid; + + SessionId session_round; + sw_atomic_t start; + sw_atomic_t shutdown; + + 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 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 Factory { + protected: + Server *server_; + + public: + 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; + virtual bool dispatch(SendData *) = 0; + virtual bool finish(SendData *) = 0; + virtual bool notify(DataHead *) = 0; + virtual bool end(SessionId sesion_id, int flags) = 0; +}; + +class BaseFactory : public Factory { + public: + BaseFactory(Server *server); + ~BaseFactory(); + bool start() override; + bool shutdown() override; + bool dispatch(SendData *) override; + 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 { + public: + ProcessFactory(Server *server); + ~ProcessFactory(); + bool start() override; + bool shutdown() override; + bool dispatch(SendData *) override; + bool finish(SendData *) override; + bool notify(DataHead *) override; + 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, + SW_SERVER_EVENT_CONNECT, + SW_SERVER_EVENT_CLOSE_FORCE, + // task + SW_SERVER_EVENT_TASK, + SW_SERVER_EVENT_FINISH, + // pipe + SW_SERVER_EVENT_PIPE_MESSAGE, + // event operate + SW_SERVER_EVENT_PAUSE_RECV, + SW_SERVER_EVENT_RESUME_RECV, + // buffer event + SW_SERVER_EVENT_BUFFER_FULL, + SW_SERVER_EVENT_BUFFER_EMPTY, + // 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 { + HOOK_MASTER_START, + HOOK_MASTER_TIMER, + HOOK_REACTOR_START, + HOOK_WORKER_START, + HOOK_TASK_WORKER_START, + HOOK_MASTER_CONNECT, + HOOK_REACTOR_CONNECT, + HOOK_WORKER_CONNECT, + HOOK_REACTOR_RECEIVE, + HOOK_WORKER_RECEIVE, + HOOK_REACTOR_CLOSE, + HOOK_WORKER_CLOSE, + HOOK_MANAGER_START, + HOOK_MANAGER_TIMER, + HOOK_PROCESS_TIMER, + HOOK_END = SW_MAX_HOOK_TYPE - 1, + }; + + enum CloseFlag { + CLOSE_RESET = 1u << 1, + CLOSE_ACTIVELY = 1u << 2, + }; + + /** + * reactor thread/process num + */ + uint16_t reactor_num = 0; + /** + * worker process num + */ + uint32_t worker_num = 0; + + uint8_t dgram_port_num = 0; + + /** + * package dispatch mode + */ + uint8_t dispatch_mode = DISPATCH_FDMOD; + + /** + * No idle work process is available. + */ + bool scheduler_warning = false; + + int worker_uid = 0; + int worker_groupid = 0; + + /** + * worker process max request + */ + uint32_t max_request = 0; + uint32_t max_request_grace = 0; + + network::Socket *udp_socket_ipv4 = nullptr; + network::Socket *udp_socket_ipv6 = nullptr; + network::Socket *dgram_socket = nullptr; + 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; + + /** + * worker(worker and task_worker) process chroot / user / group + */ + std::string chroot_; + std::string user_; + std::string group_; + + /** + * run as a daemon process + */ + bool daemonize = false; + /** + * have dgram socket + */ + bool have_dgram_sock = false; + /** + * have stream socket + */ + bool have_stream_sock = false; + /** + * open cpu affinity setting + */ + bool open_cpu_affinity = false; + /** + * disable notice when use SW_DISPATCH_ROUND and SW_DISPATCH_QUEUE + */ + bool disable_notify = false; + /** + * discard the timeout request + */ + bool discard_timeout_request = false; + /** + * parse cookie header + */ + bool http_parse_cookie = true; + /** + * parse x-www-form-urlencoded data + */ + bool http_parse_post = true; + /** + * parse multipart/form-data files to match $_FILES + */ + bool http_parse_files = false; + /** + * http content compression + */ + bool http_compression = false; + /** + * RFC-7692 + */ + bool websocket_compression = false; + /** + * handle static files + */ + bool enable_static_handler = false; + /** + * show file list in the current directory + */ + bool http_autoindex = false; + /** + * enable onConnect/onClose event when use dispatch_mode=1/3 + */ + bool enable_unsafe_event = false; + /** + * waiting for worker onConnect callback function to return + */ + bool enable_delay_receive = false; + /** + * reuse port + */ + bool enable_reuse_port = false; + /** + * asynchronous reloading + */ + bool reload_async = true; + /** + * use event object + */ + bool event_object = false; + /** + * use task object + */ + bool task_object = false; + /** + * enable coroutine in task worker + */ + bool task_enable_coroutine = false; + /** + * yield coroutine when the output buffer is full + */ + bool send_yield = true; + /** + * enable coroutine + */ + bool enable_coroutine = true; + /** + * disable multi-threads + */ + bool single_thread = false; + /** + * server status + */ + bool running = true; + + int *cpu_affinity_available = 0; + int cpu_affinity_available_num = 0; + + UnixSocket *pipe_command = nullptr; + MessageBus message_bus; + + double send_timeout = 0; + + uint16_t heartbeat_check_interval = 0; + + time_t reload_time = 0; + time_t warning_time = 0; + long timezone_ = 0; + TimerNode *master_timer = nullptr; + TimerNode *heartbeat_timer = nullptr; + + /* buffer output/input setting*/ + uint32_t output_buffer_size = UINT_MAX; + uint32_t input_buffer_size = SW_INPUT_BUFFER_SIZE; + uint32_t max_queued_bytes = 0; + + /** + * the master process and worker process communicate using unix socket dgram. + * ipc_max_size represents the maximum size of each datagram, + * which is obtained from the kernel send buffer of unix socket in swServer_set_ipc_max_size function. + */ + uint32_t ipc_max_size = SW_IPC_MAX_SIZE; + + 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; + + ListenPort *get_primary_port() { + return ports.front(); + } + + 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; + } + } + return nullptr; + } + + 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; + } + + ListenPort *get_port_by_fd(int fd) const { + return get_port_by_server_fd(connection_list[fd].server_fd); + } + + ListenPort *get_port_by_session_id(SessionId session_id) const { + Connection *conn = get_connection_by_session_id(session_id); + if (!conn) { + return nullptr; + } + return get_port_by_fd(conn->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 = TASK_IPC_UNIXSOCK; + uint32_t task_max_request = 0; + uint32_t task_max_request_grace = 0; + std::vector> task_notify_pipes; + EventData *task_results = nullptr; + + /** + * 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 + */ + 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; + ServerGS *gs = nullptr; + + std::shared_ptr> locations = nullptr; + std::shared_ptr> http_index_files = nullptr; + std::shared_ptr> http_compression_types = nullptr; + + 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 + */ + uint8_t http_compression_level = 0; + uint32_t compression_min_length; + /** + * master process pid + */ + std::string pid_file; + + EventData *last_task = nullptr; + std::queue *buffer_pool = nullptr; + + const Allocator *recv_buffer_allocator = &SwooleG.std_allocator; + size_t recv_buffer_size = SW_BUFFER_SIZE_BIG; + + int manager_alarm = 0; + + /** + * message queue key + */ + uint64_t message_queue_key = 0; + + 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 onBeforeReload; + std::function onAfterReload; + /** + * Worker Process + */ + std::function onPipeMessage; + std::function onWorkerStart; + std::function onWorkerStop; + std::function onWorkerExit; + std::function onUserWorkerStart; + /** + * Connection + */ + std::function onReceive; + std::function onPacket; + std::function onClose; + std::function onConnect; + std::function onBufferFull; + std::function onBufferEmpty; + /** + * Task Worker + */ + std::function onTask; + std::function onFinish; + /** + * for MessageBus + */ + std::function msg_id_generator; + /** + * Hook + */ + int (*dispatch_func)(Server *, Connection *, SendData *) = nullptr; + + public: + Server(enum Mode _mode = MODE_BASE); + ~Server(); + + bool set_document_root(const std::string &path) { + if (path.length() > 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)) { + swoole_warning("document_root[%s] does not exist", path.c_str()); + return false; + } + + document_root = std::string(_realpath); + return true; + } + + 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(); + bool reload(bool reload_all_workers); + bool shutdown(); + + int add_worker(Worker *worker); + 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()]); + } + + 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; + } + + int get_maxfd() { + return gs->max_fd; + } + + void set_maxfd(int maxfd) { + gs->max_fd = maxfd; + } + + 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); + + const std::string &get_document_root() { + return document_root; + } + + String *get_recv_buffer(network::Socket *_socket) { + String *buffer = _socket->recv_buffer; + if (buffer == nullptr) { + buffer = swoole::make_string(SW_BUFFER_SIZE_BIG, recv_buffer_allocator); + if (!buffer) { + return nullptr; + } + _socket->recv_buffer = buffer; + } + + return buffer; + } + + MessageBus *get_worker_message_bus() { +#ifdef SW_THREAD + return sw_likely(is_thread_mode()) ? SwooleTG.message_bus : &message_bus; +#else + return &message_bus; +#endif + } + + uint32_t get_worker_buffer_num() { + return is_base_mode() ? 1 : reactor_num + dgram_port_num; + } + + bool is_support_unsafe_events() { + if (is_hash_dispatch_mode()) { + return true; + } else { + return enable_unsafe_event; + } + } + + bool is_process_mode() { + return mode_ == MODE_PROCESS; + } + + bool is_base_mode() { + return mode_ == MODE_BASE; + } + + bool is_thread_mode() { + return mode_ == MODE_THREAD; + } + + bool is_enable_coroutine() { + if (is_task_worker()) { + return task_enable_coroutine; + } else if (is_manager()) { + return false; + } else { + return enable_coroutine; + } + } + + 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; + } + + bool is_support_send_yield() { + return is_hash_dispatch_mode(); + } + + bool if_require_packet_callback(ListenPort *port, bool isset) { +#ifdef SW_USE_OPENSSL + return (port->is_dgram() && !port->ssl && !isset); +#else + return (port->is_dgram() && !isset); +#endif + } + + bool if_require_receive_callback(ListenPort *port, bool isset) { +#ifdef SW_USE_OPENSSL + return (((port->is_dgram() && port->ssl) || port->is_stream()) && !isset); +#else + return (port->is_stream() && !isset); +#endif + } + + 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]); + } + + // Task Worker + uint32_t task_worker_max = task_worker_num + worker_num; + if (worker_id < task_worker_max) { + return &(gs->task_workers.workers[worker_id - worker_num]); + } + + // User Worker + 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]); + } + + 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(); + + Pipe *get_pipe_object(int pipe_fd) { + return (Pipe *) connection_list[pipe_fd].object; + } + + size_t get_all_worker_num() { + return get_core_worker_num() + get_user_worker_num(); + } + + size_t get_user_worker_num() { + return user_worker_list.size(); + } + + size_t get_core_worker_num() { + return worker_num + task_worker_num; + } + + ReactorThread *get_thread(int reactor_id) { + return &reactor_threads[reactor_id]; + } + + bool is_started() { + return gs->start; + } + + bool is_created() { + return factory != nullptr; + } + + bool is_running() { + return running; + } + + bool is_master() { + return swoole_get_process_type() == SW_PROCESS_MASTER; + } + + bool is_worker() { + return swoole_get_process_type() == SW_PROCESS_EVENTWORKER; + } + + bool is_event_worker() { + return is_worker(); + } + + bool is_task_worker() { + return swoole_get_process_type() == SW_PROCESS_TASKWORKER; + } + + bool is_manager() { + return swoole_get_process_type() == SW_PROCESS_MANAGER; + } + + bool is_user_worker() { + return swoole_get_process_type() == SW_PROCESS_USERWORKER; + } + + bool is_worker_thread() { + return is_thread_mode() && swoole_get_thread_type() == Server::THREAD_WORKER; + } + + bool is_worker_process() { + return !is_thread_mode() && (is_worker() || is_task_worker()); + } + + bool is_reactor_thread() { + return swoole_get_thread_type() == Server::THREAD_REACTOR; + } + + bool is_single_worker() { + return (worker_num == 1 && task_worker_num == 0 && max_request == 0 && get_user_worker_num() == 0); + } + + bool isset_hook(enum HookType type) { + assert(type <= HOOK_END); + return hooks[type]; + } + + bool is_sync_process() { + if (is_manager()) { + return true; + } + if (is_task_worker() && !task_enable_coroutine) { + return true; + } + return false; + } + bool is_shutdown() { + return gs->shutdown; + } + + // 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: + return true; + default: + return false; + } + } + + 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: + case SW_SERVER_EVENT_RESUME_RECV: + case SW_SERVER_EVENT_BUFFER_FULL: + case SW_SERVER_EVENT_BUFFER_EMPTY: + return true; + default: + return false; + } + } + + int get_connection_fd(SessionId session_id) const { + return session_list[session_id % SW_SESSION_LIST_SIZE].fd; + } + + Connection *get_connection_verify_no_ssl(SessionId session_id) { + Session *session = get_session(session_id); + int fd = session->fd; + Connection *conn = get_connection(fd); + if (!conn || conn->active == 0) { + return nullptr; + } + if (session->id != session_id || conn->session_id != session_id) { + return nullptr; + } + return conn; + } + + 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) { + return nullptr; + } +#endif + return conn; + } + + Connection *get_connection(int fd) const { + if ((uint32_t) fd > max_connection) { + return nullptr; + } + return &connection_list[fd]; + } + + 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; + } + + Connection *get_connection_by_session_id(SessionId session_id) const { + return get_connection(get_connection_fd(session_id)); + } + + Session *get_session(SessionId session_id) { + return &session_list[session_id % SW_SESSION_LIST_SIZE]; + } + + void clear_timer(); + static void timer_callback(Timer *timer, TimerNode *tnode); + + int create_task_workers(); + int create_user_workers(); + int start_manager_process(); + + 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); + 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(const Protocol *proto, network::Socket *_socket, const RecvData *rdata); + + int send_to_connection(SendData *); + ssize_t send_to_worker_from_worker(Worker *dst_worker, const void *buf, size_t len, int flags); + + ssize_t send_to_worker_from_worker(WorkerId id, EventData *data, int flags) { + return send_to_worker_from_worker(get_worker(id), data, data->size(), flags); + } + + ssize_t send_to_reactor_thread(const EventData *ev_data, size_t sendn, SessionId session_id); + + 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); + bool sendwait(SessionId session_id, const void *data, uint32_t length); + bool close(SessionId session_id, bool reset); + + 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_event_worker(Worker *worker); + void init_task_workers(); + void init_port_protocol(ListenPort *port); + void init_signal_handler(); + void init_ipc_max_size(); + void init_pipe_sockets(MessageBus *mb); + + void set_max_connection(uint32_t _max_connection); + + 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; + } + gs->session_round = value; + } + + int create_pipe_buffers(); + void release_pipe_buffers(); + void create_worker(Worker *worker); + void destroy_worker(Worker *worker); + void disable_accept(); + void destroy_http_request(Connection *conn); + + int schedule_worker(int fd, SendData *data); + + size_t get_connection_num() const { + if (gs->connection_nums) { + size_t num = 0; + for (uint32_t i = 0; i < worker_num; i++) { + num += gs->connection_nums[i]; + } + return num; + } else { + return gs->connection_num; + } + } + + static int wait_other_worker(ProcessPool *pool, const ExitStatus &exit_status); + static void read_worker_message(ProcessPool *pool, EventData *msg); + + void drain_worker_pipe(); + void clean_worker_connections(Worker *worker); + + /** + * [Worker] + */ + 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 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; + ServerPortGS *port_gs_list = nullptr; + /** + * http static file directory + */ + std::string document_root; + std::mutex lock_; + uint32_t max_connection = 0; + TimerNode *enable_accept_timer = nullptr; + std::thread heartbeat_thread; + /** + * The number of pipe per reactor maintenance + */ + 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(); + int start_reactor_threads(); + int start_reactor_processes(); + 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::RecvData swRecvData; + +extern swoole::Server *g_server_instance; + +static inline swoole::Server *sw_server() { + return g_server_instance; +} diff --git a/include/swoole_signal.h b/include/swoole_signal.h new file mode 100644 index 00000000000..5416dedce5d --- /dev/null +++ b/include/swoole_signal.h @@ -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. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole.h" + +#include + +namespace swoole { +typedef void (*SignalHandler)(int); + +struct Signal { + SignalHandler handler; + uint16_t signo; + bool activated; +}; +} // namespace swoole + +typedef swoole::SignalHandler swSignalHandler; + +#ifdef HAVE_SIGNALFD +void swoole_signalfd_init(); +#endif + +SW_API swSignalHandler swoole_signal_set(int signo, swSignalHandler func); +SW_API bool swoole_signal_isset(int signo); +SW_API swSignalHandler swoole_signal_set(int signo, swSignalHandler func, int restart, int mask); +SW_API swSignalHandler swoole_signal_get_handler(int signo); + +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 new file mode 100644 index 00000000000..35606ee3811 --- /dev/null +++ b/include/swoole_socket.h @@ -0,0 +1,677 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Twosee | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include +#include +#include +#include +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) +#include +#endif +#include +#include +#include +#include + +#include +#include + +#include "swoole.h" +#include "swoole_ssl.h" +#include "swoole_buffer.h" +#include "swoole_file.h" + +#ifndef SOCK_NONBLOCK +#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) +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 { +struct GetaddrinfoRequest; + +namespace network { + +struct SendfileTask { + off_t offset; + size_t length; + char filename[0]; +}; + +struct SendfileRequest { + File file; + size_t length; + off_t offset; + + public: + SendfileRequest(const char *filename, off_t _offset, size_t _length) : file(filename, O_RDONLY) { + offset = _offset; + length = _length; + } +}; + +struct Address { + union { + struct sockaddr ss; + struct sockaddr_in inet_v4; + struct sockaddr_in6 inet_v6; + struct sockaddr_un un; + } addr; + socklen_t len; + SocketType type; + + bool assign(SocketType _type, const std::string &_host, int _port); + bool assign(const std::string &url); + + const char *get_ip() { + return get_addr(); + } + + int get_port(); + const char *get_addr(); + + bool is_loopback_addr() { + if (type == SW_SOCK_TCP || type == SW_SOCK_UDP) { + return IN_IS_ADDR_LOOPBACK(&addr.inet_v4.sin_addr); + } else if (type == SW_SOCK_TCP6 || type == SW_SOCK_UDP6) { + return IN6_IS_ADDR_LOOPBACK(&addr.inet_v6.sin6_addr); + } + return false; + } + + static bool verify_ip(int __af, const std::string &str) { + char tmp_address[INET6_ADDRSTRLEN]; + return inet_pton(__af, str.c_str(), tmp_address) != -1; + } +}; + +struct IOVector { + // we should modify iov_iterator instead of iov, iov is readonly + struct iovec *iov = nullptr; + struct iovec *iov_iterator = nullptr; + int count = 0; + int remain_count = 0; + int index = 0; + size_t offset_bytes = 0; + + IOVector(struct iovec *_iov, int _iovcnt); + ~IOVector(); + + void update_iterator(ssize_t __n); + + struct iovec *get_iterator() { + return iov_iterator; + } + + 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; + } + + int get_index() { + return index; + } + + size_t get_offset_bytes() { + return offset_bytes; + } +}; + +struct Socket { + static double default_dns_timeout; + static double default_connect_timeout; + static double default_read_timeout; + static double default_write_timeout; + static uint32_t default_buffer_size; + + int fd; + FdType fd_type; + SocketType socket_type; + int events; + bool enable_tcp_nodelay; + + uchar removed : 1; + uchar silent_remove : 1; + uchar nonblock : 1; + uchar cloexec : 1; + uchar direct_send : 1; +#ifdef SW_USE_OPENSSL + uchar ssl_send_ : 1; + uchar ssl_want_read : 1; + uchar ssl_want_write : 1; + 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 +#endif + uchar dontwait : 1; + uchar close_wait : 1; + uchar send_wait : 1; + uchar tcp_nopush : 1; + uchar tcp_nodelay : 1; + uchar skip_recv : 1; + uchar recv_wait : 1; + uchar event_hup : 1; + uchar dont_restart : 1; + + // memory buffer size [user space] + uint32_t buffer_size; + uint32_t chunk_size; + + void *object; + +#ifdef SW_USE_OPENSSL + SSL *ssl; + uint32_t ssl_state; +#endif + + Address info; + double recv_timeout_ = default_read_timeout; + double send_timeout_ = default_write_timeout; + + double last_received_time; + double last_sent_time; + + Buffer *out_buffer; + Buffer *in_buffer; + String *recv_buffer; + + TimerNode *recv_timer; + TimerNode *send_timer; + + size_t total_recv_bytes; + size_t total_send_bytes; + + // for reactor + int handle_send(); + int handle_sendfile(); + // 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); + bool set_timeout(double timeout); + bool set_recv_timeout(double timeout); + bool set_send_timeout(double timeout); + + bool set_nonblock() { + return set_fd_option(1, -1); + } + + bool set_block() { + return set_fd_option(0, -1); + } + + bool set_fd_option(int _nonblock, int _cloexec); + + int set_option(int level, int optname, int optval) { + return setsockopt(fd, level, optname, &optval, sizeof(optval)); + } + + int set_option(int level, int optname, const void *optval, socklen_t optlen) { + return setsockopt(fd, level, optname, optval, optlen); + } + + int get_option(int level, int optname, void *optval, socklen_t *optlen) { + return getsockopt(fd, level, optname, optval, optlen); + } + + int get_option(int level, int optname, int *optval) { + socklen_t optlen = sizeof(*optval); + return get_option(level, optname, optval, &optlen); + } + + int get_fd() { + return fd; + } + + 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); + } + + int set_tcp_nopush(int nopush) { +#ifdef TCP_CORK + if (set_option(IPPROTO_TCP, TCP_CORK, nopush) == SW_ERR) { + return -1; + } else { + tcp_nopush = nopush; + return 0; + } +#else + return -1; +#endif + } + + int set_reuse_addr(int enable = 1) { + return set_option(SOL_SOCKET, SO_REUSEADDR, enable); + } + + int set_reuse_port(int enable = 1) { +#ifdef SO_REUSEPORT + return set_option(SOL_SOCKET, SO_REUSEPORT, enable); +#endif + return -1; + } + + int set_tcp_nodelay(int nodelay = 1) { + if (set_option(IPPROTO_TCP, TCP_NODELAY, nodelay) == SW_ERR) { + return -1; + } else { + tcp_nodelay = nodelay; + return 0; + } + } + + bool check_liveness() { + char buf; + errno = 0; + ssize_t retval = peek(&buf, sizeof(buf), MSG_DONTWAIT); + return !(retval == 0 || (retval < 0 && catch_read_error(errno) == SW_CLOSE)); + } + + /** + * socket io operation + */ + int sendfile(const char *filename, off_t offset, size_t length); + ssize_t recv(void *__buf, size_t __n, int __flags); + ssize_t send(const void *__buf, size_t __n, int __flags); + 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); + + ssize_t writev(const struct iovec *iov, size_t iovcnt) { + return ::writev(fd, iov, iovcnt); + } + + int bind(const Address &sa) { + return ::bind(fd, &sa.addr.ss, sizeof(sa.addr.ss)); + } + + int listen(int backlog = 0) { + return ::listen(fd, backlog <= 0 ? SW_BACKLOG : backlog); + } + + void clean(); + ssize_t send_blocking(const void *__data, size_t __len); + ssize_t send_async(const void *__data, size_t __len); + ssize_t recv_blocking(void *__data, size_t __len, int flags); + 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); + + int connect(const Address &sa) { + return ::connect(fd, &sa.addr.ss, sa.len); + } + + int connect(const Address *sa) { + return ::connect(fd, &sa->addr.ss, sa->len); + } + + int connect(const std::string &host, int port) { + Address addr; + addr.assign(socket_type, host, port); + return connect(addr); + } + +#ifdef SW_USE_OPENSSL + void ssl_clear_error() { + ERR_clear_error(); + ssl_want_read = 0; + ssl_want_write = 0; + } + int ssl_create(SSLContext *_ssl_context, int _flags); + int ssl_connect(); + 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); + 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 + + 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); + } + + bool cork() { + if (tcp_nopush) { + return false; + } +#ifdef TCP_CORK + if (set_tcp_nopush(1) < 0) { + 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) { + swoole_sys_warning("set_tcp_nodelay(fd=%d, OFF) failed", fd); + } + return true; + } + + bool uncork() { + if (!tcp_nopush) { + return false; + } +#ifdef TCP_CORK + if (set_tcp_nopush(0) < 0) { + 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) { + 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(SocketType type) { + return (type == SW_SOCK_UDP || type == SW_SOCK_UDP6 || type == SW_SOCK_UNIX_DGRAM); + } + + static inline int is_stream(SocketType type) { + return (type == SW_SOCK_TCP || type == SW_SOCK_TCP6 || type == SW_SOCK_UNIX_STREAM); + } + + bool is_stream() { + return socket_type == SW_SOCK_TCP || socket_type == SW_SOCK_TCP6 || socket_type == SW_SOCK_UNIX_STREAM; + } + + bool is_dgram() { + return socket_type == SW_SOCK_UDP || socket_type == SW_SOCK_UDP6 || socket_type == SW_SOCK_UNIX_DGRAM; + } + + bool is_inet4() { + return socket_type == SW_SOCK_TCP || socket_type == SW_SOCK_UDP; + } + + bool is_inet6() { + return socket_type == SW_SOCK_TCP6 || socket_type == SW_SOCK_UDP6; + } + + bool is_inet() { + return is_inet4() || is_inet6(); + } + + bool is_local() { + return socket_type == SW_SOCK_UNIX_STREAM || socket_type == SW_SOCK_UNIX_DGRAM; + } + + ssize_t write(const void *__buf, size_t __len) { + return ::write(fd, __buf, __len); + } + + ssize_t read(void *__buf, size_t __len) { + 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); + } + + 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); + + 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; + } + return sendto(addr, data, len, 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); + } + + int catch_error(int err) const { + switch (err) { + case EFAULT: + abort(); + return SW_ERROR; + case EBADF: + case ENOENT: + return SW_INVALID; + case ECONNRESET: + case ECONNABORTED: + case EPIPE: + case ENOTCONN: + case ETIMEDOUT: + case ECONNREFUSED: + case ENETDOWN: + case ENETUNREACH: + case EHOSTDOWN: + case EHOSTUNREACH: + case SW_ERROR_SSL_BAD_CLIENT: + case SW_ERROR_SSL_RESET: + return SW_CLOSE; + case EAGAIN: +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif +#ifdef HAVE_KQUEUE + case ENOBUFS: +#endif + case 0: + return SW_WAIT; + default: + return SW_ERROR; + } + } + + 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 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); + return SW_SOCK_UNIX_STREAM; + } else if (host.find(':') != std::string::npos) { + return SW_SOCK_TCP6; + } else { + return SW_SOCK_TCP; + } + } + + 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; + *sock_type = SOCK_STREAM; + break; + case SW_SOCK_UNIX_STREAM: + *sock_domain = AF_UNIX; + *sock_type = SOCK_STREAM; + break; + case SW_SOCK_UDP: + *sock_domain = AF_INET; + *sock_type = SOCK_DGRAM; + break; + case SW_SOCK_UDP6: + *sock_domain = AF_INET6; + *sock_type = SOCK_DGRAM; + break; + case SW_SOCK_UNIX_DGRAM: + *sock_domain = AF_UNIX; + *sock_type = SOCK_DGRAM; + break; + case SW_SOCK_TCP: + *sock_domain = AF_INET; + *sock_type = SOCK_STREAM; + break; + default: + return SW_ERR; + } + + return SW_OK; + } +}; + +int gethostbyname(int type, const char *name, char *addr); +int getaddrinfo(GetaddrinfoRequest *req); + +} // namespace network + +/** + * This function will never return NULL; if memory allocation fails, a C++ exception will be thrown. + * Must use the `socket->free()` function to release the object pointer instead of the `delete` operator. + * When the socket is released, it will close the file descriptor (fd). + * If you do not want the fd to be closed, use `socket->move_fd()` to relinquish ownership of the fd. + */ +network::Socket *make_socket(int fd, FdType fd_type); +/** + * The following three functions will return a null pointer if the socket creation fails. + * It is essential to check the return value; + * if it is nullptr, you should inspect errno to determine the cause of the error. + */ +network::Socket *make_socket(SocketType socket_type, FdType fd_type, int flags); +network::Socket *make_socket( + SocketType type, FdType fd_type, int sock_domain, int sock_type, int socket_protocol, int flags); +int socket(int sock_domain, int sock_type, int socket_protocol, int flags); +network::Socket *make_server_socket(SocketType socket_type, + const char *address, + int port = 0, + int backlog = SW_BACKLOG); +/** + * Verify if the input string is an IP address, + * where AF_INET indicates an IPv4 address, such as 192.168.1.100, + * and AF_INET6 indicates an IPv6 address, for example, 2001:0000:130F:0000:0000:09C0:876A:130B. + */ +bool verify_ip(int __af, const std::string &str); +} // namespace swoole diff --git a/include/swoole_socket_hook.h b/include/swoole_socket_hook.h new file mode 100644 index 00000000000..bce09f4afdd --- /dev/null +++ b/include/swoole_socket_hook.h @@ -0,0 +1,52 @@ +/* + +----------------------------------------------------------------------+ + | 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 SW_SOCKET_HOOK_H_ +#define SW_SOCKET_HOOK_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "swoole_coroutine_c_api.h" + +#define socket(domain, type, protocol) swoole_coroutine_socket(domain, type, protocol) +#define send(sockfd, buf, len, flags) swoole_coroutine_send(sockfd, buf, len, flags) +#define read(sockfd, buf, len) swoole_coroutine_read(sockfd, buf, len) +#define write(sockfd, buf, len) swoole_coroutine_write(sockfd, buf, len) +#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) +#define gethostbyname(name) swoole_coroutine_gethostbyname(name) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/swoole_ssl.h b/include/swoole_ssl.h new file mode 100644 index 00000000000..469c85d3270 --- /dev/null +++ b/include/swoole_ssl.h @@ -0,0 +1,189 @@ +/* + +----------------------------------------------------------------------+ + | 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: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole.h" + +#ifdef SW_USE_OPENSSL + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L +#define SW_SUPPORT_DTLS +#endif + +#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 swSSLState { + SW_SSL_STATE_HANDSHAKE = 0, + SW_SSL_STATE_READY = 1, + SW_SSL_STATE_WAIT_STREAM = 2, +}; + +enum swSSLVersion { + SW_SSL_SSLv2 = 1u << 1, + SW_SSL_SSLv3 = 1u << 2, + SW_SSL_TLSv1 = 1u << 3, + SW_SSL_TLSv1_1 = 1u << 4, + SW_SSL_TLSv1_2 = 1u << 5, + SW_SSL_TLSv1_3 = 1u << 6, + SW_SSL_DTLS = 1u << 7, +}; + +#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 swSSLMethod { + SW_SSLv23_METHOD = 0, + SW_SSLv3_METHOD, + SW_SSLv3_SERVER_METHOD, + SW_SSLv3_CLIENT_METHOD, + SW_SSLv23_SERVER_METHOD, + SW_SSLv23_CLIENT_METHOD, + SW_TLSv1_METHOD, + SW_TLSv1_SERVER_METHOD, + SW_TLSv1_CLIENT_METHOD, +#ifdef TLS1_1_VERSION + SW_TLSv1_1_METHOD, + SW_TLSv1_1_SERVER_METHOD, + SW_TLSv1_1_CLIENT_METHOD, +#endif +#ifdef TLS1_2_VERSION + SW_TLSv1_2_METHOD, + SW_TLSv1_2_SERVER_METHOD, + SW_TLSv1_2_CLIENT_METHOD, +#endif +#ifdef SW_SUPPORT_DTLS + SW_DTLS_CLIENT_METHOD, + SW_DTLS_SERVER_METHOD, +#endif +}; + +namespace swoole { + +struct SSLContext { + uchar http : 1; + uchar http_v2 : 1; + uchar prefer_server_ciphers : 1; + uchar session_tickets : 1; + uchar stapling : 1; + uchar stapling_verify : 1; + std::string ciphers; + std::string ecdh_curve; + std::string session_cache; + std::string dhparam; + std::string cert_file; + std::string key_file; + std::string passphrase; + std::string client_cert_file; +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + 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; + uchar disable_compress : 1; + uchar verify_peer : 1; + uchar allow_self_signed : 1; + uint32_t protocols; + uint8_t create_flag; + SSL_CTX *context; + + SSL_CTX *get_context() { + 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) { + swoole_warning("ssl cert file[%s] not found", _cert_file.c_str()); + return false; + } + cert_file = _cert_file; + return true; + } + + bool set_key_file(const std::string &_key_file) { + if (access(_key_file.c_str(), R_OK) < 0) { + swoole_warning("ssl key file[%s] not found", _key_file.c_str()); + return false; + } + key_file = _key_file; + return true; + } + + bool create(); + bool set_capath(); + bool set_ciphers(); + bool set_client_certificate(); + bool set_ecdh_curve(); + bool set_dhparam(); + ~SSLContext(); +}; +} + +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 new file mode 100644 index 00000000000..c35d7bf78dc --- /dev/null +++ b/include/swoole_static_handler.h @@ -0,0 +1,174 @@ +/* + +----------------------------------------------------------------------+ + | 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_server.h" +#include "swoole_http.h" +#include "swoole_mime_type.h" + +#include +#include + +namespace swoole { +namespace http_server { +class StaticHandler { + private: + Server *serv; + std::string request_url; + std::string dir_path; + std::set dir_files; + std::string index_file; + typedef struct { + off_t offset; + size_t length; + char part_header[SW_HTTP_SERVER_PART_HEADER]; + } task_t; + std::vector tasks; + + size_t l_filename = 0; + char filename[PATH_MAX]; + struct stat file_stat; + bool last = false; + std::string content_type; + std::string boundary; + std::string end_part; + size_t content_length = 0; + + public: + int status_code = SW_HTTP_OK; + StaticHandler(Server *_server, const char *url, size_t url_length) : request_url(url, url_length) { + serv = _server; + } + + /** + * @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); + 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(); + + time_t get_file_mtime() { +#ifdef __MACH__ + return file_stat.st_mtimespec.tv_sec; +#else + return file_stat.st_mtim.tv_sec; +#endif + } + + std::string get_date_last_modified(); + + 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(); + } + + 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(); + } + + std::string get_filename_std_string() { + return std::string(filename, l_filename); + } + + bool get_absolute_path(); + + size_t get_filesize() { + return file_stat.st_size; + } + + const std::vector &get_tasks() { + return tasks; + } + + 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_server +}; // namespace swoole diff --git a/include/swoole_string.h b/include/swoole_string.h new file mode 100644 index 00000000000..7adb7a5edeb --- /dev/null +++ b/include/swoole_string.h @@ -0,0 +1,329 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Twosee | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole.h" + +#include + +#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 { + +typedef std::function StringExplodeHandler; + +class String { + private: + void alloc(size_t _size, const Allocator *_allocator) { + if (_allocator == nullptr) { + _allocator = sw_std_allocator(); + } + + _size = SW_MEM_ALIGNED_SIZE(_size); + length = 0; + size = _size; + offset = 0; + str = (char *) _allocator->malloc(_size); + allocator = _allocator; + + if (str == nullptr) { + throw std::bad_alloc(); + } + } + + void move(String &&src) { + str = src.str; + length = src.length; + offset = src.offset; + size = src.size; + allocator = src.allocator; + + src.str = nullptr; + src.length = 0; + src.size = 0; + src.offset = 0; + } + + public: + size_t length; + size_t size; + off_t offset; + char *str; + const Allocator *allocator; + + String() { + length = size = offset = 0; + str = nullptr; + allocator = nullptr; + } + + explicit String(size_t _size, const Allocator *_allocator = nullptr) { + alloc(_size, _allocator); + } + + String(const char *_str, size_t _length) { + 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); + length = _str.length; + offset = _str.offset; + } + + String(String &&src) { + move(std::move(src)); + } + + String &operator=(String &src) { + if (&src == this) { + return *this; + } + if (allocator && str) { + allocator->free(str); + } + alloc(src.size, src.allocator); + memcpy(src.str, str, src.length); + length = src.length; + offset = src.offset; + return *this; + } + + String &operator=(String &&src) { + if (&src == this) { + return *this; + } + if (allocator && str) { + allocator->free(str); + } + move(std::move(src)); + return *this; + } + + ~String() { + if (allocator && str) { + allocator->free(str); + } + } + + char *value() { + return str; + } + + size_t get_length() { + return length; + } + + size_t capacity() { + return size; + } + + std::string to_std_string() { + return std::string(str, length); + } + + bool contains(const char *needle, size_t l_needle) { + return swoole_strnstr(str, length, needle, l_needle) != nullptr; + } + + bool contains(const std::string &needle) { + return contains(needle.c_str(), needle.size()); + } + + bool grow(size_t incr_value) { + length += incr_value; + if (length == size && !reserve(size * 2)) { + return false; + } else { + return true; + } + } + + String *substr(size_t offset, size_t len) { + if (offset + len > length) { + return nullptr; + } + auto _substr = new String(len); + _substr->append(str + offset, len); + return _substr; + } + + bool empty() { + return str == nullptr || length == 0; + } + + void clear() { + length = 0; + offset = 0; + } + + bool extend() { + return extend(size * 2); + } + + bool extend(size_t new_size) { + assert(new_size > size); + return reserve(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); + } + + 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); + + int append(const std::string &append_str) { + return append(append_str.c_str(), append_str.length()); + } + + 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)) { + return SW_ERR; + } + } + + memcpy(str + length, append_str.str, append_str.length); + length += append_str.length; + return SW_OK; + } + + 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, swoole_pagesize())); + } + + memcpy(str + _offset, write_str->str, write_str->length); + if (new_length > length) { + length = new_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, swoole_pagesize())); + } + + memcpy(str + _offset, write_str, _length); + if (new_length > length) { + length = new_length; + } + } + + 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(bool print_value = true); + + enum FormatFlag { + FORMAT_APPEND = 1 << 0, + FORMAT_GROW = 1 << 1, + }; + + template + 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++; + + 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; + } + + 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); + void reduce(off_t offset); +}; + +inline String *make_string(size_t size, const Allocator *allocator = nullptr) { + return new String(size, allocator); +} +} // namespace swoole diff --git a/include/swoole_table.h b/include/swoole_table.h new file mode 100644 index 00000000000..d7b0af240dc --- /dev/null +++ b/include/swoole_table.h @@ -0,0 +1,311 @@ +/* + +----------------------------------------------------------------------+ + | 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_memory.h" +#include "swoole_util.h" +#include "swoole_lock.h" +#include "swoole_hash.h" + +#include + +#include +#include + +//#define SW_TABLE_DEBUG 0 +#define SW_TABLE_FORCE_UNLOCK_TIME 2000 // milliseconds +#define SW_TABLE_USE_PHP_HASH + +namespace swoole { + +typedef uint32_t TableStringLength; +typedef uint64_t (*HashFunc)(const char *key, size_t len); + +struct TableColumn; + +struct TableRow { + sw_atomic_t lock_; + pid_t lock_pid; + /** + * 1:used, 0:empty + */ + uint8_t active; + uint8_t key_len; + /** + * next slot + */ + TableRow *next; + /** + * Hash Key + */ + char key[SW_TABLE_KEY_SIZE]; + char data[0]; + + void lock(); + + void unlock() { + sw_spinlock_release(&lock_); + } + + void clear() { + sw_memset_zero((char *) &lock_pid, sizeof(TableRow) - offsetof(TableRow, lock_pid)); + } + + 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 { + 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 { + SW_TABLE_FLAG_NEW_ROW = 1, + SW_TABLE_FLAG_CONFLICT = 1u << 1, +}; + +struct TableColumn { + enum Type { + TYPE_INT = 1, + TYPE_FLOAT, + TYPE_STRING, + }; + + enum Type type; + uint32_t size; + std::string name; + size_t index; + + TableColumn(const std::string &_name, enum Type _type, size_t _size) { + index = 0; + name = _name; + type = _type; + switch (_type) { + case TYPE_INT: + size = sizeof(long); + break; + case TYPE_FLOAT: + size = sizeof(double); + break; + case TYPE_STRING: + size = _size + sizeof(TableStringLength); + break; + default: + abort(); + break; + } + } + + void clear(TableRow *row); +}; + +class Table { + private: + std::unordered_map *column_map; + Mutex *mutex; + size_t size; + size_t mask; + size_t item_size; + size_t memory_size; + float conflict_proportion; + + /** + * total rows that in active state(shm) + */ + sw_atomic_t row_num; + + TableRow **rows; + FixedPool *pool; + + TableIterator *iterator; + HashFunc hash_func; + bool created; + + void *memory; + + 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 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() const { + return created; + } + + bool ready() { + return memory != nullptr; + } + + void set_hash_func(HashFunc _fn) { + hash_func = _fn; + } + + size_t get_size() const { + return size; + } + + TableRow *get_by_index(uint32_t index) { + TableRow *row = rows[index]; + return row->active ? row : nullptr; + } + + TableColumn *get_column(const std::string &key) { + auto i = column_map->find(key); + if (i == column_map->end()) { + return nullptr; + } else { + return i->second; + } + } + + 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->current_; + } + + void rewind() { + 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; + assert(index < size); + return rows[index]; + } + + 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((char *) new_row + offsetof(TableRow, active), sizeof(TableRow) - offsetof(TableRow, active)); + memcpy(new_row->key, key, keylen); + new_row->key[keylen] = '\0'; + new_row->key_len = keylen; + new_row->active = 1; + sw_atomic_fetch_add(&(row_num), 1); + } + + int lock() { + return mutex->lock(); + } + + int unlock() { + return mutex->unlock(); + } +}; +} // namespace swoole diff --git a/include/swoole_thread.h b/include/swoole_thread.h new file mode 100644 index 00000000000..e3bff3a464c --- /dev/null +++ b/include/swoole_thread.h @@ -0,0 +1,64 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole.h" +#include "swoole_lock.h" + +#include + +#if defined(__linux__) +#include /* syscall(SYS_gettid) */ +#elif defined(__FreeBSD__) +#include /* pthread_getthreadid_np() */ +#elif defined(__OpenBSD__) +#include /* getthrid() */ +#elif defined(_AIX) +#include /* thread_self() */ +#elif defined(__NetBSD__) +#include /* _lwp_self() */ +#elif defined(__CYGWIN__) || defined(WIN32) +#include /* GetCurrentThreadId() */ +#endif + +static long swoole_thread_get_native_id(void) { +#ifdef __APPLE__ + uint64_t native_id; + (void) pthread_threadid_np(NULL, &native_id); +#elif defined(__linux__) + pid_t native_id = syscall(SYS_gettid); +#elif defined(__FreeBSD__) + int native_id = pthread_getthreadid_np(); +#elif defined(__OpenBSD__) + pid_t native_id = getthrid(); +#elif defined(_AIX) + tid_t native_id = thread_self(); +#elif defined(__NetBSD__) + lwpid_t native_id = _lwp_self(); +#elif defined(__CYGWIN__) || defined(WIN32) + DWORD native_id = GetCurrentThreadId(); +#endif + return native_id; +} + +static bool swoole_thread_set_name(const char *name) { +#if defined(__APPLE__) + return pthread_setname_np(name) == 0; +#else + return pthread_setname_np(pthread_self(), name) == 0; +#endif +} diff --git a/include/swoole_timer.h b/include/swoole_timer.h new file mode 100644 index 00000000000..b41b033c69a --- /dev/null +++ b/include/swoole_timer.h @@ -0,0 +1,147 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Twosee | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole.h" +#include "swoole_heap.h" +#include "swoole_reactor.h" + +#include + +#define SW_TIMER_MIN_MS 1 +#define SW_TIMER_MIN_SEC 0.001 +#define SW_TIMER_MAX_MS LONG_MAX +#define SW_TIMER_MAX_SEC ((double) LONG_MAX / 1000) + +namespace swoole { + +typedef std::function TimerDestructor; + +struct TimerNode { + enum Type { + TYPE_KERNEL, + TYPE_PHP, + }; + long id; + enum Type type; + int64_t exec_msec; + int64_t interval; + uint64_t exec_count; + uint64_t round; + bool removed; + HeapNode *heap_node; + TimerCallback callback; + void *data; + TimerDestructor destructor; +}; + +class Timer { + private: + /*--------------signal timer--------------*/ + Reactor *reactor_ = nullptr; + Heap heap; + std::unordered_map map; + uint64_t round; + long _next_id; + long _current_id; + /*---------------event timer--------------*/ + struct timeval base_time; + /*----------------------------------------*/ + std::function set; + std::function close; + + bool init_with_reactor(Reactor *reactor); + bool init_with_user_scheduler(const TimerScheduler &scheduler); + bool init_with_system_timer(); + + public: + long next_msec_; + + Timer(); + ~Timer(); + static int now(struct timeval *time); + + int64_t get_relative_msec() { + struct timeval _now; + if (now(&_now) < 0) { + return SW_ERR; + } + int64_t msec1 = (_now.tv_sec - base_time.tv_sec) * 1000; + int64_t msec2 = (_now.tv_usec - base_time.tv_usec) / 1000; + return msec1 + msec2; + } + + inline static int64_t get_absolute_msec() { + struct timeval now; + if (Timer::now(&now) < 0) { + return SW_ERR; + } + int64_t msec1 = (now.tv_sec) * 1000; + int64_t msec2 = (now.tv_usec) / 1000; + return msec1 + msec2; + } + + Reactor *get_reactor() { + return reactor_; + } + + bool init(); + TimerNode *add(long _msec, bool persistent, void *data, const TimerCallback &callback); + bool remove(TimerNode *tnode); + void update(TimerNode *tnode) { + heap.change_priority(tnode->exec_msec, tnode->heap_node); + } + void delay(TimerNode *tnode, long delay_ms) { + long now_ms = get_relative_msec(); + tnode->exec_msec = (now_ms < 0 ? tnode->exec_msec : now_ms) + delay_ms; + update(tnode); + } + void reinit(Reactor *reactor); + int select(); + + TimerNode *get(long id) { + auto it = map.find(id); + if (it == map.end()) { + return nullptr; + } else { + return it->second; + } + } + + TimerNode *get(long id, const enum TimerNode::Type type) { + TimerNode *tnode = get(id); + return (tnode && tnode->type == type) ? tnode : nullptr; + } + + size_t count() { + return map.size(); + } + + uint64_t get_round() { + return round; + } + + bool remove(long id) { + return remove(get(id)); + } + + const std::unordered_map &get_map() { + return map; + } +}; +} // namespace swoole diff --git a/include/swoole_util.h b/include/swoole_util.h new file mode 100644 index 00000000000..2aa5a9d1721 --- /dev/null +++ b/include/swoole_util.h @@ -0,0 +1,247 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Twosee | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define __SCOPEGUARD_CONCATENATE_IMPL(s1, s2) s1##s2 +#define __SCOPEGUARD_CONCATENATE(s1, s2) __SCOPEGUARD_CONCATENATE_IMPL(s1, s2) + +namespace swoole { + +namespace std_string { +template +inline std::string format(const char *format, Args... args) { + size_t size = snprintf(nullptr, 0, format, args...) + 1; // Extra space for '\0' + std::unique_ptr buf(new char[size]); + snprintf(buf.get(), size, format, args...); + return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside +} + +inline std::string vformat(const char *format, va_list args) { + va_list _args; + va_copy(_args, args); + size_t size = vsnprintf(nullptr, 0, format, _args) + 1; // Extra space for '\0' + va_end(_args); + std::unique_ptr buf(new char[size]); + vsnprintf(buf.get(), size, format, args); + return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside +} +} // namespace std_string + +// Keep parameter 'steady' as false for backward compatibility. +template +static inline long time(bool steady = false) { + if (sw_likely(steady)) { + auto now = std::chrono::steady_clock::now(); + return std::chrono::duration_cast(now.time_since_epoch()).count(); + } else { + auto now = std::chrono::system_clock::now(); + return std::chrono::duration_cast(now.time_since_epoch()).count(); + } +} + +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: + ScopeGuard(Fun &&f) : _fun(std::forward(f)), _active(true) {} + + ~ScopeGuard() { + if (_active) { + _fun(); + } + } + + void dismiss() { + _active = false; + } + + ScopeGuard() = delete; + ScopeGuard(const ScopeGuard &) = delete; + ScopeGuard &operator=(const ScopeGuard &) = delete; + + ScopeGuard(ScopeGuard &&rhs) : _fun(std::move(rhs._fun)), _active(rhs._active) { + rhs.dismiss(); + } + + private: + Fun _fun; + 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 {}; + +template +inline ScopeGuard operator+(ScopeGuardOnExit, Fun &&fn) { + return ScopeGuard(std::forward(fn)); +} +} // namespace detail + +// Helper macro +#define ON_SCOPE_EXIT \ + auto __SCOPEGUARD_CONCATENATE(ext_exitBlock_, __LINE__) = swoole::detail::ScopeGuardOnExit() + [&]() + +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])) { + str[i] = 0; + len--; + } else { + break; + } + } + return len; +} + +static inline size_t rtrim(const char *str, size_t len) { + for (size_t i = len; i > 0;) { + if (isspace(str[--i])) { + len--; + } else { + break; + } + } + return len; +} + +static inline ssize_t substr_len(const char *str, size_t len, char separator, bool before = false) { + const char *substr = (const char *) memchr(str, separator, len); + if (substr == nullptr) { + return -1; + } + 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 ccb6f41d8bc..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,21 +18,21 @@ #ifndef SWOOLE_VERSION_H_ #define SWOOLE_VERSION_H_ -#define SWOOLE_MAJOR_VERSION 4 -#define SWOOLE_MINOR_VERSION 5 -#define SWOOLE_RELEASE_VERSION 0 -#define SWOOLE_EXTRA_VERSION "" -#define SWOOLE_VERSION "4.5.0" -#define SWOOLE_VERSION_ID 40500 -#define SWOOLE_API_VERSION_ID 0x202003a +#define SWOOLE_MAJOR_VERSION 6 +#define SWOOLE_MINOR_VERSION 0 +#define SWOOLE_RELEASE_VERSION 0 +#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"\ - "The Swoole developers probably don't know about it,\n"\ - "and unless you report it, chances are it won't be fixed.\n"\ - "You can read How to report a bug doc before submitting any bug reports:\n"\ - ">> https://github.com/swoole/swoole-src/blob/master/.github/ISSUE.md \n"\ - "Please do not send bug reports in the mailing list or personal letters.\n"\ +#define SWOOLE_BUG_REPORT \ + "A bug occurred in Swoole-v" SWOOLE_VERSION ", please report it.\n" \ + "The Swoole developers probably don't know about it,\n" \ + "and unless you report it, chances are it won't be fixed.\n" \ + "You can read How to report a bug doc before submitting any bug reports:\n" \ + ">> https://github.com/swoole/swoole-src/blob/master/.github/ISSUE.md \n" \ + "Please do not send bug reports in the mailing list or personal letters.\n" \ "The issue page is also suitable to submit feature requests.\n" #endif diff --git a/include/swoole_websocket.h b/include/swoole_websocket.h new file mode 100644 index 00000000000..2a1f68b7f39 --- /dev/null +++ b/include/swoole_websocket.h @@ -0,0 +1,162 @@ +/* + +----------------------------------------------------------------------+ + | 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: Tianfeng Han | + +----------------------------------------------------------------------+ +*/ + +#pragma once + +#include "swoole_http.h" + +#define SW_WEBSOCKET_SEC_KEY_LEN 16 +#define SW_WEBSOCKET_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" +#define SW_WEBSOCKET_HEADER_LEN 2 +#define SW_WEBSOCKET_MASK_LEN 4 +#define SW_WEBSOCKET_MASK_DATA "258E" +#define SW_WEBSOCKET_EXT16_MAX_LEN 0xFFFF +#define SW_WEBSOCKET_EXT16_LENGTH 0x7E +#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 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 Flag { + FLAG_FIN = 1 << 0, /* BC: must be 1 */ + FLAG_COMPRESS = 1 << 1, + // readonly for user + FLAG_RSV1 = 1 << 2, + FLAG_RSV2 = 1 << 3, + FLAG_RSV3 = 1 << 4, + FLAG_MASK = 1 << 5, + // for encoder/decoder + FLAG_ENCODE_HEADER_ONLY = 1 << 6, + FLAGS_ALL = /* used to prevent overflow */ + FLAG_FIN | FLAG_RSV1 | FLAG_RSV2 | FLAG_RSV3 | FLAG_MASK | FLAG_COMPRESS +}; + +struct Header { + /** + * fin:1 rsv1:1 rsv2:1 rsv3:1 opcode:4 + */ + uchar OPCODE : 4; + uchar RSV3 : 1; + uchar RSV2 : 1; + uchar RSV1 : 1; + uchar FIN : 1; + uchar LENGTH : 7; + uchar MASK : 1; +}; + +struct Frame { + Header header; + char mask_key[SW_WEBSOCKET_MASK_LEN]; + uint16_t header_length; + size_t payload_length; + char *payload; +}; + +#define WEBSOCKET_VERSION 13 + +enum Opcode { + OPCODE_CONTINUATION = 0x0, + OPCODE_TEXT = 0x1, + OPCODE_BINARY = 0x2, + OPCODE_CLOSE = 0x8, + OPCODE_PING = 0x9, + OPCODE_PONG = 0xa, +}; + +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 get_flags(Frame *frame) { + uchar flags = 0; + if (frame->header.FIN) { + flags |= FLAG_FIN; + } + if (frame->header.RSV1) { + flags |= FLAG_RSV1; + } + if (frame->header.RSV2) { + flags |= FLAG_RSV2; + } + if (frame->header.RSV3) { + flags |= FLAG_RSV3; + } + if (frame->header.MASK) { + flags |= FLAG_MASK; + } + return flags; +} + +static inline uchar set_flags(uchar fin, uchar mask, uchar rsv1, uchar rsv2, uchar rsv3) { + uchar flags = 0; + if (fin) { + flags |= FLAG_FIN; + } + if (mask) { + flags |= FLAG_MASK; + } + if (rsv1) { + flags |= FLAG_RSV1; + } + if (rsv2) { + flags |= FLAG_RSV2; + } + if (rsv3) { + flags |= FLAG_RSV3; + } + return flags; +} + +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); + +static inline bool decode(Frame *frame, String *str) { + return decode(frame, str->str, str->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 new file mode 100644 index 00000000000..0b852c1856c --- /dev/null +++ b/include/swoole_wheel_timer.h @@ -0,0 +1,86 @@ +/* + +----------------------------------------------------------------------+ + | 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 +#include + +namespace swoole { + +struct WheelTimerNode; + +using WheelTimerCallback = std::function; + +struct WheelTimerNode { + std::list::iterator position_; + uint16_t index_; + WheelTimerCallback callback_; +}; + +class WheelTimer { + private: + uint64_t round_ = 0; + uint16_t size_; + std::vector> buckets_; + + void push(WheelTimerNode *node) { + node->index_ = (round_ + size_ - 1) % size_; + buckets_[node->index_].push_front(node); + node->position_ = buckets_[node->index_].begin(); + } + + public: + WheelTimer(uint16_t size) { + size_ = size; + buckets_.resize(size); + } + + uint64_t get_round() { + return round_; + } + + WheelTimerNode *add(const WheelTimerCallback &cb) { + WheelTimerNode *node = new WheelTimerNode; + push(node); + node->callback_ = cb; + return node; + } + + void update(WheelTimerNode *node) { + buckets_[node->index_].erase(node->position_); + push(node); + } + + void remove(WheelTimerNode *node) { + buckets_[node->index_].erase(node->position_); + delete node; + } + + void next() { + uint16_t current_index = round_ % size_; + round_++; + std::list &_list = buckets_[current_index]; + for (auto node : _list) { + node->callback_(node); + delete node; + } + _list.clear(); + } +}; +} // namespace swoole diff --git a/include/table.h b/include/table.h deleted file mode 100644 index 079e31cec08..00000000000 --- a/include/table.h +++ /dev/null @@ -1,260 +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 | - +----------------------------------------------------------------------+ -*/ - -#pragma once - -#include "atomic.h" -#include "hash.h" -#include -#include -#include - -typedef uint32_t swTable_string_length_t; - -struct swTableRow -{ - sw_atomic_t lock; - pid_t lock_pid; - /** - * 1:used, 0:empty - */ - uint8_t active; - uint8_t key_len; - /** - * next slot - */ - swTableRow *next; - /** - * Hash Key - */ - char key[SW_TABLE_KEY_SIZE]; - char data[0]; -}; - -struct swTable_iterator -{ - uint32_t absolute_index; - uint32_t collision_index; - swTableRow *row; -}; - -enum swTableColumn_type -{ - SW_TABLE_INT = 1, - SW_TABLE_INT8, - SW_TABLE_INT16, - SW_TABLE_INT32, -#ifdef __x86_64__ - SW_TABLE_INT64, -#endif - SW_TABLE_FLOAT, - SW_TABLE_STRING, -}; - -struct swTableColumn -{ - enum swTableColumn_type type; - uint32_t size; - std::string name; - size_t index; - - swTableColumn(const std::string &_name, enum swTableColumn_type _type, size_t _size) : - name(_name) - { - index = 0; - switch (_type) - { - case SW_TABLE_INT: - switch (_size) - { - case 1: - size = 1; - type = SW_TABLE_INT8; - break; - case 2: - size = 2; - type = SW_TABLE_INT16; - break; -#ifdef __x86_64__ - case 8: - size = 8; - type = SW_TABLE_INT64; - break; -#endif - default: - size = 4; - type = SW_TABLE_INT32; - break; - } - break; - case SW_TABLE_FLOAT: - size = sizeof(double); - type = SW_TABLE_FLOAT; - break; - case SW_TABLE_STRING: - size = _size + sizeof(swTable_string_length_t); - type = SW_TABLE_STRING; - break; - default: - abort(); - break; - } - } -}; - -struct swTable -{ - std::unordered_map *column_map; - std::vector *column_list; - swLock lock; - size_t size; - size_t mask; - size_t item_size; - size_t memory_size; - float conflict_proportion; - - /** - * total rows that in active state(shm) - */ - sw_atomic_t row_num; - - swTableRow **rows; - swMemoryPool *pool; - - swTable_iterator *iterator; - uint64_t (*hash_func)(const char *key, size_t len); - - void *memory; -}; - -enum swoole_table_find -{ - SW_TABLE_FIND_EQ = 1, - SW_TABLE_FIND_NEQ, - SW_TABLE_FIND_GT, - SW_TABLE_FIND_LT, - SW_TABLE_FIND_LEFTLIKE, - SW_TABLE_FIND_RIGHTLIKE, - SW_TABLE_FIND_LIKE, -}; - -swTable* swTable_new(uint32_t rows_size, float conflict_proportion); -size_t swTable_get_memory_size(swTable *table); -int swTable_create(swTable *table); -void swTable_free(swTable *table); -bool swTableColumn_add(swTable *table, const std::string &name, enum swTableColumn_type type, size_t size); -swTableRow* swTableRow_set(swTable *table, const char *key, int keylen, swTableRow **rowlock); -swTableRow* swTableRow_get(swTable *table, const char *key, int keylen, swTableRow **rowlock); - -void swTable_iterator_rewind(swTable *table); -swTableRow* swTable_iterator_current(swTable *table); -void swTable_iterator_forward(swTable *table); -int swTableRow_del(swTable *table, char *key, int keylen); - -static sw_inline swTableColumn* swTableColumn_get(swTable *table, const std::string &key) -{ - auto i = table->column_map->find(key); - if (i == table->column_map->end()) - { - return nullptr; - } - else - { - return i->second; - } -} - -static sw_inline void swTableRow_lock(swTableRow *row) -{ - sw_atomic_t *lock = &row->lock; - uint32_t i, n; - while(1) - { - if (*lock == 0 && sw_atomic_cmp_set(lock, 0, 1)) - { - _success: row->lock_pid = SwooleG.pid; - return; - } - if (SW_CPU_NUM > 1) - { - for (n = 1; n < SW_SPINLOCK_LOOP_N; n <<= 1) - { - for (i = 0; i < n; i++) - { - sw_atomic_cpu_pause(); - } - if (*lock == 0 && sw_atomic_cmp_set(lock, 0, 1)) - { - goto _success; - } - } - } - if (kill(row->lock_pid, 0) < 0 && errno == ESRCH) - { - *lock = 1; - goto _success; - } - swYield(); - } -} - -static sw_inline void swTableRow_unlock(swTableRow *row) -{ - sw_spinlock_release(&row->lock); -} - -static sw_inline void swTableRow_set_value(swTableRow *row, swTableColumn *col, void *value, size_t vlen) -{ - int8_t _i8; - int16_t _i16; - int32_t _i32; -#ifdef __x86_64__ - int64_t _i64; -#endif - switch(col->type) - { - case SW_TABLE_INT8: - _i8 = *(int8_t *) value; - memcpy(row->data + col->index, &_i8, 1); - break; - case SW_TABLE_INT16: - _i16 = *(int16_t *) value; - memcpy(row->data + col->index, &_i16, 2); - break; - case SW_TABLE_INT32: - _i32 = *(int32_t *) value; - memcpy(row->data + col->index, &_i32, 4); - break; -#ifdef __x86_64__ - case SW_TABLE_INT64: - _i64 = *(int64_t *) value; - memcpy(row->data + col->index, &_i64, 8); - break; -#endif - case SW_TABLE_FLOAT: - memcpy(row->data + col->index, value, sizeof(double)); - break; - default: - if (vlen > (col->size - sizeof(swTable_string_length_t))) - { - swWarn("[key=%s,field=%s]string value is too long", row->key, col->name.c_str()); - vlen = col->size - sizeof(swTable_string_length_t); - } - memcpy(row->data + col->index, &vlen, sizeof(swTable_string_length_t)); - memcpy(row->data + col->index + sizeof(swTable_string_length_t), value, vlen); - break; - } -} diff --git a/include/uthash.h b/include/uthash.h deleted file mode 100644 index b7384decfd1..00000000000 --- a/include/uthash.h +++ /dev/null @@ -1,935 +0,0 @@ -/* -Copyright (c) 2003-2013, Troy D. Hanson http://troydhanson.github.com/uthash/ -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. - -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 UTHASH_H -#define UTHASH_H - -#include /* memcmp,strlen */ -#include /* ptrdiff_t */ -#include /* exit() */ - -/* These macros use decltype or the earlier __typeof GNU extension. - As decltype is only available in newer compilers (VS2010 or gcc 4.3+ - when compiling c++ source) this code uses whatever method is needed - or, for VS2008 where neither is available, uses casting workarounds. */ -#ifdef _MSC_VER /* MS compiler */ -#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ -#define DECLTYPE(x) (decltype(x)) -#else /* VS2008 or older (or VS2010 in C mode) */ -#define NO_DECLTYPE -#define DECLTYPE(x) -#endif -#else /* GNU, Sun and other compilers */ -#define DECLTYPE(x) (__typeof(x)) -#endif - -#ifdef NO_DECLTYPE -#define DECLTYPE_ASSIGN(dst,src) \ -do { \ - char **_da_dst = (char**)(&(dst)); \ - *_da_dst = (char*)(src); \ -} while(0) -#else -#define DECLTYPE_ASSIGN(dst,src) \ -do { \ - (dst) = DECLTYPE(dst)(src); \ -} while(0) -#endif - -/* a number of the hash function use uint32_t which isn't defined on win32 */ -#ifdef _MSC_VER -typedef unsigned int uint32_t; -typedef unsigned char uint8_t; -#else -#include /* uint32_t */ -#endif - -#define UTHASH_VERSION 1.9.8 - -#ifndef uthash_fatal -#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ -#endif -#ifndef uthash_malloc -#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ -#endif -#ifndef uthash_free -#define uthash_free(ptr,sz) free(ptr) /* free fcn */ -#endif - -#ifndef uthash_noexpand_fyi -#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ -#endif -#ifndef uthash_expand_fyi -#define uthash_expand_fyi(tbl) /* can be defined to log expands */ -#endif - -/* initial number of buckets */ -#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */ -#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */ -#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */ - -/* calculate the element whose hash handle address is hhe */ -#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) - -#define HASH_FIND(hh,head,keyptr,keylen,out) \ -do { \ - unsigned _hf_bkt,_hf_hashv; \ - out=NULL; \ - if (head) { \ - HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \ - if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \ - HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \ - keyptr,keylen,out); \ - } \ - } \ -} while (0) - -#ifdef HASH_BLOOM -#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM) -#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0) -#define HASH_BLOOM_MAKE(tbl) \ -do { \ - (tbl)->bloom_nbits = HASH_BLOOM; \ - (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ - if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ - memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ - (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ -} while (0) - -#define HASH_BLOOM_FREE(tbl) \ -do { \ - uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ -} while (0) - -#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8))) -#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8))) - -#define HASH_BLOOM_ADD(tbl,hashv) \ - HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) - -#define HASH_BLOOM_TEST(tbl,hashv) \ - HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) - -#else -#define HASH_BLOOM_MAKE(tbl) -#define HASH_BLOOM_FREE(tbl) -#define HASH_BLOOM_ADD(tbl,hashv) -#define HASH_BLOOM_TEST(tbl,hashv) (1) -#define HASH_BLOOM_BYTELEN 0 -#endif - -#define HASH_MAKE_TABLE(hh,head) \ -do { \ - (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ - sizeof(UT_hash_table)); \ - if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ - memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ - (head)->hh.tbl->tail = &((head)->hh); \ - (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ - (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ - (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ - (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ - HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ - if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ - memset((head)->hh.tbl->buckets, 0, \ - HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ - HASH_BLOOM_MAKE((head)->hh.tbl); \ - (head)->hh.tbl->signature = HASH_SIGNATURE; \ -} while(0) - -#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ - HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add) - -#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ -do { \ - replaced=NULL; \ - HASH_FIND(hh,head,&((add)->fieldname),keylen_in,replaced); \ - if (replaced!=NULL) { \ - HASH_DELETE(hh,head,replaced); \ - }; \ - HASH_ADD(hh,head,fieldname,keylen_in,add); \ -} while(0) - -#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ -do { \ - unsigned _ha_bkt; \ - (add)->hh.next = NULL; \ - (add)->hh.key = (char*)(keyptr); \ - (add)->hh.keylen = (unsigned)(keylen_in); \ - if (!(head)) { \ - head = (add); \ - (head)->hh.prev = NULL; \ - HASH_MAKE_TABLE(hh,head); \ - } else { \ - (head)->hh.tbl->tail->next = (add); \ - (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ - (head)->hh.tbl->tail = &((add)->hh); \ - } \ - (head)->hh.tbl->num_items++; \ - (add)->hh.tbl = (head)->hh.tbl; \ - HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \ - (add)->hh.hashv, _ha_bkt); \ - HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \ - HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \ - HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \ - HASH_FSCK(hh,head); \ -} while(0) - -#define HASH_TO_BKT( hashv, num_bkts, bkt ) \ -do { \ - bkt = ((hashv) & ((num_bkts) - 1)); \ -} while(0) - -/* delete "delptr" from the hash table. - * "the usual" patch-up process for the app-order doubly-linked-list. - * The use of _hd_hh_del below deserves special explanation. - * These used to be expressed using (delptr) but that led to a bug - * if someone used the same symbol for the head and deletee, like - * HASH_DELETE(hh,users,users); - * We want that to work, but by changing the head (users) below - * we were forfeiting our ability to further refer to the deletee (users) - * in the patch-up process. Solution: use scratch space to - * copy the deletee pointer, then the latter references are via that - * scratch pointer rather than through the repointed (users) symbol. - */ -#define HASH_DELETE(hh,head,delptr) \ -do { \ - unsigned _hd_bkt; \ - struct UT_hash_handle *_hd_hh_del; \ - if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ - uthash_free((head)->hh.tbl->buckets, \ - (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ - HASH_BLOOM_FREE((head)->hh.tbl); \ - uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ - head = NULL; \ - } else { \ - _hd_hh_del = &((delptr)->hh); \ - if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ - (head)->hh.tbl->tail = \ - (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ - (head)->hh.tbl->hho); \ - } \ - if ((delptr)->hh.prev) { \ - ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ - (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ - } else { \ - DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ - } \ - if (_hd_hh_del->next) { \ - ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \ - (head)->hh.tbl->hho))->prev = \ - _hd_hh_del->prev; \ - } \ - HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ - HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ - (head)->hh.tbl->num_items--; \ - } \ - HASH_FSCK(hh,head); \ -} while (0) - - -/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ -#define HASH_FIND_STR(head,findstr,out) \ - HASH_FIND(hh,head,findstr,strlen(findstr),out) -#define HASH_ADD_STR(head,strfield,add) \ - HASH_ADD(hh,head,strfield,strlen(add->strfield),add) -#define HASH_REPLACE_STR(head,strfield,add,replaced) \ - HASH_REPLACE(hh,head,strfield,strlen(add->strfield),add,replaced) -#define HASH_FIND_INT(head,findint,out) \ - HASH_FIND(hh,head,findint,sizeof(int),out) -#define HASH_ADD_INT(head,intfield,add) \ - HASH_ADD(hh,head,intfield,sizeof(int),add) -#define HASH_REPLACE_INT(head,intfield,add,replaced) \ - HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) -#define HASH_FIND_PTR(head,findptr,out) \ - HASH_FIND(hh,head,findptr,sizeof(void *),out) -#define HASH_ADD_PTR(head,ptrfield,add) \ - HASH_ADD(hh,head,ptrfield,sizeof(void *),add) -#define HASH_REPLACE_PTR(head,ptrfield,add) \ - HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) -#define HASH_DEL(head,delptr) \ - HASH_DELETE(hh,head,delptr) - -/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. - * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. - */ -#ifdef HASH_DEBUG -#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) -#define HASH_FSCK(hh,head) \ -do { \ - unsigned _bkt_i; \ - unsigned _count, _bkt_count; \ - char *_prev; \ - struct UT_hash_handle *_thh; \ - if (head) { \ - _count = 0; \ - for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ - _bkt_count = 0; \ - _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ - _prev = NULL; \ - while (_thh) { \ - if (_prev != (char*)(_thh->hh_prev)) { \ - HASH_OOPS("invalid hh_prev %p, actual %p\n", \ - _thh->hh_prev, _prev ); \ - } \ - _bkt_count++; \ - _prev = (char*)(_thh); \ - _thh = _thh->hh_next; \ - } \ - _count += _bkt_count; \ - if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ - HASH_OOPS("invalid bucket count %d, actual %d\n", \ - (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ - } \ - } \ - if (_count != (head)->hh.tbl->num_items) { \ - HASH_OOPS("invalid hh item count %d, actual %d\n", \ - (head)->hh.tbl->num_items, _count ); \ - } \ - /* traverse hh in app order; check next/prev integrity, count */ \ - _count = 0; \ - _prev = NULL; \ - _thh = &(head)->hh; \ - while (_thh) { \ - _count++; \ - if (_prev !=(char*)(_thh->prev)) { \ - HASH_OOPS("invalid prev %p, actual %p\n", \ - _thh->prev, _prev ); \ - } \ - _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ - _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ - (head)->hh.tbl->hho) : NULL ); \ - } \ - if (_count != (head)->hh.tbl->num_items) { \ - HASH_OOPS("invalid app item count %d, actual %d\n", \ - (head)->hh.tbl->num_items, _count ); \ - } \ - } \ -} while (0) -#else -#define HASH_FSCK(hh,head) -#endif - -/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to - * the descriptor to which this macro is defined for tuning the hash function. - * The app can #include to get the prototype for write(2). */ -#ifdef HASH_EMIT_KEYS -#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ -do { \ - unsigned _klen = fieldlen; \ - write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ - write(HASH_EMIT_KEYS, keyptr, fieldlen); \ -} while (0) -#else -#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) -#endif - -/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ -#ifdef HASH_FUNCTION -#define HASH_FCN HASH_FUNCTION -#else -#define HASH_FCN HASH_JEN -#endif - -/* The Bernstein hash function, used in Perl prior to v5.6 */ -#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \ -do { \ - unsigned _hb_keylen=keylen; \ - char *_hb_key=(char*)(key); \ - (hashv) = 0; \ - while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \ - bkt = (hashv) & (num_bkts-1); \ -} while (0) - - -/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at - * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ -#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ -do { \ - unsigned _sx_i; \ - char *_hs_key=(char*)(key); \ - hashv = 0; \ - for(_sx_i=0; _sx_i < keylen; _sx_i++) \ - hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ - bkt = hashv & (num_bkts-1); \ -} while (0) - -#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \ -do { \ - unsigned _fn_i; \ - char *_hf_key=(char*)(key); \ - hashv = 2166136261UL; \ - for(_fn_i=0; _fn_i < keylen; _fn_i++) \ - hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \ - bkt = hashv & (num_bkts-1); \ -} while(0) - -#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ -do { \ - unsigned _ho_i; \ - char *_ho_key=(char*)(key); \ - hashv = 0; \ - for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ - hashv += _ho_key[_ho_i]; \ - hashv += (hashv << 10); \ - hashv ^= (hashv >> 6); \ - } \ - hashv += (hashv << 3); \ - hashv ^= (hashv >> 11); \ - hashv += (hashv << 15); \ - bkt = hashv & (num_bkts-1); \ -} while(0) - -#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ -do { \ - unsigned _hj_i,_hj_j,_hj_k; \ - unsigned char *_hj_key=(unsigned char*)(key); \ - hashv = 0xfeedbeef; \ - _hj_i = _hj_j = 0x9e3779b9; \ - _hj_k = (unsigned)(keylen); \ - while (_hj_k >= 12) { \ - _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ - + ( (unsigned)_hj_key[2] << 16 ) \ - + ( (unsigned)_hj_key[3] << 24 ) ); \ - _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ - + ( (unsigned)_hj_key[6] << 16 ) \ - + ( (unsigned)_hj_key[7] << 24 ) ); \ - hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ - + ( (unsigned)_hj_key[10] << 16 ) \ - + ( (unsigned)_hj_key[11] << 24 ) ); \ - \ - HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ - \ - _hj_key += 12; \ - _hj_k -= 12; \ - } \ - hashv += keylen; \ - switch ( _hj_k ) { \ - case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \ - case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \ - case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \ - case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \ - case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \ - case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \ - case 5: _hj_j += _hj_key[4]; \ - case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \ - case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \ - case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \ - case 1: _hj_i += _hj_key[0]; \ - } \ - HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ - bkt = hashv & (num_bkts-1); \ -} while(0) - -/* The Paul Hsieh hash function */ -#undef get16bits -#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ - || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) -#define get16bits(d) (*((const uint16_t *) (d))) -#endif - -#if !defined (get16bits) -#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ - +(uint32_t)(((const uint8_t *)(d))[0]) ) -#endif -#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ -do { \ - unsigned char *_sfh_key=(unsigned char*)(key); \ - uint32_t _sfh_tmp, _sfh_len = keylen; \ - \ - int _sfh_rem = _sfh_len & 3; \ - _sfh_len >>= 2; \ - hashv = 0xcafebabe; \ - \ - /* Main loop */ \ - for (;_sfh_len > 0; _sfh_len--) { \ - hashv += get16bits (_sfh_key); \ - _sfh_tmp = (uint32_t)(get16bits (_sfh_key+2)) << 11 ^ hashv; \ - hashv = (hashv << 16) ^ _sfh_tmp; \ - _sfh_key += 2*sizeof (uint16_t); \ - hashv += hashv >> 11; \ - } \ - \ - /* Handle end cases */ \ - switch (_sfh_rem) { \ - case 3: hashv += get16bits (_sfh_key); \ - hashv ^= hashv << 16; \ - hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)] << 18); \ - hashv += hashv >> 11; \ - break; \ - case 2: hashv += get16bits (_sfh_key); \ - hashv ^= hashv << 11; \ - hashv += hashv >> 17; \ - break; \ - case 1: hashv += *_sfh_key; \ - hashv ^= hashv << 10; \ - hashv += hashv >> 1; \ - } \ - \ - /* Force "avalanching" of final 127 bits */ \ - hashv ^= hashv << 3; \ - hashv += hashv >> 5; \ - hashv ^= hashv << 4; \ - hashv += hashv >> 17; \ - hashv ^= hashv << 25; \ - hashv += hashv >> 6; \ - bkt = hashv & (num_bkts-1); \ -} while(0) - -#ifdef HASH_USING_NO_STRICT_ALIASING -/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. - * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. - * MurmurHash uses the faster approach only on CPU's where we know it's safe. - * - * Note the preprocessor built-in defines can be emitted using: - * - * gcc -m64 -dM -E - < /dev/null (on gcc) - * cc -## a.c (where a.c is a simple test file) (Sun Studio) - */ -#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) -#define MUR_GETBLOCK(p,i) p[i] -#else /* non intel */ -#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 0x3) == 0) -#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 0x3) == 1) -#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 0x3) == 2) -#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 0x3) == 3) -#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) -#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) -#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) -#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) -#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) -#else /* assume little endian non-intel */ -#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) -#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) -#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) -#endif -#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ - (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ - (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ - MUR_ONE_THREE(p)))) -#endif -#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) -#define MUR_FMIX(_h) \ -do { \ - _h ^= _h >> 16; \ - _h *= 0x85ebca6b; \ - _h ^= _h >> 13; \ - _h *= 0xc2b2ae35l; \ - _h ^= _h >> 16; \ -} while(0) - -#define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \ -do { \ - const uint8_t *_mur_data = (const uint8_t*)(key); \ - const int _mur_nblocks = (keylen) / 4; \ - uint32_t _mur_h1 = 0xf88D5353; \ - uint32_t _mur_c1 = 0xcc9e2d51; \ - uint32_t _mur_c2 = 0x1b873593; \ - uint32_t _mur_k1 = 0; \ - const uint8_t *_mur_tail; \ - const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+_mur_nblocks*4); \ - int _mur_i; \ - for(_mur_i = -_mur_nblocks; _mur_i; _mur_i++) { \ - _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ - _mur_k1 *= _mur_c1; \ - _mur_k1 = MUR_ROTL32(_mur_k1,15); \ - _mur_k1 *= _mur_c2; \ - \ - _mur_h1 ^= _mur_k1; \ - _mur_h1 = MUR_ROTL32(_mur_h1,13); \ - _mur_h1 = _mur_h1*5+0xe6546b64; \ - } \ - _mur_tail = (const uint8_t*)(_mur_data + _mur_nblocks*4); \ - _mur_k1=0; \ - switch((keylen) & 3) { \ - case 3: _mur_k1 ^= _mur_tail[2] << 16; \ - case 2: _mur_k1 ^= _mur_tail[1] << 8; \ - case 1: _mur_k1 ^= _mur_tail[0]; \ - _mur_k1 *= _mur_c1; \ - _mur_k1 = MUR_ROTL32(_mur_k1,15); \ - _mur_k1 *= _mur_c2; \ - _mur_h1 ^= _mur_k1; \ - } \ - _mur_h1 ^= (keylen); \ - MUR_FMIX(_mur_h1); \ - hashv = _mur_h1; \ - bkt = hashv & (num_bkts-1); \ -} while(0) -#endif /* HASH_USING_NO_STRICT_ALIASING */ - -/* key comparison function; return 0 if keys equal */ -#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) - -/* iterate over items in a known bucket to find desired item */ -#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ -do { \ - if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \ - else out=NULL; \ - while (out) { \ - if ((out)->hh.keylen == keylen_in) { \ - if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) break; \ - } \ - if ((out)->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); \ - else out = NULL; \ - } \ -} while(0) - -/* add an item to a bucket */ -#define HASH_ADD_TO_BKT(head,addhh) \ -do { \ - head.count++; \ - (addhh)->hh_next = head.hh_head; \ - (addhh)->hh_prev = NULL; \ - if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \ - (head).hh_head=addhh; \ - if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \ - && (addhh)->tbl->noexpand != 1) { \ - HASH_EXPAND_BUCKETS((addhh)->tbl); \ - } \ -} while(0) - -/* remove an item from a given bucket */ -#define HASH_DEL_IN_BKT(hh,head,hh_del) \ - (head).count--; \ - if ((head).hh_head == hh_del) { \ - (head).hh_head = hh_del->hh_next; \ - } \ - if (hh_del->hh_prev) { \ - hh_del->hh_prev->hh_next = hh_del->hh_next; \ - } \ - if (hh_del->hh_next) { \ - hh_del->hh_next->hh_prev = hh_del->hh_prev; \ - } - -/* Bucket expansion has the effect of doubling the number of buckets - * and redistributing the items into the new buckets. Ideally the - * items will distribute more or less evenly into the new buckets - * (the extent to which this is true is a measure of the quality of - * the hash function as it applies to the key domain). - * - * With the items distributed into more buckets, the chain length - * (item count) in each bucket is reduced. Thus by expanding buckets - * the hash keeps a bound on the chain length. This bounded chain - * length is the essence of how a hash provides constant time lookup. - * - * The calculation of tbl->ideal_chain_maxlen below deserves some - * explanation. First, keep in mind that we're calculating the ideal - * maximum chain length based on the *new* (doubled) bucket count. - * In fractions this is just n/b (n=number of items,b=new num buckets). - * Since the ideal chain length is an integer, we want to calculate - * ceil(n/b). We don't depend on floating point arithmetic in this - * hash, so to calculate ceil(n/b) with integers we could write - * - * ceil(n/b) = (n/b) + ((n%b)?1:0) - * - * and in fact a previous version of this hash did just that. - * But now we have improved things a bit by recognizing that b is - * always a power of two. We keep its base 2 log handy (call it lb), - * so now we can write this with a bit shift and logical AND: - * - * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) - * - */ -#define HASH_EXPAND_BUCKETS(tbl) \ -do { \ - unsigned _he_bkt; \ - unsigned _he_bkt_i; \ - struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ - UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ - _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ - 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ - if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ - memset(_he_new_buckets, 0, \ - 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ - tbl->ideal_chain_maxlen = \ - (tbl->num_items >> (tbl->log2_num_buckets+1)) + \ - ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \ - tbl->nonideal_items = 0; \ - for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ - { \ - _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ - while (_he_thh) { \ - _he_hh_nxt = _he_thh->hh_next; \ - HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \ - _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ - if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ - tbl->nonideal_items++; \ - _he_newbkt->expand_mult = _he_newbkt->count / \ - tbl->ideal_chain_maxlen; \ - } \ - _he_thh->hh_prev = NULL; \ - _he_thh->hh_next = _he_newbkt->hh_head; \ - if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \ - _he_thh; \ - _he_newbkt->hh_head = _he_thh; \ - _he_thh = _he_hh_nxt; \ - } \ - } \ - uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ - tbl->num_buckets *= 2; \ - tbl->log2_num_buckets++; \ - tbl->buckets = _he_new_buckets; \ - tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ - (tbl->ineff_expands+1) : 0; \ - if (tbl->ineff_expands > 1) { \ - tbl->noexpand=1; \ - uthash_noexpand_fyi(tbl); \ - } \ - uthash_expand_fyi(tbl); \ -} while(0) - - -/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ -/* Note that HASH_SORT assumes the hash handle name to be hh. - * HASH_SRT was added to allow the hash handle name to be passed in. */ -#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) -#define HASH_SRT(hh,head,cmpfcn) \ -do { \ - unsigned _hs_i; \ - unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ - struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ - if (head) { \ - _hs_insize = 1; \ - _hs_looping = 1; \ - _hs_list = &((head)->hh); \ - while (_hs_looping) { \ - _hs_p = _hs_list; \ - _hs_list = NULL; \ - _hs_tail = NULL; \ - _hs_nmerges = 0; \ - while (_hs_p) { \ - _hs_nmerges++; \ - _hs_q = _hs_p; \ - _hs_psize = 0; \ - for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ - _hs_psize++; \ - _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ - ((void*)((char*)(_hs_q->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - if (! (_hs_q) ) break; \ - } \ - _hs_qsize = _hs_insize; \ - while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \ - if (_hs_psize == 0) { \ - _hs_e = _hs_q; \ - _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ - ((void*)((char*)(_hs_q->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - _hs_qsize--; \ - } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \ - _hs_e = _hs_p; \ - if (_hs_p){ \ - _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ - ((void*)((char*)(_hs_p->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - } \ - _hs_psize--; \ - } else if (( \ - cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ - DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ - ) <= 0) { \ - _hs_e = _hs_p; \ - if (_hs_p){ \ - _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ - ((void*)((char*)(_hs_p->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - } \ - _hs_psize--; \ - } else { \ - _hs_e = _hs_q; \ - _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ - ((void*)((char*)(_hs_q->next) + \ - (head)->hh.tbl->hho)) : NULL); \ - _hs_qsize--; \ - } \ - if ( _hs_tail ) { \ - _hs_tail->next = ((_hs_e) ? \ - ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ - } else { \ - _hs_list = _hs_e; \ - } \ - if (_hs_e) { \ - _hs_e->prev = ((_hs_tail) ? \ - ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ - } \ - _hs_tail = _hs_e; \ - } \ - _hs_p = _hs_q; \ - } \ - if (_hs_tail){ \ - _hs_tail->next = NULL; \ - } \ - if ( _hs_nmerges <= 1 ) { \ - _hs_looping=0; \ - (head)->hh.tbl->tail = _hs_tail; \ - DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ - } \ - _hs_insize *= 2; \ - } \ - HASH_FSCK(hh,head); \ - } \ -} while (0) - -/* This function selects items from one hash into another hash. - * The end result is that the selected items have dual presence - * in both hashes. There is no copy of the items made; rather - * they are added into the new hash through a secondary hash - * hash handle that must be present in the structure. */ -#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ -do { \ - unsigned _src_bkt, _dst_bkt; \ - void *_last_elt=NULL, *_elt; \ - UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ - ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ - if (src) { \ - for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ - for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ - _src_hh; \ - _src_hh = _src_hh->hh_next) { \ - _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ - if (cond(_elt)) { \ - _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ - _dst_hh->key = _src_hh->key; \ - _dst_hh->keylen = _src_hh->keylen; \ - _dst_hh->hashv = _src_hh->hashv; \ - _dst_hh->prev = _last_elt; \ - _dst_hh->next = NULL; \ - if (_last_elt_hh) { _last_elt_hh->next = _elt; } \ - if (!dst) { \ - DECLTYPE_ASSIGN(dst,_elt); \ - HASH_MAKE_TABLE(hh_dst,dst); \ - } else { \ - _dst_hh->tbl = (dst)->hh_dst.tbl; \ - } \ - HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ - HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ - (dst)->hh_dst.tbl->num_items++; \ - _last_elt = _elt; \ - _last_elt_hh = _dst_hh; \ - } \ - } \ - } \ - } \ - HASH_FSCK(hh_dst,dst); \ -} while (0) - -#define HASH_CLEAR(hh,head) \ -do { \ - if (head) { \ - uthash_free((head)->hh.tbl->buckets, \ - (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ - HASH_BLOOM_FREE((head)->hh.tbl); \ - uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ - (head)=NULL; \ - } \ -} while(0) - -#define HASH_OVERHEAD(hh,head) \ - (size_t)((((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ - ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ - (sizeof(UT_hash_table)) + \ - (HASH_BLOOM_BYTELEN))) - -#ifdef NO_DECLTYPE -#define HASH_ITER(hh,head,el,tmp) \ -for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \ - el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL)) -#else -#define HASH_ITER(hh,head,el,tmp) \ -for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \ - el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL)) -#endif - -/* obtain a count of items in the hash */ -#define HASH_COUNT(head) HASH_CNT(hh,head) -#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0) - -typedef struct UT_hash_bucket { - struct UT_hash_handle *hh_head; - unsigned count; - - /* expand_mult is normally set to 0. In this situation, the max chain length - * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If - * the bucket's chain exceeds this length, bucket expansion is triggered). - * However, setting expand_mult to a non-zero value delays bucket expansion - * (that would be triggered by additions to this particular bucket) - * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. - * (The multiplier is simply expand_mult+1). The whole idea of this - * multiplier is to reduce bucket expansions, since they are expensive, in - * situations where we know that a particular bucket tends to be overused. - * It is better to let its chain length grow to a longer yet-still-bounded - * value, than to do an O(n) bucket expansion too often. - */ - unsigned expand_mult; - -} UT_hash_bucket; - -/* random signature used only to find hash tables in external analysis */ -#define HASH_SIGNATURE 0xa0111fe1 -#define HASH_BLOOM_SIGNATURE 0xb12220f2 - -typedef struct UT_hash_table { - UT_hash_bucket *buckets; - unsigned num_buckets, log2_num_buckets; - unsigned num_items; - struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ - ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ - - /* in an ideal situation (all buckets used equally), no bucket would have - * more than ceil(#items/#buckets) items. that's the ideal chain length. */ - unsigned ideal_chain_maxlen; - - /* nonideal_items is the number of items in the hash whose chain position - * exceeds the ideal chain maxlen. these items pay the penalty for an uneven - * hash distribution; reaching them in a chain traversal takes >ideal steps */ - unsigned nonideal_items; - - /* ineffective expands occur when a bucket doubling was performed, but - * afterward, more than half the items in the hash had nonideal chain - * positions. If this happens on two consecutive expansions we inhibit any - * further expansion, as it's not helping; this happens when the hash - * function isn't a good fit for the key domain. When expansion is inhibited - * the hash will still work, albeit no longer in constant time. */ - unsigned ineff_expands, noexpand; - - uint32_t signature; /* used only to find hash tables in external analysis */ -#ifdef HASH_BLOOM - uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ - uint8_t *bloom_bv; - char bloom_nbits; -#endif - -} UT_hash_table; - -typedef struct UT_hash_handle { - struct UT_hash_table *tbl; - void *prev; /* prev element in app order */ - void *next; /* next element in app order */ - struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ - struct UT_hash_handle *hh_next; /* next hh in bucket order */ - void *key; /* ptr to enclosing struct's key */ - unsigned keylen; /* enclosing struct's key len */ - unsigned hashv; /* result of hash-fcn(key) */ -} UT_hash_handle; - -#endif /* UTHASH_H */ diff --git a/include/websocket.h b/include/websocket.h deleted file mode 100644 index e634a122bdf..00000000000 --- a/include/websocket.h +++ /dev/null @@ -1,181 +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: Tianfeng Han | - +----------------------------------------------------------------------+ -*/ - -#ifndef SW_WEBSOCKET_H_ -#define SW_WEBSOCKET_H_ - -#include "http.h" - -SW_EXTERN_C_BEGIN - -#define SW_WEBSOCKET_SEC_KEY_LEN 16 -#define SW_WEBSOCKET_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" -#define SW_WEBSOCKET_HEADER_LEN 2 -#define SW_WEBSOCKET_MASK_LEN 4 -#define SW_WEBSOCKET_MASK_DATA "258E" -#define SW_WEBSOCKET_EXT16_MAX_LEN 0xFFFF -#define SW_WEBSOCKET_EXT16_LENGTH 0x7E -#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, -}; - -enum swWebSocket_frame_flag -{ - SW_WEBSOCKET_FLAG_FIN = 1 << 0, /* BC: must be 1 */ - SW_WEBSOCKET_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, -}; - -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 -}; - -typedef struct -{ - /** - * fin:1 rsv1:1 rsv2:1 rsv3:1 opcode:4 - */ - uchar OPCODE :4; - uchar RSV3 :1; - uchar RSV2 :1; - uchar RSV1 :1; - uchar FIN :1; - uchar LENGTH :7; - uchar MASK :1; - -} swWebSocket_frame_header; - -typedef struct -{ - swWebSocket_frame_header header; - char mask_key[SW_WEBSOCKET_MASK_LEN]; - uint16_t header_length; - size_t payload_length; - char *payload; -} 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 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, -}; - -static inline uchar swWebSocket_get_flags(swWebSocket_frame *frame) -{ - uchar flags = 0; - if (frame->header.FIN) - { - flags |= SW_WEBSOCKET_FLAG_FIN; - } - if (frame->header.RSV1) - { - flags |= SW_WEBSOCKET_FLAG_RSV1; - } - if (frame->header.RSV2) - { - flags |= SW_WEBSOCKET_FLAG_RSV2; - } - if (frame->header.RSV3) - { - flags |= SW_WEBSOCKET_FLAG_RSV3; - } - if (frame->header.MASK) - { - flags |= SW_WEBSOCKET_FLAG_MASK; - } - return flags; -} - -static inline uchar swWebSocket_set_flags(uchar fin, uchar mask, uchar rsv1, uchar rsv2, uchar rsv3) -{ - uchar flags = 0; - if (fin) - { - flags |= SW_WEBSOCKET_FLAG_FIN; - } - if (mask) - { - flags |= SW_WEBSOCKET_FLAG_MASK; - } - if (rsv1) - { - flags |= SW_WEBSOCKET_FLAG_RSV1; - } - if (rsv2) - { - flags |= SW_WEBSOCKET_FLAG_RSV2; - } - if (rsv3) - { - flags |= SW_WEBSOCKET_FLAG_RSV3; - } - return flags; -} - -void swWebSocket_encode(swString *buffer, const char *data, size_t length, char opcode, uint8_t flags); -void swWebSocket_decode(swWebSocket_frame *frame, swString *data); -int swWebSocket_pack_close_frame(swString *buffer, int code, char* reason, size_t length, uint8_t flags); -void swWebSocket_print_frame(swWebSocket_frame *frame); - -ssize_t swWebSocket_get_package_length(swProtocol *protocol, swSocket *conn, char *data, uint32_t length); -int swWebSocket_dispatch_frame(swProtocol *protocol, swSocket *conn, char *data, uint32_t length); - -SW_EXTERN_C_END - -#endif /* SW_WEBSOCKET_H_ */ diff --git a/include/wrapper/base.hpp b/include/wrapper/base.hpp deleted file mode 100755 index 45dc96b2388..00000000000 --- a/include/wrapper/base.hpp +++ /dev/null @@ -1,24 +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 | - +----------------------------------------------------------------------+ - */ - -#pragma once - -#include "swoole.h" - -namespace swoole -{ - -} diff --git a/include/wrapper/client.hpp b/include/wrapper/client.hpp deleted file mode 100755 index aad9d16b64b..00000000000 --- a/include/wrapper/client.hpp +++ /dev/null @@ -1,19 +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 | - +----------------------------------------------------------------------+ - */ - -#pragma once - -#include "client.h" diff --git a/include/wrapper/server.hpp b/include/wrapper/server.hpp deleted file mode 100755 index bca1e30307d..00000000000 --- a/include/wrapper/server.hpp +++ /dev/null @@ -1,174 +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 | - +----------------------------------------------------------------------+ - */ - -#pragma once - -#include -#include -#include - -#include "base.hpp" -#include "server.h" - -using namespace std; - -namespace swoole -{ -class ClientInfo -{ -public: - char address[256]; - int port; - int server_socket; -}; - -extern swString *_callback_buffer; - -struct DataBuffer -{ - size_t length; - void *buffer; - - DataBuffer() - { - length = 0; - buffer = NULL; - } - - DataBuffer(const char *str) - { - copy((void *) str, strlen(str)); - } - - DataBuffer(string &str) - { - copy((void *) str.c_str(), str.length()); - } - - DataBuffer(const char *str, size_t length) - { - copy((void *) str, length); - } - - void copy(void *_data, size_t _length) - { - alloc(_length); - memcpy(buffer, _data, _length); - } - - void *alloc(size_t _size) - { - if (_size >= _callback_buffer->size) - { - size_t new_size = _callback_buffer->size * 2; - while (new_size < _size + 1) - { - new_size *= 2; - } - if (swString_extend(_callback_buffer, new_size) < 0) - { - abort(); - } - } - length = _size; - buffer = _callback_buffer->str; - ((char *) buffer)[_size] = '\0'; - return buffer; - } -}; - -enum -{ - EVENT_onStart = 1u << 1, - EVENT_onShutdown = 1u << 2, - EVENT_onWorkerStart = 1u << 3, - EVENT_onWorkerStop = 1u << 4, - EVENT_onConnect = 1u << 5, - EVENT_onReceive = 1u << 6, - EVENT_onPacket = 1u << 7, - EVENT_onClose = 1u << 8, - EVENT_onTask = 1u << 9, - EVENT_onFinish = 1u << 10, - EVENT_onPipeMessage = 1u << 11, -}; - -class Server -{ -public: - Server(string _host, int _port, int _mode = SW_MODE_PROCESS, enum swSocket_type _type = SW_SOCK_TCP); - - virtual ~Server() - { - } - ; - - bool start(void); - void setEvents(int _events); - bool listen(string host, int port, enum swSocket_type type); - bool send(int fd, const char *data, int length); - bool send(int fd, const DataBuffer &data); - bool sendfile(int fd, string &file, off_t offset = 0, size_t length = 0); - bool sendMessage(int worker_id, DataBuffer &data); - bool sendwait(int fd, const DataBuffer &data); - bool close(int fd, bool reset = false); - bool sendto(const string &ip, int port, const DataBuffer &data, int server_socket = -1); - int task(DataBuffer &data, int dst_worker_id = -1); - bool finish(DataBuffer &data); - DataBuffer taskwait(const DataBuffer &data, double timeout = SW_TASKWAIT_TIMEOUT, int dst_worker_id = -1); - map taskWaitMulti(const vector &data, double timeout = SW_TASKWAIT_TIMEOUT); - - int getLastError() - { - return SwooleG.error; - } - - virtual void onStart() = 0; - virtual void onShutdown() = 0; - virtual void onWorkerStart(int worker_id) = 0; - virtual void onWorkerStop(int worker_id) = 0; - virtual void onReceive(int fd, const DataBuffer &data) = 0; - virtual void onConnect(int fd) = 0; - virtual void onClose(int fd) = 0; - virtual void onPacket(const DataBuffer &, ClientInfo &) = 0; - virtual void onPipeMessage(int src_worker_id, const DataBuffer &) = 0; - virtual void onTask(int, int, const DataBuffer &) = 0; - virtual void onFinish(int, const DataBuffer &) = 0; - -public: - static int _onReceive(swServer *serv, swEventData *req); - static void _onConnect(swServer *serv, swDataHead *info); - static void _onClose(swServer *serv, swDataHead *info); - static int _onPacket(swServer *serv, swEventData *req); - static void _onPipeMessage(swServer *serv, swEventData *req); - static void _onStart(swServer *serv); - static void _onShutdown(swServer *serv); - static void _onWorkerStart(swServer *serv, int worker_id); - static void _onWorkerStop(swServer *serv, int worker_id); - static int _onTask(swServer *serv, swEventData *task); - static int _onFinish(swServer *serv, swEventData *task); - -private: - int check_task_param(int dst_worker_id); - -protected: - swServer serv; - vector ports; - string host; - int port; - int mode; - int events; -}; -} diff --git a/make.sh b/make.sh deleted file mode 100755 index 2b18a868806..00000000000 --- a/make.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -e -__DIR__=$(cd "$(dirname "$0")";pwd) - -cd ${__DIR__} -phpize --clean -phpize -./configure --enable-openssl --enable-sockets --enable-mysqlnd --enable-http2 -make clean -make -j -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 670ef935d6b..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-04-26 - + 2024-12-16 + - 4.5.0 - 4.0 + 6.0.0 + 6.0 stable @@ -54,126 +61,212 @@ Apache2.0 - New APIs - --- - + DTLS supports, we can build WebRTC applications now (#3188) (@matyhtf) - + Built-in `FastCGI` client, we can proxy HTTP request to FPM with one line of code now (swoole/library#17) (@twose) - + `Co::wait`, `Co::waitPid`, `Co::waitSignal` (#3158) (@twose) - + `Co::waitEvent` (#3197) (@twose) - + `Co::set(['exit_condition' => $callable])` to exit condition of reactor (#2918) (#3012) (@twose) - + `Co::getElapsed` to get the coroutine elapsed time (#3162) (@doubaokun) - + `Socket::checkLiveness`, `Socket::peek` (#3057) (@twose) - + `Socket->setProtocol(['open_fastcgi_protocol' => $bool])` (#3103) (@twose) - + `Server::getInstance`, `Server::get(Master|Manager|Worker)Pid`, `Server::getWorkerId` (#2793) (#3019) (@matyhtf) - + `Server::getWorkerStatus` (SWOOLE_WORKER_BUSY, SWOOLE_WORKER_IDLE) (#3225) (@matyhtf) - + `Server->on('beforeReload', $callable)` and `Server->on('afterReload', $callable)` (Server reload event on manager process) (#3130) (@huanghantao) - + `Http\Server` support `http_index_files` and `http_autoindex` with static handler (#3171) (@huanghantao) - + `Http2\Client->read(float $timeout = -1)`, support Coroutine\HTTP2\Client read more than once (#3011) (#3117) (@twose) - + `Http\Request->getContent` (alias of rawContent) (#3128) (@huanghantao) - + `swoole_mime_type_(add|set|delete|get|exists)` (mime APIs) (#3134) (@twose) - - Enhancement - --- - + Optimize memory-copy between master and worker (4x faster) (#3075) (#3087) (@huanghantao) - + Optimize websocket dispatch (1x faster) (#3076) (@matyhtf) - + Optimize memory-copy in websocket_construct_frame (1x faster) (#3097) (@matyhtf) - + Optimize SSL validation (#3226) (@matyhtf) - + Separate SSL accept and SSL handshake (#3214) (@twose) - + Support MIPS (#3196) (@ekongyun) - + Resolve the domain name when use udp coroutine client (#3236) (#3239) (@huanghantao) - + Do not close stdout and stderr (show error logs after shutdown) (#3249) (@twose) - + Support some common options for Coroutine\Http\Server (#3257) (@twose) - + Support set cookies when websocket handshake (#3270) (#3272) (@twose) - + Support CURLOPT_FAILONERROR (swoole/library#20) (@sy-records) - + Support CURLOPT_FAILONERROR (swoole/library#20) (@sy-records) - + Support CURLOPT_SSLCERTTYPE, CURLOPT_SSLCERT, CURLOPT_SSLKEYTYPE, CURLOPT_SSLKEY (swoole/library#22) (@sy-records) - + Support CURLOPT_HTTPGET (swoole/library@d730bd08) (@shiguangqi) - + Compatible with different versions of Redis extension as much as possible (swoole/library#24) (@twose) - + Disable Cloning of Database Proxy Objects (swoole/library#23) (@deminy) - - Remove - --- - - Remove `Runtime::enableStrictMode` method (b45838e3) (@twose) - - Remove `Buffer` class (559a49a8) (@twose) - - Kernel - --- - * New C++ API: `coroutine::async` with lambda (#3127) (@matyhtf) - * Refactor event API and reactor fd to swSocket (#3030) (@matyhtf) - * Convert all source files to C++ (#3030) (71f987f3) (@matyhtf) - * Code optimizations (#3063) (#3067) (#3115) (#3135) (#3138) (#3139) (#3151) (#3168) (@huanghantao) - * Code optimization for header files (#3051) (@matyhtf) - * Refactor `enable_reuse_port` option (#3192) (@matyhtf) - * Reactor Socket API (#3193) (@matyhtf) - * Reduce one system call (3b5aa85d) (@matyhtf) - * Remove swServerGS::now (#3152) (@huanghantao) - * Improve protocol setter (#3108) (@twose) - * Use the Better way to init C object on stack (#3069) (@twose) - * Use type `uchar` for bit field (#3071) (@twose) - * Support parallel test (#3215) (@twose) - - Fixed - --- - * Fixed `enable_delay_receive` (#3221) (#3224) (@matyhtf) - * All other bug fix patches have already been merged into the v4.4.x branch, we will not reiterate them here + - 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. - - + - - - + + - - - - + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + - - - + + - + + + + + + + + + + + + + + + + - - + + + + + - - - + + + + + - - - + + + + + + + + - - + + + + - - - @@ -217,12 +310,14 @@ + + @@ -233,6 +328,8 @@ + + @@ -285,11 +382,13 @@ + - + + + - @@ -298,54 +397,43 @@ - - - - + - - - + + - + - - - + + - - - - - - + - @@ -371,61 +459,66 @@ + - - - - - + + + + + - - - - - + - - - + + + + + + + + + + + + + + - - - @@ -451,16 +544,16 @@ + - - + @@ -471,27 +564,38 @@ - + + + + + + + + + + + + + + + + + + + - - - - - - - @@ -501,117 +605,273 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + - - - + + - - - - + + - - + - + - - + + + + - - - - + - @@ -629,53 +889,19 @@ + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + @@ -688,24 +914,12 @@ + - - - - - - - - - - - - - @@ -730,15 +944,22 @@ + + + + + + + @@ -749,17 +970,45 @@ + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -787,7 +1036,9 @@ + + @@ -803,18 +1054,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -826,16 +1103,22 @@ + + + + + + @@ -843,11 +1126,16 @@ + + + + + @@ -859,17 +1147,29 @@ + + + + + + + + + + + + @@ -886,6 +1186,16 @@ + + + + + + + + + + @@ -897,21 +1207,37 @@ + + + + + + + + + + + + + + + + @@ -921,6 +1247,15 @@ + + + + + + + + + @@ -948,6 +1283,7 @@ + @@ -964,6 +1300,7 @@ + @@ -974,9 +1311,8 @@ - - + @@ -986,30 +1322,141 @@ + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + @@ -1026,15 +1473,20 @@ + + + + + @@ -1042,10 +1494,14 @@ + + + + @@ -1057,25 +1513,39 @@ + + + + + + + + + + + + + + @@ -1083,20 +1553,29 @@ + + + + + + + + + @@ -1105,6 +1584,7 @@ + @@ -1114,13 +1594,21 @@ - - - - - + + + + + + + + + + + + + @@ -1129,6 +1617,12 @@ + + + + + + @@ -1138,60 +1632,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1202,50 +1760,21 @@ + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1253,170 +1782,234 @@ + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - + + + + + + + + + + + @@ -1424,15 +2017,81 @@ + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1445,7 +2104,9 @@ + + @@ -1454,13 +2115,20 @@ + + + + + + + @@ -1470,27 +2138,44 @@ + + + + + + + + + + + + + + + + + @@ -1505,67 +2190,103 @@ + + + + - + + + + + + + + + + + + + + + + + + - - - + + + + + + + - - + + + + + + + + + + + + + @@ -1573,9 +2294,11 @@ + + @@ -1590,6 +2313,8 @@ + + @@ -1609,7 +2334,9 @@ - + + + @@ -1617,18 +2344,39 @@ + + + + + + + + + + + + + + + + + + + + + @@ -1642,141 +2390,176 @@ + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + - + + + + - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -1787,10 +2570,10 @@ - - + + @@ -1804,6 +2587,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1813,41 +2638,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + - - - - - - - - - - - + + - 7.1.0 + 8.1.0 1.4.0 @@ -1856,9 +2730,18 @@ swoole - - - - + + + + + + + + + + + + + diff --git a/php_swoole.h b/php_swoole.h index 6e706d8e8a1..ce78ee064b6 100644 --- a/php_swoole.h +++ b/php_swoole.h @@ -10,16 +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 | +----------------------------------------------------------------------+ */ #ifndef PHP_SWOOLE_H #define PHP_SWOOLE_H -// C++ build format macros must defined earlier #ifdef __cplusplus -#define __STDC_FORMAT_MACROS +extern "C" { #endif #include "php.h" @@ -34,332 +33,40 @@ #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 -// zend iterator interface -#if PHP_VERSION_ID < 70200 -#ifdef HAVE_PCRE -#include "ext/spl/spl_iterators.h" -#define zend_ce_countable spl_ce_Countable -#define SW_HAVE_COUNTABLE 1 -#endif -#else -#define SW_HAVE_COUNTABLE 1 -#endif - -#include "swoole_api.h" -#include "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_CHECK_CALLBACK -#define PHP_SWOOLE_CLIENT_USE_POLL - -#ifndef ZEND_MOD_END -#define ZEND_MOD_END {NULL,NULL,NULL} +#ifdef __cplusplus +} #endif -#define SW_HOST_SIZE 128 - -extern PHPAPI int php_array_merge(HashTable *dest, HashTable *src); - extern zend_module_entry swoole_module_entry; - #define phpext_swoole_ptr &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 - -#ifdef __APPLE__ -#define SIOCGIFHWADDR SIOCGIFCONF -#define ifr_hwaddr ifr_addr -#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,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 < 70100 -#error "require PHP version 7.1 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, -}; -//--------------------------------------------------------- -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; -//--------------------------------------------------------- -typedef struct -{ - zend_fcall_info fci; - zend_fcall_info_cache fci_cache; -} php_swoole_fci; -//--------------------------------------------------------- - -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_version); -PHP_FUNCTION(swoole_cpu_num); -PHP_FUNCTION(swoole_set_process_name); -PHP_FUNCTION(swoole_get_local_ip); -PHP_FUNCTION(swoole_get_local_mac); -PHP_FUNCTION(swoole_clear_dns_cache); -PHP_FUNCTION(swoole_internal_call_user_shutdown_begin); -//--------------------------------------------------------- -// Coroutine API -//--------------------------------------------------------- -PHP_FUNCTION(swoole_coroutine_create); -PHP_FUNCTION(swoole_coroutine_exec); -PHP_FUNCTION(swoole_coroutine_gethostbyname); -PHP_FUNCTION(swoole_coroutine_defer); -//--------------------------------------------------------- -// 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 - -PHP_FUNCTION(swoole_strerror); -PHP_FUNCTION(swoole_errno); -PHP_FUNCTION(swoole_last_error); - - -/** - * 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_async_coro_minit(int module_number); -void php_swoole_coroutine_minit(int module_number); -void php_swoole_coroutine_system_minit(int module_number); -void php_swoole_coroutine_scheduler_minit(int module_number); -void php_swoole_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(); - -void php_swoole_process_clean(); -int php_swoole_process_start(swWorker *process, zval *zobject); - -int php_swoole_reactor_init(); - -// shutdown -void php_swoole_register_shutdown_function(const char *function); -void php_swoole_register_shutdown_function_prepend(const char *function); -void php_swoole_register_rshutdown_callback(swCallback cb, void *private_data); - -// event -void php_swoole_event_init(); -void php_swoole_event_wait(); -void php_swoole_event_exit(); - -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); -} - -#ifdef SW_HAVE_ZLIB -#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) -#endif -int php_swoole_websocket_frame_pack_ex(swString *buffer, zval *zdata, zend_long opcode, uint8_t flags, zend_bool mask, zend_bool allow_compress); -int php_swoole_websocket_frame_object_pack_ex(swString *buffer, zval *zdata, zend_bool mask, zend_bool allow_compress); -void php_swoole_websocket_frame_unpack(swString *data, zval *zframe); -void php_swoole_websocket_frame_unpack_ex(swString *data, zval *zframe, uchar allow_uncompress); - -int php_swoole_task_pack(swEventData *task, zval *data); -zval* php_swoole_task_unpack(swEventData *task_result); - -#ifdef SW_HAVE_ZLIB -int php_swoole_zlib_decompress(z_stream *stream, swString *buffer, char *body, int length); -#endif - -int swoole_convert_to_fd(zval *zsocket); -int swoole_convert_to_fd_ex(zval *zsocket, int *async); - -#ifdef SWOOLE_SOCKETS_SUPPORT -php_socket *swoole_convert_to_socket(int sock); -void swoole_php_socket_free(zval *zsocket); -#endif - -ssize_t php_swoole_length_func(swProtocol *protocol, swSocket *_socket, char *data, uint32_t length); -int php_swoole_client_onPackage(swConnection *conn, char *data, uint32_t length); -zend_bool php_swoole_signal_isset_handler(int signo); - +// clang-format off ZEND_BEGIN_MODULE_GLOBALS(swoole) zend_bool display_errors; zend_bool cli; zend_bool use_shortname; - zend_bool enable_coroutine; zend_bool enable_preemptive_scheduler; zend_bool enable_library; + zend_bool enable_fiber_mock; long socket_buffer_size; - php_swoole_req_status req_status; + int req_status; + HashTable *in_autoload; ZEND_END_MODULE_GLOBALS(swoole) +// clang-format on extern ZEND_DECLARE_MODULE_GLOBALS(swoole); @@ -369,770 +76,4 @@ extern ZEND_DECLARE_MODULE_GLOBALS(swoole); #define SWOOLE_G(v) (swoole_globals.v) #endif -/* PHP 7 compatibility patches */ - -// Fixed C++ warning (https://github.com/php/php-src/commit/ec31924cd68df4f5591664d487baaba0d01b1daf) -#if PHP_VERSION_ID < 70200 -#define sw_zend_bailout() _sw_zend_bailout(__FILE__, __LINE__) -static sw_inline void _sw_zend_bailout(const char *filename, uint32_t lineno) -{ - _zend_bailout((char *)filename, lineno); -} -#else -#define sw_zend_bailout() zend_bailout() -#endif - -// Fixed in php-7.0.28, php-7.1.15RC1, php-7.2.3RC1 (https://github.com/php/php-src/commit/e88e83d3e5c33fcd76f08b23e1a2e4e8dc98ce41) -#if PHP_MAJOR_VERSION == 7 && ((PHP_MINOR_VERSION == 0 && PHP_RELEASE_VERSION < 28) || (PHP_MINOR_VERSION == 1 && PHP_RELEASE_VERSION < 15) || (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 -#if PHP_VERSION_ID < 70100 -#define zend_wrong_parameters_count_error zend_wrong_paramers_count_error -#endif - -// 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_SET_REFCOUNT -# define GC_SET_REFCOUNT(p, rc) do { \ - GC_REFCOUNT(p) = rc; \ -} while (0) -#endif - -#ifndef GC_ADDREF -#define GC_ADDREF(ref) ++GC_REFCOUNT(ref) -#define GC_DELREF(ref) --GC_REFCOUNT(ref) -#endif - -#ifndef GC_IS_RECURSIVE -#define GC_IS_RECURSIVE(p) \ - (ZEND_HASH_GET_APPLY_COUNT(p) >= 1) -#define GC_PROTECT_RECURSION(p) \ - ZEND_HASH_INC_APPLY_COUNT(p) -#define GC_UNPROTECT_RECURSION(p) \ - ZEND_HASH_DEC_APPLY_COUNT(p) -#endif - -#ifndef ZEND_CLOSURE_OBJECT -#define ZEND_CLOSURE_OBJECT(func) (zend_object*)func->op_array.prototype -#endif - -#ifndef ZEND_HASH_APPLY_PROTECTION -#define ZEND_HASH_APPLY_PROTECTION(p) 1 -#endif/*}}}*/ - -/* PHP 7.4 compatibility macro {{{*/ -#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 - -#ifndef ZEND_THIS -#define ZEND_THIS (&EX(This)) -#endif -/*}}}*/ - -/* PHP 7 wrapper functions / macros */ - -//----------------------------------Zval API------------------------------------ - -// ide-helper -#ifdef SW_DEBUG -#undef RETURN_BOOL -#undef RETURN_NULL -#undef RETURN_LONG -#undef RETURN_DOUBLE -#undef RETURN_STR -#undef RETURN_INTERNED_STR -#undef RETURN_NEW_STR -#undef RETURN_STR_COPY -#undef RETURN_STRING -#undef RETURN_STRINGL -#undef RETURN_EMPTY_STRING -#undef RETURN_RES -#undef RETURN_ARR -#undef RETURN_EMPTY_ARRAY -#undef RETURN_OBJ -#undef RETURN_ZVAL -#undef RETURN_FALSE -#undef RETURN_TRUE -#undef ZVAL_LONG -#undef ZVAL_DOUBLE -#define RETURN_BOOL(b) do { RETVAL_BOOL(b); return; } while (0) -#define RETURN_NULL() do { RETVAL_NULL(); return;} while (0) -#define RETURN_LONG(l) do { RETVAL_LONG(l); return; } while (0) -#define RETURN_DOUBLE(d) do { RETVAL_DOUBLE(d); return; } while (0) -#define RETURN_STR(s) do { RETVAL_STR(s); return; } while (0) -#define RETURN_INTERNED_STR(s) do { RETVAL_INTERNED_STR(s); return; } while (0) -#define RETURN_NEW_STR(s) do { RETVAL_NEW_STR(s); return; } while (0) -#define RETURN_STR_COPY(s) do { RETVAL_STR_COPY(s); return; } while (0) -#define RETURN_STRING(s) do { RETVAL_STRING(s); return; } while (0) -#define RETURN_STRINGL(s, l) do { RETVAL_STRINGL(s, l); return; } while (0) -#define RETURN_EMPTY_STRING() do { RETVAL_EMPTY_STRING(); return; } while (0) -#define RETURN_RES(r) do { RETVAL_RES(r); return; } while (0) -#define RETURN_ARR(r) do { RETVAL_ARR(r); return; } while (0) -#define RETURN_EMPTY_ARRAY() do { RETVAL_EMPTY_ARRAY(); return; } while (0) -#define RETURN_OBJ(r) do { RETVAL_OBJ(r); return; } while (0) -#define RETURN_ZVAL(zv, copy, dtor) do { RETVAL_ZVAL(zv, copy, dtor); return; } while (0) -#define RETURN_FALSE do { RETVAL_FALSE; return; } while (0) -#define RETURN_TRUE do { RETVAL_TRUE; return; } while (0) -#define ZVAL_LONG(z, l) do { \ - zval *__z = (z); \ - Z_LVAL_P(__z) = l; \ - Z_TYPE_INFO_P(__z) = IS_LONG; \ - } while (0) -#define ZVAL_DOUBLE(z, d) do { \ - zval *__z = (z); \ - Z_DVAL_P(__z) = d; \ - Z_TYPE_INFO_P(__z) = IS_DOUBLE; \ - } while (0) -#endif - -// 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_ZEND_REGISTER_RESOURCE(return_value, result, le_result) ZVAL_RES(return_value,zend_register_resource(result, le_result)) - -#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 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 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 (UNEXPECTED(alloc_len - real_len > SwooleG.pagesize)) - { - 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)) - -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; - } -} - -#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 int add_assoc_ulong_safe_ex(zval *arg, const char *key, size_t key_len, zend_ulong value) -{ - if (sw_likely(value <= ZEND_LONG_MAX)) - { - return 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); - return add_assoc_stringl_ex(arg, key, key_len, buf, len); - } -} - -static sw_inline int add_assoc_ulong_safe(zval *arg, const char *key, zend_ulong value) -{ - return 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, namespaceName, snake_name, shortName, methods, parent_ce) do { \ - zend_class_entry _##module##_ce; \ - INIT_CLASS_ENTRY(_##module##_ce, namespaceName, methods); \ - module##_ce = zend_register_internal_class_ex(&_##module##_ce, parent_ce); \ - SW_CLASS_ALIAS(snake_name, module); \ - SW_CLASS_ALIAS_SHORT_NAME(shortName, module); \ -} while (0) - -#define SW_INIT_CLASS_ENTRY(module, namespaceName, snake_name, shortName, methods) \ - SW_INIT_CLASS_ENTRY_BASE(module, namespaceName, snake_name, shortName, methods, NULL); \ - memcpy(&module##_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)) - -#define SW_INIT_CLASS_ENTRY_EX(module, namespaceName, snake_name, shortName, methods, parent_module) \ - SW_INIT_CLASS_ENTRY_BASE(module, namespaceName, snake_name, shortName, methods, parent_module##_ce); \ - memcpy(&module##_handlers, &parent_module##_handlers, sizeof(zend_object_handlers)) - -#define SW_INIT_CLASS_ENTRY_EX2(module, namespaceName, snake_name, shortName, methods, parent_module_ce, parent_module_handlers) \ - SW_INIT_CLASS_ENTRY_BASE(module, namespaceName, snake_name, shortName, methods, parent_module_ce); \ - memcpy(&module##_handlers, parent_module_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(shortName, module) do { \ - if (SWOOLE_G(use_shortname)) { \ - SW_CLASS_ALIAS(shortName, 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 -( - HashTable *origin_function_table, const char *origin, size_t origin_length, - HashTable *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 -} - -static sw_inline zend_object *sw_zend_create_object(zend_class_entry *ce, zend_object_handlers *handlers) -{ - zend_object* object = (zend_object *) ecalloc(1, sizeof(zend_object) + zend_object_properties_size(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, obj, s, len, silent, &rv); - if (UNEXPECTED(property == &EG(uninitialized_zval))) - { - zend_update_property_null(ce, obj, s, len); - return zend_read_property(ce, obj, s, len, 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, 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_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, obj, s, len, &ztmp); - zval_ptr_dtor(&ztmp); - return zend_read_property(ce, 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, 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) - -//----------------------------------Function API------------------------------------ - -#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 - -/** - * 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_ex(zval *zcallable, zval *zobject, 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 = zend_is_callable_ex(zcallable, zobject ? Z_OBJ_P(zobject) : NULL, check_flags, &name, fci_cache, error); - 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; -} - -/* 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.no_separation = 0; - - 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(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_url_encode_hash_ex(HASH_OF(zdata), formstr, NULL, 0, NULL, 0, NULL, 0, NULL, NULL, (int) PHP_QUERY_RFC1738) == FAILURE) - { - 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/php_swoole_cxx.cc b/php_swoole_cxx.cc deleted file mode 100644 index 6c2fec5ff02..00000000000 --- a/php_swoole_cxx.cc +++ /dev/null @@ -1,69 +0,0 @@ -#include "php_swoole_cxx.h" - -bool zend::include(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 = NULL; - 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; -} - -//for compatibly with dis_eval -zend_op_array* (*old_compile_string)(zval *source_string, ZEND_STR_CONST char *filename); - -zend_op_array* zend::swoole_compile_string(zval *source_string, ZEND_STR_CONST char *filename) -{ - zend_op_array *opa = old_compile_string(source_string, filename); - opa->type = ZEND_USER_FUNCTION; - return opa; -} - - -bool zend::eval(std::string code, std::string 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; -} diff --git a/php_swoole_cxx.h b/php_swoole_cxx.h deleted file mode 100644 index 513c60b3d03..00000000000 --- a/php_swoole_cxx.h +++ /dev/null @@ -1,379 +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 | - +----------------------------------------------------------------------+ -*/ - -#pragma once - -#include "php_swoole.h" -#include "swoole_cxx.h" -#include "swoole_coroutine.h" -#include "swoole_api.h" - -#include - -#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); } - -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 void php_swoole_init_socket_object(zval *zobject, swoole::coroutine::Socket *socket); -SW_API swoole::coroutine::Socket* php_swoole_get_socket(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); - -php_stream *php_swoole_create_stream_from_socket(php_socket_t _fd, int domain, int type, int protocol STREAMS_DC); - -// timer -SW_API bool php_swoole_timer_clear(swTimer_node *tnode); -SW_API bool php_swoole_timer_clear_all(); - -namespace zend { -//-----------------------------------namespace begin-------------------------------------------- -class string -{ -public: - string() - { - str = nullptr; - } - - string(const char *_str, size_t len) - { - str = zend_string_init(_str, len, 0); - } - - string(const std::string &_str) - { - str = zend_string_init(_str.c_str(), _str.length(), 0); - } - - string(zval *v) - { - str = zval_get_string(v); - } - - string(zend_string *&v) - { - str = zend_string_copy(v); - } - - string(zend_string *&&v) - { - str = v; - } - - void operator =(zval* v) - { - if (str) - { - zend_string_release(str); - } - str = zval_get_string(v); - } - - inline char* val() - { - return ZSTR_VAL(str); - } - - inline size_t len() - { - return ZSTR_LEN(str); - } - - zend_string* get() - { - return str; - } - - std::string to_std_string() - { - return std::string(val(), len()); - } - - char* dup() - { - return sw_likely(len() > 0) ? sw_strndup(val(), len()) : nullptr; - } - - char* edup() - { - return sw_likely(len() > 0) ? estrndup(val(), len()) : nullptr; - } - - ~string() - { - if (str) - { - zend_string_release(str); - } - } - -private: - zend_string *str; -}; - -class string_ptr -{ -public: - string_ptr(zend_string *str) : - str(str) - { - } - string_ptr(string_ptr &&o) - { - str = o.str; - o.str = nullptr; - } - ~string_ptr() - { - if (str) - { - zend_string_release(str); - } - } -private: - zend_string *str; -}; - -class key_value -{ -public: - zend_ulong index; - zend_string *key; - zval zvalue; - - key_value(zend_ulong _index, zend_string *_key, zval *_zvalue) - { - index = _index; - key = _key ? zend_string_copy(_key) : nullptr; - ZVAL_DEREF(_zvalue); - zvalue = *_zvalue; - Z_TRY_ADDREF(zvalue); - } - - inline 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); - } - - ~key_value() - { - if (key) - { - zend_string_release(key); - } - zval_ptr_dtor(&zvalue); - } -}; - -class ArrayIterator -{ -public: - ArrayIterator(Bucket *p) - { - _ptr = p; - _key = _ptr->key; - _val = &_ptr->val; - _index = _ptr->h; - pe = p; - } - ArrayIterator(Bucket *p, Bucket *_pe) - { - _ptr = p; - _key = _ptr->key; - _val = &_ptr->val; - _index = _ptr->h; - 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; - } -private: - void skipUndefBucket() - { - while (_ptr != pe) - { - _val = &_ptr->val; - if (_val && Z_TYPE_P(_val) == IS_INDIRECT) - { - _val = Z_INDIRECT_P(_val); - } - if (UNEXPECTED(Z_TYPE_P(_val) == IS_UNDEF)) - { - ++_ptr; - continue; - } - if (_ptr->key) - { - _key = _ptr->key; - _index = 0; - } - else - { - _index = _ptr->h; - _key = NULL; - } - break; - } - } - - zval *_val; - zend_string *_key; - Bucket *_ptr; - Bucket *pe; - zend_ulong _index; -}; - -class Array -{ -public: - zval *arr; - - Array(zval *_arr) - { - assert(Z_TYPE_P(_arr) == IS_ARRAY); - arr = _arr; - } - - inline size_t count() - { - return zend_hash_num_elements(Z_ARRVAL_P(arr)); - } - - inline bool set(zend_ulong index, zval *value) - { - return add_index_zval(arr, index, value) == SUCCESS; - } - - inline bool append(zval *value) - { - return add_next_index_zval(arr, value) == SUCCESS; - } - - inline bool set(zend_ulong index, zend_resource *res) - { - zval tmp; - ZVAL_RES(&tmp, res); - return set(index, &tmp); - } - - ArrayIterator begin() - { - return ArrayIterator(Z_ARRVAL_P(arr)->arData, Z_ARRVAL_P(arr)->arData + Z_ARRVAL_P(arr)->nNumUsed); - } - - ArrayIterator end() - { - return ArrayIterator(Z_ARRVAL_P(arr)->arData + Z_ARRVAL_P(arr)->nNumUsed); - } -}; - -enum process_pipe_type -{ - PIPE_TYPE_NONE = 0, - PIPE_TYPE_STREAM = 1, - PIPE_TYPE_DGRAM = 2, -}; - -class process -{ -public: - zend_object *zsocket = nullptr; - enum process_pipe_type pipe_type; - bool enable_coroutine; - - process(enum process_pipe_type pipe_type, bool enable_coroutine) : - pipe_type(pipe_type), enable_coroutine(enable_coroutine) { } - - ~process() - { - if (zsocket) - { - OBJ_RELEASE(zsocket); - } - } - -}; - -namespace function -{ - /* must use this API to call event callbacks to ensure that exceptions are handled correctly */ - inline bool call(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *retval, const bool enable_coroutine) - { - bool success; - if (enable_coroutine) - { - if (retval) - { - /* the coroutine has no return value */ - ZVAL_NULL(retval); - } - success = swoole::PHPCoroutine::create(fci_cache, argc, argv) >= 0; - } - else - { - success = sw_zend_call_function_ex(NULL, fci_cache, argc, argv, retval) == SUCCESS; - } - /* we have no chance to return to ZendVM to check the exception */ - if (UNEXPECTED(EG(exception))) - { - zend_exception_error(EG(exception), E_ERROR); - } - return success; - } -} - -bool include(std::string file); -bool eval(std::string code, std::string filename = ""); - -#if PHP_VERSION_ID < 80000 -#define ZEND_STR_CONST -#else -#define ZEND_STR_CONST const -#endif - -zend_op_array* swoole_compile_string(zval *source_string, ZEND_STR_CONST char *filename); -//-----------------------------------namespace end-------------------------------------------- -} diff --git a/php_swoole_library.h b/php_swoole_library.h deleted file mode 100644 index 7f4497da702..00000000000 --- a/php_swoole_library.h +++ /dev/null @@ -1,6478 +0,0 @@ -/** - * Generated by build-library.php, Please DO NOT modify! - */ - -/* $Id: 1e586057e634264f664f7e261eab2dc68f462cf9 */ - -static const char* swoole_library_source_constants = - "\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" - "define('SWOOLE_LIBRARY', true);\n"; - -static const char* swoole_library_source_std_exec = - "\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" - "function swoole_exec(string $command, &$output = null, &$returnVar = null)\n" - "{\n" - " $result = Swoole\\Coroutine::exec($command);\n" - " if ($result) {\n" - " $outputList = explode(PHP_EOL, $result['output']);\n" - " foreach ($outputList as &$value) {\n" - " $value = rtrim($value);\n" - " }\n" - " if (($endLine = end($outputList)) === '') {\n" - " array_pop($outputList);\n" - " $endLine = end($outputList);\n" - " }\n" - " if ($output) {\n" - " $output = array_merge($output, $outputList);\n" - " } else {\n" - " $output = $outputList;\n" - " }\n" - " $returnVar = $result['code'];\n" - " return $endLine;\n" - " }\n" - " return false;\n" - "}\n" - "\n" - "function swoole_shell_exec(string $cmd)\n" - "{\n" - " $result = Swoole\\Coroutine::exec($cmd);\n" - " if ($result && $result['output'] !== '') {\n" - " return $result['output'];\n" - " }\n" - " return null;\n" - "}\n"; - -static const char* swoole_library_source_core_constant = - "\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" - "class Constant\n" - "{\n" - " public const EVENT_RECEIVE = 'receive';\n" - "\n" - " public const EVENT_CONNECT = 'connect';\n" - "\n" - " public const EVENT_CLOSE = 'close';\n" - "\n" - " public const EVENT_PACKET = 'packet';\n" - "\n" - " public const EVENT_REQUEST = 'request';\n" - "\n" - " public const EVENT_MESSAGE = 'message';\n" - "\n" - " public const EVENT_OPEN = 'open';\n" - "\n" - " public const EVENT_HANDSHAKE = 'handshake';\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" - "\n" - " public const EVENT_PIPE_MESSAGE = 'pipeMessage';\n" - "\n" - " public const EVENT_MANAGER_START = 'managerStart';\n" - "\n" - " public const EVENT_MANAGER_STOP = 'managerStop';\n" - "\n" - " public const EVENT_ERROR = 'error';\n" - "\n" - " /* {{{ OPTION */\n" - " public const OPTION_ENABLE_SIGNALFD = 'enable_signalfd';\n" - "\n" - " public const OPTION_DNS_CACHE_REFRESH_TIME = 'dns_cache_refresh_time';\n" - "\n" - " public const OPTION_SOCKET_BUFFER_SIZE = 'socket_buffer_size';\n" - "\n" - " public const OPTION_SOCKET_SEND_TIMEOUT = 'socket_send_timeout';\n" - "\n" - " public const OPTION_LOG_LEVEL = 'log_level';\n" - "\n" - " public const OPTION_THREAD_NUM = 'thread_num';\n" - "\n" - " public const OPTION_MIN_THREAD_NUM = 'min_thread_num';\n" - "\n" - " public const OPTION_MAX_THREAD_NUM = 'max_thread_num';\n" - "\n" - " public const OPTION_DISPLAY_ERRORS = 'display_errors';\n" - "\n" - " public const OPTION_SOCKET_DONTWAIT = 'socket_dontwait';\n" - "\n" - " public const OPTION_DNS_LOOKUP_RANDOM = 'dns_lookup_random';\n" - "\n" - " public const OPTION_DNS_SERVER = 'dns_server';\n" - "\n" - " public const OPTION_USE_ASYNC_RESOLVER = 'use_async_resolver';\n" - "\n" - " public const OPTION_ENABLE_COROUTINE = 'enable_coroutine';\n" - "\n" - " public const OPTION_SSL_METHOD = 'ssl_method';\n" - "\n" - " public const OPTION_SSL_PROTOCOLS = 'ssl_protocols';\n" - "\n" - " public const OPTION_SSL_COMPRESS = 'ssl_compress';\n" - "\n" - " public const OPTION_SSL_CERT_FILE = 'ssl_cert_file';\n" - "\n" - " public const OPTION_SSL_KEY_FILE = 'ssl_key_file';\n" - "\n" - " public const OPTION_SSL_PASSPHRASE = 'ssl_passphrase';\n" - "\n" - " public const OPTION_SSL_HOST_NAME = 'ssl_host_name';\n" - "\n" - " public const OPTION_SSL_VERIFY_PEER = 'ssl_verify_peer';\n" - "\n" - " public const OPTION_SSL_ALLOW_SELF_SIGNED = 'ssl_allow_self_signed';\n" - "\n" - " public const OPTION_SSL_CAFILE = 'ssl_cafile';\n" - "\n" - " public const OPTION_SSL_CAPATH = 'ssl_capath';\n" - "\n" - " public const OPTION_SSL_VERIFY_DEPTH = 'ssl_verify_depth';\n" - "\n" - " public const OPTION_OPEN_EOF_CHECK = 'open_eof_check';\n" - "\n" - " public const OPTION_OPEN_EOF_SPLIT = 'open_eof_split';\n" - "\n" - " public const OPTION_PACKAGE_EOF = 'package_eof';\n" - "\n" - " public const OPTION_OPEN_MQTT_PROTOCOL = 'open_mqtt_protocol';\n" - "\n" - " public const OPTION_OPEN_LENGTH_CHECK = 'open_length_check';\n" - "\n" - " public const OPTION_PACKAGE_LENGTH_TYPE = 'package_length_type';\n" - "\n" - " public const OPTION_PACKAGE_LENGTH_OFFSET = 'package_length_offset';\n" - "\n" - " public const OPTION_PACKAGE_BODY_OFFSET = 'package_body_offset';\n" - "\n" - " public const OPTION_PACKAGE_LENGTH_FUNC = 'package_length_func';\n" - "\n" - " public const OPTION_PACKAGE_MAX_LENGTH = 'package_max_length';\n" - "\n" - " public const OPTION_BUFFER_HIGH_WATERMARK = 'buffer_high_watermark';\n" - "\n" - " public const OPTION_BUFFER_LOW_WATERMARK = 'buffer_low_watermark';\n" - "\n" - " public const OPTION_BIND_PORT = 'bind_port';\n" - "\n" - " public const OPTION_BIND_ADDRESS = 'bind_address';\n" - "\n" - " public const OPTION_OPEN_TCP_NODELAY = 'open_tcp_nodelay';\n" - "\n" - " public const OPTION_SOCKS5_HOST = 'socks5_host';\n" - "\n" - " public const OPTION_SOCKS5_PORT = 'socks5_port';\n" - "\n" - " public const OPTION_SOCKS5_USERNAME = 'socks5_username';\n" - "\n" - " public const OPTION_SOCKS5_PASSWORD = 'socks5_password';\n" - "\n" - " public const OPTION_HTTP_PROXY_HOST = 'http_proxy_host';\n" - "\n" - " public const OPTION_HTTP_PROXY_PORT = 'http_proxy_port';\n" - "\n" - " public const OPTION_HTTP_PROXY_USERNAME = 'http_proxy_username';\n" - "\n" - " public const OPTION_HTTP_PROXY_USER = 'http_proxy_user';\n" - "\n" - " public const OPTION_HTTP_PROXY_PASSWORD = 'http_proxy_password';\n" - "\n" - " public const OPTION_TIMEOUT = 'timeout';\n" - "\n" - " public const OPTION_CONNECT_TIMEOUT = 'connect_timeout';\n" - "\n" - " public const OPTION_READ_TIMEOUT = 'read_timeout';\n" - "\n" - " public const OPTION_WRITE_TIMEOUT = 'write_timeout';\n" - "\n" - " public const OPTION_SSL_DISABLE_COMPRESSION = 'ssl_disable_compression';\n" - "\n" - " public const OPTION_MAX_COROUTINE = 'max_coroutine';\n" - "\n" - " public const OPTION_HOOK_FLAGS = 'hook_flags';\n" - "\n" - " public const OPTION_ENABLE_PREEMPTIVE_SCHEDULER = 'enable_preemptive_scheduler';\n" - "\n" - " public const OPTION_C_STACK_SIZE = 'c_stack_size';\n" - "\n" - " public const OPTION_STACK_SIZE = 'stack_size';\n" - "\n" - " public const OPTION_SOCKET_DNS_TIMEOUT = 'socket_dns_timeout';\n" - "\n" - " public const OPTION_SOCKET_CONNECT_TIMEOUT = 'socket_connect_timeout';\n" - "\n" - " public const OPTION_SOCKET_TIMEOUT = 'socket_timeout';\n" - "\n" - " public const OPTION_SOCKET_READ_TIMEOUT = 'socket_read_timeout';\n" - "\n" - " public const OPTION_SOCKET_WRITE_TIMEOUT = 'socket_write_timeout';\n" - "\n" - " public const OPTION_TRACE_FLAGS = 'trace_flags';\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" - "\n" - " public const OPTION_AIO_MAX_IDLE_TIME = 'aio_max_idle_time';\n" - "\n" - " public const OPTION_RECONNECT = 'reconnect';\n" - "\n" - " public const OPTION_DEFER = 'defer';\n" - "\n" - " public const OPTION_KEEP_ALIVE = 'keep_alive';\n" - "\n" - " public const OPTION_WEBSOCKET_MASK = 'websocket_mask';\n" - "\n" - " public const OPTION_WEBSOCKET_COMPRESSION = 'websocket_compression';\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" - "\n" - " public const OPTION_SSL = 'ssl';\n" - "\n" - " public const OPTION_USER = 'user';\n" - "\n" - " public const OPTION_PASSWORD = 'password';\n" - "\n" - " public const OPTION_DATABASE = 'database';\n" - "\n" - " public const OPTION_CHARSET = 'charset';\n" - "\n" - " public const OPTION_STRICT_TYPE = 'strict_type';\n" - "\n" - " public const OPTION_FETCH_MODE = 'fetch_mode';\n" - "\n" - " public const OPTION_SERIALIZE = 'serialize';\n" - "\n" - " public const OPTION_COMPATIBILITY_MODE = 'compatibility_mode';\n" - "\n" - " public const OPTION_CHROOT = 'chroot';\n" - "\n" - " public const OPTION_GROUP = 'group';\n" - "\n" - " public const OPTION_DAEMONIZE = 'daemonize';\n" - "\n" - " public const OPTION_DEBUG_MODE = 'debug_mode';\n" - "\n" - " public const OPTION_PID_FILE = 'pid_file';\n" - "\n" - " public const OPTION_REACTOR_NUM = 'reactor_num';\n" - "\n" - " public const OPTION_SINGLE_THREAD = 'single_thread';\n" - "\n" - " public const OPTION_WORKER_NUM = 'worker_num';\n" - "\n" - " public const OPTION_MAX_WAIT_TIME = 'max_wait_time';\n" - "\n" - " public const OPTION_MAX_QUEUED_BYTES = 'max_queued_bytes';\n" - "\n" - " public const OPTION_MAX_CORO_NUM = 'max_coro_num';\n" - "\n" - " public const OPTION_SEND_TIMEOUT = 'send_timeout';\n" - "\n" - " public const OPTION_DISPATCH_MODE = 'dispatch_mode';\n" - "\n" - " public const OPTION_SEND_YIELD = 'send_yield';\n" - "\n" - " public const OPTION_DISPATCH_FUNC = 'dispatch_func';\n" - "\n" - " public const OPTION_LOG_FILE = 'log_file';\n" - "\n" - " public const OPTION_DISCARD_TIMEOUT_REQUEST = 'discard_timeout_request';\n" - "\n" - " public const OPTION_ENABLE_UNSAFE_EVENT = 'enable_unsafe_event';\n" - "\n" - " public const OPTION_ENABLE_DELAY_RECEIVE = 'enable_delay_receive';\n" - "\n" - " public const OPTION_ENABLE_REUSE_PORT = 'enable_reuse_port';\n" - "\n" - " public const OPTION_TASK_USE_OBJECT = 'task_use_object';\n" - "\n" - " public const OPTION_TASK_ENABLE_COROUTINE = 'task_enable_coroutine';\n" - "\n" - " public const OPTION_TASK_WORKER_NUM = 'task_worker_num';\n" - "\n" - " public const OPTION_TASK_IPC_MODE = 'task_ipc_mode';\n" - "\n" - " public const OPTION_TASK_TMPDIR = 'task_tmpdir';\n" - "\n" - " public const OPTION_TASK_MAX_REQUEST = 'task_max_request';\n" - "\n" - " public const OPTION_TASK_MAX_REQUEST_GRACE = 'task_max_request_grace';\n" - "\n" - " public const OPTION_MAX_CONNECTION = 'max_connection';\n" - "\n" - " public const OPTION_MAX_CONN = 'max_conn';\n" - "\n" - " public const OPTION_HEARTBEAT_CHECK_INTERVAL = 'heartbeat_check_interval';\n" - "\n" - " public const OPTION_HEARTBEAT_IDLE_TIME = 'heartbeat_idle_time';\n" - "\n" - " public const OPTION_MAX_REQUEST = 'max_request';\n" - "\n" - " public const OPTION_MAX_REQUEST_GRACE = 'max_request_grace';\n" - "\n" - " public const OPTION_RELOAD_ASYNC = 'reload_async';\n" - "\n" - " public const OPTION_OPEN_CPU_AFFINITY = 'open_cpu_affinity';\n" - "\n" - " public const OPTION_CPU_AFFINITY_IGNORE = 'cpu_affinity_ignore';\n" - "\n" - " public const OPTION_ENABLE_STATIC_HANDLER = 'enable_static_handler';\n" - "\n" - " public const OPTION_DOCUMENT_ROOT = 'document_root';\n" - "\n" - " public const OPTION_HTTP_AUTOINDEX = 'http_autoindex';\n" - "\n" - " public const OPTION_HTTP_INDEX_FILES = 'http_index_files';\n" - "\n" - " public const OPTION_STATIC_HANDLER_LOCATIONS = 'static_handler_locations';\n" - "\n" - " public const OPTION_INPUT_BUFFER_SIZE = 'input_buffer_size';\n" - "\n" - " public const OPTION_BUFFER_INPUT_SIZE = 'buffer_input_size';\n" - "\n" - " public const OPTION_OUTPUT_BUFFER_SIZE = 'output_buffer_size';\n" - "\n" - " public const OPTION_BUFFER_OUTPUT_SIZE = 'buffer_output_size';\n" - "\n" - " public const OPTION_MESSAGE_QUEUE_KEY = 'message_queue_key';\n" - "\n" - " public const OPTION_BACKLOG = 'backlog';\n" - "\n" - " public const OPTION_KERNEL_SOCKET_RECV_BUFFER_SIZE = 'kernel_socket_recv_buffer_size';\n" - "\n" - " public const OPTION_KERNEL_SOCKET_SEND_BUFFER_SIZE = 'kernel_socket_send_buffer_size';\n" - "\n" - " public const OPTION_TCP_DEFER_ACCEPT = 'tcp_defer_accept';\n" - "\n" - " public const OPTION_OPEN_TCP_KEEPALIVE = 'open_tcp_keepalive';\n" - "\n" - " public const OPTION_OPEN_HTTP_PROTOCOL = 'open_http_protocol';\n" - "\n" - " public const OPTION_OPEN_WEBSOCKET_PROTOCOL = 'open_websocket_protocol';\n" - "\n" - " public const OPTION_WEBSOCKET_SUBPROTOCOL = 'websocket_subprotocol';\n" - "\n" - " public const OPTION_OPEN_WEBSOCKET_CLOSE_FRAME = 'open_websocket_close_frame';\n" - "\n" - " public const OPTION_OPEN_HTTP2_PROTOCOL = 'open_http2_protocol';\n" - "\n" - " public const OPTION_OPEN_REDIS_PROTOCOL = 'open_redis_protocol';\n" - "\n" - " public const OPTION_TCP_KEEPIDLE = 'tcp_keepidle';\n" - "\n" - " public const OPTION_TCP_KEEPINTERVAL = 'tcp_keepinterval';\n" - "\n" - " public const OPTION_TCP_KEEPCOUNT = 'tcp_keepcount';\n" - "\n" - " public const OPTION_TCP_FASTOPEN = 'tcp_fastopen';\n" - "\n" - " public const OPTION_PACKAGE_BODY_START = 'package_body_start';\n" - "\n" - " public const OPTION_SSL_CLIENT_CERT_FILE = 'ssl_client_cert_file';\n" - "\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_OPEN_SSL = 'open_ssl';\n" - "\n" - " public const OPTION_OPEN_FASTCGI_PROTOCOL = 'open_fastcgi_protocol';\n" - "\n" - " /* }}} OPTION */\n" - "}\n"; - -static const char* swoole_library_source_core_string_object = - "\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" - "class StringObject\n" - "{\n" - " /**\n" - " * @var string\n" - " */\n" - " protected $string;\n" - "\n" - " /**\n" - " * StringObject constructor.\n" - " * @param $string\n" - " */\n" - " public function __construct(string $string = '')\n" - " {\n" - " $this->string = $string;\n" - " }\n" - "\n" - " public function __toString(): string\n" - " {\n" - " return $this->string;\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" - " {\n" - " return strpos($this->string, ...func_get_args());\n" - " }\n" - "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function lastIndexOf(string $needle, int $offset = 0)\n" - " {\n" - " return strrpos($this->string, ...func_get_args());\n" - " }\n" - "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function pos(string $needle, int $offset = 0)\n" - " {\n" - " return strpos($this->string, ...func_get_args());\n" - " }\n" - "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function rpos(string $needle, int $offset = 0)\n" - " {\n" - " return strrpos($this->string, ...func_get_args());\n" - " }\n" - "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function ipos(string $needle)\n" - " {\n" - " return stripos($this->string, $needle);\n" - " }\n" - "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function lower(): self\n" - " {\n" - " return new static(strtolower($this->string));\n" - " }\n" - "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function upper(): self\n" - " {\n" - " return new static(strtoupper($this->string));\n" - " }\n" - "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function trim(): self\n" - " {\n" - " return new static(trim($this->string));\n" - " }\n" - "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function lrim(): self\n" - " {\n" - " return new static(ltrim($this->string));\n" - " }\n" - "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function rtrim(): self\n" - " {\n" - " return new static(rtrim($this->string));\n" - " }\n" - "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function substr(int $offset, ?int $length = null)\n" - " {\n" - " return new static(substr($this->string, ...func_get_args()));\n" - " }\n" - "\n" - " public function repeat(int $n): StringObject\n" - " {\n" - " return new static(str_repeat($this->string, $n));\n" - " }\n" - "\n" - " /**\n" - " * @param null|int $count\n" - " * @return static\n" - " */\n" - " public function replace(string $search, string $replace, &$count = null)\n" - " {\n" - " return new static(str_replace($search, $replace, $this->string, $count));\n" - " }\n" - "\n" - " public function startsWith(string $needle): bool\n" - " {\n" - " return strpos($this->string, $needle) === 0;\n" - " }\n" - "\n" - " public function contains(string $subString): bool\n" - " {\n" - " return strpos($this->string, $subString) !== false;\n" - " }\n" - "\n" - " public function endsWith(string $needle): bool\n" - " {\n" - " return strrpos($this->string, $needle) === (strlen($needle) - 1);\n" - " }\n" - "\n" - " public function split(string $delimiter, int $limit = PHP_INT_MAX): ArrayObject\n" - " {\n" - " return static::detectArrayType(explode($delimiter, $this->string, $limit));\n" - " }\n" - "\n" - " public function char(int $index): string\n" - " {\n" - " return $this->string[$index];\n" - " }\n" - "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function chunkSplit(int $chunkLength = 1, string $chunkEnd = '')\n" - " {\n" - " return new static(chunk_split($this->string, ...func_get_args()));\n" - " }\n" - "\n" - " public function chunk(int $splitLength = 1): ArrayObject\n" - " {\n" - " return static::detectArrayType(str_split($this->string, ...func_get_args()));\n" - " }\n" - "\n" - " /**\n" - " * @return string\n" - " */\n" - " public function toString()\n" - " {\n" - " return $this->string;\n" - " }\n" - "\n" - " protected static function detectArrayType(array $value): ArrayObject\n" - " {\n" - " return new ArrayObject($value);\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_multibyte_string_object = - "\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" - "class MultibyteStringObject extends StringObject\n" - "{\n" - " public function length(): int\n" - " {\n" - " 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" - " {\n" - " return mb_strpos($this->string, ...func_get_args());\n" - " }\n" - "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function lastIndexOf(string $needle, int $offset = 0, ?string $encoding = null)\n" - " {\n" - " return mb_strrpos($this->string, ...func_get_args());\n" - " }\n" - "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function pos(string $needle, int $offset = 0, ?string $encoding = null)\n" - " {\n" - " return mb_strpos($this->string, ...func_get_args());\n" - " }\n" - "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function rpos(string $needle, int $offset = 0, ?string $encoding = null)\n" - " {\n" - " return mb_strrpos($this->string, ...func_get_args());\n" - " }\n" - "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function ipos(string $needle, ?string $encoding = null)\n" - " {\n" - " return mb_stripos($this->string, ...func_get_args());\n" - " }\n" - "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function substr(int $offset, ?int $length = null, ?string $encoding = null)\n" - " {\n" - " return new static(mb_substr($this->string, ...func_get_args()));\n" - " }\n" - "\n" - " public function chunk(int $splitLength = 1, ?int $limit = null): ArrayObject\n" - " {\n" - " return static::detectArrayType(mb_split($this->string, ...func_get_args()));\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_array_object = - "\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 ArrayAccess;\n" - "use Countable;\n" - "use Iterator;\n" - "use RuntimeException;\n" - "use Serializable;\n" - "\n" - "class ArrayObject implements ArrayAccess, Serializable, Countable, Iterator\n" - "{\n" - " /**\n" - " * @var array\n" - " */\n" - " protected $array;\n" - "\n" - " /**\n" - " * ArrayObject constructor.\n" - " */\n" - " public function __construct(array $array = [])\n" - " {\n" - " $this->array = $array;\n" - " }\n" - "\n" - " public function __toArray(): array\n" - " {\n" - " return $this->array;\n" - " }\n" - "\n" - " public function isEmpty(): bool\n" - " {\n" - " return empty($this->array);\n" - " }\n" - "\n" - " public function count(): int\n" - " {\n" - " return count($this->array);\n" - " }\n" - "\n" - " /**\n" - " * @return mixed\n" - " */\n" - " public function current()\n" - " {\n" - " return current($this->array);\n" - " }\n" - "\n" - " /**\n" - " * @return mixed\n" - " */\n" - " public function key()\n" - " {\n" - " return key($this->array);\n" - " }\n" - "\n" - " public function valid(): bool\n" - " {\n" - " return array_key_exists($this->key(), $this->array);\n" - " }\n" - "\n" - " /**\n" - " * @return mixed\n" - " */\n" - " public function rewind()\n" - " {\n" - " return reset($this->array);\n" - " }\n" - "\n" - " /**\n" - " * @return mixed\n" - " */\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" - " {\n" - " return static::detectType($this->array[$key]);\n" - " }\n" - "\n" - " /**\n" - " * @param mixed $key\n" - " * @param mixed $value\n" - " * @return $this\n" - " */\n" - " public function set($key, $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" - " {\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" - " {\n" - " do {\n" - " $key = $this->search($value, $strict);\n" - " if ($key) {\n" - " unset($this->array[$key]);\n" - " } else {\n" - " break;\n" - " }\n" - " } while ($loop);\n" - " return $this;\n" - " }\n" - "\n" - " /**\n" - " * @return $this\n" - " */\n" - " public function clear(): self\n" - " {\n" - " $this->array = [];\n" - " return $this;\n" - " }\n" - "\n" - " /**\n" - " * @param mixed $key\n" - " * @return null|mixed\n" - " */\n" - " public function offsetGet($key)\n" - " {\n" - " if (!array_key_exists($key, $this->array)) {\n" - " return null;\n" - " }\n" - " return $this->array[$key];\n" - " }\n" - "\n" - " /**\n" - " * @param mixed $key\n" - " * @param mixed $value\n" - " */\n" - " public function offsetSet($key, $value): void\n" - " {\n" - " $this->array[$key] = $value;\n" - " }\n" - "\n" - " /**\n" - " * @param mixed $key\n" - " */\n" - " public function offsetUnset($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" - " {\n" - " return isset($this->array[$key]);\n" - " }\n" - "\n" - " /**\n" - " * @param mixed $key\n" - " */\n" - " public function exists($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" - " {\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" - " {\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" - " {\n" - " $array = $this->array;\n" - " for (end($array); ($currentKey = key($array)) !== null; prev($array)) {\n" - " $currentValue = current($array);\n" - " if ($currentValue == $value) {\n" - " if ($strict && $currentValue !== $value) {\n" - " continue;\n" - " }\n" - " break;\n" - " }\n" - " }\n" - " return $currentKey;\n" - " }\n" - "\n" - " /**\n" - " * @param mixed $needle\n" - " * @return mixed\n" - " */\n" - " public function search($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" - " }\n" - "\n" - " public function serialize(): StringObject\n" - " {\n" - " return static::detectStringType(serialize($this->array));\n" - " }\n" - "\n" - " /**\n" - " * @param string $string\n" - " * @return $this\n" - " */\n" - " public function unserialize($string): self\n" - " {\n" - " $this->array = (array) unserialize((string) $string);\n" - " return $this;\n" - " }\n" - "\n" - " /**\n" - " * @return float|int\n" - " */\n" - " public function sum()\n" - " {\n" - " return array_sum($this->array);\n" - " }\n" - "\n" - " /**\n" - " * @return float|int\n" - " */\n" - " public function product()\n" - " {\n" - " return array_product($this->array);\n" - " }\n" - "\n" - " /**\n" - " * @param mixed $value\n" - " * @return int\n" - " */\n" - " public function push($value)\n" - " {\n" - " return array_push($this->array, $value);\n" - " }\n" - "\n" - " /**\n" - " * @param mixed $value\n" - " * @return int\n" - " */\n" - " public function pushBack($value)\n" - " {\n" - " return array_unshift($this->array, $value);\n" - " }\n" - "\n" - " /**\n" - " * @param mixed $value\n" - " * @return $this\n" - " */\n" - " public function insert(int $offset, $value): self\n" - " {\n" - " if (is_array($value) || is_object($value) || is_null($value)) {\n" - " $value = [$value];\n" - " }\n" - " array_splice($this->array, $offset, 0, $value);\n" - " return $this;\n" - " }\n" - "\n" - " /**\n" - " * @return mixed\n" - " */\n" - " public function pop()\n" - " {\n" - " return array_pop($this->array);\n" - " }\n" - "\n" - " /**\n" - " * @return mixed\n" - " */\n" - " public function popFront()\n" - " {\n" - " return array_shift($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" - " {\n" - " return new static(array_slice($this->array, ...func_get_args()));\n" - " }\n" - "\n" - " /**\n" - " * @return ArrayObject|mixed|StringObject\n" - " */\n" - " public function randomGet()\n" - " {\n" - " 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" - " return $this;\n" - " }\n" - "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function map(callable $fn): self\n" - " {\n" - " return new static(array_map($fn, $this->array));\n" - " }\n" - "\n" - " /**\n" - " * @return mixed\n" - " */\n" - " public function reduce(callable $fn)\n" - " {\n" - " return array_reduce($this->array, $fn);\n" - " }\n" - "\n" - " /**\n" - " * @param int $search_value\n" - " * @param bool $strict\n" - " * @return static\n" - " */\n" - " public function keys(int $search_value = null, $strict = false): self\n" - " {\n" - " return new static(array_keys($this->array, $search_value, $strict));\n" - " }\n" - "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function values(): self\n" - " {\n" - " return new static(array_values($this->array));\n" - " }\n" - "\n" - " /**\n" - " * @param mixed $column_key\n" - " * @param mixed ...$index\n" - " * @return static\n" - " */\n" - " public function column($column_key, ...$index): self\n" - " {\n" - " return new static(array_column($this->array, $column_key, ...$index));\n" - " }\n" - "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function unique(int $sort_flags = SORT_STRING): self\n" - " {\n" - " return new static(array_unique($this->array, $sort_flags));\n" - " }\n" - "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function reverse(bool $preserve_keys = false): self\n" - " {\n" - " return new static(array_reverse($this->array, $preserve_keys));\n" - " }\n" - "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function chunk(int $size, bool $preserve_keys = false): self\n" - " {\n" - " return new static(array_chunk($this->array, $size, $preserve_keys));\n" - " }\n" - "\n" - " /**\n" - " * Swap keys and values in an array.\n" - " * @return static\n" - " */\n" - " public function flip(): self\n" - " {\n" - " return new static(array_flip($this->array));\n" - " }\n" - "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function filter(callable $fn, int $flag = 0): self\n" - " {\n" - " return new static(array_filter($this->array, $fn, $flag));\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" - " * | asort() | value | yes | low to high | arsort() |\n" - " * | arsort() | value | yes | high to low | asort() |\n" - " * | krsort() | key | yes | high to low | ksort() |\n" - " * | ksort() | key | yes | low to high | asort() |\n" - " * | 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" - " * | 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" - " */\n" - "\n" - " /**\n" - " * @return $this\n" - " */\n" - " public function multiSort(int $sort_order = SORT_ASC, int $sort_flags = SORT_REGULAR): self\n" - " {\n" - " if (array_multisort($this->array, $sort_order, $sort_flags) !== true) {\n" - " throw new RuntimeException('array_multisort() failed');\n" - " }\n" - " return $this;\n" - " }\n" - "\n" - " /**\n" - " * @return $this\n" - " */\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" - " 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" - " 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" - " 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" - " return $this;\n" - " }\n" - "\n" - " /**\n" - " * @return $this\n" - " */\n" - " public function natcasesort(): self\n" - " {\n" - " if (natcasesort($this->array) !== true) {\n" - " throw new RuntimeException('natcasesort() failed');\n" - " }\n" - " return $this;\n" - " }\n" - "\n" - " /**\n" - " * @return $this\n" - " */\n" - " public function natsort(): self\n" - " {\n" - " if (natsort($this->array) !== true) {\n" - " throw new RuntimeException('natsort() failed');\n" - " }\n" - " return $this;\n" - " }\n" - "\n" - " /**\n" - " * @return $this\n" - " */\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" - " }\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" - " 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" - " 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" - " 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" - " 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" - " return $this;\n" - " }\n" - "\n" - " /**\n" - " * @param mixed $value\n" - " * @return ArrayObject|mixed|StringObject\n" - " */\n" - " protected static function detectType($value)\n" - " {\n" - " if (is_string($value)) {\n" - " return static::detectStringType($value);\n" - " }\n" - " if (is_array($value)) {\n" - " return static::detectArrayType($value);\n" - " }\n" - " return $value;\n" - " }\n" - "\n" - " protected static function detectStringType(string $value): StringObject\n" - " {\n" - " return new StringObject($value);\n" - " }\n" - "\n" - " /**\n" - " * @return static\n" - " */\n" - " protected static function detectArrayType(array $value): self\n" - " {\n" - " return new static($value);\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_object_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;\n" - "\n" - "use TypeError;\n" - "\n" - "class ObjectProxy\n" - "{\n" - " /** @var object */\n" - " protected $__object;\n" - "\n" - " public function __construct($object)\n" - " {\n" - " if (!is_object($object)) {\n" - " throw new TypeError('Non-object given');\n" - " }\n" - " $this->__object = $object;\n" - " }\n" - "\n" - " public function __getObject()\n" - " {\n" - " return $this->__object;\n" - " }\n" - "\n" - " public function __get(string $name)\n" - " {\n" - " return $this->__object->{$name};\n" - " }\n" - "\n" - " public function __set(string $name, $value): void\n" - " {\n" - " $this->__object->{$name} = $value;\n" - " }\n" - "\n" - " public function __isset($name)\n" - " {\n" - " return isset($this->__object->{$name});\n" - " }\n" - "\n" - " public function __unset(string $name): void\n" - " {\n" - " unset($this->__object->{$name});\n" - " }\n" - "\n" - " public function __call(string $name, array $arguments)\n" - " {\n" - " return $this->__object->{$name}(...$arguments);\n" - " }\n" - "\n" - " public function __invoke(...$arguments)\n" - " {\n" - " /** @var mixed $object */\n" - " $object = $this->__object;\n" - " return $object(...$arguments);\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_coroutine_wait_group = - "\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 BadMethodCallException;\n" - "use InvalidArgumentException;\n" - "\n" - "class WaitGroup\n" - "{\n" - " protected $chan;\n" - "\n" - " protected $count = 0;\n" - "\n" - " protected $waiting = false;\n" - "\n" - " public function __construct()\n" - " {\n" - " $this->chan = new Channel(1);\n" - " }\n" - "\n" - " public function add(int $delta = 1): void\n" - " {\n" - " if ($this->waiting) {\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" - " }\n" - " $this->count = $count;\n" - " }\n" - "\n" - " public function done(): void\n" - " {\n" - " $count = $this->count - 1;\n" - " if ($count < 0) {\n" - " throw new BadMethodCallException('WaitGroup misuse: negative counter');\n" - " }\n" - " $this->count = $count;\n" - " if ($count === 0 && $this->waiting) {\n" - " $this->chan->push(true);\n" - " }\n" - " }\n" - "\n" - " 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" - " }\n" - " if ($this->count > 0) {\n" - " $this->waiting = true;\n" - " $done = $this->chan->pop($timeout);\n" - " $this->waiting = false;\n" - " return $done;\n" - " }\n" - " return true;\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_coroutine_server = - "\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" - "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" - " public $host = '';\n" - "\n" - " /** @var int */\n" - " public $port = 0;\n" - "\n" - " /** @var int */\n" - " public $type = AF_INET;\n" - "\n" - " /** @var int */\n" - " public $fd = -1;\n" - "\n" - " /** @var int */\n" - " public $errCode = 0;\n" - "\n" - " /** @var array */\n" - " public $setting = [];\n" - "\n" - " /** @var bool */\n" - " protected $running = false;\n" - "\n" - " /** @var null|callable */\n" - " protected $fn;\n" - "\n" - " /** @var Socket */\n" - " protected $socket;\n" - "\n" - " /**\n" - " * Server constructor.\n" - " * @throws Exception\n" - " */\n" - " public function __construct(string $host, int $port = 0, bool $ssl = false, bool $reuse_port = false)\n" - " {\n" - " $_host = swoole_string($host);\n" - " if ($_host->contains('::')) {\n" - " $this->type = AF_INET6;\n" - " } elseif ($_host->startsWith('unix:/')) {\n" - " $host = $_host->substr(5)->__toString();\n" - " $this->type = AF_UNIX;\n" - " } else {\n" - " $this->type = AF_INET;\n" - " }\n" - " $this->host = $host;\n" - "\n" - " $socket = new Socket($this->type, SOCK_STREAM, 0);\n" - " if ($reuse_port and defined('SO_REUSEPORT')) {\n" - " $socket->setOption(SOL_SOCKET, SO_REUSEPORT, true);\n" - " }\n" - " if (!$socket->bind($this->host, $port)) {\n" - " throw new Exception(\"bind({$this->host}:{$port}) failed\", $socket->errCode);\n" - " }\n" - " 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->setting['open_ssl'] = $ssl;\n" - " }\n" - "\n" - " public function set(array $setting): void\n" - " {\n" - " $this->setting = array_merge($this->setting, $setting);\n" - " }\n" - "\n" - " public function handle(callable $fn): void\n" - " {\n" - " $this->fn = $fn;\n" - " }\n" - "\n" - " public function shutdown(): bool\n" - " {\n" - " $this->running = false;\n" - " return $this->socket->cancel();\n" - " }\n" - "\n" - " public function start(): bool\n" - " {\n" - " $this->running = true;\n" - " if ($this->fn === null) {\n" - " $this->errCode = SOCKET_EINVAL;\n" - " return false;\n" - " }\n" - " $socket = $this->socket;\n" - " if (!$socket->setProtocol($this->setting)) {\n" - " $this->errCode = SOCKET_EINVAL;\n" - " return false;\n" - " }\n" - "\n" - " while ($this->running) {\n" - " /** @var Socket $conn */\n" - " $conn = $socket->accept();\n" - " if ($conn) {\n" - " $conn->setProtocol($this->setting);\n" - " if (SWOOLE_COROUTINE_SOCKET_HAVE_SSL_HANDSHAKE && $this->setting['open_ssl'] ?? false) {\n" - " $fn = static function ($fn, $connection) {\n" - " /* @var $connection Connection */\n" - " if (!$connection->exportSocket()->sslHandshake()) {\n" - " return;\n" - " }\n" - " $fn($connection);\n" - " };\n" - " $arguments = [$this->fn, new Connection($conn)];\n" - " } else {\n" - " $fn = $this->fn;\n" - " $arguments = [new Connection($conn)];\n" - " }\n" - " if (Coroutine::create($fn, ...$arguments) < 0) {\n" - " goto _wait;\n" - " }\n" - " } else {\n" - " if ($socket->errCode == SOCKET_EMFILE or $socket->errCode == SOCKET_ENFILE) {\n" - " _wait:\n" - " Coroutine::sleep(1);\n" - " continue;\n" - " }\n" - " if ($socket->errCode == SOCKET_ETIMEDOUT) {\n" - " continue;\n" - " }\n" - " if ($socket->errCode == SOCKET_ECANCELED) {\n" - " break;\n" - " }\n" - " trigger_error(\"accept failed, Error: {$socket->errMsg}[{$socket->errCode}]\", E_USER_WARNING);\n" - " break;\n" - " }\n" - " }\n" - "\n" - " return true;\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_coroutine_server_connection = - "\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\\Server;\n" - "\n" - "use Swoole\\Coroutine\\Socket;\n" - "\n" - "class Connection\n" - "{\n" - " protected $socket;\n" - "\n" - " public function __construct(Socket $conn)\n" - " {\n" - " $this->socket = $conn;\n" - " }\n" - "\n" - " public function recv(float $timeout = 0)\n" - " {\n" - " return $this->socket->recvPacket($timeout);\n" - " }\n" - "\n" - " public function send(string $data)\n" - " {\n" - " return $this->socket->sendAll($data);\n" - " }\n" - "\n" - " public function close(): bool\n" - " {\n" - " return $this->socket->close();\n" - " }\n" - "\n" - " public function exportSocket(): Socket\n" - " {\n" - " return $this->socket;\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_connection_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;\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" - "\n" - " /** @var callable */\n" - " protected $constructor;\n" - "\n" - " /** @var int */\n" - " protected $size;\n" - "\n" - " /** @var int */\n" - " protected $num;\n" - "\n" - " /** @var null|string */\n" - " protected $proxy;\n" - "\n" - " public function __construct(callable $constructor, int $size = self::DEFAULT_SIZE, ?string $proxy = null)\n" - " {\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" - " {\n" - " while ($this->size > $this->num) {\n" - " $this->make();\n" - " }\n" - " }\n" - "\n" - " public function get()\n" - " {\n" - " if ($this->pool === null) {\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" - " }\n" - "\n" - " public function put($connection): void\n" - " {\n" - " if ($this->pool === null) {\n" - " return;\n" - " }\n" - " if ($connection !== null) {\n" - " $this->pool->push($connection);\n" - " } else {\n" - " /* connection broken */\n" - " $this->num -= 1;\n" - " $this->make();\n" - " }\n" - " }\n" - "\n" - " public function close(): void\n" - " {\n" - " $this->pool->close();\n" - " $this->pool = null;\n" - " $this->num = 0;\n" - " }\n" - "\n" - " protected function make(): void\n" - " {\n" - " $this->num++;\n" - " try {\n" - " if ($this->proxy) {\n" - " $connection = new $this->proxy($this->constructor);\n" - " } else {\n" - " $constructor = $this->constructor;\n" - " $connection = $constructor();\n" - " }\n" - " } catch (Throwable $throwable) {\n" - " $this->num--;\n" - " throw $throwable;\n" - " }\n" - " $this->put($connection);\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_database_object_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 Error;\n" - "\n" - "class ObjectProxy extends \\Swoole\\ObjectProxy\n" - "{\n" - " public function __clone()\n" - " {\n" - " throw new Error('Trying to clone an uncloneable database proxy object');\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_database_mysqli_config = - "\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 MysqliConfig\n" - "{\n" - " /** @var string */\n" - " protected $host = '127.0.0.1';\n" - "\n" - " /** @var int */\n" - " protected $port = 3306;\n" - "\n" - " /** @var null|string */\n" - " protected $unixSocket = '';\n" - "\n" - " /** @var string */\n" - " protected $dbname = 'test';\n" - "\n" - " /** @var string */\n" - " protected $charset = 'utf8mb4';\n" - "\n" - " /** @var string */\n" - " protected $username = 'root';\n" - "\n" - " /** @var string */\n" - " protected $password = 'root';\n" - "\n" - " /** @var array */\n" - " protected $options = [];\n" - "\n" - " public function getHost(): string\n" - " {\n" - " return $this->host;\n" - " }\n" - "\n" - " public function withHost($host): self\n" - " {\n" - " $this->host = $host;\n" - " return $this;\n" - " }\n" - "\n" - " public function getPort(): int\n" - " {\n" - " return $this->port;\n" - " }\n" - "\n" - " public function getUnixSocket(): string\n" - " {\n" - " return $this->unixSocket;\n" - " }\n" - "\n" - " public function withUnixSocket(?string $unixSocket): self\n" - " {\n" - " $this->unixSocket = $unixSocket;\n" - " return $this;\n" - " }\n" - "\n" - " public function withPort(int $port): self\n" - " {\n" - " $this->port = $port;\n" - " return $this;\n" - " }\n" - "\n" - " public function getDbname(): string\n" - " {\n" - " return $this->dbname;\n" - " }\n" - "\n" - " public function withDbname(string $dbname): self\n" - " {\n" - " $this->dbname = $dbname;\n" - " return $this;\n" - " }\n" - "\n" - " public function getCharset(): string\n" - " {\n" - " return $this->charset;\n" - " }\n" - "\n" - " public function withCharset(string $charset): self\n" - " {\n" - " $this->charset = $charset;\n" - " return $this;\n" - " }\n" - "\n" - " public function getUsername(): string\n" - " {\n" - " return $this->username;\n" - " }\n" - "\n" - " public function withUsername(string $username): self\n" - " {\n" - " $this->username = $username;\n" - " return $this;\n" - " }\n" - "\n" - " public function getPassword(): string\n" - " {\n" - " return $this->password;\n" - " }\n" - "\n" - " public function withPassword(string $password): self\n" - " {\n" - " $this->password = $password;\n" - " return $this;\n" - " }\n" - "\n" - " public function getOptions(): array\n" - " {\n" - " return $this->options;\n" - " }\n" - "\n" - " public function withOptions(array $options): self\n" - " {\n" - " $this->options = $options;\n" - " return $this;\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_database_mysqli_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" - "namespace Swoole\\Database;\n" - "\n" - "use Exception;\n" - "\n" - "class MysqliException extends Exception\n" - "{\n" - "}\n"; - -static const char* swoole_library_source_core_database_mysqli_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\\Database;\n" - "\n" - "use mysqli;\n" - "use Swoole\\ConnectionPool;\n" - "\n" - "/**\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" - " {\n" - " $this->config = $config;\n" - " parent::__construct(function () {\n" - " $mysqli = new mysqli(\n" - " $this->config->getHost(),\n" - " $this->config->getUsername(),\n" - " $this->config->getPassword(),\n" - " $this->config->getDbname(),\n" - " $this->config->getPort(),\n" - " $this->config->getUnixSocket()\n" - " );\n" - " if ($mysqli->connect_errno) {\n" - " throw new MysqliException($mysqli->connect_errno, $mysqli->connect_errno);\n" - " }\n" - " foreach ($this->config->getOptions() as $option => $value) {\n" - " $mysqli->set_opt($option, $value);\n" - " }\n" - " return $mysqli;\n" - " }, $size, MysqliProxy::class);\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_database_mysqli_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 mysqli;\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" - "\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 mysqli */\n" - " protected $__object;\n" - "\n" - " /** @var string */\n" - " protected $charsetContext;\n" - "\n" - " /** @var null|array */\n" - " protected $setOptContext;\n" - "\n" - " /** @var null|array */\n" - " protected $changeUserContext;\n" - "\n" - " /** @var callable */\n" - " protected $constructor;\n" - "\n" - " /** @var int */\n" - " protected $round = 0;\n" - "\n" - " public function __construct(callable $constructor)\n" - " {\n" - " parent::__construct($constructor());\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" - " /* no more chances or non-IO failures */\n" - " if (\n" - " !in_array($this->__object->errno, static::IO_ERRORS, true) ||\n" - " $n === 0\n" - " ) {\n" - " throw new MysqliException($this->__object->error, $this->__object->errno);\n" - " }\n" - " $this->reconnect();\n" - " continue;\n" - " }\n" - " if (strcasecmp($name, 'prepare') === 0) {\n" - " $ret = new MysqliStatementProxy($ret, $arguments[0], $this);\n" - " } elseif (strcasecmp($name, 'stmt_init') === 0) {\n" - " $ret = new MysqliStatementProxy($ret, null, $this);\n" - " }\n" - " break;\n" - " }\n" - " /* @noinspection PhpUndefinedVariableInspection */\n" - " return $ret;\n" - " }\n" - "\n" - " public function getRound(): int\n" - " {\n" - " return $this->round;\n" - " }\n" - "\n" - " public function reconnect(): void\n" - " {\n" - " $constructor = $this->constructor;\n" - " parent::__construct($constructor());\n" - " $this->round++;\n" - " /* restore context */\n" - " if ($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" - " }\n" - " if ($this->changeUserContext) {\n" - " $this->__object->change_user(...$this->changeUserContext);\n" - " }\n" - " }\n" - "\n" - " public function options(int $option, $value): bool\n" - " {\n" - " $this->setOptContext[$option] = $value;\n" - " return $this->__object->options($option, $value);\n" - " }\n" - "\n" - " public function set_opt(int $option, $value): bool\n" - " {\n" - " return $this->options($option, $value);\n" - " }\n" - "\n" - " public function set_charset(string $charset): bool\n" - " {\n" - " $this->charsetContext = $charset;\n" - " return $this->__object->set_charset($charset);\n" - " }\n" - "\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" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_database_mysqli_statement_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 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" - " protected $__object;\n" - "\n" - " /** @var null|string */\n" - " protected $queryString;\n" - "\n" - " /** @var null|array */\n" - " protected $attrSetContext;\n" - "\n" - " /** @var null|array */\n" - " protected $bindParamContext;\n" - "\n" - " /** @var null|array */\n" - " protected $bindResultContext;\n" - "\n" - " /** @var Mysqli|MysqliProxy */\n" - " protected $parent;\n" - "\n" - " /** @var int */\n" - " protected $parentRound;\n" - "\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->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" - " /* non-IO method */\n" - " if (!preg_match(static::IO_METHOD_REGEX, $name)) {\n" - " 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" - " 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" - " $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" - " $this->__object->bind_param($this->bindParamContext[0], ...$this->bindParamContext[1]);\n" - " }\n" - " if ($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" - " }\n" - " continue;\n" - " }\n" - " if (strcasecmp($name, 'prepare') === 0) {\n" - " $this->queryString = $arguments[0];\n" - " }\n" - " break;\n" - " }\n" - " /* @noinspection PhpUndefinedVariableInspection */\n" - " return $ret;\n" - " }\n" - "\n" - " public function attr_set($attr, $mode): bool\n" - " {\n" - " $this->attrSetContext[$attr] = $mode;\n" - " return $this->__object->attr_set($attr, $mode);\n" - " }\n" - "\n" - " public function bind_param($types, &...$arguments): bool\n" - " {\n" - " $this->bindParamContext = [$types, $arguments];\n" - " return $this->__object->bind_param($types, ...$arguments);\n" - " }\n" - "\n" - " public function bind_result(&...$arguments): bool\n" - " {\n" - " $this->bindResultContext = $arguments;\n" - " return $this->__object->bind_result(...$arguments);\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_database_pdo_config = - "\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 PDOConfig\n" - "{\n" - " public const DRIVER_MYSQL = 'mysql';\n" - "\n" - " public const DRIVER_PGSQL = 'pgsql';\n" - "\n" - " /** @var string */\n" - " protected $driver = self::DRIVER_MYSQL;\n" - "\n" - " /** @var string */\n" - " protected $host = '127.0.0.1';\n" - "\n" - " /** @var int */\n" - " protected $port = 3306;\n" - "\n" - " /** @var null|string */\n" - " protected $unixSocket;\n" - "\n" - " /** @var string */\n" - " protected $dbname = 'test';\n" - "\n" - " /** @var string */\n" - " protected $charset = 'utf8mb4';\n" - "\n" - " /** @var string */\n" - " protected $username = 'root';\n" - "\n" - " /** @var string */\n" - " protected $password = 'root';\n" - "\n" - " /** @var array */\n" - " protected $options = [];\n" - "\n" - " public function getDriver(): string\n" - " {\n" - " return $this->driver;\n" - " }\n" - "\n" - " public function withDriver(string $driver): self\n" - " {\n" - " $this->driver = $driver;\n" - " return $this;\n" - " }\n" - "\n" - " public function getHost(): string\n" - " {\n" - " return $this->host;\n" - " }\n" - "\n" - " public function withHost($host): self\n" - " {\n" - " $this->host = $host;\n" - " return $this;\n" - " }\n" - "\n" - " public function getPort(): int\n" - " {\n" - " return $this->port;\n" - " }\n" - "\n" - " public function hasUnixSocket(): bool\n" - " {\n" - " return isset($this->unixSocket);\n" - " }\n" - "\n" - " public function getUnixSocket(): string\n" - " {\n" - " return $this->unixSocket;\n" - " }\n" - "\n" - " public function withUnixSocket(?string $unixSocket): self\n" - " {\n" - " $this->unixSocket = $unixSocket;\n" - " return $this;\n" - " }\n" - "\n" - " public function withPort(int $port): self\n" - " {\n" - " $this->port = $port;\n" - " return $this;\n" - " }\n" - "\n" - " public function getDbname(): string\n" - " {\n" - " return $this->dbname;\n" - " }\n" - "\n" - " public function withDbname(string $dbname): self\n" - " {\n" - " $this->dbname = $dbname;\n" - " return $this;\n" - " }\n" - "\n" - " public function getCharset(): string\n" - " {\n" - " return $this->charset;\n" - " }\n" - "\n" - " public function withCharset(string $charset): self\n" - " {\n" - " $this->charset = $charset;\n" - " return $this;\n" - " }\n" - "\n" - " public function getUsername(): string\n" - " {\n" - " return $this->username;\n" - " }\n" - "\n" - " public function withUsername(string $username): self\n" - " {\n" - " $this->username = $username;\n" - " return $this;\n" - " }\n" - "\n" - " public function getPassword(): string\n" - " {\n" - " return $this->password;\n" - " }\n" - "\n" - " public function withPassword(string $password): self\n" - " {\n" - " $this->password = $password;\n" - " return $this;\n" - " }\n" - "\n" - " public function getOptions(): array\n" - " {\n" - " return $this->options;\n" - " }\n" - "\n" - " public function withOptions(array $options): self\n" - " {\n" - " $this->options = $options;\n" - " return $this;\n" - " }\n" - "\n" - " /**\n" - " * Returns the list of available drivers\n" - " *\n" - " * @return string[]\n" - " */\n" - " public static function getAvailableDrivers()\n" - " {\n" - " return [\n" - " self::DRIVER_MYSQL,\n" - " self::DRIVER_PGSQL,\n" - " ];\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_database_pdo_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\\Database;\n" - "\n" - "use PDO;\n" - "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" - " {\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" - " }, $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" - "declare(strict_types=1);\n" - "\n" - "namespace Swoole\\Database;\n" - "\n" - "use PDO;\n" - "use PDOException;\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" - " protected $__object;\n" - "\n" - " /** @var null|array */\n" - " protected $setAttributeContext;\n" - "\n" - " /** @var callable */\n" - " protected $constructor;\n" - "\n" - " /** @var int */\n" - " protected $round = 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->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" - " $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" - " }\n" - " break;\n" - " }\n" - " /* @noinspection PhpUndefinedVariableInspection */\n" - " return $ret;\n" - " }\n" - "\n" - " public function getRound(): int\n" - " {\n" - " return $this->round;\n" - " }\n" - "\n" - " public function reconnect(): void\n" - " {\n" - " $constructor = $this->constructor;\n" - " parent::__construct($constructor());\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" - " }\n" - " }\n" - "\n" - " public function setAttribute(int $attribute, $value): bool\n" - " {\n" - " $this->setAttributeContext[$attribute] = $value;\n" - " return $this->__object->setAttribute($attribute, $value);\n" - " }\n" - "\n" - " public function inTransaction(): bool\n" - " {\n" - " return $this->__object->inTransaction();\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_database_pdo_statement_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" - "use PDOStatement;\n" - "\n" - "class PDOStatementProxy extends ObjectProxy\n" - "{\n" - " /** @var PDOStatement */\n" - " protected $__object;\n" - "\n" - " /** @var null|array */\n" - " protected $setAttributeContext;\n" - "\n" - " /** @var null|array */\n" - " protected $setFetchModeContext;\n" - "\n" - " /** @var null|array */\n" - " protected $bindParamContext;\n" - "\n" - " /** @var null|array */\n" - " protected $bindColumnContext;\n" - "\n" - " /** @var null|array */\n" - " protected $bindValueContext;\n" - "\n" - " /** @var PDO|PDOProxy */\n" - " protected $parent;\n" - "\n" - " /** @var int */\n" - " protected $parentRound;\n" - "\n" - " public function __construct(PDOStatement $object, PDOProxy $parent)\n" - " {\n" - " parent::__construct($object);\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" - " if ($this->parent->getRound() === $this->parentRound) {\n" - " /* if not equal, parent has reconnected */\n" - " $this->parent->reconnect();\n" - " }\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" - " if ($this->setFetchModeContext) {\n" - " $this->__object->setFetchMode(...$this->setFetchModeContext);\n" - " }\n" - " if ($this->bindParamContext) {\n" - " foreach ($this->bindParamContext as $param => $arguments) {\n" - " $this->__object->bindParam($param, ...$arguments);\n" - " }\n" - " }\n" - " if ($this->bindColumnContext) {\n" - " foreach ($this->bindColumnContext as $column => $arguments) {\n" - " $this->__object->bindColumn($column, ...$arguments);\n" - " }\n" - " }\n" - " if ($this->bindValueContext) {\n" - " foreach ($this->bindValueContext as $value => $arguments) {\n" - " $this->__object->bindParam($value, ...$arguments);\n" - " }\n" - " }\n" - " continue;\n" - " }\n" - " break;\n" - " }\n" - " /* @noinspection PhpUndefinedVariableInspection */\n" - " return $ret;\n" - " }\n" - "\n" - " public function setAttribute(int $attribute, $value): bool\n" - " {\n" - " $this->setAttributeContext[$attribute] = $value;\n" - " return $this->__object->setAttribute($attribute, $value);\n" - " }\n" - "\n" - " public function setFetchMode(int $mode, $classNameObject = null, array $ctorarfg = []): 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" - " }\n" - "\n" - " public function bindParam($parameter, &$variable, $data_type = PDO::PARAM_STR, $length = null, $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" - " }\n" - "\n" - " public function bindColumn($column, &$param, $type = null, $maxlen = null, $driverdata = null): bool\n" - " {\n" - " $this->bindColumnContext[$column] = [$param, $type, $maxlen, $driverdata];\n" - " return $this->__object->bindColumn($column, $param, $type, $maxlen, $driverdata);\n" - " }\n" - "\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" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_database_redis_config = - "\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 RedisConfig\n" - "{\n" - " /** @var string */\n" - " protected $host = '127.0.0.1';\n" - "\n" - " /** @var int */\n" - " protected $port = 6379;\n" - "\n" - " /** @var float */\n" - " protected $timeout = 0.0;\n" - "\n" - " /** @var string */\n" - " protected $reserved = '';\n" - "\n" - " /** @var int */\n" - " protected $retry_interval = 0;\n" - "\n" - " /** @var float */\n" - " protected $read_timeout = 0.0;\n" - "\n" - " /** @var string */\n" - " protected $auth = '';\n" - "\n" - " /** @var int */\n" - " protected $dbIndex = 0;\n" - "\n" - " public function getHost()\n" - " {\n" - " return $this->host;\n" - " }\n" - "\n" - " public function withHost($host): self\n" - " {\n" - " $this->host = $host;\n" - " return $this;\n" - " }\n" - "\n" - " public function getPort(): int\n" - " {\n" - " return $this->port;\n" - " }\n" - "\n" - " public function withPort(int $port): self\n" - " {\n" - " $this->port = $port;\n" - " return $this;\n" - " }\n" - "\n" - " public function getTimeout(): float\n" - " {\n" - " return $this->timeout;\n" - " }\n" - "\n" - " public function withTimeout(float $timeout): self\n" - " {\n" - " $this->timeout = $timeout;\n" - " return $this;\n" - " }\n" - "\n" - " public function getReserved(): string\n" - " {\n" - " return $this->reserved;\n" - " }\n" - "\n" - " public function withReserved(string $reserved): self\n" - " {\n" - " $this->reserved = $reserved;\n" - " return $this;\n" - " }\n" - "\n" - " public function getRetryInterval(): int\n" - " {\n" - " return $this->retry_interval;\n" - " }\n" - "\n" - " public function withRetryInterval(int $retry_interval): self\n" - " {\n" - " $this->retry_interval = $retry_interval;\n" - " return $this;\n" - " }\n" - "\n" - " public function getReadTimeout(): float\n" - " {\n" - " return $this->read_timeout;\n" - " }\n" - "\n" - " public function withReadTimeout(float $read_timeout): self\n" - " {\n" - " $this->read_timeout = $read_timeout;\n" - " return $this;\n" - " }\n" - "\n" - " public function getAuth(): string\n" - " {\n" - " return $this->auth;\n" - " }\n" - "\n" - " public function withAuth(string $auth): self\n" - " {\n" - " $this->auth = $auth;\n" - " return $this;\n" - " }\n" - "\n" - " public function getDbIndex(): int\n" - " {\n" - " return $this->dbIndex;\n" - " }\n" - "\n" - " public function withDbIndex(int $dbIndex): self\n" - " {\n" - " $this->dbIndex = $dbIndex;\n" - " return $this;\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_database_redis_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\\Database;\n" - "\n" - "use Redis;\n" - "use Swoole\\ConnectionPool;\n" - "\n" - "/**\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" - " {\n" - " $this->config = $config;\n" - " parent::__construct(function () {\n" - " $redis = new Redis();\n" - " /* Compatible with different versions of Redis extension as much as possible */\n" - " $arguments = [\n" - " $this->config->getHost(),\n" - " $this->config->getPort(),\n" - " $this->config->getTimeout(),\n" - " $this->config->getReserved(),\n" - " $this->config->getRetryInterval(),\n" - " $this->config->getReadTimeout(),\n" - " ];\n" - " $arguments = array_filter($arguments);\n" - " $redis->connect(...$arguments);\n" - " if ($this->config->getAuth()) {\n" - " $redis->auth($this->config->getAuth());\n" - " }\n" - " if ($this->config->getDbIndex() !== 0) {\n" - " $redis->select($this->config->getDbIndex());\n" - " }\n" - " return $redis;\n" - " }, $size);\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_http_status = - "\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\\Http;\n" - "\n" - "abstract class Status\n" - "{\n" - " public const CONTINUE = 100;\n" - "\n" - " public const SWITCHING_PROTOCOLS = 101;\n" - "\n" - " public const PROCESSING = 102;\n" - "\n" - " public const OK = 200;\n" - "\n" - " public const CREATED = 201;\n" - "\n" - " public const ACCEPTED = 202;\n" - "\n" - " public const NON_AUTHORITATIVE_INFORMATION = 203;\n" - "\n" - " public const NO_CONTENT = 204;\n" - "\n" - " public const RESET_CONTENT = 205;\n" - "\n" - " public const PARTIAL_CONTENT = 206;\n" - "\n" - " public const MULTI_STATUS = 207;\n" - "\n" - " public const ALREADY_REPORTED = 208;\n" - "\n" - " public const IM_USED = 226;\n" - "\n" - " public const MULTIPLE_CHOICES = 300;\n" - "\n" - " public const MOVED_PERMANENTLY = 301;\n" - "\n" - " public const FOUND = 302;\n" - "\n" - " public const SEE_OTHER = 303;\n" - "\n" - " public const NOT_MODIFIED = 304;\n" - "\n" - " public const USE_PROXY = 305;\n" - "\n" - " public const SWITCH_PROXY = 306;\n" - "\n" - " public const TEMPORARY_REDIRECT = 307;\n" - "\n" - " public const PERMANENT_REDIRECT = 308;\n" - "\n" - " public const BAD_REQUEST = 400;\n" - "\n" - " public const UNAUTHORIZED = 401;\n" - "\n" - " public const PAYMENT_REQUIRED = 402;\n" - "\n" - " public const FORBIDDEN = 403;\n" - "\n" - " public const NOT_FOUND = 404;\n" - "\n" - " public const METHOD_NOT_ALLOWED = 405;\n" - "\n" - " public const NOT_ACCEPTABLE = 406;\n" - "\n" - " public const PROXY_AUTHENTICATION_REQUIRED = 407;\n" - "\n" - " public const REQUEST_TIME_OUT = 408;\n" - "\n" - " public const CONFLICT = 409;\n" - "\n" - " public const GONE = 410;\n" - "\n" - " public const LENGTH_REQUIRED = 411;\n" - "\n" - " public const PRECONDITION_FAILED = 412;\n" - "\n" - " public const REQUEST_ENTITY_TOO_LARGE = 413;\n" - "\n" - " public const REQUEST_URI_TOO_LARGE = 414;\n" - "\n" - " public const UNSUPPORTED_MEDIA_TYPE = 415;\n" - "\n" - " public const REQUESTED_RANGE_NOT_SATISFIABLE = 416;\n" - "\n" - " public const EXPECTATION_FAILED = 417;\n" - "\n" - " public const MISDIRECTED_REQUEST = 421;\n" - "\n" - " public const UNPROCESSABLE_ENTITY = 422;\n" - "\n" - " public const LOCKED = 423;\n" - "\n" - " public const FAILED_DEPENDENCY = 424;\n" - "\n" - " public const UNORDERED_COLLECTION = 425;\n" - "\n" - " public const UPGRADE_REQUIRED = 426;\n" - "\n" - " public const PRECONDITION_REQUIRED = 428;\n" - "\n" - " public const TOO_MANY_REQUESTS = 429;\n" - "\n" - " public const REQUEST_HEADER_FIELDS_TOO_LARGE = 431;\n" - "\n" - " public const UNAVAILABLE_FOR_LEGAL_REASONS = 451;\n" - "\n" - " public const INTERNAL_SERVER_ERROR = 500;\n" - "\n" - " public const NOT_IMPLEMENTED = 501;\n" - "\n" - " public const BAD_GATEWAY = 502;\n" - "\n" - " public const SERVICE_UNAVAILABLE = 503;\n" - "\n" - " public const GATEWAY_TIME_OUT = 504;\n" - "\n" - " public const HTTP_VERSION_NOT_SUPPORTED = 505;\n" - "\n" - " public const VARIANT_ALSO_NEGOTIATES = 506;\n" - "\n" - " public const INSUFFICIENT_STORAGE = 507;\n" - "\n" - " public const LOOP_DETECTED = 508;\n" - "\n" - " public const NOT_EXTENDED = 510;\n" - "\n" - " 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::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::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::NETWORK_AUTHENTICATION_REQUIRED => 'Network Authentication Required',\n" - " ];\n" - "\n" - " public static function getReasonPhrases(): array\n" - " {\n" - " return static::$reasonPhrases;\n" - " }\n" - "\n" - " public static function getReasonPhrase(int $value): string\n" - " {\n" - " return static::$reasonPhrases[$value] ?? 'Unknown';\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_curl_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" - "namespace Swoole\\Curl;\n" - "\n" - "use Swoole;\n" - "\n" - "class Exception extends Swoole\\Exception\n" - "{\n" - "}\n"; - -static const char* swoole_library_source_core_curl_handler = - "\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, PhpDuplicateSwitchCaseBodyInspection, PhpInconsistentReturnPointsInspection */\n" - "\n" - "declare(strict_types=1);\n" - "\n" - "namespace Swoole\\Curl;\n" - "\n" - "use CURLFile;\n" - "use ReflectionClass;\n" - "use Swoole;\n" - "use Swoole\\Coroutine\\Http\\Client;\n" - "use Swoole\\Curl\\Exception as CurlException;\n" - "use Swoole\\Http\\Status;\n" - "\n" - "final class Handler\n" - "{\n" - " /**\n" - " * @var Client\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" - " '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" - " ];\n" - "\n" - " private $withHeaderOut = false;\n" - "\n" - " private $withFileTime = false;\n" - "\n" - " private $urlInfo;\n" - "\n" - " private $postData;\n" - "\n" - " private $infile;\n" - "\n" - " private $infileSize = PHP_INT_MAX;\n" - "\n" - " private $outputStream;\n" - "\n" - " private $proxyType;\n" - "\n" - " private $proxy;\n" - "\n" - " private $proxyPort = 1080;\n" - "\n" - " private $proxyUsername;\n" - "\n" - " private $proxyPassword;\n" - "\n" - " private $clientOptions = [];\n" - "\n" - " private $followLocation = false;\n" - "\n" - " private $autoReferer = false;\n" - "\n" - " private $maxRedirects;\n" - "\n" - " private $withHeader = false;\n" - "\n" - " private $nobody = false;\n" - "\n" - " /** @var callable */\n" - " private $headerFunction;\n" - "\n" - " /** @var callable */\n" - " private $readFunction;\n" - "\n" - " /** @var callable */\n" - " private $writeFunction;\n" - "\n" - " /** @var callable */\n" - " private $progressFunction;\n" - "\n" - " private $returnTransfer = false;\n" - "\n" - " private $method = '';\n" - "\n" - " private $headers = [];\n" - "\n" - " private $transfer;\n" - "\n" - " private $errCode = 0;\n" - "\n" - " private $errMsg = '';\n" - "\n" - " private $failOnError = false;\n" - "\n" - " private $closed = false;\n" - "\n" - " public function __construct(string $url = '')\n" - " {\n" - " if ($url) {\n" - " $this->setUrl($url);\n" - " }\n" - " }\n" - "\n" - " /* ====== Public APIs ====== */\n" - "\n" - " public function isAvailable(): bool\n" - " {\n" - " if ($this->closed) {\n" - " trigger_error('supplied resource is not a valid cURL handle resource', E_USER_WARNING);\n" - " return false;\n" - " }\n" - " return true;\n" - " }\n" - "\n" - " public function setOpt(int $opt, $value): bool\n" - " {\n" - " return $this->isAvailable() and $this->setOption($opt, $value);\n" - " }\n" - "\n" - " public function exec()\n" - " {\n" - " if (!$this->isAvailable()) {\n" - " return false;\n" - " }\n" - " return $this->execute();\n" - " }\n" - "\n" - " public function getInfo()\n" - " {\n" - " return $this->isAvailable() ? $this->info : false;\n" - " }\n" - "\n" - " public function errno()\n" - " {\n" - " return $this->isAvailable() ? $this->errCode : false;\n" - " }\n" - "\n" - " public function error()\n" - " {\n" - " return $this->isAvailable() ? $this->errMsg : false;\n" - " }\n" - "\n" - " public function reset()\n" - " {\n" - " if (!$this->isAvailable()) {\n" - " return false;\n" - " }\n" - " foreach ((new ReflectionClass(static::class))->getDefaultProperties() as $name => $value) {\n" - " $this->{$name} = $value;\n" - " }\n" - " }\n" - "\n" - " public function getContent()\n" - " {\n" - " if (!$this->isAvailable()) {\n" - " return false;\n" - " }\n" - " return $this->transfer;\n" - " }\n" - "\n" - " public function close()\n" - " {\n" - " if (!$this->isAvailable()) {\n" - " return false;\n" - " }\n" - " foreach ($this as &$property) {\n" - " $property = null;\n" - " }\n" - " $this->closed = true;\n" - " }\n" - "\n" - " private function create(?array $urlInfo = null): void\n" - " {\n" - " if ($urlInfo === null) {\n" - " $urlInfo = $this->urlInfo;\n" - " }\n" - " $this->client = new Client($urlInfo['host'], $urlInfo['port'], $urlInfo['scheme'] === 'https');\n" - " }\n" - "\n" - " private function getUrl(): string\n" - " {\n" - " if (empty($this->urlInfo['path'])) {\n" - " $url = '/';\n" - " } else {\n" - " $url = $this->urlInfo['path'];\n" - " }\n" - " if (!empty($this->urlInfo['query'])) {\n" - " $url .= '?' . $this->urlInfo['query'];\n" - " }\n" - " if (!empty($this->urlInfo['fragment'])) {\n" - " $url .= '#' . $this->urlInfo['fragment'];\n" - " }\n" - " return $url;\n" - " }\n" - "\n" - " private function setUrl(string $url, bool $setInfo = true): bool\n" - " {\n" - " if (strlen($url) === 0) {\n" - " $this->setError(CURLE_URL_MALFORMAT, 'No URL set!');\n" - " return false;\n" - " }\n" - " if (strpos($url, '://') === false) {\n" - " $url = 'http://' . $url;\n" - " }\n" - " if ($setInfo) {\n" - " $urlInfo = parse_url($url);\n" - " if (!is_array($urlInfo)) {\n" - " $this->setError(CURLE_URL_MALFORMAT, \"URL[{$url}] using bad/illegal format\");\n" - " return false;\n" - " }\n" - " if (!$this->setUrlInfo($urlInfo)) {\n" - " return false;\n" - " }\n" - " }\n" - " $this->info['url'] = $url;\n" - " return true;\n" - " }\n" - "\n" - " private function setUrlInfo(array $urlInfo): bool\n" - " {\n" - " if (empty($urlInfo['scheme'])) {\n" - " $urlInfo['scheme'] = 'http';\n" - " }\n" - " $scheme = $urlInfo['scheme'];\n" - " if ($scheme !== 'http' and $scheme !== 'https') {\n" - " $this->setError(CURLE_UNSUPPORTED_PROTOCOL, \"Protocol \\\"{$scheme}\\\" not supported or disabled in libcurl\");\n" - " return false;\n" - " }\n" - " $host = $urlInfo['host'];\n" - " if ($this->info['primary_port'] !== 0) {\n" - " /* keep same with cURL, primary_port has the highest priority */\n" - " $urlInfo['port'] = $this->info['primary_port'];\n" - " } elseif (empty($urlInfo['port'])) {\n" - " $urlInfo['port'] = $scheme === 'https' ? 443 : 80;\n" - " } else {\n" - " $urlInfo['port'] = intval($urlInfo['port']);\n" - " }\n" - " $port = $urlInfo['port'];\n" - " if ($this->client) {\n" - " $oldUrlInfo = $this->urlInfo;\n" - " if (\n" - " $host !== $oldUrlInfo['host'] or\n" - " $port !== $oldUrlInfo['port'] or\n" - " $scheme !== $oldUrlInfo['scheme']\n" - " ) {\n" - " /* target changed */\n" - " $this->create($urlInfo);\n" - " }\n" - " }\n" - " $this->urlInfo = $urlInfo;\n" - " return true;\n" - " }\n" - "\n" - " private function setPort(int $port): void\n" - " {\n" - " $this->info['primary_port'] = $port;\n" - " if ($this->urlInfo['port'] !== $port) {\n" - " $this->urlInfo['port'] = $port;\n" - " if ($this->client) {\n" - " /* target changed */\n" - " $this->create();\n" - " }\n" - " }\n" - " }\n" - "\n" - " private function setError($code, $msg = ''): void\n" - " {\n" - " $this->errCode = $code;\n" - " $this->errMsg = $msg ? $msg : curl_strerror($code);\n" - " }\n" - "\n" - " /**\n" - " * @param mixed $value\n" - " * @throws Swoole\\Curl\\Exception\n" - " */\n" - " private function setOption(int $opt, $value): bool\n" - " {\n" - " switch ($opt) {\n" - " // case CURLOPT_STDERR:\n" - " // case CURLOPT_WRITEHEADER:\n" - " 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" - " return false;\n" - " }\n" - " break;\n" - " }\n" - "\n" - " switch ($opt) {\n" - " /*\n" - " * Basic\n" - " */\n" - " case CURLOPT_URL:\n" - " return $this->setUrl((string) $value);\n" - " case CURLOPT_PORT:\n" - " $this->setPort((int) $value);\n" - " break;\n" - " case CURLOPT_RETURNTRANSFER:\n" - " $this->returnTransfer = $value;\n" - " $this->transfer = '';\n" - " break;\n" - " case CURLOPT_ENCODING:\n" - " if (empty($value)) {\n" - " if (defined('SWOOLE_HAVE_ZLIB')) {\n" - " $value = 'gzip, deflate';\n" - " }\n" - " if (defined('SWOOLE_HAVE_BROTLI')) {\n" - " if (!empty($value)) {\n" - " $value = 'br, ' . $value;\n" - " } else {\n" - " $value = 'br';\n" - " }\n" - " }\n" - " if (empty($value)) {\n" - " break;\n" - " }\n" - " }\n" - " $this->headers['Accept-Encoding'] = $value;\n" - " 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" - " }\n" - " $this->proxyType = $value;\n" - " break;\n" - " case CURLOPT_PROXY:\n" - " $this->proxy = $value;\n" - " break;\n" - " case CURLOPT_PROXYPORT:\n" - " $this->proxyPort = $value;\n" - " break;\n" - " case CURLOPT_PROXYUSERNAME:\n" - " $this->proxyUsername = $value;\n" - " break;\n" - " case CURLOPT_PROXYPASSWORD:\n" - " $this->proxyPassword = $value;\n" - " break;\n" - " case CURLOPT_PROXYUSERPWD:\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_NOBODY:\n" - " $this->nobody = boolval($value);\n" - " $this->method = 'HEAD';\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" - " }\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" - " 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" - " case CURLOPT_WRITEHEADER:\n" - " case CURLOPT_BUFFERSIZE:\n" - " case CURLOPT_SSLCERTTYPE:\n" - " case CURLOPT_SSLKEYTYPE:\n" - " break;\n" - " /*\n" - " * SSL\n" - " */\n" - " case CURLOPT_SSL_VERIFYHOST:\n" - " break;\n" - " case CURLOPT_SSL_VERIFYPEER:\n" - " $this->clientOptions['ssl_verify_peer'] = $value;\n" - " break;\n" - " case CURLOPT_SSLCERT:\n" - " $this->clientOptions['ssl_cert_file'] = $value;\n" - " break;\n" - " case CURLOPT_SSLKEY:\n" - " $this->clientOptions['ssl_key_file'] = $value;\n" - " break;\n" - " /*\n" - " * Http POST\n" - " */\n" - " case CURLOPT_POST:\n" - " $this->method = 'POST';\n" - " break;\n" - " case CURLOPT_POSTFIELDS:\n" - " $this->postData = $value;\n" - " if (!$this->method) {\n" - " $this->method = 'POST';\n" - " }\n" - " break;\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" - " 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" - " $headerValue = trim($header[1] ?? '');\n" - " if (strlen($headerValue) === 0) {\n" - " continue;\n" - " }\n" - " $this->headers[$headerName] = $headerValue;\n" - " }\n" - " break;\n" - " case CURLOPT_REFERER:\n" - " $this->headers['Referer'] = $value;\n" - " break;\n" - " case CURLINFO_HEADER_OUT:\n" - " $this->withHeaderOut = boolval($value);\n" - " break;\n" - " case CURLOPT_FILETIME:\n" - " $this->withFileTime = boolval($value);\n" - " break;\n" - " case CURLOPT_USERAGENT:\n" - " $this->headers['User-Agent'] = $value;\n" - " break;\n" - " case CURLOPT_CUSTOMREQUEST:\n" - " $this->method = (string) $value;\n" - " break;\n" - " case CURLOPT_PROTOCOLS:\n" - " if ($value > 3) {\n" - " throw new CurlException(\"swoole_curl_setopt(): CURLOPT_PROTOCOLS[{$value}] is not supported\");\n" - " }\n" - " break;\n" - " case CURLOPT_HTTP_VERSION:\n" - " if ($value != CURL_HTTP_VERSION_1_1) {\n" - " trigger_error(\"swoole_curl_setopt(): CURLOPT_HTTP_VERSION[{$value}] is not supported\", E_USER_WARNING);\n" - " return false;\n" - " }\n" - " break;\n" - " case CURLOPT_FAILONERROR:\n" - " $this->failOnError = $value;\n" - " break;\n" - " /*\n" - " * Http Cookie\n" - " */\n" - " case CURLOPT_COOKIE:\n" - " $this->headers['Cookie'] = $value;\n" - " break;\n" - " case CURLOPT_CONNECTTIMEOUT:\n" - " $this->clientOptions['connect_timeout'] = $value;\n" - " break;\n" - " case CURLOPT_CONNECTTIMEOUT_MS:\n" - " $this->clientOptions['connect_timeout'] = $value / 1000;\n" - " break;\n" - " case CURLOPT_TIMEOUT:\n" - " $this->clientOptions['timeout'] = $value;\n" - " break;\n" - " case CURLOPT_TIMEOUT_MS:\n" - " $this->clientOptions['timeout'] = $value / 1000;\n" - " break;\n" - " case CURLOPT_FILE:\n" - " $this->outputStream = $value;\n" - " break;\n" - " case CURLOPT_HEADER:\n" - " $this->withHeader = $value;\n" - " break;\n" - " case CURLOPT_HEADERFUNCTION:\n" - " $this->headerFunction = $value;\n" - " break;\n" - " case CURLOPT_READFUNCTION:\n" - " $this->readFunction = $value;\n" - " break;\n" - " case CURLOPT_WRITEFUNCTION:\n" - " $this->writeFunction = $value;\n" - " break;\n" - " case CURLOPT_PROGRESSFUNCTION:\n" - " $this->progressFunction = $value;\n" - " break;\n" - " case CURLOPT_HTTPAUTH:\n" - " if (!($value & CURLAUTH_BASIC)) {\n" - " trigger_error(\"swoole_curl_setopt(): CURLOPT_HTTPAUTH[{$value}] is not supported\", E_USER_WARNING);\n" - " return false;\n" - " }\n" - " break;\n" - " case CURLOPT_USERPWD:\n" - " $this->headers['Authorization'] = 'Basic ' . base64_encode($value);\n" - " break;\n" - " case CURLOPT_FOLLOWLOCATION:\n" - " $this->followLocation = $value;\n" - " break;\n" - " case CURLOPT_AUTOREFERER:\n" - " $this->autoReferer = $value;\n" - " break;\n" - " case CURLOPT_MAXREDIRS:\n" - " $this->maxRedirects = $value;\n" - " break;\n" - " case CURLOPT_PUT:\n" - " case CURLOPT_UPLOAD:\n" - " /* after libcurl 7.12, CURLOPT_PUT is replaced by CURLOPT_UPLOAD */\n" - " $this->method = 'PUT';\n" - " break;\n" - " case CURLOPT_INFILE:\n" - " $this->infile = $value;\n" - " break;\n" - " case CURLOPT_INFILESIZE:\n" - " $this->infileSize = $value;\n" - " break;\n" - " case CURLOPT_HTTPGET:\n" - " /* Since GET is the default, this is only necessary if the request method has been changed. */\n" - " $this->method = 'GET';\n" - " break;\n" - " default:\n" - " throw new Swoole\\Curl\\Exception(\"swoole_curl_setopt(): option[{$opt}] is not supported\");\n" - " }\n" - " return true;\n" - " }\n" - "\n" - " private function execute()\n" - " {\n" - " $this->info['redirect_count'] = $this->info['starttransfer_time'] = 0;\n" - " $this->info['redirect_url'] = '';\n" - " $timeBegin = microtime(true);\n" - " /*\n" - " * Socket\n" - " */\n" - " if (!$this->urlInfo) {\n" - " $this->setError(CURLE_URL_MALFORMAT, 'No URL set or URL using bad/illegal format');\n" - " return false;\n" - " }\n" - " if (!$this->client) {\n" - " $this->create();\n" - " }\n" - " do {\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" - " if (!filter_var($proxy, FILTER_VALIDATE_IP)) {\n" - " $ip = Swoole\\Coroutine::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" - " }\n" - " /*\n" - " * Client Options\n" - " */\n" - " $client->set(\n" - " $this->clientOptions +\n" - " ($proxyOptions ?? [])\n" - " );\n" - " /*\n" - " * Method\n" - " */\n" - " if ($this->method) {\n" - " $client->setMethod($this->method);\n" - " }\n" - " /*\n" - " * Infile\n" - " */\n" - " if ($this->infile) {\n" - " $data = '';\n" - " while (true) {\n" - " $nLength = $this->infileSize - strlen($data);\n" - " if ($nLength === 0) {\n" - " break;\n" - " }\n" - " if (feof($this->infile)) {\n" - " break;\n" - " }\n" - " $data .= fread($this->infile, $nLength);\n" - " }\n" - " $client->setData($data);\n" - " $this->infile = null;\n" - " $this->infileSize = PHP_INT_MAX;\n" - " }\n" - " /*\n" - " * Upload File\n" - " */\n" - " if ($this->postData and is_array($this->postData)) {\n" - " foreach ($this->postData as $k => $v) {\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" - " /*\n" - " * Post Data\n" - " */\n" - " if ($this->postData) {\n" - " if (is_string($this->postData) and empty($this->headers['Content-Type'])) {\n" - " $this->headers['Content-Type'] = 'application/x-www-form-urlencoded';\n" - " }\n" - " $client->setData($this->postData);\n" - " $this->postData = [];\n" - " }\n" - " /*\n" - " * Http Headers\n" - " */\n" - " $this->headers['Host'] = $this->urlInfo['host'];\n" - " // remove empty headers (keep same with raw cURL)\n" - " foreach ($this->headers as $headerName => $headerValue) {\n" - " if ($headerValue === '') {\n" - " unset($this->headers[$headerName]);\n" - " }\n" - " }\n" - " $client->setHeaders($this->headers);\n" - " /**\n" - " * Execute.\n" - " */\n" - " $executeResult = $client->execute($this->getUrl());\n" - " if (!$executeResult) {\n" - " $errCode = $client->errCode;\n" - " if ($errCode == SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED or $errCode == SWOOLE_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT) {\n" - " $this->setError(CURLE_COULDNT_RESOLVE_HOST, 'Could not resolve host: ' . $client->host);\n" - " }\n" - " $this->info['total_time'] = microtime(true) - $timeBegin;\n" - " return false;\n" - " }\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" - " 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" - " }\n" - " // force GET\n" - " if (in_array($client->statusCode, [Status::MOVED_PERMANENTLY, Status::FOUND, Status::SEE_OTHER])) {\n" - " $this->method = 'GET';\n" - " }\n" - " if ($this->autoReferer) {\n" - " $this->headers['Referer'] = $this->info['url'];\n" - " }\n" - " $this->setUrl($redirectUrl, false);\n" - " $this->setUrlInfo($redirectParsedUrl);\n" - " $this->info['redirect_count']++;\n" - " } else {\n" - " $this->info['redirect_url'] = $redirectUrl;\n" - " break;\n" - " }\n" - " } elseif ($this->failOnError && $client->statusCode >= 400) {\n" - " $this->setError(CURLE_HTTP_RETURNED_ERROR, \"The requested URL returned error: {$client->statusCode} \" . Status::getReasonPhrase($client->statusCode));\n" - " return false;\n" - " } 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" - " $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" - " $headerContent = '';\n" - " if ($client->headers) {\n" - " $cb = $this->headerFunction;\n" - " if ($client->statusCode > 0) {\n" - " $row = \"HTTP/1.1 {$client->statusCode} \" . Status::getReasonPhrase($client->statusCode) . \"\\r\\n\";\n" - " if ($cb) {\n" - " $cb($this, $row);\n" - " }\n" - " $headerContent .= $row;\n" - " }\n" - " foreach ($client->headers as $k => $v) {\n" - " $row = \"{$k}: {$v}\\r\\n\";\n" - " if ($cb) {\n" - " $cb($this, $row);\n" - " }\n" - " $headerContent .= $row;\n" - " }\n" - " $headerContent .= \"\\r\\n\";\n" - " $this->info['header_size'] = strlen($headerContent);\n" - " if ($cb) {\n" - " $cb($this, '');\n" - " }\n" - " } else {\n" - " $this->info['header_size'] = 0;\n" - " }\n" - "\n" - " if ($client->body and $this->readFunction) {\n" - " $cb = $this->readFunction;\n" - " $cb($this, $this->outputStream, strlen($client->body));\n" - " }\n" - "\n" - " if ($this->withHeader) {\n" - " $transfer = $headerContent . $client->body;\n" - " } else {\n" - " $transfer = $client->body;\n" - " }\n" - "\n" - " if ($this->withHeaderOut) {\n" - " $headerOutContent = $client->getHeaderOut();\n" - " $this->info['request_header'] = $headerOutContent ? $headerOutContent . \"\\r\\n\\r\\n\" : '';\n" - " }\n" - " if ($this->withFileTime) {\n" - " if (isset($client->headers['last-modified'])) {\n" - " $this->info['filetime'] = strtotime($client->headers['last-modified']);\n" - " } else {\n" - " $this->info['filetime'] = -1;\n" - " }\n" - " }\n" - "\n" - " if ($this->returnTransfer) {\n" - " return $this->transfer = $transfer;\n" - " }\n" - " if ($this->outputStream) {\n" - " return fwrite($this->outputStream, $transfer) === strlen($transfer);\n" - " }\n" - " echo $transfer;\n" - "\n" - " return true;\n" - " }\n" - "\n" - " /* ====== Redirect helper ====== */\n" - "\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" - " $fragment = isset($parsedUrl['fragment']) ? '#' . $parsedUrl['fragment'] : '';\n" - " return $scheme . $user . $pass . $host . $port . $path . $query . $fragment;\n" - " }\n" - "\n" - " private function getRedirectUrl(string $location): array\n" - " {\n" - " $uri = parse_url($location);\n" - " if (isset($uri['host'])) {\n" - " $redirectUri = $uri;\n" - " } else {\n" - " if (!isset($location[0])) {\n" - " return [];\n" - " }\n" - " $redirectUri = $this->urlInfo;\n" - " $redirectUri['query'] = '';\n" - " if ($location[0] === '/') {\n" - " $redirectUri['path'] = $location;\n" - " } else {\n" - " $path = dirname($redirectUri['path'] ?? '');\n" - " if ($path === '.') {\n" - " $path = '/';\n" - " }\n" - " if (isset($location[1]) and substr($location, 0, 2) === './') {\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" - " }\n" - " }\n" - " }\n" - " return $redirectUri;\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_fast_cgi = - "\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" - "/**\n" - " * FastCGI constants.\n" - " */\n" - "class FastCGI\n" - "{\n" - " /**\n" - " * Number of bytes in a FCGI_Header. Future versions of the protocol\n" - " * will not reduce this number.\n" - " */\n" - " public const HEADER_LEN = 8;\n" - "\n" - " /**\n" - " * Format of FCGI_HEADER for unpacking in PHP\n" - " */\n" - " public const HEADER_FORMAT = 'Cversion/Ctype/nrequestId/ncontentLength/CpaddingLength/Creserved';\n" - "\n" - " /**\n" - " * Max content length of a record\n" - " */\n" - " public const MAX_CONTENT_LENGTH = 65535;\n" - "\n" - " /**\n" - " * Value for version component of FCGI_Header\n" - " */\n" - " public const VERSION_1 = 1;\n" - "\n" - " /**\n" - " * Values for type component of FCGI_Header\n" - " */\n" - " public const BEGIN_REQUEST = 1;\n" - "\n" - " public const ABORT_REQUEST = 2;\n" - "\n" - " public const END_REQUEST = 3;\n" - "\n" - " public const PARAMS = 4;\n" - "\n" - " public const STDIN = 5;\n" - "\n" - " public const STDOUT = 6;\n" - "\n" - " public const STDERR = 7;\n" - "\n" - " public const DATA = 8;\n" - "\n" - " public const GET_VALUES = 9;\n" - "\n" - " public const GET_VALUES_RESULT = 10;\n" - "\n" - " public const UNKNOWN_TYPE = 11;\n" - "\n" - " /**\n" - " * Value for requestId component of FCGI_Header\n" - " */\n" - " public const DEFAULT_REQUEST_ID = 1;\n" - "\n" - " /**\n" - " * Mask for flags component of FCGI_BeginRequestBody\n" - " */\n" - " public const KEEP_CONN = 1;\n" - "\n" - " /**\n" - " * Values for role component of FCGI_BeginRequestBody\n" - " */\n" - " public const RESPONDER = 1;\n" - "\n" - " public const AUTHORIZER = 2;\n" - "\n" - " public const FILTER = 3;\n" - "\n" - " /**\n" - " * Values for protocolStatus component of FCGI_EndRequestBody\n" - " */\n" - " public const REQUEST_COMPLETE = 0;\n" - "\n" - " public const CANT_MPX_CONN = 1;\n" - "\n" - " public const OVERLOADED = 2;\n" - "\n" - " public const UNKNOWN_ROLE = 3;\n" - "}\n"; - -static const char* swoole_library_source_core_fast_cgi_record = - "\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\\FastCGI;\n" - "\n" - "use Swoole\\FastCGI;\n" - "\n" - "/**\n" - " * FastCGI record.\n" - " */\n" - "class Record\n" - "{\n" - " /**\n" - " * Identifies the FastCGI protocol version.\n" - " *\n" - " * @var int\n" - " */\n" - " protected $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" - "\n" - " /**\n" - " * Identifies the FastCGI request to which the record belongs.\n" - " *\n" - " * @var int\n" - " */\n" - " protected $requestId = FastCGI::DEFAULT_REQUEST_ID;\n" - "\n" - " /**\n" - " * Reserved byte for future proposes\n" - " *\n" - " * @var int\n" - " */\n" - " protected $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" - "\n" - " /**\n" - " * The number of bytes in the paddingData component of the record.\n" - " *\n" - " * @var int\n" - " */\n" - " private $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" - "\n" - " /**\n" - " * Padding data, between 0 and 255 bytes of data, which are ignored.\n" - " *\n" - " * @var string\n" - " */\n" - " private $paddingData = '';\n" - "\n" - " /**\n" - " * Returns the binary message representation of record\n" - " */\n" - " final public function __toString(): string\n" - " {\n" - " $headerPacket = pack(\n" - " 'CCnnCC',\n" - " $this->version,\n" - " $this->type,\n" - " $this->requestId,\n" - " $this->contentLength,\n" - " $this->paddingLength,\n" - " $this->reserved\n" - " );\n" - "\n" - " $payloadPacket = $this->packPayload();\n" - " $paddingPacket = pack(\"a{$this->paddingLength}\", $this->paddingData);\n" - "\n" - " return $headerPacket . $payloadPacket . $paddingPacket;\n" - " }\n" - "\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" - " {\n" - " $self = new static();\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" - "\n" - " $payload = substr($data, FastCGI::HEADER_LEN);\n" - " self::unpackPayload($self, $payload);\n" - " if (get_called_class() !== __CLASS__ && $self->contentLength > 0) {\n" - " static::unpackPayload($self, $payload);\n" - " }\n" - "\n" - " return $self;\n" - " }\n" - "\n" - " /**\n" - " * Sets the content data and adjusts the length fields\n" - " *\n" - " * @return static\n" - " */\n" - " public function setContentData(string $data): self\n" - " {\n" - " $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" - " } else {\n" - " $this->contentData = $data;\n" - " }\n" - " $extraLength = $this->contentLength % 8;\n" - " $this->paddingLength = $extraLength ? (8 - $extraLength) : 0;\n" - " return $this;\n" - " }\n" - "\n" - " /**\n" - " * Returns the context data from the record\n" - " */\n" - " public function getContentData(): string\n" - " {\n" - " return $this->contentData;\n" - " }\n" - "\n" - " /**\n" - " * Returns the version of record\n" - " */\n" - " public function getVersion(): int\n" - " {\n" - " return $this->version;\n" - " }\n" - "\n" - " /**\n" - " * Returns record type\n" - " */\n" - " public function getType(): int\n" - " {\n" - " return $this->type;\n" - " }\n" - "\n" - " /**\n" - " * Returns request ID\n" - " */\n" - " public function getRequestId(): int\n" - " {\n" - " return $this->requestId;\n" - " }\n" - "\n" - " /**\n" - " * Sets request ID\n" - " *\n" - " * There should be only one unique ID for all active requests,\n" - " * use random number or preferably resetting auto-increment.\n" - " *\n" - " * @return static\n" - " */\n" - " public function setRequestId(int $requestId): self\n" - " {\n" - " $this->requestId = $requestId;\n" - " return $this;\n" - " }\n" - "\n" - " /**\n" - " * Returns the size of content length\n" - " */\n" - " final public function getContentLength(): int\n" - " {\n" - " return $this->contentLength;\n" - " }\n" - "\n" - " /**\n" - " * Returns the size of padding length\n" - " */\n" - " final public function getPaddingLength(): int\n" - " {\n" - " return $this->paddingLength;\n" - " }\n" - "\n" - " /**\n" - " * 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" - " {\n" - " [\n" - " $self->contentData,\n" - " $self->paddingData\n" - " ] = array_values(\n" - " unpack(\"a{$self->contentLength}contentData/a{$self->paddingLength}paddingData\", $data)\n" - " );\n" - " }\n" - "\n" - " /**\n" - " * Implementation of packing the payload\n" - " */\n" - " protected function packPayload(): string\n" - " {\n" - " return pack(\"a{$this->contentLength}\", $this->contentData);\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_fast_cgi_record_params = - "\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\\FastCGI\\Record;\n" - "\n" - "use Swoole\\FastCGI;\n" - "use Swoole\\FastCGI\\Record;\n" - "\n" - "/**\n" - " * Params request record\n" - " */\n" - "class Params extends Record\n" - "{\n" - " /**\n" - " * List of params\n" - " *\n" - " * @var array\n" - " */\n" - " protected $values = [];\n" - "\n" - " /**\n" - " * Constructs a param request\n" - " */\n" - " public function __construct(array $values = [])\n" - " {\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" - " public function getValues(): array\n" - " {\n" - " return $this->values;\n" - " }\n" - "\n" - " /**\n" - " * {@inheritdoc}\n" - " * @param static $self\n" - " */\n" - " protected static function unpackPayload($self, string $data): void\n" - " {\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" - "\n" - " $formatParts = [\n" - " $isLongName ? 'NnameLength' : 'CnameLength',\n" - " $isLongValue ? 'NvalueLength' : 'CvalueLength',\n" - " ];\n" - " $format = join('/', $formatParts);\n" - " [$nameLength, $valueLength] = array_values(unpack($format, $data));\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" - " );\n" - "\n" - " $self->values[$nameData] = $valueData;\n" - "\n" - " $keyValueLength = $dataOffset + $nameLength + $valueLength;\n" - " $data = substr($data, $keyValueLength);\n" - " $currentOffset += $keyValueLength;\n" - " } while ($currentOffset < $self->getContentLength());\n" - " }\n" - "\n" - " /** {@inheritdoc} */\n" - " protected function packPayload(): string\n" - " {\n" - " $payload = '';\n" - " foreach ($this->values as $nameData => $valueData) {\n" - " if ($valueData === null) {\n" - " continue;\n" - " }\n" - " $nameLength = strlen($nameData);\n" - " $valueLength = strlen((string) $valueData);\n" - " $isLongName = $nameLength > 127;\n" - " $isLongValue = $valueLength > 127;\n" - " $formatParts = [\n" - " $isLongName ? 'N' : 'C',\n" - " $isLongValue ? 'N' : 'C',\n" - " \"a{$nameLength}\",\n" - " \"a{$valueLength}\",\n" - " ];\n" - " $format = join('', $formatParts);\n" - "\n" - " $payload .= pack(\n" - " $format,\n" - " $isLongName ? ($nameLength | 0x80000000) : $nameLength,\n" - " $isLongValue ? ($valueLength | 0x80000000) : $valueLength,\n" - " $nameData,\n" - " $valueData\n" - " );\n" - " }\n" - "\n" - " return $payload;\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_fast_cgi_record_abort_request = - "\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\\FastCGI\\Record;\n" - "\n" - "use Swoole\\FastCGI;\n" - "use Swoole\\FastCGI\\Record;\n" - "\n" - "/**\n" - " * The Web server sends a FCGI_ABORT_REQUEST record to abort a request\n" - " */\n" - "class AbortRequest extends Record\n" - "{\n" - " public function __construct(int $requestId = 0)\n" - " {\n" - " $this->type = FastCGI::ABORT_REQUEST;\n" - " $this->setRequestId($requestId);\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_fast_cgi_record_begin_request = - "\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\\FastCGI\\Record;\n" - "\n" - "use Swoole\\FastCGI;\n" - "use Swoole\\FastCGI\\Record;\n" - "\n" - "/**\n" - " * The Web server sends a FCGI_BEGIN_REQUEST record to start a request.\n" - " */\n" - "class BeginRequest extends Record\n" - "{\n" - " /**\n" - " * The role component sets the role the Web server expects the application to play.\n" - " * The currently-defined roles are:\n" - " * FCGI_RESPONDER\n" - " * FCGI_AUTHORIZER\n" - " * FCGI_FILTER\n" - " *\n" - " * @var int\n" - " */\n" - " protected $role = FastCGI::UNKNOWN_ROLE;\n" - "\n" - " /**\n" - " * The flags component contains a bit that controls connection shutdown.\n" - " *\n" - " * flags & FCGI_KEEP_CONN:\n" - " * 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" - "\n" - " /**\n" - " * Reserved data, 5 bytes maximum\n" - " *\n" - " * @var string\n" - " */\n" - " protected $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->reserved1 = $reserved;\n" - " $this->setContentData($this->packPayload());\n" - " }\n" - "\n" - " /**\n" - " * Returns the role\n" - " *\n" - " * The role component sets the role the Web server expects the application to play.\n" - " * The currently-defined roles are:\n" - " * FCGI_RESPONDER\n" - " * FCGI_AUTHORIZER\n" - " * FCGI_FILTER\n" - " */\n" - " public function getRole(): int\n" - " {\n" - " return $this->role;\n" - " }\n" - "\n" - " /**\n" - " * Returns the flags\n" - " *\n" - " * The flags component contains a bit that controls connection shutdown.\n" - " *\n" - " * flags & FCGI_KEEP_CONN:\n" - " * 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" - " public function getFlags(): int\n" - " {\n" - " return $this->flags;\n" - " }\n" - "\n" - " /**\n" - " * {@inheritdoc}\n" - " * @param static $self\n" - " */\n" - " protected static function unpackPayload($self, string $data): void\n" - " {\n" - " [\n" - " $self->role,\n" - " $self->flags,\n" - " $self->reserved1\n" - " ] = array_values(unpack('nrole/Cflags/a5reserved', $data));\n" - " }\n" - "\n" - " /** {@inheritdoc} */\n" - " protected function packPayload(): string\n" - " {\n" - " return pack(\n" - " 'nCa5',\n" - " $this->role,\n" - " $this->flags,\n" - " $this->reserved1\n" - " );\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_fast_cgi_record_data = - "\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\\FastCGI\\Record;\n" - "\n" - "use Swoole\\FastCGI;\n" - "use Swoole\\FastCGI\\Record;\n" - "\n" - "/**\n" - " * Data binary stream\n" - " *\n" - " * FCGI_DATA is a second stream record type used to send additional data to the application.\n" - " */\n" - "class Data extends Record\n" - "{\n" - " public function __construct(string $contentData = '')\n" - " {\n" - " $this->type = FastCGI::DATA;\n" - " $this->setContentData($contentData);\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_fast_cgi_record_end_request = - "\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\\FastCGI\\Record;\n" - "\n" - "use Swoole\\FastCGI;\n" - "use Swoole\\FastCGI\\Record;\n" - "\n" - "/**\n" - " * The application sends a FCGI_END_REQUEST record to terminate a request, either because the application\n" - " * has processed the request or because the application has rejected the request.\n" - " */\n" - "class EndRequest extends Record\n" - "{\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" - "\n" - " /**\n" - " * The protocolStatus component is a protocol-level status code.\n" - " *\n" - " * The possible protocolStatus values are:\n" - " * FCGI_REQUEST_COMPLETE: normal end of request.\n" - " * FCGI_CANT_MPX_CONN: rejecting a new request.\n" - " * This happens when a Web server sends concurrent requests over one connection to an application that is\n" - " * designed to process one request at a time per connection.\n" - " * FCGI_OVERLOADED: rejecting a new request.\n" - " * 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" - "\n" - " /**\n" - " * Reserved data, 3 bytes maximum\n" - " *\n" - " * @var string\n" - " */\n" - " protected $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" - " $this->protocolStatus = $protocolStatus;\n" - " $this->appStatus = $appStatus;\n" - " $this->reserved1 = $reserved;\n" - " $this->setContentData($this->packPayload());\n" - " }\n" - "\n" - " /**\n" - " * Returns app status\n" - " *\n" - " * The appStatus component is an application-level status code. Each role documents its usage of appStatus.\n" - " */\n" - " public function getAppStatus(): int\n" - " {\n" - " return $this->appStatus;\n" - " }\n" - "\n" - " /**\n" - " * Returns the protocol status\n" - " *\n" - " * The possible protocolStatus values are:\n" - " * FCGI_REQUEST_COMPLETE: normal end of request.\n" - " * FCGI_CANT_MPX_CONN: rejecting a new request.\n" - " * This happens when a Web server sends concurrent requests over one connection to an application that is\n" - " * designed to process one request at a time per connection.\n" - " * FCGI_OVERLOADED: rejecting a new request.\n" - " * 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" - " public function getProtocolStatus(): int\n" - " {\n" - " return $this->protocolStatus;\n" - " }\n" - "\n" - " /**\n" - " * {@inheritdoc}\n" - " * @param static $self\n" - " */\n" - " protected static function unpackPayload($self, string $data): void\n" - " {\n" - " [\n" - " $self->appStatus,\n" - " $self->protocolStatus,\n" - " $self->reserved1\n" - " ] = array_values(unpack('NappStatus/CprotocolStatus/a3reserved', $data));\n" - " }\n" - "\n" - " /** {@inheritdoc} */\n" - " protected function packPayload(): string\n" - " {\n" - " return pack(\n" - " 'NCa3',\n" - " $this->appStatus,\n" - " $this->protocolStatus,\n" - " $this->reserved1\n" - " );\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_fast_cgi_record_get_values = - "\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\\FastCGI\\Record;\n" - "\n" - "use Swoole\\FastCGI;\n" - "\n" - "/**\n" - " * GetValues API\n" - " *\n" - " * The Web server can query specific variables within the application.\n" - " * The server will typically perform a query on application startup in order to to automate certain aspects of\n" - " * system configuration.\n" - " *\n" - " * The application responds by sending a record {FCGI_GET_VALUES_RESULT, 0, ...} with the values supplied.\n" - " * If the application doesn't understand a variable name that was included in the query, it omits that name from\n" - " * the response.\n" - " *\n" - " * FCGI_GET_VALUES is designed to allow an open-ended set of variables.\n" - " *\n" - " * The initial set provides information to help the server perform application and connection management:\n" - " * FCGI_MAX_CONNS: The maximum number of concurrent transport connections this application will accept,\n" - " * e.g. \"1\" or \"10\".\n" - " * FCGI_MAX_REQS: The maximum number of concurrent requests this application will accept, e.g. \"1\" or \"50\".\n" - " * FCGI_MPXS_CONNS: \"0\" if this application does not multiplex connections (i.e. handle concurrent requests\n" - " * over each connection), \"1\" otherwise.\n" - " */\n" - "class GetValues extends Params\n" - "{\n" - " /**\n" - " * Constructs a request\n" - " *\n" - " * @param array $keys List of keys to receive\n" - " */\n" - " public function __construct(array $keys = [])\n" - " {\n" - " parent::__construct(array_fill_keys($keys, ''));\n" - " $this->type = FastCGI::GET_VALUES;\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_fast_cgi_record_get_values_result = - "\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\\FastCGI\\Record;\n" - "\n" - "use Swoole\\FastCGI;\n" - "\n" - "/**\n" - " * GetValues API\n" - " *\n" - " * The Web server can query specific variables within the application.\n" - " * The server will typically perform a query on application startup in order to to automate certain aspects of\n" - " * system configuration.\n" - " *\n" - " * The application responds by sending a record {FCGI_GET_VALUES_RESULT, 0, ...} with the values supplied.\n" - " * If the application doesn't understand a variable name that was included in the query, it omits that name from\n" - " * the response.\n" - " *\n" - " * FCGI_GET_VALUES is designed to allow an open-ended set of variables.\n" - " *\n" - " * The initial set provides information to help the server perform application and connection management:\n" - " * FCGI_MAX_CONNS: The maximum number of concurrent transport connections this application will accept,\n" - " * e.g. \"1\" or \"10\".\n" - " * FCGI_MAX_REQS: The maximum number of concurrent requests this application will accept, e.g. \"1\" or \"50\".\n" - " * FCGI_MPXS_CONNS: \"0\" if this application does not multiplex connections (i.e. handle concurrent requests\n" - " * over each connection), \"1\" otherwise.\n" - " */\n" - "class GetValuesResult extends Params\n" - "{\n" - " /**\n" - " * Constructs a param request\n" - " */\n" - " public function __construct(array $values = [])\n" - " {\n" - " parent::__construct($values);\n" - " $this->type = FastCGI::GET_VALUES_RESULT;\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_fast_cgi_record_stdin = - "\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\\FastCGI\\Record;\n" - "\n" - "use Swoole\\FastCGI;\n" - "use Swoole\\FastCGI\\Record;\n" - "\n" - "/**\n" - " * Stdin binary stream\n" - " *\n" - " * FCGI_STDIN is a stream record type used in sending arbitrary data from the Web server to the application\n" - " */\n" - "class Stdin extends Record\n" - "{\n" - " public function __construct(string $contentData = '')\n" - " {\n" - " $this->type = FastCGI::STDIN;\n" - " $this->setContentData($contentData);\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_fast_cgi_record_stdout = - "\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\\FastCGI\\Record;\n" - "\n" - "use Swoole\\FastCGI;\n" - "use Swoole\\FastCGI\\Record;\n" - "\n" - "/**\n" - " * Stdout binary stream\n" - " *\n" - " * FCGI_STDOUT is a stream record for sending arbitrary data from the application to the Web server\n" - " */\n" - "class Stdout extends Record\n" - "{\n" - " public function __construct(string $contentData = '')\n" - " {\n" - " $this->type = FastCGI::STDOUT;\n" - " $this->setContentData($contentData);\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_fast_cgi_record_stderr = - "\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\\FastCGI\\Record;\n" - "\n" - "use Swoole\\FastCGI;\n" - "use Swoole\\FastCGI\\Record;\n" - "\n" - "/**\n" - " * Stderr binary stream\n" - " *\n" - " * FCGI_STDERR is a stream record for sending arbitrary data from the application to the Web server\n" - " */\n" - "class Stderr extends Record\n" - "{\n" - " public function __construct(string $contentData = '')\n" - " {\n" - " $this->type = FastCGI::STDERR;\n" - " $this->setContentData($contentData);\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_fast_cgi_record_unknown_type = - "\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\\FastCGI\\Record;\n" - "\n" - "use Swoole\\FastCGI;\n" - "use Swoole\\FastCGI\\Record;\n" - "\n" - "/**\n" - " * Record for unknown queries\n" - " *\n" - " * The set of management record types is likely to grow in future versions of this protocol.\n" - " * To provide for this evolution, the protocol includes the FCGI_UNKNOWN_TYPE management record.\n" - " * When an application receives a management record whose type T it does not understand, the application responds\n" - " * with {FCGI_UNKNOWN_TYPE, 0, {T}}.\n" - " */\n" - "class UnknownType extends Record\n" - "{\n" - " /**\n" - " * Type of the unrecognized management record.\n" - " *\n" - " * @var int\n" - " */\n" - " protected $type1;\n" - "\n" - " /**\n" - " * Reserved data, 7 bytes maximum\n" - " *\n" - " * @var string\n" - " */\n" - " protected $reserved1;\n" - "\n" - " public function __construct(int $type = 0, string $reserved = '')\n" - " {\n" - " $this->type = FastCGI::UNKNOWN_TYPE;\n" - " $this->type1 = $type;\n" - " $this->reserved1 = $reserved;\n" - " $this->setContentData($this->packPayload());\n" - " }\n" - "\n" - " /**\n" - " * Returns the unrecognized type\n" - " */\n" - " public function getUnrecognizedType(): int\n" - " {\n" - " return $this->type1;\n" - " }\n" - "\n" - " /**\n" - " * {@inheritdoc}\n" - " * @param static $self\n" - " */\n" - " public static function unpackPayload($self, string $data): void\n" - " {\n" - " [$self->type1, $self->reserved1] = array_values(unpack('Ctype/a7reserved', $data));\n" - " }\n" - "\n" - " /** {@inheritdoc} */\n" - " protected function packPayload(): string\n" - " {\n" - " return pack(\n" - " 'Ca7',\n" - " $this->type1,\n" - " $this->reserved1\n" - " );\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_fast_cgi_frame_parser = - "\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\\FastCGI;\n" - "\n" - "use DomainException;\n" - "use RuntimeException;\n" - "use Swoole\\FastCGI;\n" - "\n" - "/**\n" - " * Utility class to simplify parsing of FastCGI protocol data.\n" - " */\n" - "class FrameParser\n" - "{\n" - " /**\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" - " ];\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" - " {\n" - " $bufferLength = strlen($buffer);\n" - " if ($bufferLength < FastCGI::HEADER_LEN) {\n" - " return false;\n" - " }\n" - "\n" - " $fastInfo = unpack(FastCGI::HEADER_FORMAT, $buffer);\n" - " if ($bufferLength < FastCGI::HEADER_LEN + $fastInfo['contentLength'] + $fastInfo['paddingLength']) {\n" - " return false;\n" - " }\n" - "\n" - " return true;\n" - " }\n" - "\n" - " /**\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" - " {\n" - " $bufferLength = strlen($buffer);\n" - " if ($bufferLength < FastCGI::HEADER_LEN) {\n" - " throw new RuntimeException('Not enough data in the buffer to parse');\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" - " }\n" - "\n" - " /** @var Record $className */\n" - " $className = self::$classMapping[$recordType];\n" - " $record = $className::unpack($buffer);\n" - "\n" - " $offset = FastCGI::HEADER_LEN + $record->getContentLength() + $record->getPaddingLength();\n" - " $buffer = substr($buffer, $offset);\n" - "\n" - " return $record;\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_fast_cgi_message = - "\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\\FastCGI;\n" - "\n" - "class Message\n" - "{\n" - " /** @var array */\n" - " protected $params = [];\n" - "\n" - " /** @var string */\n" - " protected $body = '';\n" - "\n" - " /** @var string */\n" - " protected $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" - " {\n" - " $this->params[$name] = $value;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutParam(string $name): self\n" - " {\n" - " unset($this->params[$name]);\n" - " return $this;\n" - " }\n" - "\n" - " public function getParams(): array\n" - " {\n" - " return $this->params;\n" - " }\n" - "\n" - " public function withParams(array $params): self\n" - " {\n" - " $this->params = $params;\n" - " return $this;\n" - " }\n" - "\n" - " public function withAddedParams(array $params): self\n" - " {\n" - " $this->params = $params + $this->params;\n" - " return $this;\n" - " }\n" - "\n" - " public function getBody(): string\n" - " {\n" - " return $this->body;\n" - " }\n" - "\n" - " public function withBody($body): self\n" - " {\n" - " $this->body = (string) $body;\n" - " return $this;\n" - " }\n" - "\n" - " public function getError(): string\n" - " {\n" - " return $this->error;\n" - " }\n" - "\n" - " public function withError(string $error): self\n" - " {\n" - " $this->error = $error;\n" - " return $this;\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_fast_cgi_request = - "\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\\FastCGI;\n" - "\n" - "use Swoole\\FastCGI;\n" - "use Swoole\\FastCGI\\Record\\BeginRequest;\n" - "use Swoole\\FastCGI\\Record\\Params;\n" - "use Swoole\\FastCGI\\Record\\Stdin;\n" - "\n" - "class Request extends Message\n" - "{\n" - " protected $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" - " if (empty($body)) {\n" - " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}}\";\n" - " } else {\n" - " $stdinList = [];\n" - " while (true) {\n" - " $stdinList[] = $stdin = new Stdin($body);\n" - " $stdinLength = $stdin->getContentLength();\n" - " if ($stdinLength === strlen($body)) {\n" - " break;\n" - " }\n" - " $body = substr($body, $stdinLength);\n" - " }\n" - " $stdinList[] = new Stdin();\n" - " $stdin = implode($stdinList);\n" - " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}{$stdin}}\";\n" - " }\n" - " return $message;\n" - " }\n" - "\n" - " public function getKeepConn(): bool\n" - " {\n" - " return $this->keepConn;\n" - " }\n" - "\n" - " public function withKeepConn(bool $keepConn): self\n" - " {\n" - " $this->keepConn = $keepConn;\n" - " return $this;\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_fast_cgi_response = - "\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\\FastCGI;\n" - "\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" - " if (!static::verify($records)) {\n" - " throw new InvalidArgumentException('Bad records');\n" - " }\n" - " $body = '';\n" - " $error = '';\n" - " foreach ($records as $record) {\n" - " if ($record instanceof Stdout) {\n" - " if ($record->getContentLength() > 0) {\n" - " $body .= $record->getContentData();\n" - " }\n" - " } elseif ($record instanceof Stderr) {\n" - " if ($record->getContentLength() > 0) {\n" - " $error .= $record->getContentData();\n" - " }\n" - " }\n" - " }\n" - " $this->withBody($body)->withError($error);\n" - " }\n" - "\n" - " public static function verify(array $records): bool\n" - " {\n" - " return !empty($records) && $records[count($records) - 1] instanceof EndRequest;\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_fast_cgi_http_request = - "\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\\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" - " '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" - " ];\n" - "\n" - " public function getScheme(): ?string\n" - " {\n" - " return $this->params['REQUEST_SCHEME'] ?? null;\n" - " }\n" - "\n" - " public function withScheme(string $scheme): self\n" - " {\n" - " $this->params['REQUEST_SCHEME'] = $scheme;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutScheme(): void\n" - " {\n" - " unset($this->params['REQUEST_SCHEME']);\n" - " }\n" - "\n" - " public function getMethod(): ?string\n" - " {\n" - " return $this->params['REQUEST_METHOD'] ?? null;\n" - " }\n" - "\n" - " public function withMethod(string $method): self\n" - " {\n" - " $this->params['REQUEST_METHOD'] = $method;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutMethod(): void\n" - " {\n" - " unset($this->params['REQUEST_METHOD']);\n" - " }\n" - "\n" - " public function getDocumentRoot(): ?string\n" - " {\n" - " return $this->params['DOCUMENT_ROOT'] ?? null;\n" - " }\n" - "\n" - " public function withDocumentRoot(string $documentRoot): self\n" - " {\n" - " $this->params['DOCUMENT_ROOT'] = $documentRoot;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutDocumentRoot(): void\n" - " {\n" - " unset($this->params['DOCUMENT_ROOT']);\n" - " }\n" - "\n" - " public function getScriptFilename(): ?string\n" - " {\n" - " return $this->params['SCRIPT_FILENAME'] ?? null;\n" - " }\n" - "\n" - " public function withScriptFilename(string $scriptFilename): self\n" - " {\n" - " $this->params['SCRIPT_FILENAME'] = $scriptFilename;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutScriptFilename(): void\n" - " {\n" - " unset($this->params['SCRIPT_FILENAME']);\n" - " }\n" - "\n" - " public function getScriptName(): ?string\n" - " {\n" - " return $this->params['SCRIPT_NAME'] ?? null;\n" - " }\n" - "\n" - " public function withScriptName(string $scriptName): self\n" - " {\n" - " $this->params['SCRIPT_NAME'] = $scriptName;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutScriptName(): void\n" - " {\n" - " unset($this->params['SCRIPT_NAME']);\n" - " }\n" - "\n" - " public function withUri(string $uri): self\n" - " {\n" - " $info = parse_url($uri);\n" - " return $this->withRequestUri($uri)\n" - " ->withDocumentUri($info['path'] ?? '')\n" - " ->withQueryString($info['query'] ?? '');\n" - " }\n" - "\n" - " public function getDocumentUri(): ?string\n" - " {\n" - " return $this->params['DOCUMENT_URI'] ?? null;\n" - " }\n" - "\n" - " public function withDocumentUri(string $documentUri): self\n" - " {\n" - " $this->params['DOCUMENT_URI'] = $documentUri;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutDocumentUri(): void\n" - " {\n" - " unset($this->params['DOCUMENT_URI']);\n" - " }\n" - "\n" - " public function getRequestUri(): ?string\n" - " {\n" - " return $this->params['REQUEST_URI'] ?? null;\n" - " }\n" - "\n" - " public function withRequestUri(string $requestUri): self\n" - " {\n" - " $this->params['REQUEST_URI'] = $requestUri;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutRequestUri(): void\n" - " {\n" - " unset($this->params['REQUEST_URI']);\n" - " }\n" - "\n" - " public function withQuery($query): self\n" - " {\n" - " if (is_array($query)) {\n" - " $query = http_build_query($query);\n" - " }\n" - " return $this->withQueryString($query);\n" - " }\n" - "\n" - " public function getQueryString(): ?string\n" - " {\n" - " return $this->params['QUERY_STRING'] ?? null;\n" - " }\n" - "\n" - " public function withQueryString(string $queryString): self\n" - " {\n" - " $this->params['QUERY_STRING'] = $queryString;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutQueryString(): void\n" - " {\n" - " unset($this->params['QUERY_STRING']);\n" - " }\n" - "\n" - " public function getContentType(): ?string\n" - " {\n" - " return $this->params['CONTENT_TYPE'] ?? null;\n" - " }\n" - "\n" - " public function withContentType(string $contentType): self\n" - " {\n" - " $this->params['CONTENT_TYPE'] = $contentType;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutContentType(): void\n" - " {\n" - " unset($this->params['CONTENT_TYPE']);\n" - " }\n" - "\n" - " public function getContentLength(): ?int\n" - " {\n" - " return isset($this->params['CONTENT_LENGTH']) ? (int) $this->params['CONTENT_LENGTH'] : null;\n" - " }\n" - "\n" - " public function withContentLength(int $contentLength): self\n" - " {\n" - " $this->params['CONTENT_LENGTH'] = (string) $contentLength;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutContentLength(): void\n" - " {\n" - " unset($this->params['CONTENT_LENGTH']);\n" - " }\n" - "\n" - " public function getGatewayInterface(): ?string\n" - " {\n" - " return $this->params['GATEWAY_INTERFACE'] ?? null;\n" - " }\n" - "\n" - " public function withGatewayInterface(string $gatewayInterface): self\n" - " {\n" - " $this->params['GATEWAY_INTERFACE'] = $gatewayInterface;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutGatewayInterface(): void\n" - " {\n" - " unset($this->params['GATEWAY_INTERFACE']);\n" - " }\n" - "\n" - " public function getServerProtocol(): ?string\n" - " {\n" - " return $this->params['SERVER_PROTOCOL'] ?? null;\n" - " }\n" - "\n" - " public function withServerProtocol(string $serverProtocol): self\n" - " {\n" - " $this->params['SERVER_PROTOCOL'] = $serverProtocol;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutServerProtocol(): void\n" - " {\n" - " unset($this->params['SERVER_PROTOCOL']);\n" - " }\n" - "\n" - " public function withProtocolVersion(string $protocolVersion): self\n" - " {\n" - " if (!is_numeric($protocolVersion)) {\n" - " throw new InvalidArgumentException('Protocol version must be numeric');\n" - " }\n" - " $this->params['SERVER_PROTOCOL'] = \"HTTP/{$protocolVersion}\";\n" - " return $this;\n" - " }\n" - "\n" - " public function getServerSoftware(): ?string\n" - " {\n" - " return $this->params['SERVER_SOFTWARE'] ?? null;\n" - " }\n" - "\n" - " public function withServerSoftware(string $serverSoftware): self\n" - " {\n" - " $this->params['SERVER_SOFTWARE'] = $serverSoftware;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutServerSoftware(): void\n" - " {\n" - " unset($this->params['SERVER_SOFTWARE']);\n" - " }\n" - "\n" - " public function getRemoteAddr(): ?string\n" - " {\n" - " return $this->params['REMOTE_ADDR'] ?? null;\n" - " }\n" - "\n" - " public function withRemoteAddr(string $remoteAddr): self\n" - " {\n" - " $this->params['REMOTE_ADDR'] = $remoteAddr;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutRemoteAddr(): void\n" - " {\n" - " unset($this->params['REMOTE_ADDR']);\n" - " }\n" - "\n" - " public function getRemotePort(): ?int\n" - " {\n" - " return isset($this->params['REMOTE_PORT']) ? (int) $this->params['REMOTE_PORT'] : null;\n" - " }\n" - "\n" - " public function withRemotePort(int $remotePort): self\n" - " {\n" - " $this->params['REMOTE_PORT'] = (string) $remotePort;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutRemotePort(): void\n" - " {\n" - " unset($this->params['REMOTE_PORT']);\n" - " }\n" - "\n" - " public function getServerAddr(): ?string\n" - " {\n" - " return $this->params['SERVER_ADDR'] ?? null;\n" - " }\n" - "\n" - " public function withServerAddr(string $serverAddr): self\n" - " {\n" - " $this->params['SERVER_ADDR'] = $serverAddr;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutServerAddr(): void\n" - " {\n" - " unset($this->params['SERVER_ADDR']);\n" - " }\n" - "\n" - " public function getServerPort(): ?int\n" - " {\n" - " return isset($this->params['SERVER_PORT']) ? (int) $this->params['SERVER_PORT'] : null;\n" - " }\n" - "\n" - " public function withServerPort(int $serverPort): self\n" - " {\n" - " $this->params['SERVER_PORT'] = (string) $serverPort;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutServerPort(): void\n" - " {\n" - " unset($this->params['SERVER_PORT']);\n" - " }\n" - "\n" - " public function getServerName(): ?string\n" - " {\n" - " return $this->params['SERVER_NAME'] ?? null;\n" - " }\n" - "\n" - " public function withServerName(string $serverName): self\n" - " {\n" - " $this->params['SERVER_NAME'] = $serverName;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutServerName(): void\n" - " {\n" - " unset($this->params['SERVER_NAME']);\n" - " }\n" - "\n" - " public function getRedirectStatus(): ?string\n" - " {\n" - " return $this->params['REDIRECT_STATUS'] ?? null;\n" - " }\n" - "\n" - " public function withRedirectStatus(string $redirectStatus): self\n" - " {\n" - " $this->params['REDIRECT_STATUS'] = $redirectStatus;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutRedirectStatus(): void\n" - " {\n" - " unset($this->params['REDIRECT_STATUS']);\n" - " }\n" - "\n" - " public function getHeader(string $name): ?string\n" - " {\n" - " return $this->params[static::convertHeaderNameToParamName($name)] ?? null;\n" - " }\n" - "\n" - " public function withHeader(string $name, string $value): self\n" - " {\n" - " $this->params[static::convertHeaderNameToParamName($name)] = $value;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutHeader(string $name): void\n" - " {\n" - " unset($this->params[static::convertHeaderNameToParamName($name)]);\n" - " }\n" - "\n" - " public function getHeaders(): array\n" - " {\n" - " $headers = [];\n" - " foreach ($this->params as $name => $value) {\n" - " if (strpos($name, 'HTTP_') === 0) {\n" - " $headers[static::convertParamNameToHeaderName($name)] = $value;\n" - " }\n" - " }\n" - " return $headers;\n" - " }\n" - "\n" - " public function withHeaders(array $headers): self\n" - " {\n" - " foreach ($headers as $name => $value) {\n" - " $this->withHeader($name, $value);\n" - " }\n" - " return $this;\n" - " }\n" - "\n" - " /** @return $this */\n" - " public function withBody($body): Message\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" - " return $this->withContentLength(strlen($body));\n" - " }\n" - "\n" - " protected static function convertHeaderNameToParamName(string $name)\n" - " {\n" - " return 'HTTP_' . str_replace('-', '_', strtoupper($name));\n" - " }\n" - "\n" - " protected static function convertParamNameToHeaderName(string $name)\n" - " {\n" - " return ucwords(str_replace('_', '-', substr($name, strlen('HTTP_'))), '-');\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_fast_cgi_http_response = - "\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\\FastCGI;\n" - "\n" - "use Swoole\\Http\\Status;\n" - "\n" - "class HttpResponse extends Response\n" - "{\n" - " /** @var int */\n" - " protected $statusCode;\n" - "\n" - " /** @var string */\n" - " protected $reasonPhrase;\n" - "\n" - " /** @var array */\n" - " protected $headers = [];\n" - "\n" - " /** @var array */\n" - " protected $headersMap = [];\n" - "\n" - " /** @var array */\n" - " protected $setCookieHeaderLines = [];\n" - "\n" - " public function __construct(array $records = [])\n" - " {\n" - " parent::__construct($records);\n" - " $body = (string) $this->getBody();\n" - " if (strlen($body) === 0) {\n" - " return;\n" - " }\n" - " [$headers, $body] = @explode(\"\\r\\n\\r\\n\", $body, 2);\n" - " $headers = explode(\"\\r\\n\", $headers);\n" - " foreach ($headers as $header) {\n" - " [$name, $value] = @explode(': ', $header, 2);\n" - " if (strcasecmp($name, 'Status') === 0) {\n" - " [$statusCode, $reasonPhrase] = @explode(' ', $value, 2);\n" - " } elseif (strcasecmp($name, 'Set-Cookie') === 0) {\n" - " $this->withSetCookieHeaderLine($value);\n" - " } else {\n" - " $this->withHeader($name, $value);\n" - " }\n" - " }\n" - " $statusCode = (int) ($statusCode ?? Status::OK);\n" - " $reasonPhrase = (string) ($reasonPhrase ?? Status::getReasonPhrase($statusCode));\n" - " $this->withStatusCode($statusCode)->withReasonPhrase($reasonPhrase);\n" - " $this->withBody($body);\n" - " }\n" - "\n" - " public function getStatusCode(): int\n" - " {\n" - " return $this->statusCode;\n" - " }\n" - "\n" - " public function withStatusCode(int $statusCode): self\n" - " {\n" - " $this->statusCode = $statusCode;\n" - " return $this;\n" - " }\n" - "\n" - " public function getReasonPhrase(): string\n" - " {\n" - " return $this->reasonPhrase;\n" - " }\n" - "\n" - " public function withReasonPhrase(string $reasonPhrase): self\n" - " {\n" - " $this->reasonPhrase = $reasonPhrase;\n" - " return $this;\n" - " }\n" - "\n" - " public function getHeader(string $name): ?string\n" - " {\n" - " $name = $this->headersMap[strtolower($name)] ?? null;\n" - " return $name ? $this->headers[$name] : null;\n" - " }\n" - "\n" - " public function getHeaders(): array\n" - " {\n" - " return $this->headers;\n" - " }\n" - "\n" - " public function withHeader(string $name, string $value): self\n" - " {\n" - " $this->headers[$name] = $value;\n" - " $this->headersMap[strtolower($name)] = $name;\n" - " return $this;\n" - " }\n" - "\n" - " public function withHeaders(array $headers): self\n" - " {\n" - " foreach ($headers as $name => $value) {\n" - " $this->withHeader($name, $value);\n" - " }\n" - " return $this;\n" - " }\n" - "\n" - " public function getSetCookieHeaderLines(): array\n" - " {\n" - " return $this->setCookieHeaderLines;\n" - " }\n" - "\n" - " public function withSetCookieHeaderLine(string $value): self\n" - " {\n" - " $this->setCookieHeaderLines[] = $value;\n" - " return $this;\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_coroutine_fast_cgi_client = - "\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\\FastCGI;\n" - "\n" - "use InvalidArgumentException;\n" - "use Swoole\\Coroutine\\FastCGI\\Client\\Exception;\n" - "use Swoole\\Coroutine\\Socket;\n" - "use Swoole\\FastCGI\\FrameParser;\n" - "use Swoole\\FastCGI\\HttpRequest;\n" - "use Swoole\\FastCGI\\HttpResponse;\n" - "use Swoole\\FastCGI\\Record\\EndRequest;\n" - "use Swoole\\FastCGI\\Request;\n" - "use Swoole\\FastCGI\\Response;\n" - "\n" - "class Client\n" - "{\n" - " /** @var int */\n" - " protected $af;\n" - "\n" - " /** @var string */\n" - " protected $host;\n" - "\n" - " /** @var int */\n" - " protected $port;\n" - "\n" - " /** @var bool */\n" - " protected $ssl;\n" - "\n" - " /** @var Socket */\n" - " protected $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" - " $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" - " }\n" - "\n" - " /**\n" - " * @throws Exception\n" - " * @return HttpResponse|Response\n" - " */\n" - " public function execute(Request $request, float $timeout = -1): Response\n" - " {\n" - " if (!$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" - " ]);\n" - " if (!$socket->connect($this->host, $this->port, $timeout)) {\n" - " $this->ioException();\n" - " }\n" - " } else {\n" - " $socket = $this->socket;\n" - " }\n" - " $sendData = (string) $request;\n" - " if ($socket->sendAll($sendData) !== strlen($sendData)) {\n" - " $this->ioException();\n" - " }\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" - " }\n" - " }\n" - " do {\n" - " $records[] = $record = FrameParser::parseFrame($recvData);\n" - " } while (strlen($recvData) !== 0);\n" - " if ($record instanceof EndRequest) {\n" - " if (!$request->getKeepConn()) {\n" - " $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" - " }\n" - " }\n" - " /* never here */\n" - " exit(1);\n" - " }\n" - "\n" - " public static function parseUrl(string $url): array\n" - " {\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" - " }\n" - " $host = \"unix:/{$host}\";\n" - " }\n" - " return [$host, $port];\n" - " }\n" - "\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" - " $documentUri = $scriptName;\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" - " ->withDocumentUri($documentUri)\n" - " ->withRequestUri($requestUri)\n" - " ->withQueryString($query)\n" - " ->withBody($data)\n" - " ->withMethod($request->getContentLength() === 0 ? 'GET' : 'POST');\n" - " $response = $client->execute($request, $timeout);\n" - " return $response->getBody();\n" - " }\n" - "\n" - " protected function ioException(?int $errno = null): void\n" - " {\n" - " $socket = $this->socket;\n" - " if ($errno !== null) {\n" - " $socket->errCode = $errno;\n" - " $socket->errMsg = swoole_strerror($errno);\n" - " }\n" - " $socket->close();\n" - " $this->socket = null;\n" - " throw new Exception($socket->errMsg, $socket->errCode);\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_coroutine_fast_cgi_client_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" - "namespace Swoole\\Coroutine\\FastCGI\\Client;\n" - "\n" - "class Exception extends \\Swoole\\Exception\n" - "{\n" - "}\n"; - -static const char* swoole_library_source_core_coroutine_fast_cgi_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\\FastCGI;\n" - "\n" - "use InvalidArgumentException;\n" - "use Swoole\\FastCGI\\HttpRequest;\n" - "use Swoole\\FastCGI\\HttpResponse;\n" - "use Swoole\\Http;\n" - "\n" - "class Proxy\n" - "{\n" - " /* @var string */\n" - " protected $host;\n" - "\n" - " /* @var int */\n" - " protected $port;\n" - "\n" - " /* @var float */\n" - " protected $timeout = -1;\n" - "\n" - " /* @var string */\n" - " protected $documentRoot;\n" - "\n" - " /* @var bool */\n" - " protected $https = false;\n" - "\n" - " /* @var string */\n" - " protected $index = 'index.php';\n" - "\n" - " /* @var array */\n" - " protected $params = [];\n" - "\n" - " /* @var null|callable */\n" - " protected $staticFileFilter;\n" - "\n" - " 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" - " }\n" - "\n" - " public function withTimeout(float $timeout): self\n" - " {\n" - " $this->timeout = $timeout;\n" - " return $this;\n" - " }\n" - "\n" - " public function withHttps(bool $https): self\n" - " {\n" - " $this->https = $https;\n" - " return $this;\n" - " }\n" - "\n" - " public function withIndex(string $index): self\n" - " {\n" - " $this->index = $index;\n" - " return $this;\n" - " }\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" - " {\n" - " $this->params[$name] = $value;\n" - " return $this;\n" - " }\n" - "\n" - " public function withoutParam(string $name): self\n" - " {\n" - " unset($this->params[$name]);\n" - " return $this;\n" - " }\n" - "\n" - " public function getParams(): array\n" - " {\n" - " return $this->params;\n" - " }\n" - "\n" - " public function withParams(array $params): self\n" - " {\n" - " $this->params = $params;\n" - " return $this;\n" - " }\n" - "\n" - " public function withAddedParams(array $params): self\n" - " {\n" - " $this->params = $params + $this->params;\n" - " return $this;\n" - " }\n" - "\n" - " public function withStaticFileFilter(?callable $filter): self\n" - " {\n" - " $this->staticFileFilter = $filter;\n" - " return $this;\n" - " }\n" - "\n" - " public function translateRequest($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" - " }\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" - " return $request;\n" - " }\n" - "\n" - " public function translateResponse(HttpResponse $response, $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" - " }\n" - "\n" - " public function pass($userRequest, $userResponse): void\n" - " {\n" - " if (!($userRequest instanceof HttpRequest)) {\n" - " $request = $this->translateRequest($userRequest);\n" - " } else {\n" - " $request = $userRequest;\n" - " }\n" - " unset($userRequest);\n" - " if ($this->staticFileFilter) {\n" - " $filter = $this->staticFileFilter;\n" - " if ($filter($request, $userResponse)) {\n" - " return;\n" - " }\n" - " }\n" - " $client = new Client($this->host, $this->port);\n" - " $response = $client->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" - " 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" - " }\n" - " return false;\n" - " }\n" - " throw new InvalidArgumentException('Not supported on ' . get_class($userResponse));\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" - "{\n" - " return $obj->setOpt($opt, $value);\n" - "}\n" - "\n" - "function swoole_curl_setopt_array(Swoole\\Curl\\Handler $obj, $array): bool\n" - "{\n" - " foreach ($array as $k => $v) {\n" - " if ($obj->setOpt($k, $v) !== true) {\n" - " return false;\n" - " }\n" - " }\n" - " return true;\n" - "}\n" - "\n" - "function swoole_curl_exec(Swoole\\Curl\\Handler $obj)\n" - "{\n" - " return $obj->exec();\n" - "}\n" - "\n" - "function swoole_curl_getinfo(Swoole\\Curl\\Handler $obj, int $opt = 0)\n" - "{\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" - " }\n" - " return $info;\n" - "}\n" - "\n" - "function swoole_curl_errno(Swoole\\Curl\\Handler $obj)\n" - "{\n" - " return $obj->errno();\n" - "}\n" - "\n" - "function swoole_curl_error(Swoole\\Curl\\Handler $obj)\n" - "{\n" - " return $obj->error();\n" - "}\n" - "\n" - "function swoole_curl_reset(Swoole\\Curl\\Handler $obj)\n" - "{\n" - " return $obj->reset();\n" - "}\n" - "\n" - "function swoole_curl_close(Swoole\\Curl\\Handler $obj)\n" - "{\n" - " return $obj->close();\n" - "}\n" - "\n" - "function swoole_curl_multi_getcontent(Swoole\\Curl\\Handler $obj)\n" - "{\n" - " return $obj->getContent();\n" - "}\n"; - -static const char* swoole_library_source_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" - "if (SWOOLE_USE_SHORTNAME) {\n" - " function _string(string $string = ''): Swoole\\StringObject\n" - " {\n" - " return new Swoole\\StringObject($string);\n" - " }\n" - "\n" - " function _mbstring(string $string = ''): Swoole\\MultibyteStringObject\n" - " {\n" - " return new Swoole\\MultibyteStringObject($string);\n" - " }\n" - "\n" - " function _array(array $array = []): Swoole\\ArrayObject\n" - " {\n" - " return new Swoole\\ArrayObject($array);\n" - " }\n" - "}\n" - "\n" - "function swoole_string(string $string = ''): Swoole\\StringObject\n" - "{\n" - " return new Swoole\\StringObject($string);\n" - "}\n" - "\n" - "function swoole_mbstring(string $string = ''): Swoole\\MultibyteStringObject\n" - "{\n" - " return new Swoole\\MultibyteStringObject($string);\n" - "}\n" - "\n" - "function swoole_array(array $array = []): Swoole\\ArrayObject\n" - "{\n" - " return new Swoole\\ArrayObject($array);\n" - "}\n" - "\n" - "function swoole_array_default_value(array $array, $key, $default_value = null)\n" - "{\n" - " return array_key_exists($key, $array) ? $array[$key] : $default_value;\n" - "}\n"; - -static const char* swoole_library_source_alias = - "\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" - "if (SWOOLE_USE_SHORTNAME) {\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" - " class_alias(Swoole\\Coroutine\\FastCGI\\Client::class, Co\\FastCGI\\Client::class, true);\n" - " class_alias(Swoole\\Coroutine\\FastCGI\\Client\\Exception::class, Co\\FastCGI\\Client\\Exception::class, true);\n" - " class_alias(Swoole\\Coroutine\\FastCGI\\Proxy::class, Co\\FastCGI\\Proxy::class, true);\n" - "}\n"; - -static const char* swoole_library_source_alias_ns = - "\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" - " function run(callable $fn, ...$args)\n" - " {\n" - " $s = new Scheduler();\n" - " $s->add($fn, ...$args);\n" - " return $s->start();\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" - " }\n" - "}\n"; - -static 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_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_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_ext_curl, "@swoole-src/library/ext/curl.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"); -} diff --git a/remake_library.sh b/remake_library.sh deleted file mode 100755 index 9f10dffa835..00000000000 --- a/remake_library.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh -__DIR__=$(cd "$(dirname "$0")";pwd) - -set -e -cd ${__DIR__} -set +e -echo "rm swoole.lo" -rm swoole.lo -echo "rm php_swoole_library.h" -rm php_swoole_library.h -set -e -/usr/bin/env php tools/build-library.php -echo "remake..." -make $* > /dev/null -echo "done" 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 cb3bc5c2ec4..d5ab76f905c 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -10,299 +10,450 @@ | to obtain it through the world-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_cxx.h" -#include "atomic.h" -#include "async.h" -#include "coroutine_c_api.h" +#include "swoole.h" #include +#include +#include #include #include -#include - -#include -#ifdef HAVE_EXECINFO -#include +#ifdef __MACH__ +#include #endif +#include #include #include +#include + +#include "swoole_api.h" +#include "swoole_string.h" +#include "swoole_signal.h" +#include "swoole_memory.h" +#include "swoole_protocol.h" +#include "swoole_util.h" +#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 -swGlobal_t SwooleG; -swWorkerGlobal_t SwooleWG; -__thread swThreadGlobal_t SwooleTG; +using swoole::NameResolver; +using swoole::String; +using swoole::coroutine::System; -#ifdef __MACH__ -static __thread char _sw_error_buf[SW_ERROR_MSG_SIZE]; -char* sw_error_() -{ - return _sw_error_buf; -} +#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 -__thread char sw_error[SW_ERROR_MSG_SIZE]; + int fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) { + return -1; + } + + size_t read_bytes; + ssize_t n; + for (read_bytes = 0; read_bytes < size; read_bytes += (size_t) n) { + n = read(fd, (char *) buffer + read_bytes, size - read_bytes); + if (n <= 0) { + break; + } + } + + close(fd); + + return read_bytes; +#endif +} +#endif + +swoole::Global SwooleG = {}; +thread_local swoole::ThreadGlobal SwooleTG = {}; +thread_local char sw_error[SW_ERROR_MSG_SIZE]; +std::mutex sw_thread_lock; + +static swoole::Logger *g_logger_instance = nullptr; + +static void swoole_fatal_error_impl(int code, const char *format, ...); + +swoole::Logger *sw_logger() { + return g_logger_instance; +} + +void *sw_malloc(size_t size) { + return SwooleG.std_allocator.malloc(size); +} + +void sw_free(void *ptr) { + return SwooleG.std_allocator.free(ptr); +} + +void *sw_calloc(size_t nmemb, size_t size) { + return SwooleG.std_allocator.calloc(nmemb, size); +} + +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 -static void swoole_fatal_error(int code, const char *format, ...); +#ifdef SW_USE_OPENSSL + SwooleG.bug_report_message += swoole_ssl_get_version_message(); +#endif +} -void swoole_init(void) -{ - if (SwooleG.init) - { +void swoole_init(void) { + if (SwooleG.init) { return; } - bzero(&SwooleG, sizeof(SwooleG)); - bzero(&SwooleWG, sizeof(SwooleWG)); - bzero(sw_error, SW_ERROR_MSG_SIZE); + SwooleG = {}; + sw_memset_zero(sw_error, SW_ERROR_MSG_SIZE); SwooleG.running = 1; SwooleG.init = 1; SwooleG.enable_coroutine = 1; - - SwooleG.log_fd = STDOUT_FILENO; - SwooleG.write_log = swLog_put; - SwooleG.fatal_error = swoole_fatal_error; - + 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(); - //get system uname + + // DNS options + SwooleG.dns_tries = 1; + SwooleG.dns_resolvconf_path = SW_DNS_RESOLV_CONF; + + // get system uname uname(&SwooleG.uname); - //random seed - srandom(time(NULL)); + // random seed + srandom(time(nullptr)); SwooleG.pid = getpid(); + g_logger_instance = new swoole::Logger; + #ifdef SW_DEBUG - SwooleG.log_level = 0; + sw_logger()->set_level(0); SwooleG.trace_flags = 0x7fffffff; #else - SwooleG.log_level = SW_LOG_INFO; + sw_logger()->set_level(SW_LOG_INFO); #endif - //init global shared memory - SwooleG.memory_pool = swMemoryGlobal_new(SW_GLOBAL_MEMORY_PAGESIZE, 1); - if (SwooleG.memory_pool == NULL) - { - printf("[Core] Fatal Error: global memory allocation failure"); - exit(1); - } - - if (swMutex_create(&SwooleG.lock, 0) < 0) - { - printf("[Core] mutex init failure"); - exit(1); - } - + // init global shared memory + SwooleG.memory_pool = new swoole::GlobalMemory(SW_GLOBAL_MEMORY_PAGESIZE, true); SwooleG.max_sockets = SW_MAX_SOCKETS_DEFAULT; struct rlimit rlmt; - if (getrlimit(RLIMIT_NOFILE, &rlmt) < 0) - { - swSysWarn("getrlimit() failed"); - } - else - { + if (getrlimit(RLIMIT_NOFILE, &rlmt) < 0) { + 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); } - SwooleG.socket_buffer_size = SW_SOCKET_BUFFER_SIZE; - SwooleG.socket_send_timeout = SW_SOCKET_SEND_TIMEOUT; - - SwooleTG.buffer_stack = swString_new(SW_STACK_BUFFER_SIZE); - if (SwooleTG.buffer_stack == NULL) - { - exit(3); - } + SwooleTG.buffer_stack = new swoole::String(SW_STACK_BUFFER_SIZE); - if (!SwooleG.task_tmpdir) - { - SwooleG.task_tmpdir = sw_strndup(SW_TASK_TMP_FILE, sizeof(SW_TASK_TMP_FILE)); - SwooleG.task_tmpdir_len = sizeof(SW_TASK_TMP_FILE); + if (!swoole_set_task_tmpdir(SW_TASK_TMP_DIR)) { + exit(4); } - char *tmp_dir = swoole_dirname(SwooleG.task_tmpdir); - //create tmp dir - if (access(tmp_dir, R_OK) < 0 && swoole_mkdir_recursive(tmp_dir) < 0) - { - swWarn("create task tmp dir(%s) failed", tmp_dir); - } - if (tmp_dir) - { - sw_free(tmp_dir); - } - - //init signalfd + // init signalfd #ifdef HAVE_SIGNALFD - swSignalfd_init(); - SwooleG.use_signalfd = 1; + swoole_signalfd_init(); SwooleG.enable_signalfd = 1; #endif + + // init bug report message + bug_report_message_init(); +} + +SW_EXTERN_C_BEGIN + +SW_API int swoole_add_hook(enum swGlobalHookType type, swHookFunc func, int push_back) { + assert(type <= SW_GLOBAL_HOOK_END); + return swoole::hook_add(SwooleG.hooks, type, func, push_back); +} + +SW_API void swoole_call_hook(enum swGlobalHookType type, void *arg) { + assert(type <= SW_GLOBAL_HOOK_END); + swoole::hook_call(SwooleG.hooks, type, arg); } -SW_API const char* swoole_version(void) -{ +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; } -SW_API int swoole_version_id(void) -{ +SW_API int swoole_version_id(void) { return SWOOLE_VERSION_ID; } -void swoole_clean(void) -{ - if (SwooleG.task_tmpdir) - { - sw_free(SwooleG.task_tmpdir); - } - if (SwooleTG.timer) - { +SW_API int swoole_api_version_id(void) { + return SWOOLE_API_VERSION_ID; +} + +SW_EXTERN_C_END + +void swoole_clean(void) { + if (SwooleTG.timer) { swoole_timer_free(); } - if (SwooleTG.reactor) - { + if (SwooleTG.reactor) { swoole_event_free(); } - if (SwooleG.memory_pool != NULL) - { - SwooleG.memory_pool->destroy(SwooleG.memory_pool); + if (SwooleG.memory_pool != nullptr) { + delete SwooleG.memory_pool; + } + if (g_logger_instance) { + 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); } - bzero(&SwooleG, sizeof(SwooleG)); } -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"); +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; } - if (SwooleTG.aio_init) - { - swFatalError(SW_ERROR_OPERATION_NOT_SUPPORT, "can not create server after using async file operation"); + 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) != '/') { + swoole_warning("wrong absolute path '%s'", dir.c_str()); + return false; + } + + if (access(dir.c_str(), R_OK) < 0 && !swoole_mkdir_recursive(dir)) { + swoole_warning("create task tmp dir(%s) failed", dir.c_str()); + return false; + } + + sw_tg_buffer()->format("%s/" SW_TASK_TMP_FILE, dir.c_str()); + SwooleG.task_tmpfile = sw_tg_buffer()->to_std_string(); + + if (SwooleG.task_tmpfile.length() >= SW_TASK_TMP_PATH_SIZE) { + 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()) { + swoole_fatal_error(SW_ERROR_OPERATION_NOT_SUPPORT, "must be forked outside the coroutine"); + } + 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) - { + if (flags & SW_FORK_PRECHECK) { return 0; } pid_t pid = fork(); - if (pid == 0) - { - if (flags & SW_FORK_DAEMON) - { - SwooleG.pid = getpid(); + if (pid == 0) { + SwooleG.pid = getpid(); + if (flags & SW_FORK_DAEMON) { return pid; } /** * [!!!] All timers and event loops must be cleaned up after fork */ - if (SwooleTG.timer) - { + if (swoole_timer_is_available()) { swoole_timer_free(); } - if (!(flags & SW_FORK_EXEC)) - { - /** - * reset SwooleG.memory_pool - */ - SwooleG.memory_pool = swMemoryGlobal_new(SW_GLOBAL_MEMORY_PAGESIZE, 1); - if (SwooleG.memory_pool == NULL) - { - printf("[Worker] Fatal Error: global memory allocation failure"); - exit(1); - } - /** - * reopen log file - */ - swLog_reopen(SW_FALSE); - /** - * reset eventLoop - */ - if (SwooleTG.reactor) - { + if (SwooleG.memory_pool) { + delete SwooleG.memory_pool; + } + if (!(flags & SW_FORK_EXEC)) { + // reset SwooleG.memory_pool + SwooleG.memory_pool = new swoole::GlobalMemory(SW_GLOBAL_MEMORY_PAGESIZE, true); + // reopen log file + sw_logger()->reopen(); + // reset eventLoop + if (swoole_event_is_available()) { swoole_event_free(); - swTraceLog(SW_TRACE_REACTOR, "reactor has been destroyed"); + swoole_trace_log(SW_TRACE_REACTOR, "reactor has been destroyed"); } - } - else - { + } else { /** * close log fd */ - swLog_free(); + sw_logger()->close(); } /** * reset signal handler */ - swSignal_clear(); - /** - * reset global struct - */ - bzero(&SwooleWG, sizeof(SwooleWG)); - SwooleG.pid = getpid(); + swoole_signal_clear(); + + if (swoole_isset_hook(SW_GLOBAL_HOOK_AFTER_FORK)) { + swoole_call_hook(SW_GLOBAL_HOOK_AFTER_FORK, nullptr); + } } + return pid; } -uint64_t swoole_hash_key(char *str, int str_len) -{ - uint64_t hash = 5381; - int c, i = 0; - for (c = *str++; i < str_len; i++) - { - hash = (*((hash * 33) + str)) & 0x7fffffff; - hash = ((hash << 5) + hash) + c; +void swoole_thread_init(void) { + if (!SwooleTG.buffer_stack) { + SwooleTG.buffer_stack = new String(SW_STACK_BUFFER_SIZE); } - return hash; + swoole_signal_block_all(); } -void swoole_dump_ascii(const char *data, size_t size) -{ - for (size_t i = 0; i < size; i++) - { - printf("%d ", (unsigned) data[i]); +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]); } printf("\n"); } -void swoole_dump_bin(const char *data, char type, size_t size) -{ +void swoole_dump_bin(const char *data, char type, size_t size) { int i; int type_size = swoole_type_size(type); - if (type_size <= 0) - { + if (type_size <= 0) { return; } int n = size / type_size; - for (i = 0; i < n; i++) - { + for (i = 0; i < n; i++) { printf("%d,", swoole_unpack(type, data + type_size * i)); } printf("\n"); } -void swoole_dump_hex(const char *data, size_t outlen) -{ - for (size_t i = 0; i < outlen; ++i) - { - if ((i & 0x0fu) == 0) - { +void swoole_dump_hex(const char *data, size_t outlen) { + for (size_t i = 0; i < outlen; ++i) { + if ((i & 0x0fu) == 0) { printf("%08zX: ", i); } printf("%02X ", data[i]); - if (((i + 1) & 0x0fu) == 0) - { + if (((i + 1) & 0x0fu) == 0) { printf("\n"); } } @@ -312,78 +463,40 @@ void swoole_dump_hex(const char *data, size_t outlen) /** * Recursive directory creation */ -int swoole_mkdir_recursive(const char *dir) -{ +bool swoole_mkdir_recursive(const std::string &dir) { char tmp[PATH_MAX]; - int i, len = strlen(dir); + size_t i, len = dir.length(); - if (len + 1 > PATH_MAX) /* PATH_MAX limit includes string trailing null character */ - { - swWarn("mkdir(%s) failed. Path exceeds the limit of %d characters", dir, PATH_MAX - 1); - return -1; + // PATH_MAX limit includes string trailing null character + if (len + 1 > PATH_MAX) { + swoole_warning("mkdir(%s) failed. Path exceeds the limit of %d characters", dir.c_str(), PATH_MAX - 1); + return false; } - strncpy(tmp, dir, PATH_MAX); + swoole_strlcpy(tmp, dir.c_str(), PATH_MAX); - if (dir[len - 1] != '/') - { + if (dir[len - 1] != '/') { strcat(tmp, "/"); } len = strlen(tmp); - - for (i = 1; i < len; i++) - { - if (tmp[i] == '/') - { + for (i = 1; i < len; i++) { + if (tmp[i] == '/') { tmp[i] = 0; - if (access(tmp, R_OK) != 0) - { - if (mkdir(tmp, 0755) == -1) - { - swSysWarn("mkdir(%s) failed", tmp); + if (access(tmp, R_OK) != 0) { + if (mkdir(tmp, 0755) == -1) { + swoole_sys_warning("mkdir(%s) failed", tmp); return -1; } } tmp[i] = '/'; } } - return 0; -} -/** - * get parent dir name - */ -char* swoole_dirname(char *file) -{ - char *dirname = sw_strdup(file); - if (dirname == NULL) - { - swWarn("strdup() failed"); - return NULL; - } - - int i = strlen(dirname); - - if (dirname[i - 1] == '/') - { - i -= 2; - } - - for (; i > 0; i--) - { - if ('/' == dirname[i]) - { - dirname[i] = 0; - break; - } - } - return dirname; + return true; } -int swoole_type_size(char type) -{ - switch (type) - { +int swoole_type_size(char type) { + switch (type) { case 'c': case 'C': return 1; @@ -402,19 +515,17 @@ int swoole_type_size(char type) } } -char* swoole_dec2hex(int value, int base) -{ +char *swoole_dec2hex(ulong_t value, int base) { assert(base > 1 && base < 37); static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; - char buf[(sizeof(unsigned long) << 3) + 1]; + char buf[(sizeof(ulong_t) << 3) + 1]; char *ptr, *end; end = ptr = buf + sizeof(buf) - 1; *ptr = '\0'; - do - { + do { *--ptr = digits[value % base]; value /= base; } while (ptr > buf && value); @@ -422,81 +533,43 @@ char* swoole_dec2hex(int value, int base) return sw_strndup(ptr, end - ptr); } -size_t swoole_hex2dec(char** hex) -{ +ulong_t swoole_hex2dec(const char *hex, size_t *parsed_bytes) { size_t value = 0; - while (1) - { - char c = **hex; - if ((c >= '0') && (c <= '9')) - { + *parsed_bytes = 0; + const char *p = hex; + + if (strncasecmp(hex, "0x", 2) == 0) { + p += 2; + } + + while (1) { + char c = *p; + if ((c >= '0') && (c <= '9')) { value = value * 16 + (c - '0'); - } - else - { + } else { c = toupper(c); - if ((c >= 'A') && (c <= 'Z')) - { + if ((c >= 'A') && (c <= 'Z')) { value = value * 16 + (c - 'A') + 10; - } - else - { + } else { break; } } - (*hex)++; + p++; } + *parsed_bytes = p - hex; return value; } -size_t swoole_sync_writefile(int fd, const void *data, size_t len) -{ - ssize_t n = 0; - size_t count = len, towrite, written = 0; - - while (count > 0) - { - towrite = count; - if (towrite > SW_FILE_CHUNK_SIZE) - { - towrite = SW_FILE_CHUNK_SIZE; - } - n = write(fd, data, towrite); - if (n > 0) - { - data = (char*) data + n; - count -= n; - written += n; - } - else if (n == 0) - { - break; - } - else - { - if (errno == EINTR || errno == EAGAIN) - { - continue; - } - swSysWarn("write(%d, %zu) failed", fd, towrite); - break; - } - } - return written; -} - #ifndef RAND_MAX -#define RAND_MAX 2147483647 +#define RAND_MAX 2147483647 #endif -int swoole_rand(int min, int max) -{ - static int _seed = 0; +int swoole_rand(int min, int max) { + static time_t _seed = 0; assert(max > min); - if (_seed == 0) - { - _seed = time(NULL); + if (_seed == 0) { + _seed = time(nullptr); srand(_seed); } @@ -505,8 +578,7 @@ int swoole_rand(int min, int max) return _rand; } -int swoole_system_random(int min, int max) -{ +int swoole_system_random(int min, int max) { static int dev_random_fd = -1; char *next_random_byte; int bytes_to_read; @@ -514,11 +586,9 @@ int swoole_system_random(int min, int max) assert(max > min); - if (dev_random_fd == -1) - { + if (dev_random_fd == -1) { dev_random_fd = open("/dev/urandom", O_RDONLY); - if (dev_random_fd < 0) - { + if (dev_random_fd < 0) { return swoole_rand(min, max); } } @@ -526,64 +596,46 @@ int swoole_system_random(int min, int max) next_random_byte = (char *) &random_value; 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"); + if (read(dev_random_fd, next_random_byte, bytes_to_read) < bytes_to_read) { + swoole_sys_warning("read() from /dev/urandom failed"); return SW_ERR; } return min + (random_value % (max - min + 1)); } -void swoole_redirect_stdout(int new_fd) -{ - if (dup2(new_fd, STDOUT_FILENO) < 0) - { - swSysWarn("dup2(STDOUT_FILENO) failed"); +void swoole_redirect_stdout(int new_fd) { + if (dup2(new_fd, STDOUT_FILENO) < 0) { + swoole_sys_warning("dup2(STDOUT_FILENO) failed"); } - if (dup2(new_fd, STDERR_FILENO) < 0) - { - swSysWarn("dup2(STDERR_FILENO) failed"); + if (dup2(new_fd, STDERR_FILENO) < 0) { + swoole_sys_warning("dup2(STDERR_FILENO) failed"); } } -int swoole_version_compare(const char *version1, const char *version2) -{ +int swoole_version_compare(const char *version1, const char *version2) { int result = 0; - while (result == 0) - { - char* tail1; - char* tail2; + while (result == 0) { + char *tail1; + char *tail2; unsigned long ver1 = strtoul(version1, &tail1, 10); unsigned long ver2 = strtoul(version2, &tail2, 10); - if (ver1 < ver2) - { + if (ver1 < ver2) { result = -1; - } - else if (ver1 > ver2) - { + } else if (ver1 > ver2) { result = +1; - } - else - { + } else { version1 = tail1; version2 = tail2; - if (*version1 == '\0' && *version2 == '\0') - { + if (*version1 == '\0' && *version2 == '\0') { break; - } - else if (*version1 == '\0') - { + } else if (*version1 == '\0') { result = -1; - } - else if (*version2 == '\0') - { + } else if (*version2 == '\0') { result = +1; - } - else - { + } else { version1++; version2++; } @@ -592,267 +644,15 @@ int swoole_version_compare(const char *version1, const char *version2) return result; } -double swoole_microtime(void) -{ - struct timeval t; - gettimeofday(&t, NULL); - return (double) t.tv_sec + ((double) t.tv_usec / 1000000); -} - -void swoole_rtrim(char *str, int len) -{ - int i; - for (i = len; i > 0;) - { - switch (str[--i]) - { - case ' ': - case '\0': - case '\n': - case '\r': - case '\t': - case '\v': - str[i] = 0; - break; - default: - return; - } - } -} - -int swoole_tmpfile(char *filename) -{ -#if defined(HAVE_MKOSTEMP) && defined(HAVE_EPOLL) - int tmp_fd = mkostemp(filename, O_WRONLY | O_CREAT); -#else - int tmp_fd = mkstemp(filename); -#endif - - if (tmp_fd < 0) - { - swSysWarn("mkstemp(%s) failed", filename); - return SW_ERR; - } - else - { - return tmp_fd; - } -} - -long swoole_file_get_size(FILE *fp) -{ - long pos = ftell(fp); - if (fseek(fp, 0L, SEEK_END) < 0) - { - return SW_ERR; - } - long size = ftell(fp); - if (fseek(fp, pos, SEEK_SET) < 0) - { - return SW_ERR; - } - return size; -} - -long swoole_file_size(const char *filename) -{ - struct stat file_stat; - if (lstat(filename, &file_stat) < 0) - { - swSysWarn("lstat(%s) failed", filename); - SwooleG.error = errno; - return -1; - } - if ((file_stat.st_mode & S_IFMT) != S_IFREG) - { - SwooleG.error = EISDIR; - return -1; - } - return file_stat.st_size; -} - -swString* swoole_file_get_contents(const char *filename) -{ - long filesize = swoole_file_size(filename); - if (filesize < 0) - { - return NULL; - } - else if (filesize == 0) - { - swoole_error_log(SW_LOG_TRACE, SW_ERROR_FILE_EMPTY, "file[%s] is empty", filename); - return NULL; - } - else if (filesize > SW_MAX_FILE_CONTENT) - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_FILE_TOO_LARGE, "file[%s] is too large", filename); - return NULL; - } - - int fd = open(filename, O_RDONLY); - if (fd < 0) - { - swSysWarn("open(%s) failed", filename); - return NULL; - } - swString *content = swString_new(filesize); - if (!content) - { - close(fd); - return NULL; - } - - int readn = 0; - int n; - - while(readn < filesize) - { - n = pread(fd, content->str + readn, filesize - readn, readn); - if (n < 0) - { - if (errno == EINTR) - { - continue; - } - else - { - swSysWarn("pread(%d, %ld, %d) failed", fd, filesize - readn, readn); - swString_free(content); - close(fd); - return NULL; - } - } - readn += n; - } - close(fd); - content->length = readn; - return content; -} - -int swoole_file_put_contents(const char *filename, const char *content, size_t length) -{ - if (length <= 0) - { - swoole_error_log(SW_LOG_TRACE, SW_ERROR_FILE_EMPTY, "content is empty"); - return SW_ERR; - } - if (length > SW_MAX_FILE_CONTENT) - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_FILE_TOO_LARGE, "content is too large"); - return SW_ERR; - } - - int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0666); - if (fd < 0) - { - swSysWarn("open(%s) failed", filename); - return SW_ERR; - } - - size_t n, chunk_size, written = 0; - - while(written < length) - { - chunk_size = length - written; - if (chunk_size > SW_BUFFER_SIZE_BIG) - { - chunk_size = SW_BUFFER_SIZE_BIG; - } - n = write(fd, content + written, chunk_size); - if (n < 0) - { - if (errno == EINTR) - { - continue; - } - else - { - swSysWarn("write(%d, %d) failed", fd, chunk_size); - close(fd); - return -1; - } - } - written += n; - } - close(fd); - return SW_OK; -} - -size_t swoole_sync_readfile(int fd, void *buf, size_t len) -{ - ssize_t n = 0; - size_t count = len, toread, readn = 0; - - while (count > 0) - { - toread = count; - if (toread > SW_FILE_CHUNK_SIZE) - { - toread = SW_FILE_CHUNK_SIZE; - } - n = read(fd, buf, toread); - if (n > 0) - { - buf = (char *) buf + n; - count -= n; - readn += n; - } - else if (n == 0) - { - break; - } - else - { - if (errno == EINTR || errno == EAGAIN) - { - continue; - } - swSysWarn("read() failed"); - break; - } - } - return readn; -} - -swString* swoole_sync_readfile_eof(int fd) -{ - ssize_t n = 0; - swString *data = swString_new(SW_BUFFER_SIZE_STD); - if (data == NULL) - { - return data; - } - - while (1) - { - n = read(fd, data->str + data->length, data->size - data->length); - if (n <= 0) - { - return data; - } - else - { - if (swString_grow(data, (size_t) n)) - { - return data; - } - } - } - return data; -} - /** * Maximum common divisor */ -uint32_t swoole_common_divisor(uint32_t u, uint32_t v) -{ +uint32_t swoole_common_divisor(uint32_t u, uint32_t v) { assert(u > 0); assert(v > 0); uint32_t t; - while (u > 0) - { - if (u < v) - { + while (u > 0) { + if (u < v) { t = u; u = v; v = t; @@ -865,8 +665,7 @@ uint32_t swoole_common_divisor(uint32_t u, uint32_t v) /** * The least common multiple */ -uint32_t swoole_common_multiple(uint32_t u, uint32_t v) -{ +uint32_t swoole_common_multiple(uint32_t u, uint32_t v) { assert(u > 0); assert(v > 0); @@ -874,8 +673,7 @@ uint32_t swoole_common_multiple(uint32_t u, uint32_t v) uint32_t n_cup = v; int res = m_cup % n_cup; - while (res != 0) - { + while (res != 0) { m_cup = n_cup; n_cup = res; res = m_cup % n_cup; @@ -883,214 +681,44 @@ uint32_t swoole_common_multiple(uint32_t u, uint32_t v) return u * v / n_cup; } -size_t sw_snprintf(char *buf, size_t size, const char *format, ...) -{ +size_t sw_snprintf(char *buf, size_t size, const char *format, ...) { va_list args; va_start(args, format); int retval = vsnprintf(buf, size, format, args); va_end(args); - if (sw_unlikely(retval < 0)) - { + + if (size == 0) { + return retval; + } else if (sw_unlikely(retval < 0)) { retval = 0; buf[0] = '\0'; - } - else if (sw_unlikely(retval >= (int )size)) - { + } else if (sw_unlikely(retval >= (int) size)) { retval = size - 1; buf[retval] = '\0'; } return retval; } -size_t sw_vsnprintf(char *buf, size_t size, const char *format, va_list args) -{ +size_t sw_vsnprintf(char *buf, size_t size, const char *format, va_list args) { int retval = vsnprintf(buf, size, format, args); - if (sw_unlikely(retval < 0)) - { + if (sw_unlikely(retval < 0)) { retval = 0; buf[0] = '\0'; - } - else if (sw_unlikely(retval >= (int )size)) - { + } else if (sw_unlikely(retval >= (int) size)) { retval = size - 1; buf[retval] = '\0'; } return retval; } -int swoole_ioctl_set_block(int sock, int nonblock) -{ - int ret; - do - { - ret = ioctl(sock, FIONBIO, &nonblock); - } - while (ret == -1 && errno == EINTR); - - if (ret < 0) - { - swSysWarn("ioctl(%d, FIONBIO, %d) failed", sock, nonblock); - return SW_ERR; - } - else - { - return SW_OK; - } -} - -int swoole_fcntl_set_option(int sock, int nonblock, int cloexec) -{ - int opts, ret; - - if (nonblock >= 0) - { - do - { - opts = fcntl(sock, F_GETFL); - } - while (opts < 0 && errno == EINTR); - - if (opts < 0) - { - swSysWarn("fcntl(%d, GETFL) failed", sock); - } - - if (nonblock) - { - opts = opts | O_NONBLOCK; - } - else - { - opts = opts & ~O_NONBLOCK; - } - - do - { - ret = fcntl(sock, F_SETFL, opts); - } - while (ret < 0 && errno == EINTR); - - if (ret < 0) - { - swSysWarn("fcntl(%d, SETFL, opts) failed", sock); - return SW_ERR; - } - } - -#ifdef FD_CLOEXEC - if (cloexec >= 0) - { - do - { - opts = fcntl(sock, F_GETFD); - } - while (opts < 0 && errno == EINTR); - - if (opts < 0) - { - swSysWarn("fcntl(%d, GETFL) failed", sock); - } - - if (cloexec) - { - opts = opts | FD_CLOEXEC; - } - else - { - opts = opts & ~FD_CLOEXEC; - } - - do - { - ret = fcntl(sock, F_SETFD, opts); - } - while (ret < 0 && errno == EINTR); - - if (ret < 0) - { - swSysWarn("fcntl(%d, SETFD, opts) failed", sock); - return SW_ERR; - } - } -#endif - return SW_OK; -} - -static int *swoole_kmp_borders(char *needle, size_t nlen) -{ - if (!needle) - { - return NULL; - } - - int i, j, *borders = (int *) sw_malloc((nlen + 1) * sizeof(*borders)); - if (!borders) - { - return NULL; - } - - i = 0; - j = -1; - borders[i] = j; - while ((uint32_t) i < nlen) - { - while (j >= 0 && needle[i] != needle[j]) - { - j = borders[j]; - } - ++i; - ++j; - borders[i] = j; - } - return borders; -} - -static char *swoole_kmp_search(char *haystack, size_t haylen, char *needle, uint32_t nlen, int *borders) -{ - uint32_t max_index = haylen - nlen, i = 0, j = 0; - - while (i <= max_index) - { - while (j < nlen && *haystack && needle[j] == *haystack) - { - ++j; - ++haystack; - } - if (j == nlen) - { - return haystack - nlen; - } - if (!(*haystack)) - { - return NULL; - } - if (j == 0) - { - ++haystack; - ++i; - } - else - { - do - { - i += j - (uint32_t) borders[j]; - j = borders[j]; - } while (j > 0 && needle[j] != *haystack); - } - } - return NULL; -} - -int swoole_itoa(char *buf, long value) -{ +int swoole_itoa(char *buf, long value) { long i = 0, j; long sign_mask; unsigned long nn; sign_mask = value >> (sizeof(long) * 8 - 1); nn = (value + sign_mask) ^ sign_mask; - do - { + do { buf[i++] = nn % 10 + '0'; } while (nn /= 10); @@ -1101,8 +729,7 @@ int swoole_itoa(char *buf, long value) int s_len = i; char swap; - for (i = 0, j = s_len - 1; i < j; ++i, --j) - { + for (i = 0, j = s_len - 1; i < j; ++i, --j) { swap = buf[i]; buf[i] = buf[j]; buf[j] = swap; @@ -1111,295 +738,53 @@ int swoole_itoa(char *buf, long value) return s_len; } -char *swoole_kmp_strnstr(char *haystack, char *needle, uint32_t length) -{ - if (!haystack || !needle) - { - return NULL; - } - size_t nlen = strlen(needle); - if (length < nlen) - { - return NULL; - } - int *borders = swoole_kmp_borders(needle, nlen); - if (!borders) - { - return NULL; - } - char *match = swoole_kmp_search(haystack, length, needle, nlen, borders); - sw_free(borders); - return match; -} - -/** - * DNS lookup - */ -#ifdef HAVE_GETHOSTBYNAME2_R -int swoole_gethostbyname(int flags, const char *name, char *addr) -{ - int __af = flags & (~SW_DNS_LOOKUP_RANDOM); - int index = 0; - int rc, err; - int buf_len = 256; - struct hostent hbuf; - struct hostent *result; - - char *buf = (char*) sw_malloc(buf_len); - if (!buf) - { - return SW_ERR; - } - memset(buf, 0, buf_len); - while ((rc = gethostbyname2_r(name, __af, &hbuf, buf, buf_len, &result, &err)) == ERANGE) - { - buf_len *= 2; - char *tmp = (char*) sw_realloc(buf, buf_len); - if (NULL == tmp) - { - sw_free(buf); - return SW_ERR; - } - else - { - buf = tmp; - } - } - - if (0 != rc || NULL == result) - { - sw_free(buf); - return SW_ERR; - } - - union - { - char v4[INET_ADDRSTRLEN]; - char v6[INET6_ADDRSTRLEN]; - } addr_list[SW_DNS_HOST_BUFFER_SIZE]; - - int i = 0; - for (i = 0; i < SW_DNS_HOST_BUFFER_SIZE; i++) - { - if (hbuf.h_addr_list[i] == NULL) - { - break; - } - if (__af == AF_INET) - { - memcpy(addr_list[i].v4, hbuf.h_addr_list[i], hbuf.h_length); - } - else - { - memcpy(addr_list[i].v6, hbuf.h_addr_list[i], hbuf.h_length); - } - } - if (__af == AF_INET) - { - memcpy(addr, addr_list[index].v4, hbuf.h_length); - } - else - { - memcpy(addr, addr_list[index].v6, hbuf.h_length); - } - - sw_free(buf); - - return SW_OK; -} -#else -int swoole_gethostbyname(int flags, const char *name, char *addr) -{ - int __af = flags & (~SW_DNS_LOOKUP_RANDOM); - int index = 0; - - struct hostent *host_entry; - if (!(host_entry = gethostbyname2(name, __af))) - { - return SW_ERR; - } - - union - { - char v4[INET_ADDRSTRLEN]; - char v6[INET6_ADDRSTRLEN]; - } addr_list[SW_DNS_HOST_BUFFER_SIZE]; - - int i = 0; - for (i = 0; i < SW_DNS_HOST_BUFFER_SIZE; i++) - { - if (host_entry->h_addr_list[i] == NULL) - { - break; - } - if (__af == AF_INET) - { - memcpy(addr_list[i].v4, host_entry->h_addr_list[i], host_entry->h_length); - } - else - { - memcpy(addr_list[i].v6, host_entry->h_addr_list[i], host_entry->h_length); - } - } - if (__af == AF_INET) - { - memcpy(addr, addr_list[index].v4, host_entry->h_length); - } - else - { - memcpy(addr, addr_list[index].v6, host_entry->h_length); - } - return SW_OK; -} -#endif - -int swoole_getaddrinfo(swRequest_getaddrinfo *req) -{ - struct addrinfo *result = NULL; - struct addrinfo *ptr = NULL; - struct addrinfo hints; - - bzero(&hints, sizeof(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); - if (ret != 0) - { - req->error = ret; - return SW_ERR; - } - - void *buffer = req->result; - int i = 0; - for (ptr = result; ptr != NULL; ptr = ptr->ai_next) - { - switch (ptr->ai_family) - { - case AF_INET: - memcpy((char *) buffer + (i * sizeof(struct sockaddr_in)), 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)); - break; - default: - swWarn("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; -} - -SW_API int swoole_add_function(const char *name, void* func) -{ - if (SwooleG.functions == NULL) - { - SwooleG.functions = swHashMap_new(64, NULL); - if (SwooleG.functions == NULL) - { - return SW_ERR; - } - } - if (swHashMap_find(SwooleG.functions, (char *) name, strlen(name)) != NULL) - { - swWarn("Function '%s' has already been added", name); - return SW_ERR; - } - return swHashMap_add(SwooleG.functions, (char *) name, strlen(name), func); -} - -SW_API void* swoole_get_function(const char *name, uint32_t length) -{ - if (!SwooleG.functions) - { - return NULL; - } - return swHashMap_find(SwooleG.functions, name, length); -} - -SW_API int swoole_add_hook(enum swGlobal_hook_type type, swCallback func, int push_back) -{ - return swoole::hook_add(SwooleG.hooks, type, func, push_back); -} - -SW_API void swoole_call_hook(enum swGlobal_hook_type type, void *arg) -{ - swoole::hook_call(SwooleG.hooks, type, arg); -} - -int swoole_shell_exec(const char *command, pid_t *pid, uint8_t get_error_stream) -{ +int swoole_shell_exec(const char *command, pid_t *pid, bool get_error_stream) { pid_t child_pid; int fds[2]; - if (pipe(fds) < 0) - { + if (pipe(fds) < 0) { return SW_ERR; } - if ((child_pid = fork()) == -1) - { - swSysWarn("fork() failed"); + if ((child_pid = fork()) == -1) { + swoole_sys_warning("fork() failed"); close(fds[0]); close(fds[1]); return SW_ERR; } - if (child_pid == 0) - { + if (child_pid == 0) { close(fds[SW_PIPE_READ]); - if (get_error_stream) - { - if (fds[SW_PIPE_WRITE] == fileno(stdout)) - { + if (get_error_stream) { + if (fds[SW_PIPE_WRITE] == fileno(stdout)) { dup2(fds[SW_PIPE_WRITE], fileno(stderr)); - } - else if (fds[SW_PIPE_WRITE] == fileno(stderr)) - { + } else if (fds[SW_PIPE_WRITE] == fileno(stderr)) { dup2(fds[SW_PIPE_WRITE], fileno(stdout)); - } - else - { + } else { dup2(fds[SW_PIPE_WRITE], fileno(stdout)); dup2(fds[SW_PIPE_WRITE], fileno(stderr)); close(fds[SW_PIPE_WRITE]); } - } - else - { - if (fds[SW_PIPE_WRITE] != fileno(stdout)) - { + } else { + if (fds[SW_PIPE_WRITE] != fileno(stdout)) { dup2(fds[SW_PIPE_WRITE], fileno(stdout)); close(fds[SW_PIPE_WRITE]); } } - execl("/bin/sh", "sh", "-c", command, NULL); + execl("/bin/sh", "sh", "-c", command, nullptr); exit(127); - } - else - { + } else { *pid = child_pid; close(fds[SW_PIPE_WRITE]); } return fds[SW_PIPE_READ]; } -char* swoole_string_format(size_t n, const char *format, ...) -{ - char *buf = (char*) sw_malloc(n); - if (!buf) - { - return NULL; +char *swoole_string_format(size_t n, const char *format, ...) { + char *buf = (char *) sw_malloc(n); + if (!buf) { + return nullptr; } int ret; @@ -1407,79 +792,100 @@ char* swoole_string_format(size_t n, const char *format, ...) va_start(va_list, format); ret = vsnprintf(buf, n, format, va_list); va_end(va_list); - if (ret >= 0) - { + if (ret >= 0) { return buf; } sw_free(buf); - return NULL; + return nullptr; } -int swoole_get_systemd_listen_fds() -{ - int ret; - char *e; +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', +}; - e = getenv("LISTEN_FDS"); - if (!e) - { - return 0; +void swoole_random_string(char *buf, size_t size) { + size_t i = 0; + for (; i < size; i++) { + buf[i] = characters[swoole_rand(0, sizeof(characters) - 1)]; } - ret = atoi(e); - if (ret < 1) - { - swWarn("invalid LISTEN_FDS"); - return 0; + 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)]); } - else if (ret >= SW_MAX_LISTEN_PORT) - { +} + +size_t swoole_random_bytes(char *buf, size_t size) { + size_t read_bytes = 0; + ssize_t n; + + while (read_bytes < size) { + size_t amount_to_read = size - read_bytes; + n = getrandom(buf + read_bytes, amount_to_read, 0); + if (n == -1) { + if (errno == EINTR || errno == EAGAIN) { + continue; + } else { + break; + } + } + read_bytes += (size_t) n; + } + + return read_bytes; +} + +bool swoole_get_env(const char *name, int *value) { + const char *e = getenv(name); + if (!e) { + return false; + } + *value = std::stoi(e); + return true; +} + +int swoole_get_systemd_listen_fds() { + int ret; + if (!swoole_get_env("LISTEN_FDS", &ret)) { + 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"); - return 0; + return -1; } return ret; } -#ifdef HAVE_EXECINFO -void swoole_print_trace(void) -{ +#ifdef HAVE_BOOST_STACKTRACE +#include +#include +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]; + void *array[16]; int stack_num = backtrace(array, size); - char** stacktrace = backtrace_symbols(array, stack_num); + char **stacktrace = backtrace_symbols(array, stack_num); int i; - for (i = 0; i < stack_num; ++i) - { + for (i = 0; i < stack_num; ++i) { printf("%s\n", stacktrace[i]); } free(stacktrace); } +#else +void swoole_print_backtrace(void) {} #endif -#ifndef HAVE_CLOCK_GETTIME -#ifdef __MACH__ -int clock_gettime(clock_id_t which_clock, struct timespec *t) -{ - // be more careful in a multithreaded environement - if (!orwl_timestart) - { - mach_timebase_info_data_t tb = - { 0}; - mach_timebase_info(&tb); - orwl_timebase = tb.numer; - orwl_timebase /= tb.denom; - orwl_timestart = mach_absolute_time(); - } - double diff = (mach_absolute_time() - orwl_timestart) * orwl_timebase; - t->tv_sec = diff * ORWL_NANO; - t->tv_nsec = diff - (t->tv_sec * ORWL_GIGA); - return 0; -} -#endif -#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; @@ -1487,40 +893,108 @@ static void swoole_fatal_error(int code, const char *format, ...) va_start(args, format); retval += sw_vsnprintf(sw_error + retval, SW_ERROR_MSG_SIZE - retval, format, args); va_end(args); - SwooleG.write_log(SW_LOG_ERROR, sw_error, retval); - exit(1); + sw_logger()->put(SW_LOG_ERROR, sw_error, retval); + swoole_exit(1); +} + +void swoole_exit(int __status) { +#ifdef SW_THREAD + /** + * If multiple threads call exit simultaneously, it can result in a crash. + * Implementing locking mechanisms can prevent concurrent calls to exit. + */ + std::unique_lock _lock(sw_thread_lock); +#endif + exit(__status); +} + +namespace swoole { +//------------------------------------------------------------------------------- +size_t DataHead::dump(char *_buf, size_t _len) { + return sw_snprintf(_buf, + _len, + "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, + ext_flags, + time); +} + +void DataHead::print() { + sw_tg_buffer()->length = dump(sw_tg_buffer()->str, sw_tg_buffer()->size); + printf("%.*s", (int) sw_tg_buffer()->length, sw_tg_buffer()->str); +} + +std::string dirname(const std::string &file) { + size_t index = file.find_last_of('/'); + if (index == std::string::npos) { + return std::string(); + } else if (index == 0) { + return "/"; + } + return file.substr(0, index); +} + +int hook_add(void **hooks, int type, const Callback &func, int push_back) { + if (hooks[type] == nullptr) { + hooks[type] = new std::list; + } + + std::list *l = reinterpret_cast *>(hooks[type]); + if (push_back) { + l->push_back(func); + } else { + l->push_front(func); + } + + return SW_OK; } -void swDataHead_dump(const swDataHead *data) -{ - printf("swDataHead[%p]\n" - "{\n" - " int fd = %d;\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" - "}\n", data, data->fd, data->len, data->reactor_id, data->type, data->flags, data->server_fd); +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); + } } /** * return the first file of the intersection, in order of vec1 */ -std::string swoole::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; +std::string intersection(std::vector &vec1, std::set &vec2) { + for (const auto &vec1_item : vec1) { + if (vec2.find(vec1_item) != vec2.end()) { + return vec1_item; } - return false; - }); + } - return result; + return ""; +} + +double microtime(void) { + struct timeval t; + gettimeofday(&t, nullptr); + return (double) t.tv_sec + ((double) t.tv_usec / 1000000); } + +//------------------------------------------------------------------------------- +}; // namespace swoole diff --git a/src/protocol/base64.cc b/src/core/base64.cc similarity index 83% rename from src/protocol/base64.cc rename to src/core/base64.cc index f8da0e530ba..0c430bfbf24 100644 --- a/src/protocol/base64.cc +++ b/src/core/base64.cc @@ -12,13 +12,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 -#include -#include "base64.h" +#include "swoole_base64.h" + +namespace swoole { + +// clang-format off /* BASE 64 encode table */ static char base64en[] = @@ -56,15 +58,14 @@ static signed char base64de[] = { 44, 45, 46, 47, 48, 49, 50, 51, }; -size_t swBase64_encode(const unsigned char *in, size_t inlen, char *out) -{ +// clang-format on + +size_t base64_encode(const unsigned char *in, size_t inlen, char *out) { size_t i, j; - for (i = j = 0; i < inlen; i++) - { + for (i = j = 0; i < inlen; i++) { int s = i % 3; /* from 6/gcd(6, 8) */ - switch (s) - { + switch (s) { case 0: out[j++] = base64en[(in[i] >> 2) & 0x3F]; continue; @@ -79,14 +80,11 @@ size_t swBase64_encode(const unsigned char *in, size_t inlen, char *out) /* move back */ i -= 1; /* check the last and add padding */ - if ((i % 3) == 0) - { + if ((i % 3) == 0) { out[j++] = base64en[(in[i] & 0x3) << 4]; out[j++] = BASE64_PAD; out[j++] = BASE64_PAD; - } - else if ((i % 3) == 1) - { + } else if ((i % 3) == 1) { out[j++] = base64en[(in[i] & 0xF) << 2]; out[j++] = BASE64_PAD; } @@ -95,41 +93,34 @@ size_t swBase64_encode(const unsigned char *in, size_t inlen, char *out) return j; } -size_t swBase64_decode(const char *in, size_t inlen, char* out) -{ +size_t base64_decode(const char *in, size_t inlen, char *out) { size_t i, j; - for (i = j = 0; i < inlen; i++) - { + for (i = j = 0; i < inlen; i++) { int c; int s = i % 4; /* from 8/gcd(6, 8) */ - if (in[i] == '=') - { + if (in[i] == '=') { break; } - if (in[i] < BASE64DE_FIRST || in[i] > BASE64DE_LAST || (c = base64de[in[i] - BASE64DE_FIRST]) == -1) - { + if (in[i] < BASE64DE_FIRST || in[i] > BASE64DE_LAST || (c = base64de[in[i] - BASE64DE_FIRST]) == -1) { return 0; } - switch (s) - { + switch (s) { case 0: out[j] = c << 2; continue; case 1: out[j++] += (c >> 4) & 0x3; /* if not last char with padding */ - if (i < (inlen - 3) || in[inlen - 2] != '=') - out[j] = (c & 0xF) << 4; + if (i < (inlen - 3) || in[inlen - 2] != '=') out[j] = (c & 0xF) << 4; continue; case 2: out[j++] += (c >> 2) & 0xF; /* if not last char with padding */ - if (i < (inlen - 2) || in[inlen - 1] != '=') - out[j] = (c & 0x3) << 6; + if (i < (inlen - 2) || in[inlen - 1] != '=') out[j] = (c & 0x3) << 6; continue; case 3: out[j++] += c; @@ -139,3 +130,5 @@ size_t swBase64_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 62c9eeff3ca..2ce833790ca 100644 --- a/src/core/channel.cc +++ b/src/core/channel.cc @@ -10,159 +10,145 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | @link https://www.swoole.com/ | + | @contact team@swoole.com | + | @license https://github.com/swoole/swoole-src/blob/master/LICENSE | + | @Author Tianfeng Han | +----------------------------------------------------------------------+ */ #include "swoole.h" +#include "swoole_memory.h" +#include "swoole_channel.h" +#include "swoole_lock.h" +#include "swoole_pipe.h" -#define SW_CHANNEL_MIN_MEM (1024*64) +namespace swoole { -typedef struct _swChannel_item -{ +#define SW_CHANNEL_MIN_MEM (1024 * 64) + +struct ChannelSlice { int length; char data[0]; -} swChannel_item; +}; -swChannel* swChannel_new(size_t size, size_t maxlen, int flags) -{ +Channel *Channel::make(size_t size, size_t maxlen, int flags) { assert(size >= maxlen); - int ret; void *mem; - //use shared memory - if (flags & SW_CHAN_SHM) - { + // use shared memory + if (flags & SW_CHAN_SHM) { /** * overflow space */ - mem = sw_shm_malloc(size + sizeof(swChannel) + maxlen + sizeof(swChannel_item)); - } - else - { - mem = sw_malloc(size + sizeof(swChannel) + maxlen + sizeof(swChannel_item)); + mem = sw_shm_malloc(size + sizeof(Channel) + maxlen + sizeof(ChannelSlice)); + } else { + mem = sw_malloc(size + sizeof(Channel) + maxlen + sizeof(ChannelSlice)); } - if (mem == NULL) - { - swWarn("swChannel_create: malloc(%ld) failed", size); - return NULL; + if (mem == nullptr) { + swoole_warning("alloc(%ld) failed", size); + return nullptr; } - swChannel *object = (swChannel *) mem; - mem = (char*) mem + sizeof(swChannel); - bzero(object, sizeof(swChannel)); + Channel *object = (Channel *) mem; + mem = (char *) mem + sizeof(Channel); + + *object = {}; - //overflow space + // overflow space object->size = size; object->mem = mem; object->maxlen = maxlen; - object->flag = flags; - - //use lock - if (flags & SW_CHAN_LOCK) - { - //init lock - if (swMutex_create(&object->lock, 1) < 0) - { - swWarn("mutex init failed"); - return NULL; - } + object->flags = flags; + + // use lock + if (flags & SW_CHAN_LOCK) { + // init lock + object->lock = new Mutex(Mutex::PROCESS_SHARED); } - //use notify - if (flags & SW_CHAN_NOTIFY) - { - ret = swPipeNotify_auto(&object->notify_fd, 1, 1); - if (ret < 0) - { - swWarn("notify_fd init failed"); - return NULL; + // use notify + if (flags & SW_CHAN_NOTIFY) { + object->notify_pipe = new Pipe(true); + if (!object->notify_pipe->ready()) { + swoole_warning("notify_fd init failed"); + delete object->notify_pipe; + return nullptr; } } + return object; } /** * push data(no lock) */ -int swChannel_in(swChannel *object, void *in, int data_length) -{ - assert(data_length <= object->maxlen); - if (swChannel_full(object)) - { +int Channel::in(const void *in_data, int data_length) { + assert(data_length <= maxlen); + if (full()) { return SW_ERR; } - swChannel_item *item; + ChannelSlice *item; int msize = sizeof(item->length) + data_length; - if (object->tail < object->head) - { - //no enough memory space - if ((object->head - object->tail) < msize) - { + if (tail < head) { + // no enough memory space + if ((head - tail) < msize) { return SW_ERR; } - item = (swChannel_item *) ((char*) object->mem + object->tail); - object->tail += msize; - } - else - { - item = (swChannel_item *) ((char*) object->mem + object->tail); - object->tail += msize; - if (object->tail >= (off_t) object->size) - { - object->tail = 0; - object->tail_tag = 1 - object->tail_tag; + item = (ChannelSlice *) ((char *) mem + tail); + tail += msize; + } else { + item = (ChannelSlice *) ((char *) mem + tail); + tail += msize; + if (tail >= (off_t) size) { + tail = 0; + tail_tag = 1 - tail_tag; } } - object->num++; - object->bytes += data_length; + num++; + bytes += data_length; item->length = data_length; - memcpy(item->data, in, data_length); + memcpy(item->data, in_data, data_length); return SW_OK; } /** * pop data(no lock) */ -int swChannel_out(swChannel *object, void *out, int buffer_length) -{ - if (swChannel_empty(object)) - { +int Channel::out(void *out_buf, int buffer_length) { + if (empty()) { return SW_ERR; } - swChannel_item *item = (swChannel_item *) ((char*) object->mem + object->head); + ChannelSlice *item = (ChannelSlice *) ((char *) mem + head); assert(buffer_length >= item->length); - memcpy(out, item->data, item->length); - object->head += (item->length + sizeof(item->length)); - if (object->head >= (off_t) object->size) - { - object->head = 0; - object->head_tag = 1 - object->head_tag; + memcpy(out_buf, item->data, item->length); + head += (item->length + sizeof(item->length)); + if (head >= (off_t) size) { + head = 0; + head_tag = 1 - head_tag; } - object->num--; - object->bytes -= item->length; + num--; + bytes -= item->length; return item->length; } /** * peek data */ -int swChannel_peek(swChannel *object, void *out, int buffer_length) -{ - if (swChannel_empty(object)) - { +int Channel::peek(void *out, int buffer_length) { + if (empty()) { return SW_ERR; } int length; - object->lock.lock(&object->lock); - swChannel_item *item = (swChannel_item *) ((char*) object->mem + object->head); + lock->lock(); + ChannelSlice *item = (ChannelSlice *) ((char *) mem + head); assert(buffer_length >= item->length); memcpy(out, item->data, item->length); length = item->length; - object->lock.unlock(&object->lock); + lock->unlock(); return length; } @@ -170,82 +156,82 @@ int swChannel_peek(swChannel *object, void *out, int buffer_length) /** * wait notify */ -int swChannel_wait(swChannel *object) -{ - assert(object->flag & SW_CHAN_NOTIFY); - uint64_t flag; - return object->notify_fd.read(&object->notify_fd, &flag, sizeof(flag)); +int Channel::wait() { + assert(flags & SW_CHAN_NOTIFY); + uint64_t value; + return notify_pipe->read(&value, sizeof(value)); } /** * new data coming, notify to customer */ -int swChannel_notify(swChannel *object) -{ - assert(object->flag & SW_CHAN_NOTIFY); - uint64_t flag = 1; - return object->notify_fd.write(&object->notify_fd, &flag, sizeof(flag)); +int Channel::notify() { + assert(flags & SW_CHAN_NOTIFY); + uint64_t value = 1; + return notify_pipe->write(&value, sizeof(value)); } /** * push data (lock) */ -int swChannel_push(swChannel *object, void *in, int data_length) -{ - assert(object->flag & SW_CHAN_LOCK); - object->lock.lock(&object->lock); - int ret = swChannel_in(object, in, data_length); - object->lock.unlock(&object->lock); +int Channel::push(const void *in_data, int data_length) { + assert(flags & SW_CHAN_LOCK); + lock->lock(); + int ret = in(in_data, data_length); + lock->unlock(); return ret; } /** * free channel */ -void swChannel_free(swChannel *object) -{ - if (object->flag & SW_CHAN_LOCK) - { - object->lock.free(&object->lock); +void Channel::destroy() { + if (flags & SW_CHAN_LOCK) { + delete lock; } - if (object->flag & SW_CHAN_NOTIFY) - { - object->notify_fd.close(&object->notify_fd); + if (flags & SW_CHAN_NOTIFY) { + notify_pipe->close(); + delete notify_pipe; } - if (object->flag & SW_CHAN_SHM) - { - sw_shm_free(object); - } - else - { - sw_free(object); + if (flags & SW_CHAN_SHM) { + sw_shm_free(this); + } else { + sw_free(this); } } /** * pop data (lock) */ -int swChannel_pop(swChannel *object, void *out, int buffer_length) -{ - assert(object->flag & SW_CHAN_LOCK); - object->lock.lock(&object->lock); - int n = swChannel_out(object, out, buffer_length); - object->lock.unlock(&object->lock); +int Channel::pop(void *out_buf, int buffer_length) { + assert(flags & SW_CHAN_LOCK); + lock->lock(); + int n = out(out_buf, buffer_length); + lock->unlock(); return n; } -void swChannel_print(swChannel *chan) -{ - printf("swChannel\n{\n" - " off_t head = %ld;\n" - " off_t tail = %ld;\n" - " size_t size = %ld;\n" - " char head_tag = %d;\n" - " char tail_tag = %d;\n" - " int num = %d;\n" - " size_t bytes = %ld;\n" - " int flag = %d;\n" - " int maxlen = %d;\n" - "\n}\n", (long)chan->head, (long)chan->tail, chan->size, chan->tail_tag, chan->head_tag, chan->num, chan->bytes, - chan->flag, chan->maxlen); +void Channel::print() { + printf("Channel\n{\n" + " off_t head = %ld;\n" + " off_t tail = %ld;\n" + " size_t size = %ld;\n" + " char head_tag = %d;\n" + " char tail_tag = %d;\n" + " int num = %d;\n" + " size_t bytes = %ld;\n" + " int flag = %d;\n" + " int maxlen = %d;\n" + "\n}\n", + (long) head, + (long) tail, + size, + tail_tag, + head_tag, + num, + bytes, + flags, + maxlen); } + +} // namespace swoole diff --git a/src/core/crc32.cc b/src/core/crc32.cc new file mode 100644 index 00000000000..aabf6f784c8 --- /dev/null +++ b/src/core/crc32.cc @@ -0,0 +1,54 @@ +/* + +----------------------------------------------------------------------+ + | 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 "swoole.h" +#include "swoole_hash.h" + +static const int CRC32_TABLE_SIZE = 256; +static uint32_t crc32_table[CRC32_TABLE_SIZE]; +static bool generated = false; + +static void generate_table(uint32_t (&table)[CRC32_TABLE_SIZE]) { + uint32_t polynomial = 0xEDB88320; + for (uint32_t i = 0; i < CRC32_TABLE_SIZE; i++) { + uint32_t c = i; + for (size_t j = 0; j < 8; j++) { + if (c & 1) { + c = polynomial ^ (c >> 1); + } else { + c >>= 1; + } + } + table[i] = c; + } +} + +uint32_t swoole_crc32(const char *data, uint32_t size) { + if (sw_unlikely(!generated)) { + generate_table(crc32_table); + } + + uint32_t crcinit = 0; + uint32_t crc = crcinit ^ 0xffffffff; + for (; size--; ++data) { + crc = ((crc >> 8) & 0x00ffffff) ^ crc32_table[(crc ^ (*data)) & 0xff]; + } + + return (crc ^ 0xffffffff); +} diff --git a/src/core/error.cc b/src/core/error.cc index 68288d1789e..4fdc154ab28 100644 --- a/src/core/error.cc +++ b/src/core/error.cc @@ -10,38 +10,28 @@ | to obtain it through the world-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 -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) -{ - if (code < SW_ERROR_BEGIN) - { +const char *swoole_strerror(int code) { + if (code < SW_ERROR_BEGIN) { return strerror(code); } /* swstrerror {{{*/ - switch(code) - { + switch (code) { case SW_ERROR_MALLOC_FAIL: return "Malloc fail"; case SW_ERROR_SYSTEM_CALL_FAIL: @@ -58,6 +48,18 @@ const char* swoole_strerror(int code) return "Operation not support"; case SW_ERROR_PROTOCOL_ERROR: 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: @@ -70,10 +72,18 @@ 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: return "Session closed by server"; case SW_ERROR_SESSION_CLOSED_BY_CLIENT: @@ -108,12 +118,16 @@ const char* swoole_strerror(int code) return "SSL bad protocol"; case SW_ERROR_SSL_RESET: return "SSL reset"; + case SW_ERROR_SSL_HANDSHAKE_FAILED: + return "SSL handshake failed"; case SW_ERROR_PACKAGE_LENGTH_TOO_LARGE: return "Package length too large"; case SW_ERROR_PACKAGE_LENGTH_NOT_FOUND: 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: @@ -126,6 +140,10 @@ const char* swoole_strerror(int code) return "Http2 stream no header"; case SW_ERROR_HTTP2_STREAM_NOT_FOUND: 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: @@ -136,6 +154,8 @@ const char* swoole_strerror(int code) return "Client no connection"; case SW_ERROR_SOCKET_CLOSED: return "Socket closed"; + case SW_ERROR_SOCKET_POLL_TIMEOUT: + return "Socket poll timeout"; case SW_ERROR_SOCKS5_UNSUPPORT_VERSION: return "Socks5 unsupport version"; case SW_ERROR_SOCKS5_UNSUPPORT_METHOD: @@ -144,10 +164,22 @@ const char* swoole_strerror(int code) return "Socks5 auth failed"; case SW_ERROR_SOCKS5_SERVER_ERROR: return "Socks5 server error"; + case SW_ERROR_SOCKS5_HANDSHAKE_FAILED: + return "Socks5 handshake failed"; case SW_ERROR_HTTP_PROXY_HANDSHAKE_ERROR: return "Http proxy handshake error"; case SW_ERROR_HTTP_INVALID_PROTOCOL: return "Http invalid protocol"; + case SW_ERROR_HTTP_PROXY_HANDSHAKE_FAILED: + 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: @@ -156,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: @@ -178,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: @@ -208,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__ @@ -217,10 +279,25 @@ const char* swoole_strerror(int code) #endif return buffer; } -/*}}}*/ + /*}}}*/ } -void swoole_throw_error(int code) -{ +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/hashmap.cc b/src/core/hashmap.cc deleted file mode 100644 index 79862612693..00000000000 --- a/src/core/hashmap.cc +++ /dev/null @@ -1,522 +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 "swoole.h" -#include "uthash.h" -#include "hash.h" - -typedef struct swHashMap_node -{ - uint64_t key_int; - char *key_str; - void *data; - UT_hash_handle hh; -} swHashMap_node; - -static int swHashMap_node_delete(swHashMap_node *root, swHashMap_node *del_node); - -static sw_inline void swHashMap_node_dtor(swHashMap *hmap, swHashMap_node *node) -{ - if (hmap->dtor) - { - hmap->dtor(node->data); - } -} - -static sw_inline void swHashMap_node_free(swHashMap *hmap, swHashMap_node *node) -{ - swHashMap_node_dtor(hmap, node); - sw_free(node->key_str); - sw_free(node); -} - -static sw_inline int swHashMap_node_add(swHashMap_node *root, swHashMap_node *add) -{ - unsigned _ha_bkt; - add->hh.next = NULL; - add->hh.key = add->key_str; - add->hh.keylen = add->key_int; - - root->hh.tbl->tail->next = add; - add->hh.prev = ELMT_FROM_HH(root->hh.tbl, root->hh.tbl->tail); - root->hh.tbl->tail = &(add->hh); - - root->hh.tbl->num_items++; - add->hh.tbl = root->hh.tbl; - add->hh.hashv = swoole_hash_jenkins(add->key_str, add->key_int); - _ha_bkt = add->hh.hashv & (root->hh.tbl->num_buckets - 1); - - HASH_ADD_TO_BKT(root->hh.tbl->buckets[_ha_bkt], &add->hh); - - return SW_OK; -} - -static sw_inline swHashMap_node* swHashMap_node_each(swHashMap* hmap) -{ - if (hmap->iterator) - { - return hmap->iterator = (struct swHashMap_node *) hmap->iterator->hh.next; - } - return NULL; -} - -swHashMap* swHashMap_new(uint32_t bucket_num, swHashMap_dtor dtor) -{ - swHashMap *hmap = (swHashMap *) sw_malloc(sizeof(swHashMap)); - if (!hmap) - { - swWarn("malloc[1] failed"); - return NULL; - } - swHashMap_node *root = (swHashMap_node *) sw_malloc(sizeof(swHashMap_node)); - if (!root) - { - swWarn("malloc[2] failed"); - sw_free(hmap); - return NULL; - } - - bzero(hmap, sizeof(swHashMap)); - hmap->root = root; - hmap->iterator = root; - - bzero(root, sizeof(swHashMap_node)); - - root->hh.tbl = (UT_hash_table*) sw_malloc(sizeof(UT_hash_table)); - if (!(root->hh.tbl)) - { - swWarn("malloc for table failed"); - sw_free(hmap); - return NULL; - } - - memset(root->hh.tbl, 0, sizeof(UT_hash_table)); - root->hh.tbl->tail = &(root->hh); - root->hh.tbl->num_buckets = SW_HASHMAP_INIT_BUCKET_N; - root->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; - root->hh.tbl->hho = (char*) (&root->hh) - (char*) root; - root->hh.tbl->buckets = (UT_hash_bucket*) sw_malloc(SW_HASHMAP_INIT_BUCKET_N * sizeof(struct UT_hash_bucket)); - if (!root->hh.tbl->buckets) - { - swWarn("malloc for buckets failed"); - sw_free(hmap); - return NULL; - } - memset(root->hh.tbl->buckets, 0, SW_HASHMAP_INIT_BUCKET_N * sizeof(struct UT_hash_bucket)); - root->hh.tbl->signature = HASH_SIGNATURE; - - hmap->dtor = dtor; - - return hmap; -} - -int swHashMap_add(swHashMap* hmap, const char *key, uint16_t key_len, void *data) -{ - swHashMap_node *node = (swHashMap_node*) sw_malloc(sizeof(swHashMap_node)); - if (node == NULL) - { - swWarn("malloc failed"); - return SW_ERR; - } - bzero(node, sizeof(swHashMap_node)); - swHashMap_node *root = hmap->root; - node->key_str = sw_strndup(key, key_len); - node->key_int = key_len; - node->data = data; - return swHashMap_node_add(root, node); -} - -int swHashMap_add_int(swHashMap *hmap, uint64_t key, void *data) -{ - swHashMap_node *node = (swHashMap_node*) sw_malloc(sizeof(swHashMap_node)); - swHashMap_node *root = hmap->root; - if (node == NULL) - { - swWarn("malloc failed"); - return SW_ERR; - } - node->key_int = key; - node->data = data; - node->key_str = NULL; - HASH_ADD_INT(root, key_int, node); - return SW_OK; -} - -static sw_inline swHashMap_node *swHashMap_node_find(swHashMap_node *root, const char *key_str, uint16_t key_len) -{ - swHashMap_node *out; - unsigned bucket, hash; - out = NULL; - if (root) - { - hash = swoole_hash_jenkins(key_str, key_len); - bucket = hash & (root->hh.tbl->num_buckets - 1); - HASH_FIND_IN_BKT(root->hh.tbl, hh, (root)->hh.tbl->buckets[bucket], key_str, key_len, out); - } - return out; -} - -static int swHashMap_node_delete(swHashMap_node *root, swHashMap_node *del_node) -{ - unsigned bucket; - struct UT_hash_handle *_hd_hh_del; - - if ((del_node->hh.prev == NULL) && (del_node->hh.next == NULL)) - { - sw_free(root->hh.tbl->buckets); - sw_free(root->hh.tbl); - } - else - { - _hd_hh_del = &(del_node->hh); - if (del_node == ELMT_FROM_HH(root->hh.tbl, root->hh.tbl->tail)) - { - root->hh.tbl->tail = (UT_hash_handle*) ((ptrdiff_t) (del_node->hh.prev) + root->hh.tbl->hho); - } - if (del_node->hh.prev) - { - ((UT_hash_handle*) ((ptrdiff_t) (del_node->hh.prev) + root->hh.tbl->hho))->next = del_node->hh.next; - } - else - { - DECLTYPE_ASSIGN(root, del_node->hh.next); - } - if (_hd_hh_del->next) - { - ((UT_hash_handle*) ((ptrdiff_t) _hd_hh_del->next + root->hh.tbl->hho))->prev = _hd_hh_del->prev; - } - HASH_TO_BKT(_hd_hh_del->hashv, root->hh.tbl->num_buckets, bucket); - HASH_DEL_IN_BKT(hh, root->hh.tbl->buckets[bucket], _hd_hh_del); - root->hh.tbl->num_items--; - } - return SW_OK; -} - -void* swHashMap_find(swHashMap* hmap, const char *key, uint16_t key_len) -{ - swHashMap_node *root = hmap->root; - swHashMap_node *ret = swHashMap_node_find(root, key, key_len); - if (ret == NULL) - { - return NULL; - } - return ret->data; -} - -void* swHashMap_find_int(swHashMap* hmap, uint64_t key) -{ - swHashMap_node *ret = NULL; - swHashMap_node *root = hmap->root; - HASH_FIND_INT(root, &key, ret); - if (ret == NULL) - { - return NULL; - } - return ret->data; -} - -int swHashMap_update(swHashMap* hmap, const char *key, uint16_t key_len, void *data) -{ - swHashMap_node *root = hmap->root; - swHashMap_node *node = swHashMap_node_find(root, key, key_len); - if (node == NULL) - { - return SW_ERR; - } - swHashMap_node_dtor(hmap, node); - node->data = data; - return SW_OK; -} - -void swHashMap_update_int(swHashMap* hmap, uint64_t key, void *data) -{ - swHashMap_node *ret = NULL; - swHashMap_node *root = hmap->root; - HASH_FIND_INT(root, &key, ret); - if (ret == NULL) - { - return; - } - swHashMap_node_dtor(hmap, ret); - ret->data = data; -} - -int swHashMap_del(swHashMap* hmap, const char *key, uint16_t key_len) -{ - swHashMap_node *root = hmap->root; - swHashMap_node *node = swHashMap_node_find(root, key, key_len); - if (node == NULL) - { - return SW_ERR; - } - swHashMap_node_delete(root, node); - swHashMap_node_free(hmap, node); - return SW_OK; -} - -int swHashMap_del_int(swHashMap *hmap, uint64_t key) -{ - swHashMap_node *ret = NULL; - swHashMap_node *root = hmap->root; - - HASH_FIND_INT(root, &key, ret); - if (ret == NULL) - { - return SW_ERR; - } - HASH_DEL(root, ret); - swHashMap_node_free(hmap, ret); - return SW_OK; -} - -int swHashMap_move(swHashMap *hmap, const char *old_key, uint16_t old_key_len, const char *new_key, uint16_t new_key_len) -{ - swHashMap_node *root = hmap->root; - swHashMap_node *node = swHashMap_node_find(root, old_key, old_key_len); - if (node == NULL) - { - return SW_ERR; - } - swHashMap_node_delete(root, node); - sw_free(node->key_str); - node->key_str = sw_strndup(new_key, new_key_len); - node->key_int = new_key_len; - return swHashMap_node_add(root, node); -} - -int swHashMap_move_int(swHashMap *hmap, uint64_t old_key, uint64_t new_key) -{ - swHashMap_node *ret = NULL; - swHashMap_node *root = hmap->root; - - HASH_FIND_INT(root, &old_key, ret); - if (ret == NULL) - { - return SW_ERR; - } - HASH_DEL(root, ret); - - ret->key_int = new_key; - HASH_ADD_INT(root, key_int, ret); - - return SW_OK; -} - -void swHashMap_rewind(swHashMap* hmap) -{ - hmap->iterator = hmap->root; -} - -/** - * Readonly, deleting elements is not safe - */ -void* swHashMap_each(swHashMap* hmap, char **key) -{ - swHashMap_node *node = swHashMap_node_each(hmap); - if (node) - { - *key = node->key_str; - return node->data; - } - else - { - return NULL; - } -} - -/** - * Readonly, deleting elements is not safe - */ -void* swHashMap_each_int(swHashMap* hmap, uint64_t *key) -{ - swHashMap_node *node = swHashMap_node_each(hmap); - if (node) - { - *key = node->key_int; - return node->data; - } - else - { - return NULL; - } -} - -uint32_t swHashMap_count(swHashMap* hmap) -{ - if (hmap == NULL) - { - return 0; - } - return HASH_COUNT(hmap->root); -} - -void swHashMap_clean(swHashMap* hmap) -{ - swHashMap_node *find, *tmp = NULL; - swHashMap_node *root = hmap->root; - HASH_ITER(hh, root, find, tmp) - { - if (find == root) continue; - swHashMap_node_delete(root, find); - swHashMap_node_free(hmap, find); - } -} - -void swHashMap_free(swHashMap* hmap) -{ - swHashMap_node *find, *tmp = NULL; - swHashMap_node *root = hmap->root; - HASH_ITER(hh, root, find, tmp) - { - if (find == root) continue; - swHashMap_node_delete(root, find); - swHashMap_node_free(hmap, find); - } - - sw_free(hmap->root->hh.tbl->buckets); - sw_free(hmap->root->hh.tbl); - sw_free(hmap->root); - - sw_free(hmap); -} - -/* {{{ COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or - * code or tables extracted from it, as desired without restriction. - * - * First, the polynomial itself and its table of feedback terms. The - * polynomial is - * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 - * - * Note that we take it "backwards" and put the highest-order term in - * the lowest-order bit. The X^32 term is "implied"; the LSB is the - * X^31 term, etc. The X^0 term (usually shown as "+1") results in - * the MSB being 1 - * - * Note that the usual hardware shift register implementation, which - * is what we're using (we're merely optimizing it by doing eight-bit - * chunks at a time) shifts bits into the lowest-order term. In our - * implementation, that means shifting towards the right. Why do we - * do it this way? Because the calculated CRC must be transmitted in - * order from highest-order term to lowest-order term. UARTs transmit - * characters in order from LSB to MSB. By storing the CRC this way - * we hand it to the UART in the order low-byte to high-byte; the UART - * sends each low-bit to hight-bit; and the result is transmission bit - * by bit from highest- to lowest-order term without requiring any bit - * shuffling on our part. Reception works similarly - * - * The feedback terms table consists of 256, 32-bit entries. Notes - * - * The table can be generated at runtime if desired; code to do so - * is shown later. It might not be obvious, but the feedback - * terms simply represent the results of eight shift/xor opera - * tions for all combinations of data and CRC register values - * - * The values must be right-shifted by eight bits by the "updcrc - * logic; the shift must be unsigned (bring in zeroes). On some - * hardware you could probably optimize the shift in assembler by - * using byte-swap instructions - * polynomial $edb88320 - * - * - * CRC32 code derived from work by Gary S. Brown. - */ - -static unsigned int crc32_tab[] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, - 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, - 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, - 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, - 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, - 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, - 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, - 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, - 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, - 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, - 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, - 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, - 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, - 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, - 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, - 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, - 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, - 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, - 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, - 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, - 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, -}; - -static inline uint32_t crc32(const char *buf, unsigned int size) -{ - const char *p; - register int crc = 0; - - p = buf; - while (size--) - { - crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); - } - - return crc ^ ~0U; -} - -uint32_t swoole_crc32(const char *data, uint32_t size) -{ - if (size < CRC_STRING_MAXLEN) - { - return crc32(data, size); - } - else - { - int i = 0; - char crc_contents[CRC_STRING_MAXLEN]; - int head = CRC_STRING_MAXLEN >> 2; - int tail = CRC_STRING_MAXLEN >> 4; - int body = CRC_STRING_MAXLEN - head - tail; - const char *p = data + head; - char *q = crc_contents + head; - int step = (size - tail - head) / body; - - memcpy(crc_contents, data, head); - for (; i < body; i++, q++, p += step) - { - *q = *p; - } - memcpy(q, p, tail); - return crc32(crc_contents, CRC_STRING_MAXLEN); - } -} diff --git a/src/core/heap.cc b/src/core/heap.cc index 6ad6d492c50..6ffbeb4877c 100644 --- a/src/core/heap.cc +++ b/src/core/heap.cc @@ -10,208 +10,156 @@ | to obtain it through the world-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 "heap.h" +#include "swoole_heap.h" -#define left(i) ((i) << 1) -#define right(i) (((i) << 1) + 1) +#define left(i) ((i) << 1) +#define right(i) (((i) << 1) + 1) #define parent(i) ((i) >> 1) -static void swHeap_bubble_up(swHeap *heap, uint32_t i); -static uint32_t swHeap_maxchild(swHeap *heap, uint32_t i); -static void swHeap_percolate_down(swHeap *heap, uint32_t i); +namespace swoole { -swHeap *swHeap_new(size_t n, uint8_t type) -{ - swHeap *heap = (swHeap *) sw_malloc(sizeof(swHeap)); - if (!heap) - { - return NULL; +Heap::Heap(size_t _n, Heap::Type _type) { + if (!(nodes = (HeapNode **) sw_malloc((_n + 1) * sizeof(void *)))) { + throw std::bad_alloc(); } - if (!(heap->nodes = (swHeap_node **) sw_malloc((n + 1) * sizeof(void *)))) - { - sw_free(heap); - return NULL; - } - heap->num = 1; - heap->size = (n + 1); - heap->type = type; - return heap; + num = 1; + size = (_n + 1); + type = _type; } -void swHeap_free(swHeap *heap) -{ - sw_free(heap->nodes); - sw_free(heap); +Heap::~Heap() { + sw_free(nodes); } -static sw_inline int swHeap_compare(uint8_t type, uint64_t a, uint64_t b) -{ - if (type == SW_MIN_HEAP) - { +int Heap::compare(uint64_t a, uint64_t b) { + if (type == Heap::MIN_HEAP) { return a > b; - } - else - { + } else { return a < b; } } -uint32_t swHeap_size(swHeap *q) -{ - return (q->num - 1); -} - -static uint32_t swHeap_maxchild(swHeap *heap, uint32_t i) -{ +uint32_t Heap::maxchild(uint32_t i) { uint32_t child_i = left(i); - if (child_i >= heap->num) - { + if (child_i >= num) { return 0; } - swHeap_node * child_node = heap->nodes[child_i]; - if ((child_i + 1) < heap->num && swHeap_compare(heap->type, child_node->priority, heap->nodes[child_i + 1]->priority)) - { + HeapNode *child_node = nodes[child_i]; + if ((child_i + 1) < num && compare(child_node->priority, nodes[child_i + 1]->priority)) { child_i++; } return child_i; } -static void swHeap_bubble_up(swHeap *heap, uint32_t i) -{ - swHeap_node *moving_node = heap->nodes[i]; +void Heap::bubble_up(uint32_t i) { + HeapNode *moving_node = nodes[i]; uint32_t parent_i; - for (parent_i = parent(i); - (i > 1) && swHeap_compare(heap->type, heap->nodes[parent_i]->priority, moving_node->priority); - i = parent_i, parent_i = parent(i)) - { - heap->nodes[i] = heap->nodes[parent_i]; - heap->nodes[i]->position = i; + for (parent_i = parent(i); (i > 1) && compare(nodes[parent_i]->priority, moving_node->priority); + i = parent_i, parent_i = parent(i)) { + nodes[i] = nodes[parent_i]; + nodes[i]->position = i; } - heap->nodes[i] = moving_node; + nodes[i] = moving_node; moving_node->position = i; } -static void swHeap_percolate_down(swHeap *heap, uint32_t i) -{ +void Heap::percolate_down(uint32_t i) { uint32_t child_i; - swHeap_node *moving_node = heap->nodes[i]; + HeapNode *moving_node = nodes[i]; - while ((child_i = swHeap_maxchild(heap, i)) - && swHeap_compare(heap->type, moving_node->priority, heap->nodes[child_i]->priority)) - { - heap->nodes[i] = heap->nodes[child_i]; - heap->nodes[i]->position = i; + while ((child_i = maxchild(i)) && compare(moving_node->priority, nodes[child_i]->priority)) { + nodes[i] = nodes[child_i]; + nodes[i]->position = i; i = child_i; } - heap->nodes[i] = moving_node; + nodes[i] = moving_node; moving_node->position = i; } -swHeap_node* swHeap_push(swHeap *heap, uint64_t priority, void *data) -{ - void *tmp; +HeapNode *Heap::push(uint64_t priority, void *data) { + HeapNode **tmp; uint32_t i; uint32_t newsize; - if (heap->num >= heap->size) - { - newsize = heap->size * 2; - if (!(tmp = sw_realloc(heap->nodes, sizeof(void *) * newsize))) - { - return NULL; + if (num >= size) { + newsize = size * 2; + if (!(tmp = (HeapNode **) sw_realloc(nodes, sizeof(HeapNode *) * newsize))) { + return nullptr; } - heap->nodes = (swHeap_node **) tmp; - heap->size = newsize; + nodes = tmp; + size = newsize; } - swHeap_node *node = (swHeap_node *) sw_malloc(sizeof(swHeap_node)); - if (!node) - { - return NULL; - } + HeapNode *node = new HeapNode; node->priority = priority; node->data = data; - i = heap->num++; - heap->nodes[i] = node; - swHeap_bubble_up(heap, i); + i = num++; + nodes[i] = node; + bubble_up(i); return node; } -void swHeap_change_priority(swHeap *heap, uint64_t new_priority, void* ptr) -{ - swHeap_node *node = (swHeap_node *) ptr; +void Heap::change_priority(uint64_t new_priority, HeapNode *node) { uint32_t pos = node->position; uint64_t old_pri = node->priority; node->priority = new_priority; - if (swHeap_compare(heap->type, old_pri, new_priority)) - { - swHeap_bubble_up(heap, pos); - } - else - { - swHeap_percolate_down(heap, pos); + if (compare(old_pri, new_priority)) { + bubble_up(pos); + } else { + percolate_down(pos); } } -void swHeap_remove(swHeap *heap, swHeap_node *node) -{ +void Heap::remove(HeapNode *node) { uint32_t pos = node->position; - heap->nodes[pos] = heap->nodes[--heap->num]; + nodes[pos] = nodes[--num]; - if (swHeap_compare(heap->type, node->priority, heap->nodes[pos]->priority)) - { - swHeap_bubble_up(heap, pos); - } - else - { - swHeap_percolate_down(heap, pos); + if (compare(node->priority, nodes[pos]->priority)) { + bubble_up(pos); + } else { + percolate_down(pos); } + delete node; } -void *swHeap_pop(swHeap *heap) -{ - swHeap_node *head; - if (!heap || heap->num == 1) - { - return NULL; +void *Heap::pop() { + HeapNode *head; + if (count() == 0) { + return nullptr; } - head = heap->nodes[1]; - heap->nodes[1] = heap->nodes[--heap->num]; - swHeap_percolate_down(heap, 1); + head = nodes[1]; + nodes[1] = nodes[--num]; + percolate_down(1); void *data = head->data; - sw_free(head); + delete head; return data; } -void *swHeap_peek(swHeap *heap) -{ - if (heap->num == 1) - { - return NULL; +void *Heap::peek() { + if (num == 1) { + return nullptr; } - swHeap_node *node = heap->nodes[1]; - if (!node) - { - return NULL; + HeapNode *node = nodes[1]; + if (!node) { + return nullptr; } return node->data; } -void swHeap_print(swHeap *heap) -{ - for(uint32_t i = 1; i < heap->num; i++) - { - printf("#%d\tpriority=%ld, data=%p\n", i, (long)heap->nodes[i]->priority, heap->nodes[i]->data); +void Heap::print() { + for (uint32_t i = 1; i < num; i++) { + printf("#%u\tpriority=%ld, data=%p\n", i, (long) nodes[i]->priority, nodes[i]->data); } } +} // namespace swoole diff --git a/src/core/log.cc b/src/core/log.cc index cc9e18e0845..987c9725cc7 100644 --- a/src/core/log.cc +++ b/src/core/log.cc @@ -10,72 +10,250 @@ | to obtain it through the world-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 +#include #include -#define SW_LOG_BUFFER_SIZE (SW_ERROR_MSG_SIZE+256) -#define SW_LOG_DATE_STRLEN 64 +#include +#include // NOLINT [build/c++11] -static int is_file = SW_FALSE; +namespace swoole { -int swLog_init(char *logfile) -{ - SwooleG.log_fd = open(logfile, O_APPEND | O_RDWR | O_CREAT, 0666); - if (SwooleG.log_fd < 0) - { - printf("open(%s) failed. Error: %s[%d]\n", logfile, strerror(errno), errno); - SwooleG.log_fd = STDOUT_FILENO; - is_file = SW_FALSE; - return SW_ERR; +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 ""; } - is_file = SW_TRUE; - return SW_OK; + + 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; } -void swLog_free(void) -{ - if (is_file) - { - close(SwooleG.log_fd); - SwooleG.log_fd = STDOUT_FILENO; - is_file = SW_FALSE; +bool Logger::open(const char *_log_file) { + if (opened) { + close(); + } + + log_file = _log_file; + + if (log_rotation) { + log_real_file = gen_real_file(log_file); + } else { + log_real_file = log_file; + } + + log_fd = ::open(log_real_file.c_str(), O_APPEND | O_RDWR | O_CREAT, 0666); + if (log_fd < 0) { + printf("open(%s) failed. Error: %s[%d]\n", log_real_file.c_str(), strerror(errno), errno); + log_fd = STDOUT_FILENO; + opened = false; + log_file = ""; + log_real_file = ""; + + return false; + } else { + opened = true; + + return true; + } +} + +void Logger::close(void) { + if (opened) { + ::close(log_fd); + log_fd = STDOUT_FILENO; + log_file = ""; + opened = false; + } +} + +int Logger::get_level() { + return log_level; +} + +void Logger::set_level(int level) { + if (level < SW_LOG_DEBUG) { + level = SW_LOG_DEBUG; + } + if (level > SW_LOG_NONE) { + level = SW_LOG_NONE; + } + log_level = level; +} + +void Logger::set_rotation(int _rotation) { + log_rotation = _rotation; +} + +bool Logger::redirect_stdout_and_stderr(int enable) { + if (enable) { + if (!opened) { + swoole_warning("no log file opened"); + return false; + } + if (redirected) { + swoole_warning("has been redirected"); + return false; + } + if ((stdout_fd = dup(STDOUT_FILENO)) < 0) { + swoole_sys_warning("dup(STDOUT_FILENO) failed"); + return false; + } + if ((stderr_fd = dup(STDERR_FILENO)) < 0) { + swoole_sys_warning("dup(STDERR_FILENO) failed"); + return false; + } + swoole_redirect_stdout(log_fd); + redirected = true; + } else { + if (!redirected) { + swoole_warning("no redirected"); + return false; + } + if (dup2(stdout_fd, STDOUT_FILENO) < 0) { + swoole_sys_warning("dup2(STDOUT_FILENO) failed"); + } + if (dup2(stderr_fd, STDERR_FILENO) < 0) { + swoole_sys_warning("dup2(STDERR_FILENO) failed"); + } + ::close(stdout_fd); + ::close(stderr_fd); + stdout_fd = -1; + stderr_fd = -1; + redirected = false; + } + + return true; +} + +void Logger::reset() { + date_format = SW_LOG_DEFAULT_DATE_FORMAT; + date_with_microseconds = false; + log_rotation = SW_LOG_ROTATION_SINGLE; + log_level = SW_LOG_INFO; +} + +bool Logger::set_date_format(const char *format) { + char date_str[SW_LOG_DATE_STRLEN]; + time_t now_sec; + + now_sec = ::time(nullptr); + size_t l_data_str = std::strftime(date_str, sizeof(date_str), format, std::localtime(&now_sec)); + + if (l_data_str == 0) { + swoole_set_last_error(SW_ERROR_INVALID_PARAMS); + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_INVALID_PARAMS, "The date format string[length=%ld] is too long", strlen(format)); + + return false; + } else { + date_format = format; + + return true; } } +void Logger::set_date_with_microseconds(bool enable) { + date_with_microseconds = enable; +} + /** * reopen log file */ -void swLog_reopen(enum swBool_type redirect) -{ - if (!SwooleG.log_file) - { +void Logger::reopen() { + if (!opened) { return; } - swLog_free(); - swLog_init(SwooleG.log_file); + + std::string new_log_file(log_file); + close(); + open(new_log_file.c_str()); /** * redirect STDOUT & STDERR to log file */ - if (redirect) - { - swoole_redirect_stdout(SwooleG.log_fd); + if (redirected) { + swoole_redirect_stdout(log_fd); } } -void swLog_put(int level, char *content, size_t length) -{ +const char *Logger::get_real_file() { + return log_real_file.c_str(); +} + +const char *Logger::get_file() { + return log_file.c_str(); +} + +std::string Logger::gen_real_file(const std::string &file) { + char date_str[16]; + auto now_sec = ::time(nullptr); + const char *fmt; + + switch (log_rotation) { + case SW_LOG_ROTATION_MONTHLY: + fmt = "%Y%m"; + break; + case SW_LOG_ROTATION_HOURLY: + fmt = "%Y%m%d%H"; + break; + case SW_LOG_ROTATION_EVERY_MINUTE: + fmt = "%Y%m%d%H%M"; + break; + case SW_LOG_ROTATION_DAILY: + default: + fmt = "%Y%m%d"; + break; + } + + size_t l_data_str = std::strftime(date_str, sizeof(date_str), fmt, std::localtime(&now_sec)); + std::string real_file = file + "." + std::string(date_str, l_data_str); + + return real_file; +} + +bool Logger::is_opened() { + return opened; +} + +void Logger::put(int level, const char *content, size_t length) { const char *level_str; char date_str[SW_LOG_DATE_STRLEN]; char log_str[SW_LOG_BUFFER_SIZE]; int n; - switch (level) - { + if (level < log_level) { + return; + } + + switch (level) { case SW_LOG_DEBUG: level_str = "DEBUG"; break; @@ -91,60 +269,73 @@ void swLog_put(int level, char *content, size_t length) case SW_LOG_ERROR: level_str = "ERROR"; break; - // case SW_LOG_INFO: + case SW_LOG_INFO: default: level_str = "INFO"; break; } - time_t t; - struct tm *p; - t = time(NULL); - p = localtime(&t); - size_t l_data_str = sw_snprintf( - date_str, SW_LOG_DATE_STRLEN, "%d-%.2d-%.2d %.2d:%.2d:%.2d", - p->tm_year + 1900, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec - ); -#if 0 - l_data_str = sw_snprintf(date_str + l_data_str, SW_LOG_DATE_STRLEN - l_data_str, " <%lf> ", swoole_microtime()); -#endif + auto now = std::chrono::system_clock::now(); + auto now_sec = std::chrono::system_clock::to_time_t(now); + size_t l_data_str = std::strftime(date_str, sizeof(date_str), date_format.c_str(), std::localtime(&now_sec)); + + if (log_rotation) { + std::string tmp = gen_real_file(log_file); + if (tmp != log_real_file) { + reopen(); + } + } + + 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); + } char process_flag = '@'; int process_id = 0; - switch(SwooleG.process_type) - { + switch (swoole_get_process_type()) { case SW_PROCESS_MASTER: process_flag = '#'; - process_id = SwooleTG.id; + process_id = swoole_get_thread_id(); break; case SW_PROCESS_MANAGER: process_flag = '$'; break; case SW_PROCESS_WORKER: process_flag = '*'; - process_id = SwooleWG.id; + process_id = swoole_get_process_id(); break; case SW_PROCESS_TASKWORKER: process_flag = '^'; - process_id = SwooleWG.id; + process_id = swoole_get_process_id(); break; default: break; } - n = sw_snprintf(log_str, SW_LOG_BUFFER_SIZE, "[%.*s %c%d.%d]\t%s\t%.*s\n", (int) l_data_str, date_str, process_flag, SwooleG.pid, process_id, level_str, (int) length, content); + n = sw_snprintf(log_str, + SW_LOG_BUFFER_SIZE, + "[%.*s %c%d.%d]\t%s\t%.*s\n", + static_cast(l_data_str), + date_str, + process_flag, + SwooleG.pid, + process_id, + level_str, + static_cast(length), + content); - if (is_file && flock(SwooleG.log_fd, LOCK_EX) == -1) - { - goto _print; + if (opened && flock(log_fd, LOCK_EX) == -1) { + return; } - if (write(SwooleG.log_fd, log_str, n) < 0) - { - _print: printf("write(log_fd=%d, size=%d) failed. Error: %s[%d].\nMessage: %.*s\n", SwooleG.log_fd, n, strerror(errno), errno, n, log_str); + write(log_fd, log_str, n); + if (opened && flock(log_fd, LOCK_UN) == -1) { + return; } - if (is_file && flock(SwooleG.log_fd, LOCK_UN) == -1) - { - printf("flock(%d, LOCK_UN) failed. Error: %s[%d]", SwooleG.log_fd, strerror(errno), errno); + if (display_backtrace_) { + swoole_print_backtrace(); } } +} // namespace swoole diff --git a/src/core/ring_queue.cc b/src/core/ring_queue.cc deleted file mode 100644 index 50f828dd3c6..00000000000 --- a/src/core/ring_queue.cc +++ /dev/null @@ -1,72 +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 -#include "swoole.h" -#include "ring_queue.h" - -int swRingQueue_init(swRingQueue *queue, int buffer_size) -{ - queue->data = (void **) sw_calloc(buffer_size, sizeof(void*)); - if (queue->data == NULL) - { - swWarn("malloc failed"); - return -1; - } - queue->size = buffer_size; - queue->head = 0; - queue->tail = 0; - queue->tag = 0; - return 0; -} - -void swRingQueue_free(swRingQueue *queue) -{ - sw_free(queue->data); -} - -int swRingQueue_push(swRingQueue *queue, void *push_data) -{ - if (swRingQueue_full(queue)) - { - return SW_ERR; - } - - queue->data[queue->tail] = push_data; - queue->tail = (queue->tail + 1) % queue->size; - - if (queue->tail == queue->head) - { - queue->tag = 1; - } - return SW_OK; -} - -int swRingQueue_pop(swRingQueue *queue, void **pop_data) -{ - if (swRingQueue_empty(queue)) - { - return SW_ERR; - } - - *pop_data = queue->data[queue->head]; - queue->head = (queue->head + 1) % queue->size; - - if (queue->tail == queue->head) - { - queue->tag = 0; - } - return SW_OK; -} diff --git a/src/core/socket.cc b/src/core/socket.cc deleted file mode 100644 index d0f31a40da1..00000000000 --- a/src/core/socket.cc +++ /dev/null @@ -1,936 +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 "swoole_api.h" -#include "ssl.h" - -#ifndef MSG_NOSIGNAL -#define MSG_NOSIGNAL 0 -#endif - -int swSocket_sendfile_sync(int sock, const char *filename, off_t offset, size_t length, double timeout) -{ - int timeout_ms = timeout < 0 ? -1 : timeout * 1000; - int file_fd = open(filename, O_RDONLY); - if (file_fd < 0) - { - swSysWarn("open(%s) failed", filename); - return SW_ERR; - } - - if (length == 0) - { - struct stat file_stat; - if (fstat(file_fd, &file_stat) < 0) - { - swSysWarn("fstat() failed"); - close(file_fd); - return SW_ERR; - } - length = file_stat.st_size; - } - else - { - length = offset + length; - } - - int n, sendn; - while (offset < (off_t) length) - { - if (swSocket_wait(sock, timeout_ms, SW_EVENT_WRITE) < 0) - { - close(file_fd); - return SW_ERR; - } - else - { - sendn = (length - offset > SW_SENDFILE_CHUNK_SIZE) ? SW_SENDFILE_CHUNK_SIZE : length - offset; - n = swoole_sendfile(sock, file_fd, &offset, sendn); - if (n <= 0) - { - close(file_fd); - swSysWarn("sendfile(%d, %s) failed", sock, filename); - return SW_ERR; - } - else - { - continue; - } - } - } - close(file_fd); - return SW_OK; -} - -/** - * clear socket buffer. - */ -void swSocket_clean(int fd) -{ - char buf[2048]; - while (recv(fd, buf, sizeof(buf), MSG_DONTWAIT) > 0); -} - -/** - * Wait socket can read or write. - */ -int swSocket_wait(int fd, int timeout_ms, int events) -{ - struct pollfd event; - event.fd = fd; - event.events = 0; - - if (timeout_ms < 0) - { - timeout_ms = -1; - } - - if (events & SW_EVENT_READ) - { - event.events |= POLLIN; - } - if (events & SW_EVENT_WRITE) - { - event.events |= POLLOUT; - } - while (1) - { - int ret = poll(&event, 1, timeout_ms); - if (ret == 0) - { - return SW_ERR; - } - else if (ret < 0 && errno != EINTR) - { - swSysWarn("poll() failed"); - return SW_ERR; - } - else - { - return SW_OK; - } - } - return SW_OK; -} - -/** - * Wait some sockets can read or write. - */ -int swSocket_wait_multi(int *list_of_fd, int n_fd, int timeout_ms, int events) -{ - assert(n_fd < 65535); - - struct pollfd *event_list = (struct pollfd *) sw_calloc(n_fd, sizeof(*event_list)); - if (!event_list) - { - swWarn("malloc[1] failed"); - return SW_ERR; - } - int i; - - int _events = 0; - if (events & SW_EVENT_READ) - { - _events |= POLLIN; - } - if (events & SW_EVENT_WRITE) - { - _events |= POLLOUT; - } - - for (i = 0; i < n_fd; i++) - { - event_list[i].fd = list_of_fd[i]; - event_list[i].events = _events; - } - - while (1) - { - int ret = poll(event_list, n_fd, timeout_ms); - if (ret == 0) - { - sw_free(event_list); - return SW_ERR; - } - else if (ret < 0 && errno != EINTR) - { - swSysWarn("poll() failed"); - sw_free(event_list); - return SW_ERR; - } - else - { - sw_free(event_list); - return ret; - } - } - sw_free(event_list); - return SW_OK; -} - -ssize_t swSocket_write_blocking(swSocket *sock, const void *__data, size_t __len) -{ - ssize_t n = 0; - ssize_t written = 0; - - while (written < (ssize_t) __len) - { - n = write(sock->fd, (char *) __data + written, __len - written); - if (n < 0) - { - if (errno == EINTR) - { - continue; - } - else if (swSocket_error(errno) == SW_WAIT - && swSocket_wait(sock->fd, (int) (SwooleG.socket_send_timeout * 1000), SW_EVENT_WRITE) == SW_OK) - { - continue; - } - else - { - swSysWarn("write %d bytes failed", __len); - return SW_ERR; - } - } - written += n; - } - - return written; -} - -ssize_t swSocket_recv_blocking(swSocket *sock, void *__data, size_t __len, int flags) -{ - ssize_t ret; - size_t read_bytes = 0; - - while (read_bytes != __len) - { - errno = 0; - ret = recv(sock->fd, (char *) __data + read_bytes, __len - read_bytes, flags); - if (ret > 0) - { - read_bytes += ret; - } - else if (ret == 0 && errno == 0) - { - return read_bytes; - } - else if (ret <= 0 && errno != 0 && errno != EINTR) - { - return ret; - } - } - return read_bytes; -} - -swSocket* swSocket_accept(swSocket *server_socket, swSocketAddress *sa) -{ - int conn; - sa->len = sizeof(sa->addr); -#ifdef HAVE_ACCEPT4 - int flags = SOCK_CLOEXEC; - if (server_socket->nonblock) - { - flags |= SOCK_NONBLOCK; - } - conn = accept4(server_socket->fd, (struct sockaddr *) &sa->addr, &sa->len, flags); -#else - conn = accept(server_socket->fd, (struct sockaddr *) &sa->addr, &sa->len); - if (conn >= 0) - { - swoole_fcntl_set_option(conn, server_socket->nonblock, 1); - } -#endif - - if (conn < 0) - { - return nullptr; - } - - swSocket *socket = swSocket_new(conn, SW_FD_SESSION); - if (!socket) - { - close(conn); - } - else - { - socket->socket_type = server_socket->socket_type; - socket->nonblock = server_socket->nonblock; - socket->cloexec = 1; - memcpy(&socket->info.addr, sa, sa->len); - socket->info.len = sa->len; - } - - return socket; -} - -ssize_t swSocket_udp_sendto(int server_sock, const char *dst_ip, int dst_port, const char *data, uint32_t len) -{ - struct sockaddr_in addr; - if (inet_aton(dst_ip, &addr.sin_addr) == 0) - { - swWarn("ip[%s] is invalid", dst_ip); - return SW_ERR; - } - addr.sin_family = AF_INET; - addr.sin_port = htons(dst_port); - return swSocket_sendto_blocking(server_sock, data, len, 0, (struct sockaddr *) &addr, sizeof(addr)); -} - -ssize_t swSocket_udp_sendto6(int server_sock, const char *dst_ip, int dst_port, const char *data, uint32_t len) -{ - struct sockaddr_in6 addr; - bzero(&addr, sizeof(addr)); - if (inet_pton(AF_INET6, dst_ip, &addr.sin6_addr) < 0) - { - swWarn("ip[%s] is invalid", dst_ip); - return SW_ERR; - } - addr.sin6_port = (uint16_t) htons(dst_port); - addr.sin6_family = AF_INET6; - return swSocket_sendto_blocking(server_sock, data, len, 0, (struct sockaddr *) &addr, sizeof(addr)); -} - -ssize_t swSocket_unix_sendto(int server_sock, const char *dst_path, const char *data, uint32_t len) -{ - struct sockaddr_un addr; - bzero(&addr, sizeof(addr)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, dst_path, sizeof(addr.sun_path) - 1); - return swSocket_sendto_blocking(server_sock, data, len, 0, (struct sockaddr *) &addr, sizeof(addr)); -} - -ssize_t swSocket_sendto_blocking(int fd, const void *__buf, size_t __n, int flag, struct sockaddr *__addr, socklen_t __addr_len) -{ - ssize_t n = 0; - - for (int i = 0; i < SW_SOCKET_SYNC_SEND_RETRY_COUNT; i++) - { - n = sendto(fd, __buf, __n, flag, __addr, __addr_len); - if (n >= 0) - { - break; - } - if (errno == EINTR) - { - continue; - } - if (swSocket_error(errno) == SW_WAIT - && swSocket_wait(fd, (int) (SwooleG.socket_send_timeout * 1000), SW_EVENT_WRITE) == SW_OK) - { - continue; - } - break; - } - - return n; -} - -int swSocket_create(enum swSocket_type type, uchar nonblock, uchar cloexec) -{ - int sock_domain; - int sock_type; - - if (swSocket_get_domain_and_type(type, &sock_domain, &sock_type) < 0) - { - swWarn("unknown socket type [%d]", type); - errno = ESOCKTNOSUPPORT; - return SW_ERR; - } - -#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) - int flags = 0; - if (nonblock) - { - flags |= SOCK_NONBLOCK; - } - if (cloexec) - { - flags |= SOCK_CLOEXEC; - } - return socket(sock_domain, sock_type | flags, 0); -#else - int sockfd = socket(sock_domain, sock_type, 0); - if (sockfd < 0) - { - return SW_ERR; - } - if (!nonblock && !cloexec) - { - return sockfd; - } - if (swoole_fcntl_set_option(sockfd, nonblock ? 1 : -1, cloexec ? 1 : -1) < 0) - { - close(sockfd); - return SW_ERR; - } - return sockfd; -#endif -} - -swSocket* swSocket_new(int fd, enum swFd_type type) -{ - swSocket *socket = (swSocket *) sw_calloc(1, sizeof(*socket)); - if (!socket) - { - swSysWarn("calloc(1, %ld) failed", sizeof(*socket)); - return NULL; - } - socket->fd = fd; - socket->fdtype = type; - socket->removed = 1; - return socket; -} - -static void socket_free_defer(void *ptr) -{ - swSocket *sock = (swSocket *) ptr; - if (sock->fd != -1 && close(sock->fd) != 0) - { - swSysWarn("close(%d) failed", sock->fd); - } - sw_free(sock); -} - -void swSocket_free(swSocket *sock) -{ - if (SwooleTG.reactor) - { - sock->removed = 1; - swoole_event_defer(socket_free_defer, sock); - } - else - { - socket_free_defer(sock); - } -} - -int swSocket_bind(swSocket *sock, const char *host, int *port) -{ - int ret; - swSocketAddress address = {}; - - int option = 1; - if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(int)) < 0) - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_SYSTEM_CALL_FAIL, "setsockopt(%d, SO_REUSEADDR) failed", sock->fd); - } - //UnixSocket - if (sock->socket_type == SW_SOCK_UNIX_DGRAM || sock->socket_type == SW_SOCK_UNIX_STREAM) - { - unlink(host); - address.addr.un.sun_family = AF_UNIX; - strncpy(address.addr.un.sun_path, host, sizeof(address.addr.un.sun_path) - 1); - ret = bind(sock->fd, (struct sockaddr *) &address.addr.un, sizeof(address.addr.un)); - } - //IPv6 - else if (sock->socket_type > SW_SOCK_UDP) - { - if (inet_pton(AF_INET6, host, &address.addr.inet_v6.sin6_addr) < 0) - { - swSysWarn("inet_pton(AF_INET6, %s) failed", host); - return SW_ERR; - } - address.addr.inet_v6.sin6_port = htons(*port); - address.addr.inet_v6.sin6_family = AF_INET6; - ret = bind(sock->fd, (struct sockaddr *) &address.addr.inet_v6, sizeof(address.addr.inet_v6)); - if (ret == 0 && *port == 0) - { - address.len = sizeof(address.addr.inet_v6); - if (getsockname(sock->fd, (struct sockaddr *) &address.addr.inet_v6, &address.len) != -1) - { - *port = ntohs(address.addr.inet_v6.sin6_port); - } - } - } - //IPv4 - else - { - if (inet_pton(AF_INET, host, &address.addr.inet_v4.sin_addr) < 0) - { - swSysWarn("inet_pton(AF_INET, %s) failed", host); - return SW_ERR; - } - address.addr.inet_v4.sin_port = htons(*port); - address.addr.inet_v4.sin_family = AF_INET; - ret = bind(sock->fd, (struct sockaddr *) &address.addr.inet_v4, sizeof(address.addr.inet_v4)); - if (ret == 0 && *port == 0) - { - address.len = sizeof(address.addr.inet_v4); - if (getsockname(sock->fd, (struct sockaddr *) &address.addr.inet_v4, &address.len) != -1) - { - *port = ntohs(address.addr.inet_v4.sin_port); - } - } - } - //bind failed - if (ret < 0) - { - swSysWarn("bind(%s:%d) failed", host, *port); - return SW_ERR; - } - - return ret; -} - -int swSocket_set_buffer_size(swSocket *sock, uint32_t buffer_size) -{ - int fd = sock->fd; - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffer_size, sizeof(buffer_size)) != 0) - { - swSysWarn("setsockopt(%d, SOL_SOCKET, SO_SNDBUF, %d) failed", fd, buffer_size); - return SW_ERR; - } - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buffer_size, sizeof(buffer_size)) != 0) - { - swSysWarn("setsockopt(%d, SOL_SOCKET, SO_RCVBUF, %d) failed", fd, buffer_size); - return SW_ERR; - } - return SW_OK; -} - -int swSocket_set_timeout(swSocket *sock, double timeout) -{ - int ret; - struct timeval timeo; - timeo.tv_sec = (int) timeout; - timeo.tv_usec = (int) ((timeout - timeo.tv_sec) * 1000 * 1000); - ret = setsockopt(sock->fd, SOL_SOCKET, SO_SNDTIMEO, (void *) &timeo, sizeof(timeo)); - if (ret < 0) - { - swSysWarn("setsockopt(SO_SNDTIMEO) failed"); - return SW_ERR; - } - ret = setsockopt(sock->fd, SOL_SOCKET, SO_RCVTIMEO, (void *) &timeo, sizeof(timeo)); - if (ret < 0) - { - swSysWarn("setsockopt(SO_RCVTIMEO) failed"); - return SW_ERR; - } - return SW_OK; -} - -swSocket* swSocket_create_server(enum swSocket_type type, const char *address, int port, int backlog) -{ - int fd = swSocket_create(type, 0, 0); - if (fd < 0) - { - swSysWarn("socket() failed"); - return nullptr; - } - swSocket *sock = swSocket_new(fd, SW_FD_STREAM_SERVER); - if (!sock) - { - close(fd); - return nullptr; - } - sock->socket_type = type; - if (swSocket_bind(sock, address, &port) < 0) - { - swSocket_free(sock); - return nullptr; - } - if (listen(fd, backlog) < 0) - { - swSysWarn("listen(%s:%d, %d) failed", address, port, backlog); - swSocket_free(sock); - return nullptr; - } - - return sock; -} - - -int swSocket_onSendfile(swSocket *conn, swBuffer_chunk *chunk) -{ - int ret; - swTask_sendfile *task = (swTask_sendfile *) chunk->store.ptr; - -#ifdef HAVE_TCP_NOPUSH - if (task->offset == 0 && conn->tcp_nopush == 0) - { - /** - * disable tcp_nodelay - */ - if (conn->tcp_nodelay) - { - int tcp_nodelay = 0; - if (setsockopt(conn->fd, IPPROTO_TCP, TCP_NODELAY, (const void *) &tcp_nodelay, sizeof(int)) != 0) - { - swSysWarn("setsockopt(TCP_NODELAY) failed"); - } - } - /** - * enable tcp_nopush - */ - if (swSocket_tcp_nopush(conn->fd, 1) == -1) - { - swSysWarn("swSocket_tcp_nopush() failed"); - } - conn->tcp_nopush = 1; - } -#endif - - int sendn = (task->length - task->offset > SW_SENDFILE_CHUNK_SIZE) ? SW_SENDFILE_CHUNK_SIZE : task->length - task->offset; - -#ifdef SW_USE_OPENSSL - if (conn->ssl) - { - ret = swSSL_sendfile(conn, task->fd, &task->offset, sendn); - } - else -#endif - { - ret = swoole_sendfile(conn->fd, task->fd, &task->offset, sendn); - } - - swTrace("ret=%d|task->offset=%ld|sendn=%d|filesize=%ld", ret, (long)task->offset, sendn, task->length); - - if (ret <= 0) - { - switch (swSocket_error(errno)) - { - case SW_ERROR: - swSysWarn("sendfile(%s, %ld, %d) failed", task->filename, (long)task->offset, sendn); - swBuffer_pop_chunk(conn->out_buffer, chunk); - return SW_OK; - case SW_CLOSE: - conn->close_wait = 1; - return SW_ERR; - case SW_WAIT: - conn->send_wait = 1; - return SW_ERR; - default: - break; - } - } - - //sendfile finish - if ((size_t) task->offset >= task->length) - { - swBuffer_pop_chunk(conn->out_buffer, chunk); - -#ifdef HAVE_TCP_NOPUSH - /** - * disable tcp_nopush - */ - if (swSocket_tcp_nopush(conn->fd, 0) == -1) - { - swSysWarn("swSocket_tcp_nopush() failed"); - } - conn->tcp_nopush = 0; - - /** - * enable tcp_nodelay - */ - if (conn->tcp_nodelay) - { - int value = 1; - if (setsockopt(conn->fd, IPPROTO_TCP, TCP_NODELAY, (const void *) &value, sizeof(int)) != 0) - { - swSysWarn("setsockopt(TCP_NODELAY) failed"); - } - } -#endif - } - return SW_OK; -} - -/** - * send buffer to client - */ -int swSocket_buffer_send(swSocket *conn) -{ - swBuffer *buffer = conn->out_buffer; - swBuffer_chunk *chunk = swBuffer_get_chunk(buffer); - uint32_t sendn = chunk->length - chunk->offset; - - if (sendn == 0) - { - swBuffer_pop_chunk(buffer, chunk); - return SW_OK; - } - - ssize_t ret = swSocket_send(conn, (char*) chunk->store.ptr + chunk->offset, sendn, 0); - if (ret < 0) - { - switch (swSocket_error(errno)) - { - case SW_ERROR: - swSysWarn("send to fd[%d] failed", conn->fd); - break; - case SW_CLOSE: - conn->close_wait = 1; - return SW_ERR; - case SW_WAIT: - conn->send_wait = 1; - return SW_ERR; - default: - break; - } - return SW_OK; - } - //chunk full send - else if (ret == sendn || sendn == 0) - { - swBuffer_pop_chunk(buffer, chunk); - } - else - { - chunk->offset += ret; - /** - * kernel is not fully processing and socket buffer is full. - */ - if (ret < sendn) - { - conn->send_wait = 1; - return SW_ERR; - } - } - return SW_OK; -} - -static char tmp_address[INET6_ADDRSTRLEN]; - -const char* swSocket_get_ip(enum swSocket_type socket_type, swSocketAddress *info) -{ - if (socket_type == SW_SOCK_TCP || socket_type == SW_SOCK_UDP) - { - return inet_ntoa(info->addr.inet_v4.sin_addr); - } - else if (socket_type == SW_SOCK_TCP6 || socket_type == SW_SOCK_UDP6) - { - if (inet_ntop(AF_INET6, &info->addr.inet_v6.sin6_addr, tmp_address, sizeof(tmp_address))) - { - return tmp_address; - } - } - else if (socket_type == SW_SOCK_UNIX_STREAM || socket_type == SW_SOCK_UNIX_DGRAM) - { - return info->addr.un.sun_path; - } - return "unknown"; -} - -int swSocket_get_port(enum swSocket_type socket_type, swSocketAddress *info) -{ - if (socket_type == SW_SOCK_TCP) - { - return ntohs(info->addr.inet_v4.sin_port); - } - else - { - return ntohs(info->addr.inet_v6.sin6_port); - } -} - -void swSocket_sendfile_destructor(swBuffer_chunk *chunk) -{ - swTask_sendfile *task = (swTask_sendfile *) chunk->store.ptr; - close(task->fd); - sw_free(task->filename); - sw_free(task); -} - -int swSocket_sendfile(swSocket *conn, const char *filename, off_t offset, size_t length) -{ - int file_fd = open(filename, O_RDONLY); - if (file_fd < 0) - { - swSysWarn("open(%s) failed", filename); - return SW_OK; - } - - struct stat file_stat; - if (fstat(file_fd, &file_stat) < 0) - { - swSysWarn("fstat(%s) failed", filename); - close(file_fd); - return SW_ERR; - } - - if (file_stat.st_size == 0) - { - swWarn("empty file[%s]", filename); - close(file_fd); - return SW_ERR; - } - - if (conn->out_buffer == NULL) - { - conn->out_buffer = swBuffer_new(SW_SEND_BUFFER_SIZE); - if (conn->out_buffer == NULL) - { - return SW_ERR; - } - } - - swBuffer_chunk error_chunk; - swTask_sendfile *task = (swTask_sendfile *) sw_malloc(sizeof(swTask_sendfile)); - if (task == NULL) - { - swWarn("malloc for swTask_sendfile failed"); - return SW_ERR; - } - bzero(task, sizeof(swTask_sendfile)); - - task->filename = sw_strdup(filename); - task->fd = file_fd; - task->offset = offset; - - - if (offset < 0 || (length + offset > (size_t) file_stat.st_size)) - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_INVALID_PARAMS, "length or offset is invalid"); - error_chunk.store.ptr = task; - swSocket_sendfile_destructor(&error_chunk); - return SW_OK; - } - if (length == 0) - { - task->length = file_stat.st_size; - } - else - { - task->length = length + offset; - } - - swBuffer_chunk *chunk = swBuffer_new_chunk(conn->out_buffer, SW_CHUNK_SENDFILE, 0); - if (chunk == NULL) - { - swWarn("get out_buffer chunk failed"); - error_chunk.store.ptr = task; - swSocket_sendfile_destructor(&error_chunk); - return SW_ERR; - } - - chunk->store.ptr = (void *) task; - chunk->destroy = swSocket_sendfile_destructor; - - return SW_OK; -} - -ssize_t swSocket_recv(swSocket *conn, void *__buf, size_t __n, int __flags) -{ - ssize_t total_bytes = 0; - - do - { -#ifdef SW_USE_OPENSSL - if (conn->ssl) - { - ssize_t retval = 0; - while ((size_t) total_bytes < __n) - { - retval = swSSL_recv(conn, ((char*)__buf) + total_bytes, __n - total_bytes); - if (retval <= 0) - { - if (total_bytes == 0) - { - total_bytes = retval; - } - break; - } - else - { - total_bytes += retval; - if (!(conn->nonblock || (__flags & MSG_WAITALL))) - { - break; - } - } - } - } - else -#endif - { - total_bytes = recv(conn->fd, __buf, __n, __flags); - } - } - while (total_bytes < 0 && errno == EINTR); - -#ifdef SW_DEBUG - if (total_bytes > 0) - { - conn->total_recv_bytes += total_bytes; - } -#endif - - if (total_bytes < 0 && swSocket_error(errno) == SW_WAIT && conn->event_hup) - { - total_bytes = 0; - } - - swTraceLog(SW_TRACE_SOCKET, "recv %ld/%ld bytes, errno=%d", total_bytes, __n, errno); - - return total_bytes; -} - -ssize_t swSocket_send(swSocket *conn, const void *__buf, size_t __n, int __flags) -{ - ssize_t retval; - - do - { -#ifdef SW_USE_OPENSSL - if (conn->ssl) - { - retval = swSSL_send(conn, __buf, __n); - } - else -#endif - { - retval = send(conn->fd, __buf, __n, __flags); - } - } - while (retval < 0 && errno == EINTR); - -#ifdef SW_DEBUG - if (retval > 0) - { - conn->total_send_bytes += retval; - } -#endif - - swTraceLog(SW_TRACE_SOCKET, "send %ld/%ld bytes, errno=%d", retval, __n, errno); - - return retval; -} - -ssize_t swSocket_peek(swSocket *conn, void *__buf, size_t __n, int __flags) -{ - ssize_t retval; - __flags |= MSG_PEEK; - do - { -#ifdef SW_USE_OPENSSL - if (conn->ssl) - { - retval = SSL_peek(conn->ssl, __buf, __n); - } - else -#endif - { - retval = recv(conn->fd, __buf, __n, __flags); - } - } - while (retval < 0 && errno == EINTR); - - swTraceLog(SW_TRACE_SOCKET, "peek %ld/%ld bytes, errno=%d", retval, __n, errno); - - return retval; -} diff --git a/src/core/string.cc b/src/core/string.cc index d401ab119e7..454c170d134 100644 --- a/src/core/string.cc +++ b/src/core/string.cc @@ -10,191 +10,167 @@ | to obtain it through the world-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_cxx.h" +#include "swoole_string.h" +#include "swoole_base64.h" -using swoole::StringExplodeHandler; +#include -swString *swString_new(size_t size) -{ - swString *str = (swString *) sw_malloc(sizeof(swString)); - if (str == NULL) - { - swWarn("malloc[1] failed"); - return NULL; - } +namespace swoole { - str->length = 0; - str->size = size; - str->offset = 0; - str->str = (char *) sw_malloc(size); +char *String::pop(size_t init_size) { + assert(length >= (size_t) offset); - if (str->str == NULL) - { - swSysWarn("malloc[2](%ld) failed", size); - sw_free(str); - return NULL; - } + char *val = str; + size_t _length = length - offset; + size_t alloc_size = SW_MEM_ALIGNED_SIZE(_length == 0 ? init_size : SW_MAX(_length, init_size)); - return str; -} - -void swString_print(swString *str) -{ - printf( - "String[length=%zu,size=%zu,offset=%jd]=%.*s\n", - str->length, str->size, (intmax_t) str->offset, (int) str->length, str->str - ); -} + char *new_val = (char *) allocator->malloc(alloc_size); + if (new_val == nullptr) { + return nullptr; + } -swString *swString_dup2(swString *src) -{ - swString *dst = swString_new(src->size); - if (dst) - { - swTrace("string dup2. new=%p, old=%p\n", dst, src); - dst->length = src->length; - dst->offset = src->offset; - memcpy(dst->str, src->str, src->length); + str = new_val; + size = alloc_size; + length = _length; + if (length > 0) { + memcpy(new_val, val + offset, length); } + offset = 0; - return dst; + return val; } -swString *swString_dup(const char *src_str, size_t length) -{ - swString *str = swString_new(length); - if (str) - { - str->length = length; - memcpy(str->str, src_str, length); +/** + * migrate data to head, [offset, length - offset] -> [0, length - offset] + */ +void String::reduce(off_t _offset) { + assert(_offset >= 0 && (size_t) _offset <= length); + if (sw_unlikely(_offset == 0)) { + return; } - - return str; + length -= _offset; + offset = 0; + if (length == 0) { + return; + } + memmove(str, str + _offset, length); } -int swString_append(swString *str, swString *append_str) -{ - size_t new_size = str->length + append_str->length; - if (new_size > str->size) - { - if (swString_extend(str, swoole_size_align(new_size * 2, SwooleG.pagesize)) < 0) - { - return SW_ERR; - } +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); } - - memcpy(str->str + str->length, append_str->str, append_str->length); - str->length += append_str->length; - return SW_OK; } -int swString_append_int(swString *str, int value) -{ +int String::append(int value) { char buf[16]; int s_len = swoole_itoa(buf, value); - size_t new_size = str->length + s_len; - if (new_size > str->size) - { - if (swString_extend(str, swoole_size_align(new_size * 2, SwooleG.pagesize)) < 0) - { + size_t new_size = length + s_len; + if (new_size > size) { + if (!reserve(new_size)) { return SW_ERR; } } - memcpy(str->str + str->length, buf, s_len); - str->length += s_len; + memcpy(str + length, buf, s_len); + length += s_len; return SW_OK; } -int swString_append_ptr(swString *str, const char *append_str, size_t length) -{ - size_t new_size = str->length + length; - if (new_size > str->size) - { - if (swString_extend(str, swoole_size_align(new_size * 2, SwooleG.pagesize)) < 0) - { - return SW_ERR; - } +int String::append(const char *append_str, size_t _length) { + size_t new_size = length + _length; + if (new_size > size and !reserve(new_size)) { + return SW_ERR; } - memcpy(str->str + str->length, append_str, length); - str->length += length; + memcpy(str + length, append_str, _length); + length += _length; return SW_OK; } -int swString_write(swString *str, off_t offset, swString *write_str) -{ - size_t new_length = offset + write_str->length; - if (new_length > str->size) - { - if (swString_extend(str, swoole_size_align(new_length * 2, SwooleG.pagesize)) < 0) - { +int String::append_random_bytes(size_t _length, bool base64) { + size_t new_size = length + _length; + size_t base_encode_size; + + if (base64) { + base_encode_size = BASE64_ENCODE_OUT_SIZE(_length) + 1; + new_size += base_encode_size; + } + + if (new_size > size) { + if (!reserve(swoole_size_align(new_size * 2, swoole_pagesize()))) { return SW_ERR; } } - memcpy(str->str + offset, write_str->str, write_str->length); - if (new_length > str->length) - { - str->length = new_length; + size_t n = swoole_random_bytes(str + length, _length); + if (n != _length) { + return SW_ERR; + } + + if (base64) { + std::unique_ptr out(new char[base_encode_size]); + n = base64_encode((uchar *) str + length, _length, out.get()); + memcpy(str + length, out.get(), n); } + length += n; + return SW_OK; } -int swString_write_ptr(swString *str, off_t offset, char *write_str, size_t length) -{ - size_t new_length = offset + length; - if (new_length > str->size) - { - if (swString_extend(str, swoole_size_align(new_length * 2, SwooleG.pagesize)) < 0) - { - return SW_ERR; - } +bool String::reserve(size_t new_size) { + if (size == 0) { + alloc(new_size, nullptr); + return true; } - memcpy(str->str + offset, write_str, length); - if (new_length > str->length) - { - str->length = new_length; + new_size = SW_MEM_ALIGNED_SIZE(new_size); + char *new_str = (char *) allocator->realloc(str, new_size); + if (new_str == nullptr) { + throw std::bad_alloc(); + return false; } - return SW_OK; -} + str = new_str; + size = new_size; -int swString_extend(swString *str, size_t new_size) -{ - assert(new_size > str->size); - char *new_str = (char *) sw_realloc(str->str, new_size); - if (new_str == NULL) - { - swSysWarn("realloc(%ld) failed", new_size); - return SW_ERR; - } + return true; +} - str->str = new_str; - str->size = new_size; - return SW_OK; +char *String::release() { + char *tmp = str; + str = nullptr; + size = 0; + length = 0; + offset = 0; + return tmp; } -char* swString_alloc(swString *str, size_t __size) -{ - if (str->length + __size > str->size) - { - if (swString_extend_align(str, str->length + __size) < 0) - { - return NULL; - } +bool String::repeat(const char *data, size_t len, size_t n) { + if (n <= 0) { + return false; } + if (len == 1) { + if ((size < length + n) && !reserve(length + n)) { + return false; + } + memset(str + length, data[0], n); + length += n; - char *tmp = str->str + str->length; - str->length += __size; - return tmp; + return true; + } + for (size_t i = 0; i < n; i++) { + append(data, len); + } + return true; } /** @@ -203,163 +179,56 @@ char* swString_alloc(swString *str, size_t __size) * 2. equal to zero, eof was not found in the target string * 3. greater than zero, 0 to retval has eof in the target string, and the position of retval is eof */ -size_t swoole::string_split(swString *str, const char *delimiter, size_t delimiter_length, const StringExplodeHandler &handler) -{ +ssize_t String::split(const char *delimiter, size_t delimiter_length, const StringExplodeHandler &handler) { #ifdef SW_LOG_TRACE_OPEN static int count; count++; #endif - const char *start_addr = str->str + str->offset; - const char *delimiter_addr = swoole_strnstr(start_addr, str->length - str->offset, delimiter, delimiter_length); - off_t offset = str->offset; + const char *start_addr = str + offset; + const char *delimiter_addr = swoole_strnstr(start_addr, length - offset, delimiter, delimiter_length); + off_t _offset = offset; size_t ret; - swTraceLog(SW_TRACE_EOF_PROTOCOL, "#[0] count=%d, length=%ld, size=%ld, offset=%ld", count, str->length, str->size, (long) str->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); - if (handler((char *) start_addr - offset, length + offset) == false) - { + swoole_trace_log(SW_TRACE_EOF_PROTOCOL, + "#[0] count=%d, length=%ld, size=%ld, offset=%jd", + count, + length, + size, + (intmax_t) offset); + + while (delimiter_addr) { + size_t _length = delimiter_addr - start_addr + delimiter_length; + swoole_trace_log(SW_TRACE_EOF_PROTOCOL, "#[4] count=%d, length=%zu", count, (size_t) (_length + offset)); + if (handler((char *) start_addr - _offset, _length + _offset) == false) { return -1; } - str->offset += length; - start_addr = str->str + str->offset; - delimiter_addr = swoole_strnstr(start_addr, str->length - str->offset, delimiter, delimiter_length); - offset = 0; + offset += _length; + start_addr = str + offset; + delimiter_addr = swoole_strnstr(start_addr, length - offset, delimiter, delimiter_length); + _offset = 0; } /** * not found eof in str */ - if (offset == str->offset) - { + if (_offset == offset) { /** - * why is str->offset not equal to str->length, - * because the str->length may contain part of eof and the other part in the next recv + * why is offset not equal to length, + * because the length may contain part of eof and the other part in the next recv */ - str->offset = str->length - delimiter_length; + offset = length - delimiter_length; } - ret = start_addr - str->str - offset; - if (ret > 0 && ret < str->length) - { - swTraceLog(SW_TRACE_EOF_PROTOCOL, "#[5] count=%d, remaining_length=%zu", count, str->length - str->offset); - } - else if (ret >= str->length) - { - swTraceLog(SW_TRACE_EOF_PROTOCOL, "#[3] length=%ld, size=%ld, offset=%ld", str->length, str->size, (long) str->offset); + ret = start_addr - str - _offset; + if (ret > 0 && ret < length) { + swoole_trace_log( + SW_TRACE_EOF_PROTOCOL, "#[5] count=%d, remaining_length=%zu", count, (size_t) (length - offset)); + } else if (ret >= length) { + swoole_trace_log( + SW_TRACE_EOF_PROTOCOL, "#[3] length=%ld, size=%zu, offset=%jd", length, size, (intmax_t) offset); } return ret; } -uint32_t swoole_utf8_decode(uchar **p, size_t n) -{ - size_t len; - uint32_t u, i, valid; - - u = **p; - - if (u >= 0xf0) - { - u &= 0x07; - valid = 0xffff; - len = 3; - } - else if (u >= 0xe0) - { - u &= 0x0f; - valid = 0x7ff; - len = 2; - } - else if (u >= 0xc2) - { - u &= 0x1f; - valid = 0x7f; - len = 1; - } - else - { - (*p)++; - return 0xffffffff; - } - - if (n - 1 < len) - { - return 0xfffffffe; - } - - (*p)++; - - while (len) - { - i = *(*p)++; - if (i < 0x80) - { - return 0xffffffff; - } - u = (u << 6) | (i & 0x3f); - len--; - } - - if (u > valid) - { - return u; - } - - return 0xffffffff; -} - -size_t swoole_utf8_length(uchar *p, size_t n) -{ - uchar c, *last; - size_t len; - - last = p + n; - - for (len = 0; p < last; len++) - { - c = *p; - if (c < 0x80) - { - p++; - continue; - } - if (swoole_utf8_decode(&p, n) > 0x10ffff) - { - /* invalid UTF-8 */ - return n; - } - } - return len; -} - -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)]; - } - buf[i] = '\0'; -} - -int swString_repeat(swString *src, const char *data, size_t len, size_t n) -{ - if (n <= 0) - { - return SW_ERR; - } - for (size_t i = 0; i < n; i++) - { - swString_append_ptr(src, data, len); - } - return SW_OK; -} +} // namespace swoole diff --git a/src/core/timer.cc b/src/core/timer.cc new file mode 100644 index 00000000000..21798968cba --- /dev/null +++ b/src/core/timer.cc @@ -0,0 +1,273 @@ +/* + +----------------------------------------------------------------------+ + | 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_reactor.h" +#include "swoole_timer.h" + +#if !defined(HAVE_CLOCK_GETTIME) && defined(__MACH__) +#include +#include +#include + +#define ORWL_NANO (+1.0E-9) +#define ORWL_GIGA UINT64_C(1000000000) + +static double orwl_timebase = 0.0; +static uint64_t orwl_timestart = 0; + +int swoole_clock_realtime(struct timespec *t) { + // be more careful in a multithreaded environement + if (!orwl_timestart) { + mach_timebase_info_data_t tb = {0}; + mach_timebase_info(&tb); + orwl_timebase = tb.numer; + orwl_timebase /= tb.denom; + orwl_timestart = mach_absolute_time(); + } + double diff = (mach_absolute_time() - orwl_timestart) * orwl_timebase; + t->tv_sec = diff * ORWL_NANO; + t->tv_nsec = diff - (t->tv_sec * ORWL_GIGA); + return 0; +} +#endif + +namespace swoole { + +Timer::Timer() : heap(1024, Heap::MIN_HEAP) { + _current_id = -1; + next_msec_ = -1; + _next_id = 1; + round = 0; + now(&base_time); +} + +bool Timer::init() { + if (now(&base_time) < 0) { + return false; + } + if (SwooleTG.reactor) { + return init_with_reactor(SwooleTG.reactor); + } else if (SwooleTG.timer_scheduler) { + return init_with_user_scheduler(SwooleTG.timer_scheduler); + } else { + return init_with_system_timer(); + } +} + +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; + return SW_OK; + }; + close = [](Timer *timer) { timer->set(timer, -1); }; + + reactor->set_end_callback(Reactor::PRIORITY_TIMER, [this](Reactor *) { select(); }); + + reactor->set_exit_condition(Reactor::EXIT_CONDITION_TIMER, + [this](Reactor *reactor, size_t &event_num) -> bool { return count() == 0; }); + + reactor->add_destroy_callback([](void *) { + if (swoole_timer_is_available()) { + swoole_timer_free(); + } + }); + + return true; +} + +void Timer::reinit(Reactor *reactor) { + init_with_reactor(reactor); + reactor->timeout_msec = next_msec_; +} + +Timer::~Timer() { + if (close) { + close(this); + } + for (auto iter = map.begin(); iter != map.end(); iter++) { + auto tnode = iter->second; + delete tnode; + } +} + +TimerNode *Timer::add(long _msec, bool persistent, void *data, const TimerCallback &callback) { + if (sw_unlikely(_msec <= 0)) { + swoole_error_log(SW_LOG_WARNING, SW_ERROR_INVALID_PARAMS, "msec value[%ld] is invalid", _msec); + return nullptr; + } + + int64_t now_msec = get_relative_msec(); + if (sw_unlikely(now_msec < 0)) { + return nullptr; + } + + TimerNode *tnode = new TimerNode(); + tnode->data = data; + tnode->type = TimerNode::TYPE_KERNEL; + tnode->exec_msec = now_msec + _msec; + tnode->interval = persistent ? _msec : 0; + tnode->removed = false; + tnode->callback = callback; + tnode->round = round; + tnode->destructor = nullptr; + + if (next_msec_ < 0 || next_msec_ > _msec) { + set(this, _msec); + next_msec_ = _msec; + } + + tnode->id = _next_id++; + if (sw_unlikely(tnode->id < 0)) { + tnode->id = 1; + _next_id = 2; + } + + tnode->heap_node = heap.push(tnode->exec_msec, tnode); + if (sw_unlikely(tnode->heap_node == nullptr)) { + delete tnode; + return nullptr; + } + map.emplace(std::make_pair(tnode->id, tnode)); + 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; +} + +bool Timer::remove(TimerNode *tnode) { + if (sw_unlikely(!tnode || tnode->removed)) { + return false; + } + if (sw_unlikely(_current_id > 0 && tnode->id == _current_id)) { + tnode->removed = true; + 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))) { + return false; + } + if (tnode->heap_node) { + heap.remove(tnode->heap_node); + } + if (tnode->destructor) { + tnode->destructor(tnode); + } + 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; +} + +int Timer::select() { + int64_t now_msec = get_relative_msec(); + if (sw_unlikely(now_msec < 0)) { + return SW_ERR; + } + + TimerNode *tnode = nullptr; + HeapNode *tmp; + + swoole_trace_log(SW_TRACE_TIMER, "timer msec=%" PRId64 ", round=%" PRId64, now_msec, round); + + while ((tmp = heap.top())) { + tnode = (TimerNode *) tmp->data; + if (tnode->exec_msec > now_msec || tnode->round == round) { + break; + } + + _current_id = tnode->id; + if (!tnode->removed) { + 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; + + // persistent timer + if (tnode->interval > 0 && !tnode->removed) { + while (tnode->exec_msec <= now_msec) { + tnode->exec_msec += tnode->interval; + } + tnode->exec_count++; + heap.change_priority(tnode->exec_msec, tmp); + continue; + } + + heap.pop(); + map.erase(tnode->id); + delete tnode; + tnode = nullptr; + } + + if (!tnode || !tmp) { + next_msec_ = -1; + set(this, -1); + } else { + next_msec_ = tnode->exec_msec - now_msec; + if (next_msec_ <= 0) { + next_msec_ = 1; + } + set(this, next_msec_); + } + round++; + + return SW_OK; +} + +int Timer::now(struct timeval *time) { +#if defined(SW_USE_MONOTONIC_TIME) && defined(CLOCK_MONOTONIC) + struct timespec _now; + if (clock_gettime(CLOCK_MONOTONIC, &_now) < 0) { + swoole_sys_warning("clock_gettime(CLOCK_MONOTONIC) failed"); + return SW_ERR; + } + time->tv_sec = _now.tv_sec; + time->tv_usec = _now.tv_nsec / 1000; +#else + if (gettimeofday(time, nullptr) < 0) { + swoole_sys_warning("gettimeofday() failed"); + return SW_ERR; + } +#endif + return SW_OK; +} + +}; // namespace swoole diff --git a/src/coroutine/base.cc b/src/coroutine/base.cc index 79aeb4d0d97..cb1b2ee47f0 100644 --- a/src/coroutine/base.cc +++ b/src/coroutine/base.cc @@ -10,110 +10,163 @@ | to obtain it through the world-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 "coroutine.h" -#include "coroutine_c_api.h" +#include "swoole_coroutine.h" +#include "swoole_coroutine_c_api.h" -using namespace swoole; +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; -sw_coro_on_swap_t Coroutine::on_yield = nullptr; -sw_coro_on_swap_t Coroutine::on_resume = nullptr; -sw_coro_on_swap_t Coroutine::on_close = nullptr; -sw_coro_bailout_t 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; -void Coroutine::yield() -{ +#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 = SW_CORO_WAITING; - if (sw_likely(on_yield)) - { + state = STATE_WAITING; + 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::resume() -{ +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)) - { + if (sw_unlikely(on_bailout)) { return; } - state = SW_CORO_RUNNING; - if (sw_likely(on_resume)) - { + state = STATE_RUNNING; + 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 = SW_CORO_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 = SW_CORO_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() -{ +void Coroutine::close() { SW_ASSERT(current == this); - state = SW_CORO_END; - if (on_close) - { + state = STATE_END; + if (on_close && task) { on_close(task); } -#ifdef SW_USE_ASM_CONTEXT - swTraceLog(SW_TRACE_CONTEXT, "coroutine#%ld stack memory use less than %ld bytes", get_cid(), ctx.get_stack_usage()); +#if !defined(SW_USE_THREAD_CONTEXT) && defined(SW_CONTEXT_DETECT_STACK_USAGE) + swoole_trace_log( + SW_TRACE_CONTEXT, "coroutine#%ld stack memory use less than %ld bytes", get_cid(), ctx.get_stack_usage()); #endif current = origin; coroutines.erase(cid); delete this; } -void Coroutine::print_list() -{ - for (auto i = coroutines.begin(); i != coroutines.end(); i++) - { +void Coroutine::print_list() { + for (auto i = coroutines.begin(); i != coroutines.end(); i++) { const char *state; - switch(i->second->state){ - case SW_CORO_INIT: + switch (i->second->state) { + case STATE_INIT: state = "[INIT]"; break; - case SW_CORO_WAITING: + case STATE_WAITING: state = "[WAITING]"; break; - case SW_CORO_RUNNING: + case STATE_RUNNING: state = "[RUNNING]"; break; - case SW_CORO_END: + case STATE_END: state = "[END]"; break; default: @@ -124,99 +177,93 @@ void Coroutine::print_list() } } -void Coroutine::set_on_yield(sw_coro_on_swap_t func) -{ +void Coroutine::set_on_yield(SwapCallback func) { on_yield = func; } -void Coroutine::set_on_resume(sw_coro_on_swap_t func) -{ +void Coroutine::set_on_resume(SwapCallback func) { on_resume = func; } -void Coroutine::set_on_close(sw_coro_on_swap_t func) -{ +void Coroutine::set_on_close(SwapCallback func) { on_close = func; } -void Coroutine::bailout(sw_coro_bailout_t func) -{ +void Coroutine::bailout(BailoutCallback func) { Coroutine *co = current; - if (!co) - { + if (!co) { // marks that it can no longer resume any coroutine - on_bailout = (sw_coro_bailout_t) -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); + if (!func) { + swoole_error("bailout without callback function"); } on_bailout = func; // find the coroutine which is closest to the main - while (co->origin) - { + while (co->origin) { co = co->origin; } // 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 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 +} // namespace swoole + +uint8_t swoole_coroutine_is_in() { + return !!swoole::Coroutine::get_current(); +} + +long swoole_coroutine_get_current_id() { + return swoole::Coroutine::get_current_cid(); } -uint8_t swoole_coroutine_is_in() -{ - return !!Coroutine::get_current(); +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; + } } -long swoole_coroutine_get_current_id() -{ - return Coroutine::get_current_cid(); +size_t swoole_coroutine_count() { + return swoole::Coroutine::coroutines.size(); } /** * for gdb */ -static std::unordered_map::iterator _gdb_iterator; +static std::unordered_map::iterator _gdb_iterator; -void swoole_coro_iterator_reset() -{ - _gdb_iterator = Coroutine::coroutines.begin(); +void swoole_coroutine_iterator_reset() { + _gdb_iterator = swoole::Coroutine::coroutines.begin(); } -Coroutine* swoole_coro_iterator_each() -{ - if (_gdb_iterator == Coroutine::coroutines.end()) - { +swoole::Coroutine *swoole_coroutine_iterator_each() { + if (_gdb_iterator == swoole::Coroutine::coroutines.end()) { return nullptr; - } - else - { - Coroutine *co = _gdb_iterator->second; + } else { + swoole::Coroutine *co = _gdb_iterator->second; _gdb_iterator++; return co; } } - -Coroutine* swoole_coro_get(long cid) -{ - auto i = Coroutine::coroutines.find(cid); - if (i == Coroutine::coroutines.end()) - { - return nullptr; - } - else - { - return i->second; - } -} - -size_t swoole_coro_count() -{ - return Coroutine::coroutines.size(); -} diff --git a/src/coroutine/channel.cc b/src/coroutine/channel.cc index f882e813523..f0806728e78 100644 --- a/src/coroutine/channel.cc +++ b/src/coroutine/channel.cc @@ -10,79 +10,80 @@ | to obtain it through the world-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 "coroutine_channel.h" +#include "swoole_coroutine_channel.h" -using swoole::coroutine::Channel; +namespace swoole { +namespace coroutine { -#include - -using namespace swoole; - -void Channel::timer_callback(swTimer *timer, swTimer_node *tnode) -{ - timer_msg_t *msg = (timer_msg_t *) tnode->data; +void Channel::timer_callback(Timer *timer, TimerNode *tnode) { + TimeoutMessage *msg = (TimeoutMessage *) tnode->data; msg->error = true; msg->timer = nullptr; - if (msg->type == CONSUMER) - { + if (msg->type == CONSUMER) { msg->chan->consumer_remove(msg->co); - } - else - { + } else { msg->chan->producer_remove(msg->co); } msg->co->resume(); } -void Channel::yield(enum opcode type) -{ +void Channel::yield(enum Opcode type) { Coroutine *co = Coroutine::get_current_safe(); - if (type == PRODUCER) - { + if (type == PRODUCER) { producer_queue.push_back(co); - swTraceLog(SW_TRACE_CHANNEL, "producer cid=%ld", co->get_cid()); - } - else - { + 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) -{ +void *Channel::pop(double timeout) { Coroutine *current_co = Coroutine::get_current_safe(); - if (closed) - { + if (closed && is_empty()) { + error_ = ERROR_CLOSED; return nullptr; } - if (is_empty() || !consumer_queue.empty()) - { - timer_msg_t msg; + if (is_empty() || !consumer_queue.empty()) { + TimeoutMessage msg; msg.error = false; - msg.timer = NULL; - if (timeout > 0) - { - long msec = (long) (timeout * 1000); + msg.timer = nullptr; + if (timeout > 0) { msg.chan = this; msg.type = CONSUMER; msg.co = current_co; - msg.timer = swoole_timer_add(msec, SW_FALSE, timer_callback, &msg); + msg.timer = swoole_timer_add(timeout, false, timer_callback, &msg); } yield(CONSUMER); - if (msg.timer) - { + if (msg.timer) { swoole_timer_del(msg.timer); } - if (msg.error || closed) - { + 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; } } @@ -94,43 +95,45 @@ void* Channel::pop(double timeout) /** * notify producer */ - if (!producer_queue.empty()) - { + if (!producer_queue.empty()) { Coroutine *co = pop_coroutine(PRODUCER); co->resume(); } return data; } -bool Channel::push(void *data, double timeout) -{ +bool Channel::push(void *data, double timeout) { Coroutine *current_co = Coroutine::get_current_safe(); - if (closed) - { + if (closed) { + error_ = ERROR_CLOSED; return false; } - if (is_full() || !producer_queue.empty()) - { - timer_msg_t msg; + if (is_full() || !producer_queue.empty()) { + TimeoutMessage msg; msg.error = false; - msg.timer = NULL; - if (timeout > 0) - { - long msec = (long) (timeout * 1000); + msg.timer = nullptr; + if (timeout > 0) { msg.chan = this; msg.type = PRODUCER; msg.co = current_co; - msg.timer = swoole_timer_add(msec, SW_FALSE, timer_callback, &msg); + msg.timer = swoole_timer_add(timeout, false, timer_callback, &msg); } yield(PRODUCER); - if (msg.timer) - { + 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; } } @@ -138,35 +141,33 @@ 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 */ - if (!consumer_queue.empty()) - { + if (!consumer_queue.empty()) { Coroutine *co = pop_coroutine(CONSUMER); co->resume(); } return true; } -bool Channel::close() -{ - if (closed) - { +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()) - { + while (!producer_queue.empty()) { Coroutine *co = pop_coroutine(PRODUCER); co->resume(); } - while (!consumer_queue.empty()) - { + while (!consumer_queue.empty()) { Coroutine *co = pop_coroutine(CONSUMER); co->resume(); } return true; } + +} // namespace coroutine +} // namespace swoole diff --git a/src/coroutine/context.cc b/src/coroutine/context.cc index 3b628b0b8e8..8a6c10a9143 100644 --- a/src/coroutine/context.cc +++ b/src/coroutine/context.cc @@ -10,94 +10,106 @@ | to obtain it through the world-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 "context.h" +#include "swoole_coroutine_context.h" -#if USE_ASM_CONTEXT +#ifdef SW_CONTEXT_PROTECT_STACK_PAGE +#include +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +#define MAP_ANONYMOUS MAP_ANON +#endif +#endif -using namespace swoole; +#ifndef SW_USE_THREAD_CONTEXT -#define MAGIC_STRING "swoole_coroutine#5652a7fb2b38be" -#define START_OFFSET (64 * 1024) +#define MAGIC_STRING "swoole_coroutine#5652a7fb2b38be" +#define START_OFFSET (64 * 1024) -Context::Context(size_t stack_size, coroutine_func_t fn, void* private_data) : - fn_(fn), stack_size_(stack_size), private_data_(private_data) -{ -#ifdef SW_CONTEXT_PROTECT_STACK_PAGE - protect_page_ = 0; -#endif +namespace swoole { +namespace coroutine { + +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; - swap_ctx_ = nullptr; - stack_ = (char*) sw_malloc(stack_size_); - if (!stack_) - { - swFatalError(SW_ERROR_MALLOC_FAIL, "failed to malloc stack memory."); +#ifdef SW_CONTEXT_PROTECT_STACK_PAGE + 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_) { + 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_); + void *sp = (void *) ((char *) stack_ + stack_size_); #ifdef USE_VALGRIND valgrind_stack_id = VALGRIND_STACK_REGISTER(sp, stack_); #endif - ctx_ = make_fcontext(sp, stack_size_, (void (*)(intptr_t))&context_func); -#ifdef SW_LOG_TRACE_OPEN +#ifdef USE_UCONTEXT + if (-1 == getcontext(&ctx_)) { + swoole_throw_error(SW_ERROR_CO_GETCONTEXT_FAILED); + sw_free(stack_); + return; + } + ctx_.uc_stack.ss_sp = stack_; + ctx_.uc_stack.ss_size = stack_size; + ctx_.uc_link = nullptr; + makecontext(&ctx_, (void (*)(void)) & context_func, 1, this); +#else + ctx_ = swoole_make_fcontext(sp, stack_size_, (void (*)(transfer_t)) & context_func); + swap_ctx_ = nullptr; +#endif + +#ifdef SW_CONTEXT_DETECT_STACK_USAGE size_t offset = START_OFFSET; - while (offset <= stack_size) - { - memcpy((char*) sp - offset + (sizeof(MAGIC_STRING) -1), SW_STRL(MAGIC_STRING)); + while (offset <= stack_size) { + memcpy((char *) sp - offset + (sizeof(MAGIC_STRING) - 1), SW_STRL(MAGIC_STRING)); offset *= 2; } #endif + #ifdef SW_CONTEXT_PROTECT_STACK_PAGE - uint32_t protect_page = get_protect_stack_page(); - if (protect_page) - { - if (protect_stack(stack_, stack_size_, protect_page)) - { - protect_page_ = protect_page; - } - } + mprotect(stack_, SwooleG.pagesize, PROT_NONE); #endif } -Context::~Context() -{ - if (stack_) - { - swTraceLog(SW_TRACE_COROUTINE, "free stack: ptr=%p", stack_); -#ifdef SW_CONTEXT_PROTECT_STACK_PAGE - if (protect_page_) - { - unprotect_stack(stack_, protect_page_); - } -#endif +Context::~Context() { + if (stack_) { + swoole_trace_log(SW_TRACE_COROUTINE, "free stack: ptr=%p", stack_); #ifdef USE_VALGRIND VALGRIND_STACK_DEREGISTER(valgrind_stack_id); #endif + +#ifdef SW_CONTEXT_PROTECT_STACK_PAGE + ::munmap(stack_, stack_size_); +#else sw_free(stack_); - stack_ = NULL; +#endif + stack_ = nullptr; } } -#ifdef SW_LOG_TRACE_OPEN -ssize_t Context::get_stack_usage() -{ +#ifdef SW_CONTEXT_DETECT_STACK_USAGE +ssize_t Context::get_stack_usage() { size_t offset = START_OFFSET; size_t retval = START_OFFSET; - void* sp = (void*) ((char*) stack_ + stack_size_); + void *sp = (void *) ((char *) stack_ + stack_size_); - while (offset < stack_size_) - { - if (memcmp((char*) sp - offset + (sizeof(MAGIC_STRING) - 1), SW_STRL(MAGIC_STRING)) != 0) - { + while (offset < stack_size_) { + if (memcmp((char *) sp - offset + (sizeof(MAGIC_STRING) - 1), SW_STRL(MAGIC_STRING)) != 0) { retval = offset * 2; } offset *= 2; @@ -107,24 +119,37 @@ ssize_t Context::get_stack_usage() } #endif -bool Context::swap_in() -{ - jump_fcontext(&swap_ctx_, ctx_, (intptr_t) this, true); +bool Context::swap_in() { +#ifdef USE_UCONTEXT + return 0 == swapcontext(&swap_ctx_, &ctx_); +#else + coroutine_transfer_t transfer_data = swoole_jump_fcontext(ctx_, (void *) this); + ctx_ = transfer_data.fctx; return true; +#endif } -bool Context::swap_out() -{ - jump_fcontext(&ctx_, swap_ctx_, (intptr_t) this, true); +bool Context::swap_out() { +#ifdef USE_UCONTEXT + return 0 == swapcontext(&ctx_, &swap_ctx_); +#else + 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(); } - +} // namespace coroutine +} // namespace swoole #endif diff --git a/src/coroutine/file_lock.cc b/src/coroutine/file_lock.cc index 50776a5dee9..af040bbdc12 100644 --- a/src/coroutine/file_lock.cc +++ b/src/coroutine/file_lock.cc @@ -10,115 +10,101 @@ | to obtain it through the world-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 "coroutine.h" -#include "coroutine_c_api.h" - -#include #include #include -#include -#include -using namespace std; -using namespace swoole; +#include "swoole_coroutine.h" +#include "swoole_coroutine_c_api.h" + +using swoole::Coroutine; -class file_lock_manager -{ -public: +class LockManager { + public: bool lock_ex = false; bool lock_sh = false; - queue _queue; + std::queue queue_; }; -static unordered_map lock_pool; +static std::unordered_map lock_pool; -static inline file_lock_manager* get_manager(char *filename) -{ - string key(filename); +static inline LockManager *get_manager(const char *filename) { + std::string key(filename); auto i = lock_pool.find(key); - file_lock_manager* lm; - if (i == lock_pool.end()) - { - lm = new file_lock_manager; + LockManager *lm; + if (i == lock_pool.end()) { + lm = new LockManager; lock_pool[key] = lm; - } - else - { + } else { lm = i->second; } return lm; } -static inline int lock_ex(char *filename, int fd) -{ - file_lock_manager*lm = get_manager(filename); - if (lm->lock_ex || lm->lock_sh) - { +static inline int lock_ex(const char *filename, int fd) { + LockManager *lm = get_manager(filename); + if (lm->lock_ex || lm->lock_sh) { Coroutine *co = Coroutine::get_current(); - lm->_queue.push(co); + lm->queue_.push(co); co->yield(); } lm->lock_ex = true; - return ::flock(fd, LOCK_EX); + if (swoole_coroutine_flock(fd, LOCK_EX) < 0) { + lm->lock_ex = false; + return -1; + } else { + return 0; + } } -static inline int lock_sh(char *filename, int fd) -{ - file_lock_manager*lm = get_manager(filename); - if (lm->lock_ex) - { +static inline int lock_sh(const char *filename, int fd) { + LockManager *lm = get_manager(filename); + if (lm->lock_ex) { Coroutine *co = Coroutine::get_current(); - lm->_queue.push(co); + lm->queue_.push(co); co->yield(); } lm->lock_sh = true; - return ::flock(fd, LOCK_SH); + if (swoole_coroutine_flock(fd, LOCK_SH) < 0) { + lm->lock_sh = false; + return -1; + } else { + return 0; + } } -static inline int lock_release(char *filename, int fd) -{ - string key(filename); +static inline int lock_release(const char *filename, int fd) { + std::string key(filename); auto i = lock_pool.find(key); - if (i == lock_pool.end()) - { - return ::flock(fd, LOCK_UN); + if (i == lock_pool.end()) { + return swoole_coroutine_flock(fd, LOCK_UN); } - file_lock_manager* lm = i->second; - if (lm->_queue.empty()) - { + LockManager *lm = i->second; + if (lm->queue_.empty()) { delete lm; lock_pool.erase(i); - return ::flock(fd, LOCK_UN); - } - else - { - Coroutine *co = lm->_queue.front(); - lm->_queue.pop(); - int retval = ::flock(fd, LOCK_UN); + return swoole_coroutine_flock(fd, LOCK_UN); + } else { + Coroutine *co = lm->queue_.front(); + lm->queue_.pop(); + int retval = swoole_coroutine_flock(fd, LOCK_UN); co->resume(); return retval; } } #ifdef LOCK_NB -static inline int lock_nb(char *filename, int fd, int operation) -{ +static inline int lock_nb(const char *filename, int fd, int operation) { int retval = ::flock(fd, operation | LOCK_NB); - if (retval == 0) - { - file_lock_manager*lm = get_manager(filename); - if (operation == LOCK_EX) - { + if (retval == 0) { + LockManager *lm = get_manager(filename); + if (operation == LOCK_EX) { lm->lock_ex = true; - } - else - { + } else { lm->lock_sh = true; } } @@ -126,23 +112,20 @@ static inline int lock_nb(char *filename, int fd, int operation) } #endif -int swoole_coroutine_flock_ex(char *filename, int fd, int operation) -{ +int swoole_coroutine_flock_ex(const char *filename, int fd, int operation) { Coroutine *co = Coroutine::get_current(); - if (sw_unlikely(SwooleTG.reactor == nullptr || !co)) - { + if (sw_unlikely(SwooleTG.reactor == nullptr || !co)) { return ::flock(fd, operation); } - char *real = realpath(filename, SwooleTG.buffer_stack->str); - if (real == NULL) - { - SwooleG.error = errno = ENOENT; + const char *real = realpath(filename, sw_tg_buffer()->str); + if (real == nullptr) { + errno = ENOENT; + swoole_set_last_error(ENOENT); return -1; } - switch (operation) - { + switch (operation) { case LOCK_EX: return lock_ex(real, fd); case LOCK_SH: @@ -151,8 +134,7 @@ int swoole_coroutine_flock_ex(char *filename, int fd, int operation) return lock_release(real, fd); default: #ifdef LOCK_NB - if (operation & LOCK_NB) - { + if (operation & LOCK_NB) { return lock_nb(real, fd, operation & (~LOCK_NB)); } #endif diff --git a/src/coroutine/hook.cc b/src/coroutine/hook.cc index 44da5e37e9d..1649d4302a5 100644 --- a/src/coroutine/hook.cc +++ b/src/coroutine/hook.cc @@ -10,716 +10,702 @@ | to obtain it through the world-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_cxx.h" -#include "coroutine_cxx_api.h" - #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 std::unordered_map; +using swoole::coroutine::translate_events_from_poll; +using swoole::coroutine::translate_events_to_poll; -static unordered_map socket_map; +#ifdef SW_USE_IOURING +using swoole::Iouring; +#endif -static sw_inline bool is_no_coro() -{ +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()) - { + if (socket_iterator == socket_map.end()) { return nullptr; } return socket_iterator->second; } -static sw_inline Socket* get_socket_ex(int sockfd) -{ - if (sw_unlikely(is_no_coro())) - { +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); } SW_EXTERN_C_BEGIN -int swoole_coroutine_socket(int domain, int type, int protocol) -{ - if (sw_unlikely(is_no_coro())) - { +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; - } - else - { + if (sw_unlikely(fd < 0)) { + return -1; + } else { + std::unique_lock _lock(socket_map_lock); socket_map[fd] = socket; } return fd; } -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 == nullptr)) - { +ssize_t swoole_coroutine_send(int sockfd, const void *buf, size_t len, int flags) { + 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 == nullptr)) - { +ssize_t swoole_coroutine_sendmsg(int sockfd, const struct msghdr *msg, int flags) { + 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 == nullptr)) - { +ssize_t swoole_coroutine_recvmsg(int sockfd, struct msghdr *msg, int flags) { + 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 == nullptr)) - { +ssize_t swoole_coroutine_recv(int sockfd, void *buf, size_t len, int flags) { + auto socket = get_socket_ex(sockfd); + if (sw_unlikely(socket == nullptr)) { return ::recv(sockfd, buf, len, flags); } - if (flags & MSG_PEEK) - { + if (flags & MSG_PEEK) { return socket->peek(buf, len); - } - else - { + } else { return socket->recv(buf, len); } } -int swoole_coroutine_close(int sockfd) -{ - Socket *socket = get_socket(sockfd); - if (socket == nullptr) - { +int swoole_coroutine_close(int sockfd) { + auto socket = get_socket(sockfd); + if (socket == nullptr) { return ::close(sockfd); } - if (socket->close()) - { - delete socket; + if (socket->close()) { + 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 == nullptr)) - { +int swoole_coroutine_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { + 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)) == nullptr - )) - { +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); - if (fds[0].events & POLLIN) - { + if (fds[0].events & POLLIN) { fds[0].revents |= POLLIN; } - if (fds[0].events & POLLOUT) - { + if (fds[0].events & POLLOUT) { fds[0].revents |= POLLOUT; } 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)) - { + +int swoole_coroutine_poll(struct pollfd *fds, nfds_t nfds, int timeout) { + 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)) - { + if (!System::socket_poll(_fds, (double) timeout / 1000)) { return -1; } - int retval; - for (auto &i : _fds) - { + int retval = 0; + for (auto &i : _fds) { int revents = i.second.revents; struct pollfd *_fd = (struct pollfd *) i.second.ptr; - _fd->revents = revents; - if (revents > 0) - { + _fd->revents = translate_events_to_poll(revents); + if (revents > 0) { retval++; } } return retval; } -#endif -static void handler_access(swAio_event *event) -{ - event->ret = access((const char*) event->buf, event->offset); - event->error = errno; -} +int swoole_coroutine_open(const char *pathname, int flags, mode_t mode) { + if (sw_unlikely(is_no_coro())) { + return open(pathname, flags, mode); + } -static void handler_flock(swAio_event *event) -{ - event->ret = ::flock(event->fd, (int) event->flags); - event->error = errno; + int ret = -1; + async([&]() { ret = open(pathname, flags, mode); }); + return ret; } -static void handler_open(swAio_event *event) -{ - event->ret = open((const char*) event->buf, event->flags, event->offset); - event->error = errno; -} +int swoole_coroutine_close_file(int fd) { + if (sw_unlikely(is_no_coro())) { + return close(fd); + } -static void handler_read(swAio_event *event) -{ - event->ret = read(event->fd, event->buf, event->nbytes); - event->error = errno; + int ret = -1; + async([&]() { ret = close(fd); }); + return ret; } -static void handler_write(swAio_event *event) -{ - event->ret = write(event->fd, event->buf, event->nbytes); - event->error = errno; +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; } -static void handler_lseek(swAio_event *event) -{ - event->ret = lseek(event->fd, event->offset, event->flags); - event->error = errno; +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; } -static void handler_fstat(swAio_event *event) -{ - event->ret = fstat(event->fd, (struct stat *) event->buf); - event->error = errno; +uint8_t swoole_coroutine_socket_exists(int fd) { + return socket_map.find(fd) != socket_map.end(); } -static void handler_unlink(swAio_event *event) -{ - event->ret = unlink((const char*) event->buf); - event->error = errno; -} +ssize_t swoole_coroutine_read(int sockfd, void *buf, size_t count) { + if (sw_unlikely(is_no_coro())) { + return read(sockfd, buf, count); + } -static void handler_mkdir(swAio_event *event) -{ - event->ret = mkdir((const char*) event->buf, event->offset); - event->error = errno; -} + auto socket = get_socket(sockfd); + if (socket != nullptr) { + return socket->read(buf, count); + } -static void handler_rmdir(swAio_event *event) -{ - event->ret = rmdir((const char*) event->buf); - event->error = errno; + ssize_t ret = -1; + async([&]() { ret = read(sockfd, buf, count); }); + return ret; } -static void handler_statvfs(swAio_event *event) -{ - event->ret = statvfs((const char *) event->buf, (struct statvfs *) event->offset); - event->error = errno; -} +ssize_t swoole_coroutine_write(int sockfd, const void *buf, size_t count) { + if (sw_unlikely(is_no_coro())) { + return write(sockfd, buf, count); + } -static void handler_rename(swAio_event *event) -{ - event->ret = rename((const char*) event->buf, (const char*) event->offset); - event->error = errno; + auto socket = get_socket(sockfd); + if (socket != nullptr) { + return socket->write(buf, count); + } + + ssize_t ret = -1; + async([&]() { ret = write(sockfd, buf, count); }); + return ret; } -static void aio_onCompleted(swAio_event *event) -{ - swAio_event *ev = (swAio_event *) event->req; - ev->ret = event->ret; - errno = event->error; - ((Coroutine *) event->object)->resume(); +off_t swoole_coroutine_lseek(int fd, off_t offset, int whence) { + if (sw_unlikely(is_no_coro())) { + return lseek(fd, offset, whence); + } + + off_t retval = -1; + async([&]() { retval = lseek(fd, offset, whence); }); + return retval; } -int swoole_coroutine_open(const char *pathname, int flags, mode_t mode) -{ - if (sw_unlikely(is_no_coro())) - { - return open(pathname, flags, mode); +int swoole_coroutine_fstat(int fd, struct stat *statbuf) { + if (sw_unlikely(is_no_coro())) { + return fstat(fd, statbuf); } - swAio_event ev; - bzero(&ev, sizeof(ev)); - ev.buf = (void*) pathname; - ev.offset = mode; - ev.flags = flags; - ev.handler = handler_open; - ev.callback = aio_onCompleted; - ev.object = Coroutine::get_current(); - ev.req = &ev; + int retval = -1; + async([&]() { retval = fstat(fd, statbuf); }); + return retval; +} - ssize_t ret = swAio_dispatch(&ev); - if (ret < 0) - { - return -1; +int swoole_coroutine_readlink(const char *pathname, char *buf, size_t len) { + if (sw_unlikely(is_no_coro())) { + return readlink(pathname, buf, len); } - ((Coroutine *) ev.object)->yield(); - return ev.ret; + + int retval = -1; + async([&]() { retval = readlink(pathname, buf, len); }); + return retval; } -ssize_t swoole_coroutine_read(int sockfd, void *buf, size_t count) -{ - if (sw_unlikely(is_no_coro())) - { - return read(sockfd, buf, count); +int swoole_coroutine_unlink(const char *pathname) { + if (sw_unlikely(is_no_coro())) { + return unlink(pathname); } - Socket *socket = get_socket(sockfd); - if (socket && socket->socket->fdtype == SW_FD_CORO_SOCKET) - { - return socket->read(buf, count); + int retval = -1; + async([&]() { retval = unlink(pathname); }); + return retval; +} + +int swoole_coroutine_statvfs(const char *path, struct statvfs *buf) { + if (sw_unlikely(is_no_coro())) { + return statvfs(path, buf); } - swAio_event ev; - bzero(&ev, sizeof(ev)); - ev.fd = sockfd; - ev.buf = buf; - ev.nbytes = count; - ev.handler = handler_read; - ev.callback = aio_onCompleted; - ev.object = Coroutine::get_current(); - ev.req = &ev; + int retval = -1; + async([&]() { retval = statvfs(path, buf); }); + return retval; +} - ssize_t ret = swAio_dispatch(&ev); - if (ret < 0) - { - return -1; +int swoole_coroutine_stat(const char *path, struct stat *statbuf) { + if (sw_unlikely(is_no_coro())) { + return stat(path, statbuf); } - ((Coroutine *) ev.object)->yield(); - return ev.ret; + + int retval = -1; + async([&]() { retval = stat(path, statbuf); }); + return retval; } -ssize_t swoole_coroutine_write(int sockfd, const void *buf, size_t count) -{ - if (sw_unlikely(is_no_coro())) - { - return write(sockfd, buf, count); +int swoole_coroutine_lstat(const char *path, struct stat *statbuf) { + if (sw_unlikely(is_no_coro())) { + return lstat(path, statbuf); } - Socket *socket = get_socket(sockfd); - if (socket && socket->socket->fdtype == SW_FD_CORO_SOCKET) - { - return socket->write(buf, count); + int retval = -1; + async([&]() { retval = lstat(path, statbuf); }); + return retval; +} + +int swoole_coroutine_mkdir(const char *pathname, mode_t mode) { + if (sw_unlikely(is_no_coro())) { + return mkdir(pathname, mode); } - swAio_event ev; - bzero(&ev, sizeof(ev)); - ev.fd = sockfd; - ev.buf = (void*) buf; - ev.nbytes = count; - ev.handler = handler_write; - ev.callback = aio_onCompleted; - ev.object = Coroutine::get_current(); - ev.req = &ev; + int retval = -1; + async([&]() { retval = mkdir(pathname, mode); }); + return retval; +} - ssize_t ret = swAio_dispatch(&ev); - if (ret < 0) - { - return -1; +int swoole_coroutine_rmdir(const char *pathname) { + if (sw_unlikely(is_no_coro())) { + return rmdir(pathname); } - ((Coroutine *) ev.object)->yield(); - return ev.ret; + + int retval = -1; + async([&]() { retval = rmdir(pathname); }); + return retval; } -off_t swoole_coroutine_lseek(int fd, off_t offset, int whence) -{ - if (sw_unlikely(is_no_coro())) - { - return lseek(fd, offset, whence); +int swoole_coroutine_rename(const char *oldpath, const char *newpath) { + if (sw_unlikely(is_no_coro())) { + return rename(oldpath, newpath); } - swAio_event ev; - bzero(&ev, sizeof(ev)); - ev.fd = fd; - ev.offset = offset; - ev.flags = whence; - ev.handler = handler_lseek; - ev.callback = aio_onCompleted; - ev.object = Coroutine::get_current(); - ev.req = &ev; + int retval = -1; + async([&]() { retval = rename(oldpath, newpath); }); + return retval; +} - ssize_t ret = swAio_dispatch(&ev); - if (ret < 0) - { - return -1; +int swoole_coroutine_access(const char *pathname, int mode) { + if (sw_unlikely(is_no_coro())) { + return access(pathname, mode); } - ((Coroutine *) ev.object)->yield(); - return ev.ret; + + int retval = -1; + async([&]() { retval = access(pathname, mode); }); + return retval; } -int swoole_coroutine_fstat(int fd, struct stat *statbuf) -{ - if (sw_unlikely(is_no_coro())) - { - return fstat(fd, statbuf); +FILE *swoole_coroutine_fopen(const char *pathname, const char *mode) { + if (sw_unlikely(is_no_coro())) { + return fopen(pathname, mode); } - swAio_event ev; - bzero(&ev, sizeof(ev)); - ev.fd = fd; - ev.buf = (void*) statbuf; - ev.handler = handler_fstat; - ev.callback = aio_onCompleted; - ev.object = Coroutine::get_current(); - ev.req = &ev; + FILE *retval = nullptr; + async([&]() { retval = fopen(pathname, mode); }); + return retval; +} - ssize_t ret = swAio_dispatch(&ev); - if (ret < 0) - { - return -1; +FILE *swoole_coroutine_fdopen(int fd, const char *mode) { + if (sw_unlikely(is_no_coro())) { + return fdopen(fd, mode); } - ((Coroutine *) ev.object)->yield(); - return ev.ret; + + FILE *retval = nullptr; + async([&]() { retval = fdopen(fd, mode); }); + return retval; } -int swoole_coroutine_unlink(const char *pathname) -{ - if (sw_unlikely(is_no_coro())) - { - return unlink(pathname); +FILE *swoole_coroutine_freopen(const char *pathname, const char *mode, FILE *stream) { + if (sw_unlikely(is_no_coro())) { + return freopen(pathname, mode, stream); } - swAio_event ev; - bzero(&ev, sizeof(ev)); - ev.buf = (void*) pathname; - ev.handler = handler_unlink; - ev.callback = aio_onCompleted; - ev.object = Coroutine::get_current(); - ev.req = &ev; + FILE *retval = nullptr; + async([&]() { retval = freopen(pathname, mode, stream); }); + return retval; +} - ssize_t ret = swAio_dispatch(&ev); - if (ret < 0) - { - return -1; +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); } - ((Coroutine *) ev.object)->yield(); - return ev.ret; + + size_t retval = 0; + async([&]() { retval = fread(ptr, size, nmemb, stream); }); + return retval; } -int swoole_coroutine_statvfs(const char *path, struct statvfs *buf) -{ - if (sw_unlikely(is_no_coro())) - { - return statvfs(path, buf); +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); } - swAio_event ev; - bzero(&ev, sizeof(ev)); - ev.buf = (void*) path; - ev.offset = (off_t) buf; - ev.handler = handler_statvfs; - ev.callback = aio_onCompleted; - ev.object = Coroutine::get_current(); - ev.req = &ev; + size_t retval = 0; + async([&]() { retval = fwrite(ptr, size, nmemb, stream); }); + return retval; +} - ssize_t ret = swAio_dispatch(&ev); - if (ret < 0) - { - return -1; +char *swoole_coroutine_fgets(char *s, int size, FILE *stream) { + if (sw_unlikely(is_no_coro())) { + return fgets(s, size, stream); } - ((Coroutine *) ev.object)->yield(); - return ev.ret; + + char *retval = nullptr; + async([&]() { retval = fgets(s, size, stream); }); + return retval; } -int swoole_coroutine_mkdir(const char *pathname, mode_t mode) -{ - if (sw_unlikely(is_no_coro())) - { - return mkdir(pathname, mode); +int swoole_coroutine_fputs(const char *s, FILE *stream) { + if (sw_unlikely(is_no_coro())) { + return fputs(s, stream); } - swAio_event ev; - bzero(&ev, sizeof(ev)); - ev.buf = (void*) pathname; - ev.offset = mode; - ev.handler = handler_mkdir; - ev.callback = aio_onCompleted; - ev.object = Coroutine::get_current(); - ev.req = &ev; + int retval = -1; + async([&]() { retval = fputs(s, stream); }); + return retval; +} - ssize_t ret = swAio_dispatch(&ev); - if (ret < 0) - { - return -1; +int swoole_coroutine_feof(FILE *stream) { + if (sw_unlikely(is_no_coro())) { + return feof(stream); } - ((Coroutine *) ev.object)->yield(); - return ev.ret; + + int retval = -1; + async([&]() { retval = feof(stream); }); + return retval; } -int swoole_coroutine_rmdir(const char *pathname) -{ - if (sw_unlikely(is_no_coro())) - { - return rmdir(pathname); +int swoole_coroutine_fclose(FILE *stream) { + if (sw_unlikely(is_no_coro())) { + return fclose(stream); } - swAio_event ev; - bzero(&ev, sizeof(ev)); - ev.buf = (void*) pathname; - ev.handler = handler_rmdir; - ev.callback = aio_onCompleted; - ev.object = Coroutine::get_current(); - ev.req = &ev; + int retval = -1; + async([&]() { retval = fclose(stream); }); + return retval; +} - ssize_t ret = swAio_dispatch(&ev); - if (ret < 0) - { - return -1; +int swoole_coroutine_flock(int fd, int operation) { + if (sw_unlikely(is_no_coro())) { + return flock(fd, operation); } - ((Coroutine *) ev.object)->yield(); - return ev.ret; + + int retval = -1; + async([&]() { retval = flock(fd, operation); }); + return retval; } -int swoole_coroutine_rename(const char *oldpath, const char *newpath) -{ - if (sw_unlikely(is_no_coro())) - { - return rename(oldpath, newpath); +DIR *swoole_coroutine_opendir(const char *name) { + if (sw_unlikely(is_no_coro())) { + return opendir(name); } - swAio_event ev; - bzero(&ev, sizeof(ev)); - ev.buf = (void*) oldpath; - ev.offset = (off_t) newpath; - ev.handler = handler_rename; - ev.callback = aio_onCompleted; - ev.object = Coroutine::get_current(); - ev.req = &ev; + DIR *retval = nullptr; + async([&]() { retval = opendir(name); }); + return retval; +} - ssize_t ret = swAio_dispatch(&ev); - if (ret < 0) - { - return -1; +struct dirent *swoole_coroutine_readdir(DIR *dirp) { + if (sw_unlikely(is_no_coro())) { + return readdir(dirp); } - ((Coroutine *) ev.object)->yield(); - return ev.ret; + + struct dirent *retval; + + async([&retval, dirp]() { retval = readdir(dirp); }); + + return retval; } -int swoole_coroutine_access(const char *pathname, int mode) -{ - if (sw_unlikely(is_no_coro())) - { - return access(pathname, mode); +int swoole_coroutine_closedir(DIR *dirp) { + if (sw_unlikely(is_no_coro())) { + return closedir(dirp); } - swAio_event ev; - bzero(&ev, sizeof(ev)); - ev.buf = (void*) pathname; - ev.offset = mode; - ev.handler = handler_access; - ev.callback = aio_onCompleted; - ev.object = Coroutine::get_current(); - ev.req = &ev; + int retval = -1; + async([&]() { retval = closedir(dirp); }); + return retval; +} - ssize_t ret = swAio_dispatch(&ev); - if (ret < 0) - { +void swoole_coroutine_sleep(int sec) { + System::sleep((double) sec); +} + +void swoole_coroutine_usleep(int usec) { + System::sleep((double) usec / 1024 / 1024); +} + +int swoole_coroutine_socket_set_timeout(int sockfd, int which, double timeout) { + auto socket = get_socket_ex(sockfd); + if (sw_unlikely(socket == nullptr)) { + errno = EINVAL; + return -1; + } + if (which == SO_RCVTIMEO) { + socket->set_timeout(timeout, Socket::TIMEOUT_READ); + return 0; + } else if (which == SO_SNDTIMEO) { + socket->set_timeout(timeout, Socket::TIMEOUT_WRITE); + return 0; + } else { + errno = EINVAL; return -1; } - ((Coroutine *) ev.object)->yield(); - return ev.ret; } -int swoole_coroutine_flock(int fd, int operation) -{ - if (sw_unlikely(is_no_coro())) - { - return flock(fd, operation); +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; +} - swAio_event ev; - bzero(&ev, sizeof(ev)); - ev.fd = fd; - ev.flags = operation; - ev.handler = handler_flock; - ev.callback = aio_onCompleted; - ev.object = Coroutine::get_current(); - ev.req = &ev; - - ssize_t ret = swAio_dispatch(&ev); - if (ret < 0) - { +int swoole_coroutine_socket_wait_event(int sockfd, int event, double timeout) { + auto socket = get_socket_ex(sockfd); + if (sw_unlikely(socket == nullptr)) { + errno = EINVAL; return -1; } - ((Coroutine *) ev.object)->yield(); - return ev.ret; + 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 swEventType) event); + socket->set_timeout(ori_timeout); + return retval ? SW_OK : SW_ERR; } -#if 0 -static void handler_opendir(swAio_event *event) -{ - swAio_event *req = (swAio_event *) event->object; - req->buf = opendir((const char*) event->buf); - event->error = errno; +int swoole_coroutine_getaddrinfo(const char *name, + const char *service, + const struct addrinfo *req, + struct addrinfo **pai) { + int retval = -1; + async([&]() { retval = getaddrinfo(name, service, req, pai); }); + return retval; } -static void handler_readdir(swAio_event *event) -{ - swAio_event *req = (swAio_event *) event->object; - req->buf = (void*) opendir((const char*) event->buf); - event->error = errno; +struct hostent *swoole_coroutine_gethostbyname(const char *name) { + struct hostent *retval = nullptr; + int _tmp_h_errno = 0; + async([&]() { + retval = gethostbyname(name); + _tmp_h_errno = h_errno; + }); + h_errno = _tmp_h_errno; + return retval; } -DIR *swoole_coroutine_opendir(const char *name) -{ - if (sw_unlikely(is_no_coro())) - { - return opendir(name); +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 } - swAio_event ev; - bzero(&ev, sizeof(ev)); - ev.buf = (void*) name; - ev.handler = handler_opendir; - ev.callback = aio_onCompleted; - ev.object = Coroutine::get_current(); - ev.req = &ev; + int retval = -1; +#ifndef HAVE_FDATASYNC + async([&]() { retval = fsync(fd); }); +#else + async([&]() { retval = fdatasync(fd); }); +#endif + return retval; +} - ssize_t ret = swAio_dispatch(&ev); - if (ret < 0) - { - return nullptr; +#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); } - coroutine_yield((coroutine_t *) ev.object); - return (DIR*) ev.buf; + return Iouring::open(pathname, flags, mode); } -struct dirent *swoole_coroutine_readdir(DIR *dirp) -{ - if (sw_unlikely(is_no_coro())) - { - return readdir(dirp); +int swoole_coroutine_iouring_close_file(int fd) { + if (sw_unlikely(is_no_coro())) { + return close(fd); } + return Iouring::close(fd); +} - swAio_event ev; - bzero(&ev, sizeof(ev)); - ev.buf = (void*) dirp; - ev.handler = handler_readdir; - ev.callback = aio_onCompleted; - ev.object = Coroutine::get_current(); - ev.req = &ev; +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 ret = swAio_dispatch(&ev); - if (ret < 0) - { - return nullptr; +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); } - coroutine_yield((coroutine_t *) ev.object); - return (struct dirent *) ev.buf; + return Iouring::write(sockfd, buf, size); } -#endif -void swoole_coroutine_sleep(int sec) -{ - System::sleep((double) sec); +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); } -void swoole_coroutine_usleep(int usec) -{ - System::sleep((double) usec / 1024 / 1024); +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_socket_set_timeout(int sockfd, int which, double timeout) -{ - Socket *socket = get_socket_ex(sockfd); - if (sw_unlikely(socket == nullptr)) - { - errno = EINVAL; - return -1; +int swoole_coroutine_iouring_unlink(const char *pathname) { + if (sw_unlikely(is_no_coro())) { + return unlink(pathname); } - if (which == SO_RCVTIMEO) - { - socket->set_timeout(timeout, swoole::SW_TIMEOUT_READ); - return 0; + 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); } - else if (which == SO_SNDTIMEO) - { - socket->set_timeout(timeout, swoole::SW_TIMEOUT_WRITE); - return 0; + 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); } - else - { - errno = EINVAL; - return -1; + 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_socket_wait_event(int sockfd, int event, double timeout) -{ - Socket *socket = get_socket_ex(sockfd); - if (sw_unlikely(socket == nullptr)) - { - errno = EINVAL; - return -1; +int swoole_coroutine_iouring_rmdir(const char *pathname) { + if (sw_unlikely(is_no_coro())) { + return rmdir(pathname); } - double ori_timeout = socket->get_timeout(event == SW_EVENT_READ ? swoole::SW_TIMEOUT_READ : swoole::SW_TIMEOUT_WRITE); - socket->set_timeout(timeout); - bool retval = socket->poll((enum swEvent_type) event); - socket->set_timeout(ori_timeout); - return retval ? 0 : -1; + 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 7a0dc06093f..464780b3b21 100644 --- a/src/coroutine/socket.cc +++ b/src/coroutine/socket.cc @@ -1,164 +1,110 @@ -#include "swoole_cxx.h" -#include "coroutine.h" -#include "coroutine_socket.h" -#include "coroutine_system.h" -#include "buffer.h" -#include "base64.h" - -#include -#include -#include - -using namespace swoole; -using namespace std; -using swoole::coroutine::System; -using swoole::coroutine::Socket; - -double Socket::default_dns_timeout = SW_DEFAULT_SOCKET_DNS_TIMEOUT; -double Socket::default_connect_timeout = SW_DEFAULT_SOCKET_CONNECT_TIMEOUT; -double Socket::default_read_timeout = SW_DEFAULT_SOCKET_READ_TIMEOUT; -double Socket::default_write_timeout = SW_DEFAULT_SOCKET_WRITE_TIMEOUT; -static thread_local char tmp_address[INET6_ADDRSTRLEN + 1]; - -#ifdef SW_USE_OPENSSL -#ifndef OPENSSL_NO_NEXTPROTONEG - -const string HTTP2_H2_ALPN("\x2h2"); -const string HTTP2_H2_16_ALPN("\x5h2-16"); -const string HTTP2_H2_14_ALPN("\x5h2-14"); - -static bool ssl_select_proto(const uchar **out, uchar *outlen, const uchar *in, uint inlen, const string &key) -{ - for (auto p = in, end = in + inlen; p + key.size() <= end; p += *p + 1) - { - if (std::equal(std::begin(key), std::end(key), p)) - { - *out = p + 1; - *outlen = *p; - return true; - } - } - return false; -} - -static bool ssl_select_h2(const uchar **out, uchar *outlen, const uchar *in, uint inlen) -{ - return ssl_select_proto(out, outlen, in, inlen, HTTP2_H2_ALPN) || - ssl_select_proto(out, outlen, in, inlen, HTTP2_H2_16_ALPN) || - ssl_select_proto(out, outlen, in, inlen, HTTP2_H2_14_ALPN); -} - -static int ssl_select_next_proto_cb(SSL *ssl, uchar **out, uchar *outlen, const uchar *in, uint inlen, void *arg) -{ -#ifdef SW_LOG_TRACE_OPEN - string info("[NPN] server offers:\n"); - for (unsigned int i = 0; i < inlen; i += in[i] + 1) - { - info += " * " + string(reinterpret_cast(&in[i + 1]), in[i]); - } - swTraceLog(SW_TRACE_HTTP2, "[NPN] server offers: %s", info.c_str()); -#endif - if (!ssl_select_h2(const_cast(out), outlen, in, inlen)) - { - swWarn("HTTP/2 protocol was not selected, expects [h2]"); - return SSL_TLSEXT_ERR_NOACK; - } - else - { - return SSL_TLSEXT_ERR_OK; - } -} -#endif -#endif - -void Socket::timer_callback(swTimer *timer, swTimer_node *tnode) -{ +/* + +----------------------------------------------------------------------+ + | 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 "swoole_string.h" +#include "swoole_util.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}; + +void Socket::timer_callback(Timer *timer, TimerNode *tnode) { Socket *socket = (Socket *) tnode->data; socket->set_err(ETIMEDOUT); - if (sw_likely(tnode == socket->read_timer)) - { + if (sw_likely(tnode == socket->read_timer)) { socket->read_timer = nullptr; socket->read_co->resume(); - } - else if (tnode == socket->write_timer) - { + } else if (tnode == socket->write_timer) { socket->write_timer = nullptr; socket->write_co->resume(); - } - else - { + } else { abort(); } } -int Socket::readable_event_callback(swReactor *reactor, swEvent *event) -{ +int Socket::readable_event_callback(Reactor *reactor, Event *event) { Socket *socket = (Socket *) event->socket->object; socket->set_err(0); #ifdef SW_USE_OPENSSL - if (sw_unlikely(socket->want_event != SW_EVENT_NULL)) - { - if (socket->want_event == SW_EVENT_READ) - { + if (sw_unlikely(socket->want_event != SW_EVENT_NULL)) { + if (socket->want_event == SW_EVENT_READ) { socket->write_co->resume(); } - } - else + } else #endif { + if (socket->recv_barrier && (*socket->recv_barrier)() && !event->socket->event_hup) { + return SW_OK; + } socket->read_co->resume(); } + return SW_OK; } -int Socket::writable_event_callback(swReactor *reactor, swEvent *event) -{ +int Socket::writable_event_callback(Reactor *reactor, Event *event) { Socket *socket = (Socket *) event->socket->object; socket->set_err(0); #ifdef SW_USE_OPENSSL - if (sw_unlikely(socket->want_event != SW_EVENT_NULL)) - { - if (socket->want_event == SW_EVENT_WRITE) - { + if (sw_unlikely(socket->want_event != SW_EVENT_NULL)) { + if (socket->want_event == SW_EVENT_WRITE) { socket->read_co->resume(); } - } - else + } else #endif { + if (socket->send_barrier && (*socket->send_barrier)() && !event->socket->event_hup) { + return SW_OK; + } socket->write_co->resume(); } + return SW_OK; } -int Socket::error_event_callback(swReactor *reactor, swEvent *event) -{ +int Socket::error_event_callback(Reactor *reactor, Event *event) { Socket *socket = (Socket *) event->socket->object; - if (socket->write_co) - { + if (socket->write_co) { socket->set_err(0); socket->write_co->resume(); } // Notice: socket maybe deleted in write coroutine - if (event->socket->object == socket && !event->socket->removed && socket->read_co) - { + if (event->socket->object == socket && !event->socket->removed && socket->read_co) { socket->set_err(0); socket->read_co->resume(); } 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) - { + if (sw_likely(!(socket->events & event))) { + if (socket->removed) { ret = swoole_event_add(socket, event) == SW_OK; - } - else - { + } else { ret = swoole_event_set(socket, socket->events | event) == SW_OK; } } @@ -166,164 +112,182 @@ bool Socket::add_event(const enum swEvent_type event) return ret; } -bool Socket::wait_event(const enum swEvent_type event, const void **__buf, size_t __n) -{ - enum swEvent_type added_event = event; - Coroutine *co = Coroutine::get_current_safe(); +#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 (sw_unlikely(socket->ssl && ((event == SW_EVENT_READ && socket->ssl_want_write) || (event == SW_EVENT_WRITE && socket->ssl_want_read)))) + if (socket->get_socket()->ssl_want_read) { + return "SSL READ"; + } else if (socket->get_socket()->ssl_want_write) { + return "SSL WRITE"; + } else +#endif { - if (sw_likely(socket->ssl_want_write && add_event(SW_EVENT_WRITE))) - { + 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. + * And these exceptions will actively set the errCode, We don't need to set the exception's errCode ourselves. + * 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 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); +#ifdef SW_USE_OPENSSL + if (sw_unlikely(socket->ssl && ((event == SW_EVENT_READ && socket->ssl_want_write) || + (event == SW_EVENT_WRITE && socket->ssl_want_read)))) { + if (sw_likely(socket->ssl_want_write && add_event(SW_EVENT_WRITE))) { want_event = SW_EVENT_WRITE; - } - else if (socket->ssl_want_read && add_event(SW_EVENT_READ)) - { + } else if (socket->ssl_want_read && add_event(SW_EVENT_READ)) { want_event = SW_EVENT_READ; - } - else - { + } else { return false; } added_event = want_event; - } - else + } else #endif - if (sw_unlikely(!add_event(event))) - { + 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" - ); - if (sw_likely(event == SW_EVENT_READ)) - { + 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) - { - if (sw_unlikely(__n > 0 && *__buf != get_write_buffer()->str)) - { - swString_clear(write_buffer); - if (swString_append_ptr(write_buffer, (const char *) *__buf, __n) != SW_OK) - { + } 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) { set_err(ENOMEM); goto _failed; } *__buf = write_buffer->str; } write_co = co; - write_co->yield(); + write_co->yield(&cancel_fn); write_co = nullptr; + } else { + assert(0); + return false; } - _failed: +_failed: #ifdef SW_USE_OPENSSL // maybe read_co and write_co are all waiting for the same event when we use SSL if (sw_likely(want_event == SW_EVENT_NULL || !has_bound())) #endif { - swReactor *reactor = SwooleTG.reactor; - if (sw_likely(added_event == SW_EVENT_READ)) - { - swReactor_remove_read_event(reactor, socket); - } - else // if (added_event == SW_EVENT_WRITE) - { - swReactor_remove_write_event(reactor, socket); + Reactor *reactor = SwooleTG.reactor; + if (sw_likely(added_event == SW_EVENT_READ)) { + reactor->remove_read_event(socket); + } else { + reactor->remove_write_event(socket); } } #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() -{ - swSocks5 *ctx = socks5_proxy; +bool Socket::socks5_handshake() { + Socks5Proxy *ctx = socks5_proxy; char *p; ssize_t n; uchar version, method, result; - swSocks5_pack(ctx->buf, socks5_proxy->l_username > 0 ? 0x02 : 0x00); + Socks5Proxy::pack(ctx->buf, !socks5_proxy->username.empty() ? 0x02 : 0x00); socks5_proxy->state = SW_SOCKS5_STATE_HANDSHAKE; - if (send(ctx->buf, 3) != 3) - { + if (send(ctx->buf, 3) != 3) { return false; } n = recv(ctx->buf, sizeof(ctx->buf)); - if (n <= 0) - { + if (n <= 0) { return false; } version = ctx->buf[0]; method = ctx->buf[1]; - if (version != SW_SOCKS5_VERSION_CODE) - { + if (version != SW_SOCKS5_VERSION_CODE) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SOCKS5_UNSUPPORT_VERSION, "SOCKS version is not supported"); return false; } - if (method != ctx->method) - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SOCKS5_UNSUPPORT_METHOD, "SOCKS authentication method not supported"); + if (method != ctx->method) { + swoole_error_log( + SW_LOG_NOTICE, SW_ERROR_SOCKS5_UNSUPPORT_METHOD, "SOCKS authentication method is not supported"); return false; } // authentication - if (method == SW_SOCKS5_METHOD_AUTH) - { + if (method == SW_SOCKS5_METHOD_AUTH) { p = ctx->buf; // username p[0] = 0x01; - p[1] = ctx->l_username; + p[1] = ctx->username.length(); p += 2; - if (ctx->l_username > 0) - { - memcpy(p, ctx->username, ctx->l_username); - p += ctx->l_username; + if (!ctx->username.empty()) { + memcpy(p, ctx->username.c_str(), ctx->username.length()); + p += ctx->username.length(); } // password - p[0] = ctx->l_password; + p[0] = ctx->password.length(); p += 1; - if (ctx->l_password > 0) - { - memcpy(p, ctx->password, ctx->l_password); - p += ctx->l_password; + if (!ctx->password.empty()) { + memcpy(p, ctx->password.c_str(), ctx->password.length()); + p += ctx->password.length(); } // auth request ctx->state = SW_SOCKS5_STATE_AUTH; - if (send(ctx->buf, p - ctx->buf) != p - ctx->buf) - { + if (send(ctx->buf, p - ctx->buf) != p - ctx->buf) { return false; } // auth response n = recv(ctx->buf, sizeof(ctx->buf)); - if (n <= 0) - { + if (n <= 0) { return false; } uchar version = ctx->buf[0]; uchar status = ctx->buf[1]; - if (version != 0x01) - { + if (version != 0x01) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SOCKS5_UNSUPPORT_VERSION, "SOCKS version is not supported"); return false; } - if (status != 0x00) - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SOCKS5_AUTH_FAILED, "SOCKS username/password authentication failed"); + if (status != 0x00) { + swoole_error_log( + SW_LOG_NOTICE, SW_ERROR_SOCKS5_AUTH_FAILED, "SOCKS username/password authentication failed"); return false; } } @@ -335,44 +299,35 @@ bool Socket::socks5_handshake() p[1] = 0x01; p[2] = 0x00; p += 3; - if (ctx->dns_tunnel) - { + if (ctx->dns_tunnel) { p[0] = 0x03; - p[1] = ctx->l_target_host; + p[1] = ctx->target_host.length(); p += 2; - memcpy(p, ctx->target_host, ctx->l_target_host); - sw_free((void *) ctx->target_host); - ctx->target_host = nullptr; - p += ctx->l_target_host; + memcpy(p, ctx->target_host.c_str(), ctx->target_host.length()); + p += ctx->target_host.length(); *(uint16_t *) p = htons(ctx->target_port); p += 2; - if (send(ctx->buf, p - ctx->buf) != p - ctx->buf) - { + if (send(ctx->buf, p - ctx->buf) != p - ctx->buf) { return false; } - } - else - { + } else { p[0] = 0x01; p += 1; - *(uint32_t *) p = htons(ctx->l_target_host); + *(uint32_t *) p = htons(ctx->target_host.length()); p += 4; *(uint16_t *) p = htons(ctx->target_port); p += 2; - if (send(ctx->buf, p - ctx->buf) != p - ctx->buf) - { + if (send(ctx->buf, p - ctx->buf) != p - ctx->buf) { return false; } } // recv response n = recv(ctx->buf, sizeof(ctx->buf)); - if (n <= 0) - { + if (n <= 0) { return false; } version = ctx->buf[0]; - if (version != SW_SOCKS5_VERSION_CODE) - { + if (version != SW_SOCKS5_VERSION_CODE) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SOCKS5_UNSUPPORT_VERSION, "SOCKS version is not supported"); return false; } @@ -383,235 +338,137 @@ bool Socket::socks5_handshake() uint32_t ip = *(uint32_t *) (buf + 4); uint16_t port = *(uint16_t *) (buf + 8); #endif - if (result == 0) - { + if (result == 0) { 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", swSocks5_strerror(result)); + } else { + 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" \ - - swString *buffer = get_read_buffer(); - - if (!buffer) - { - return false; +bool Socket::http_proxy_handshake() { + const std::string *real_host = &http_proxy->target_host; +#ifdef SW_USE_OPENSSL + if (ssl_context && !ssl_context->tls_host_name.empty()) { + real_host = &ssl_context->tls_host_name; } +#endif - //CONNECT - int n; - if (http_proxy->password) - { - char auth_buf[256]; - char encode_buf[512]; - n = sw_snprintf( - auth_buf, sizeof(auth_buf), "%.*s:%.*s", - http_proxy->l_user, http_proxy->user, - http_proxy->l_password, http_proxy->password - ); - swBase64_encode((unsigned char *) auth_buf, n, encode_buf); - n = sw_snprintf( - buffer->str, buffer->size, - HTTP_PROXY_FMT "Proxy-Authorization:Basic %s\r\n\r\n", - http_proxy->l_target_host, http_proxy->target_host, http_proxy->target_port, - http_proxy->l_target_host, http_proxy->target_host, http_proxy->target_port, - encode_buf - ); - } - else - { - n = sw_snprintf( - buffer->str, buffer->size, - HTTP_PROXY_FMT "\r\n", - http_proxy->l_target_host, http_proxy->target_host, http_proxy->target_port, - http_proxy->l_target_host, http_proxy->target_host, http_proxy->target_port - ); - } + String *send_buffer = get_write_buffer(); + ON_SCOPE_EXIT { + send_buffer->clear(); + }; - swTraceLog(SW_TRACE_HTTP_CLIENT, "proxy request: <str); + size_t n = http_proxy->pack(send_buffer, real_host); + send_buffer->length = n; + swoole_trace_log(SW_TRACE_HTTP_CLIENT, "proxy request: <str); - if (send(buffer->str, n) != n) - { + if (send(send_buffer->str, n) != (ssize_t) n) { return false; } - /* use eof protocol (provisional) */ - bool ori_open_eof_check = open_eof_check; - uint8_t ori_package_eof_len = protocol.package_eof_len; - char ori_package_eof[SW_DATA_EOF_MAXLEN]; - memcpy(ori_package_eof, SW_STRS(protocol.package_eof)); + String *recv_buffer = get_read_buffer(); + ON_SCOPE_EXIT { + recv_buffer->clear(); + }; + + ProtocolSwitch ps(this); open_eof_check = true; + open_length_check = false; 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 = 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) - { - if (isspace(*p)) - { - continue; - } - else - { - if (SW_STRCASECT(p, pe - p, "Connection established")) - { - ret = true; - } - break; - } - } + 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]: " + send_buffer->to_std_string()); + return false; } - /* revert protocol settings */ - open_eof_check = ori_open_eof_check; - protocol.package_eof_len = ori_package_eof_len; - memcpy(protocol.package_eof, SW_STRS(ori_package_eof)); - - return ret; + return true; } -void Socket::init_sock_type(enum swSocket_type _sw_type) -{ - type = _sw_type; - swSocket_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() -{ - int _fd = swSocket_create(type, 1, 1); - if (sw_unlikely(_fd < 0)) - { +bool Socket::init_sock() { + 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; } - return init_reactor_socket(_fd); + sock_fd = socket->fd; + socket->object = this; + socket->info.type = type; + return true; } -bool Socket::init_reactor_socket(int _fd) -{ - swReactor *reactor = SwooleTG.reactor; - if (sw_unlikely(!reactor)) - { - swFatalError(SW_ERROR_OPERATION_NOT_SUPPORT, "operation not support (reactor is not ready)"); - } - socket = swSocket_new(_fd, SW_FD_CORO_SOCKET); - if (socket == nullptr) - { - return false; - } - +bool Socket::init_reactor_socket(int _fd) { + socket = swoole::make_socket(_fd, SW_FD_CO_SOCKET); sock_fd = _fd; socket->object = this; socket->socket_type = type; socket->nonblock = 1; socket->cloexec = 1; - + socket->info.type = type; return true; } -Socket::Socket(int _domain, int _type, int _protocol) : - sock_domain(_domain), sock_type(_type), sock_protocol(_protocol) -{ - type = convert_to_type(_domain, _type, _protocol); - if (sw_unlikely(!init_sock())) - { +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); + 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())) - { + if (sw_unlikely(!init_sock())) { return; } init_options(); } -Socket::Socket(int _fd, enum swSocket_type _type) -{ - init_sock_type(_type); - if (sw_unlikely(!init_reactor_socket(_fd))) - { +Socket::Socket(int _fd, SocketType _type) { + if (sw_unlikely(!init_reactor_socket(_fd))) { + return; + } + if (_type == SW_SOCK_RAW) { return; } - swSocket_set_nonblock(socket); + 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 = convert_to_type(_domain, _type, _protocol); - if (sw_unlikely(!init_reactor_socket(_fd))) - { +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); + if (sw_unlikely(!init_reactor_socket(_fd))) { return; } - swSocket_set_nonblock(socket); + socket->set_nonblock(); init_options(); } -Socket::Socket(swSocket *sock, Socket *server_sock) -{ +/** + * Only used as accept member method + */ +Socket::Socket(network::Socket *sock, Socket *server_sock) { type = server_sock->type; sock_domain = server_sock->sock_domain; sock_type = server_sock->sock_type; @@ -620,7 +477,7 @@ Socket::Socket(swSocket *sock, Socket *server_sock) socket = sock; socket->object = this; socket->socket_type = type; - socket->fdtype = SW_FD_CORO_SOCKET; + socket->fd_type = SW_FD_CO_SOCKET; init_options(); /* inherits server socket options */ dns_timeout = server_sock->dns_timeout; @@ -631,180 +488,109 @@ Socket::Socket(swSocket *sock, Socket *server_sock) open_eof_check = server_sock->open_eof_check; http2 = server_sock->http2; protocol = server_sock->protocol; + connected = true; #ifdef SW_USE_OPENSSL - open_ssl = server_sock->open_ssl; + ssl_context = server_sock->ssl_context; ssl_is_server = server_sock->ssl_is_server; - if (open_ssl) - { - if (server_sock->ssl_context) - { - if (!ssl_create(server_sock->ssl_context)) - { - close(); - } - } + if (server_sock->ssl_is_enable() && !ssl_create(server_sock->get_ssl_context())) { + close(); } #endif } -bool Socket::getsockname(swSocketAddress *sa) -{ +bool Socket::getsockname(network::Address *sa) { sa->len = sizeof(sa->addr); - if (::getsockname(sock_fd, (struct sockaddr *) &sa->addr, &sa->len) != 0) - { + if (::getsockname(sock_fd, (struct sockaddr *) &sa->addr, &sa->len) != 0) { set_err(errno); return false; } + sa->type = type; return true; } -bool Socket::getpeername(swSocketAddress *sa) -{ +bool Socket::getpeername(network::Address *sa) { sa->len = sizeof(sa->addr); - if (::getpeername(sock_fd, (struct sockaddr *) &sa->addr, &sa->len) != 0) - { + if (::getpeername(sock_fd, (struct sockaddr *) &sa->addr, &sa->len) != 0) { set_err(errno); return false; } + sa->type = type; return true; } -const char* Socket::get_ip() -{ - if (type == SW_SOCK_TCP || type == SW_SOCK_UDP) - { - if (inet_ntop(AF_INET, &socket->info.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, &socket->info.addr.inet_v6.sin6_addr, tmp_address, sizeof(tmp_address))) - { - return tmp_address; - } - } - else if (type == SW_SOCK_UNIX_STREAM || type == SW_SOCK_UNIX_DGRAM) - { - return socket->info.addr.un.sun_path; - } - return "unknown"; -} - -int Socket::get_port() -{ - if (type == SW_SOCK_TCP || type == SW_SOCK_UDP) - { - return ntohs(socket->info.addr.inet_v4.sin_port); - } - else if (type == SW_SOCK_TCP6 || type == SW_SOCK_UDP6) - { - return ntohs(socket->info.addr.inet_v6.sin6_port); - } - else - { - return 0; - } -} - -bool Socket::connect(const struct sockaddr *addr, socklen_t addrlen) -{ - if (sw_unlikely(!is_available(SW_EVENT_RDWR))) - { +bool Socket::connect(const struct sockaddr *addr, socklen_t addrlen) { + if (sw_unlikely(!is_available(SW_EVENT_RDWR))) { return false; } int retval; - do - { + do { retval = ::connect(sock_fd, addr, addrlen); } while (retval < 0 && errno == EINTR); - if (retval < 0) - { - if (errno != EINPROGRESS) - { + if (retval < 0) { + if (errno != EINPROGRESS) { set_err(errno); return false; - } - else - { - timer_controller timer(&write_timer, connect_timeout, this, timer_callback); - if (!timer.start() || !wait_event(SW_EVENT_WRITE)) - { - if (closed) - { + } else { + TimerController timer(&write_timer, connect_timeout, this, timer_callback); + if (!timer.start() || !wait_event(SW_EVENT_WRITE)) { + if (is_closed()) { set_err(ECONNABORTED); } return false; - } - else - { - socklen_t len = sizeof(errCode); - if (getsockopt(sock_fd, SOL_SOCKET, SO_ERROR, &errCode, &len) < 0 || errCode != 0) - { + } else { + if (socket->get_option(SOL_SOCKET, SO_ERROR, &errCode) < 0 || errCode != 0) { set_err(errCode); return false; } } } } + connected = true; set_err(0); return true; } -bool Socket::connect(string _host, int _port, int flags) -{ - if (sw_unlikely(!is_available(SW_EVENT_RDWR))) - { +bool Socket::connect(std::string _host, int _port, int flags) { + if (sw_unlikely(!is_available(SW_EVENT_RDWR))) { return false; } #ifdef SW_USE_OPENSSL - if (open_ssl && (socks5_proxy || http_proxy)) - { + if (ssl_context && (socks5_proxy || http_proxy)) { /* If the proxy is enabled, the host will be replaced with the proxy ip, * so we have to handle the host first, * if the host is not a ip, assign it to ssl_host_name */ - union { struct in_addr sin; struct in6_addr sin6; } addr; + union { + struct in_addr sin; + struct in6_addr sin6; + } addr; if ((sock_domain == AF_INET && !inet_pton(AF_INET, _host.c_str(), &addr.sin)) || - (sock_domain == AF_INET6 && !inet_pton(AF_INET6, _host.c_str(), &addr.sin6))) - { + (sock_domain == AF_INET6 && !inet_pton(AF_INET6, _host.c_str(), &addr.sin6))) { ssl_host_name = _host; } } #endif - if (socks5_proxy) - { - //enable socks5 proxy - socks5_proxy->target_host = sw_strndup((char *) _host.c_str(), _host.size()); - socks5_proxy->l_target_host = _host.size(); + if (socks5_proxy) { + socks5_proxy->target_host = _host; socks5_proxy->target_port = _port; _host = socks5_proxy->host; _port = socks5_proxy->port; - } - else if (http_proxy) - { - //enable http proxy - http_proxy->target_host = sw_strndup((char *) _host.c_str(), _host.size()); - http_proxy->l_target_host = _host.size(); + } else if (http_proxy) { + http_proxy->target_host = _host; http_proxy->target_port = _port; _host = http_proxy->proxy_host; _port = http_proxy->proxy_port; } - if (sock_domain == AF_INET6 || sock_domain == AF_INET) - { - if (_port == -1) - { + if (is_port_required()) { + if (_port == -1) { set_err(EINVAL, "Socket of type AF_INET/AF_INET6 requires port argument"); return false; - } - else if (_port == 0 || _port >= 65536) - { - set_err(EINVAL, cpp_string::format("Invalid port [%d]", _port)); + } else if (_port == 0 || _port >= 65536) { + set_err(EINVAL, std_string::format("Invalid port [%d]", _port)); return false; } } @@ -813,72 +599,81 @@ bool Socket::connect(string _host, int _port, int flags) connect_port = _port; struct sockaddr *_target_addr = nullptr; + NameResolver::Context *ctx = resolve_context_; - 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); + NameResolver::Context _ctx{}; + if (ctx == nullptr) { + ctx = &_ctx; + } + ctx->timeout = dns_timeout; - if (!inet_pton(AF_INET, connect_host.c_str(), &socket->info.addr.inet_v4.sin_addr)) - { + std::once_flag oc; + auto name_resolve_fn = [ctx, &oc, this](int type) -> bool { + ctx->type = type; #ifdef SW_USE_OPENSSL - if (open_ssl) - { - ssl_host_name = connect_host; - } + 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(); - connect_host = System::gethostbyname(connect_host, AF_INET, dns_timeout); - read_co = write_co = nullptr; - if (connect_host.empty()) - { - set_err(SwooleG.error, swoole_strerror(SwooleG.error)); + /* 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(connect_port); + + if (!inet_pton(AF_INET, connect_host.c_str(), &socket->info.addr.inet_v4.sin_addr)) { + if (!name_resolve_fn(AF_INET)) { + set_err(swoole_get_last_error(), swoole_strerror(swoole_get_last_error())); return false; } continue; - } - else - { + } else { socket->info.len = sizeof(socket->info.addr.inet_v4); _target_addr = (struct sockaddr *) &socket->info.addr.inet_v4; break; } - } - else if (sock_domain == AF_INET6) - { + } 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 (open_ssl) - { - ssl_host_name = connect_host; - } -#endif - connect_host = System::gethostbyname(connect_host, AF_INET6, dns_timeout); - if (connect_host.empty()) - { - set_err(SwooleG.error); + if (!inet_pton(AF_INET6, connect_host.c_str(), &socket->info.addr.inet_v6.sin6_addr)) { + if (!name_resolve_fn(AF_INET6)) { + set_err(swoole_get_last_error()); return false; } continue; - } - else - { + } else { socket->info.len = sizeof(socket->info.addr.inet_v6); _target_addr = (struct sockaddr *) &socket->info.addr.inet_v6; break; } - } - else if (sock_domain == AF_UNIX) - { - if (connect_host.size() >= sizeof(socket->info.addr.un.sun_path)) - { + } else if (sock_domain == AF_UNIX) { + if (connect_host.size() >= sizeof(socket->info.addr.un.sun_path)) { + set_err(EINVAL, "unix socket file is too large"); return false; } socket->info.addr.un.sun_family = AF_UNIX; @@ -886,32 +681,40 @@ bool Socket::connect(string _host, int _port, int flags) socket->info.len = (socklen_t) (offsetof(struct sockaddr_un, sun_path) + connect_host.size()); _target_addr = (struct sockaddr *) &socket->info.addr.un; break; - } - else - { + } else { + set_err(EINVAL, "unknown protocol[%d]"); return false; } } - if (connect(_target_addr, socket->info.len) == false) - { + if (_target_addr == nullptr) { + set_err(EINVAL, "bad target host"); return false; } - //socks5 proxy - if (socks5_proxy && socks5_handshake() == false) - { + if (connect(_target_addr, socket->info.len) == false) { return false; } - //http proxy - if (http_proxy && !http_proxy->dont_handshake && http_proxy_handshake() == false) - { + + // socks5 proxy + if (socks5_proxy && socks5_handshake() == false) { + if (errCode == 0) { + set_err(SW_ERROR_SOCKS5_HANDSHAKE_FAILED); + } + return false; + } + // http proxy + if (http_proxy && !http_proxy->dont_handshake && http_proxy_handshake() == false) { + if (errCode == 0) { + set_err(SW_ERROR_HTTP_PROXY_HANDSHAKE_FAILED); + } return false; } #ifdef SW_USE_OPENSSL ssl_is_server = false; - if (open_ssl) - { - if (!ssl_handshake()) - { + if (ssl_context) { + if (!ssl_handshake()) { + if (errCode == 0) { + set_err(SW_ERROR_SSL_HANDSHAKE_FAILED); + } return false; } } @@ -919,312 +722,400 @@ bool Socket::connect(string _host, int _port, int flags) return true; } -bool Socket::check_liveness() -{ - if (!is_connect()) - { - set_err(ECONNRESET); +bool Socket::check_liveness() { + if (is_closed()) { + set_err(EBADF); return false; } - else - { - char buf; - errno = 0; - ssize_t retval = swSocket_peek(socket, &buf, sizeof(buf), 0); - if (retval == 0 || (retval < 0 && swSocket_error(errno) != SW_WAIT)) { - set_err(errno ? errno : ECONNRESET); - return false; - } + if (!socket->check_liveness()) { + set_err(errno ? errno : ECONNRESET); + return false; } set_err(0); return true; } -ssize_t Socket::peek(void *__buf, size_t __n) -{ - ssize_t retval = swSocket_peek(socket, __buf, __n, 0); - set_err(retval < 0 ? errno : 0); +ssize_t Socket::peek(void *__buf, size_t __n) { + ssize_t retval = socket->peek(__buf, __n, 0); + check_return_value(retval); return retval; } -bool Socket::poll(enum swEvent_type type) -{ - if (sw_unlikely(!is_available(type))) - { +bool Socket::poll(EventType type, double timeout) { + if (sw_unlikely(!is_available(type))) { return -1; } - timer_controller timer(&read_timer, read_timeout, this, timer_callback); - if (timer.start() && wait_event(type)) - { - return true; + TimerNode **timer_pp = type == SW_EVENT_READ ? &read_timer : &write_timer; + if (timeout == 0) { + timeout = type == SW_EVENT_READ ? read_timeout : write_timeout; } - else - { + TimerController timer(timer_pp, timeout, this, timer_callback); + if (timer.start() && wait_event(type)) { + return true; + } else { return false; } } -ssize_t Socket::recv(void *__buf, size_t __n) -{ - if (sw_unlikely(!is_available(SW_EVENT_READ))) - { +ssize_t Socket::recv(void *__buf, size_t __n) { + if (sw_unlikely(!is_available(SW_EVENT_READ))) { return -1; } ssize_t retval; - timer_controller timer(&read_timer, read_timeout, this, timer_callback); - do - { - retval = swSocket_recv(socket, __buf, __n, 0); - } while (retval < 0 && swSocket_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); - set_err(retval < 0 ? errno : 0); + TimerController timer(&read_timer, read_timeout, this, timer_callback); + do { + retval = socket->recv(__buf, __n, 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::send(const void *__buf, size_t __n) -{ - if (sw_unlikely(!is_available(SW_EVENT_WRITE))) - { +ssize_t Socket::send(const void *__buf, size_t __n) { + if (sw_unlikely(!is_available(SW_EVENT_WRITE))) { return -1; } ssize_t retval; - timer_controller timer(&write_timer, write_timeout, this, timer_callback); + TimerController timer(&write_timer, write_timeout, this, timer_callback); do { - retval = swSocket_send(socket, __buf, __n, 0); - } while (retval < 0 && swSocket_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE, &__buf, __n)); - set_err(retval < 0 ? errno : 0); + retval = socket->send(__buf, __n, 0); + } while (retval < 0 && socket->catch_write_error(errno) == SW_WAIT && timer.start() && + wait_event(SW_EVENT_WRITE, &__buf, __n)); + check_return_value(retval); return retval; } -ssize_t Socket::read(void *__buf, size_t __n) -{ - if (sw_unlikely(!is_available(SW_EVENT_READ))) - { +ssize_t Socket::read(void *__buf, size_t __n) { + if (sw_unlikely(!is_available(SW_EVENT_READ))) { return -1; } ssize_t retval; - timer_controller timer(&read_timer, read_timeout, this, timer_callback); + TimerController timer(&read_timer, read_timeout, this, timer_callback); do { - retval = ::read(sock_fd, __buf, __n); - } while (retval < 0 && swSocket_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); - set_err(retval < 0 ? errno : 0); + retval = socket->read(__buf, __n); + } 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::write(const void *__buf, size_t __n) -{ - if (sw_unlikely(!is_available(SW_EVENT_WRITE))) - { +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; + } + + String *buffer = get_read_buffer(); + size_t buffer_bytes = buffer->length - buffer->offset; + + if (__n <= buffer_bytes) { + memcpy(__buf, buffer->str + buffer->offset, __n); + buffer->offset += __n; + return __n; + } + + if (buffer_bytes > 0) { + memcpy(__buf, buffer->str + buffer->offset, buffer_bytes); + buffer->offset += buffer_bytes; + } + + if ((size_t) buffer->offset >= buffer->size / 2) { + buffer->reduce(buffer->offset); + } + + ssize_t retval = recv(buffer->str + buffer->length, buffer->size - buffer->length); + if (retval <= 0) { + return buffer_bytes > 0 ? buffer_bytes : retval; + } + + buffer->length += retval; + size_t copy_bytes = SW_MIN(__n - buffer_bytes, buffer->length - buffer->offset); + memcpy((char *) __buf + buffer_bytes, buffer->str + buffer->offset, copy_bytes); + buffer->offset += copy_bytes; + + return buffer_bytes + copy_bytes; +} + +ssize_t Socket::write(const void *__buf, size_t __n) { + if (sw_unlikely(!is_available(SW_EVENT_WRITE))) { return -1; } ssize_t retval; - timer_controller timer(&write_timer, write_timeout, this, timer_callback); + TimerController timer(&write_timer, write_timeout, this, timer_callback); do { - retval = ::write(sock_fd, (void *) __buf, __n); - } while (retval < 0 && swSocket_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE, &__buf, __n)); - set_err(retval < 0 ? errno : 0); + retval = socket->write((void *) __buf, __n); + } while (retval < 0 && socket->catch_write_error(errno) == SW_WAIT && timer.start() && + wait_event(SW_EVENT_WRITE, &__buf, __n)); + check_return_value(retval); return retval; } -ssize_t Socket::recv_all(void *__buf, size_t __n) -{ - if (sw_unlikely(!is_available(SW_EVENT_READ))) - { +ssize_t Socket::readv(network::IOVector *io_vector) { + if (sw_unlikely(!is_available(SW_EVENT_READ))) { + return -1; + } + ssize_t retval; + TimerController timer(&read_timer, read_timeout, this, timer_callback); + do { + retval = socket->readv(io_vector); + } 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::readv_all(network::IOVector *io_vector) { + if (sw_unlikely(!is_available(SW_EVENT_READ))) { return -1; } ssize_t retval, total_bytes = 0; - timer_controller timer(&read_timer, read_timeout, this, timer_callback); - while (true) - { + TimerController timer(&read_timer, read_timeout, this, timer_callback); + + retval = socket->readv(io_vector); + swoole_trace_log(SW_TRACE_SOCKET, "readv %ld bytes, errno=%d", retval, errno); + + if (retval < 0 && socket->catch_read_error(errno) != SW_WAIT) { + set_err(errno); + return retval; + } + + if (retval == 0) { + return retval; + } + + total_bytes += retval > 0 ? retval : 0; + if (io_vector->get_remain_count() == 0) { + // iov should not be modified, prevent valgrind from checking for invalid read + return retval; + } + + EventBarrier barrier = [&io_vector, &total_bytes, &retval, this]() -> bool { do { - retval = swSocket_recv(socket, (char *) __buf + total_bytes, __n - total_bytes, 0); - } while (retval < 0 && swSocket_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); - if (sw_unlikely(retval <= 0)) - { - if (total_bytes == 0) - { - total_bytes = retval; + retval = socket->readv(io_vector); + + if (retval <= 0) { + break; } - break; - } - total_bytes += retval; - if ((size_t) total_bytes == __n) - { - break; - } + + total_bytes += retval; + } while (retval > 0 && io_vector->get_remain_count() > 0); + + return retval < 0 && socket->catch_read_error(errno) == SW_WAIT; + }; + + recv_barrier = &barrier; + if (timer.start() && wait_event(SW_EVENT_READ)) { + check_return_value(retval); } - set_err(retval < 0 ? errno : 0); + recv_barrier = nullptr; + return total_bytes; } -ssize_t Socket::send_all(const void *__buf, size_t __n) -{ - if (sw_unlikely(!is_available(SW_EVENT_WRITE))) - { +ssize_t Socket::writev(network::IOVector *io_vector) { + if (sw_unlikely(!is_available(SW_EVENT_WRITE))) { + return -1; + } + ssize_t retval; + TimerController timer(&write_timer, write_timeout, this, timer_callback); + do { + retval = socket->writev(io_vector); + } while (retval < 0 && socket->catch_write_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE)); + check_return_value(retval); + + return retval; +} + +ssize_t Socket::writev_all(network::IOVector *io_vector) { + if (sw_unlikely(!is_available(SW_EVENT_WRITE))) { return -1; } ssize_t retval, total_bytes = 0; - timer_controller timer(&write_timer, write_timeout, this, timer_callback); - while (true) - { - do - { - retval = swSocket_send(socket, (char *) __buf + total_bytes, __n - total_bytes, 0); - } - while (retval < 0 && swSocket_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE, &__buf, __n)); - /** - * failed to send - */ - if (sw_unlikely(retval <= 0)) - { - if (total_bytes == 0) - { - total_bytes = retval; + TimerController timer(&write_timer, write_timeout, this, timer_callback); + + retval = socket->writev(io_vector); + swoole_trace_log(SW_TRACE_SOCKET, "writev %ld bytes, errno=%d", retval, errno); + + if (retval < 0 && socket->catch_write_error(errno) != SW_WAIT) { + set_err(errno); + return retval; + } + + if (retval == 0) { + return retval; + } + + total_bytes += retval > 0 ? retval : 0; + if (io_vector->get_remain_count() == 0) { + // iov should not be modified, prevent valgrind from checking for invalid read + return retval; + } + + EventBarrier barrier = [&io_vector, &total_bytes, &retval, this]() -> bool { + do { + retval = socket->writev(io_vector); + + if (retval <= 0) { + break; } - break; - } - total_bytes += retval; - if ((size_t) total_bytes == __n) - { - break; - } + + total_bytes += retval; + } while (retval > 0 && io_vector->get_remain_count() > 0); + + return retval < 0 && socket->catch_write_error(errno) == SW_WAIT; + }; + + send_barrier = &barrier; + if (timer.start() && wait_event(SW_EVENT_WRITE)) { + check_return_value(retval); } - set_err(retval < 0 ? errno : 0); + send_barrier = nullptr; + return total_bytes; } -ssize_t Socket::recvmsg(struct msghdr *msg, int flags) -{ - if (sw_unlikely(!is_available(SW_EVENT_READ))) - { +ssize_t Socket::recv_all(void *__buf, size_t __n) { + if (sw_unlikely(!is_available(SW_EVENT_READ))) { + return -1; + } + ssize_t retval = 0; + size_t total_bytes = 0; + TimerController timer(&read_timer, read_timeout, this, timer_callback); + + retval = socket->recv(__buf, __n, 0); + + if (retval == 0 || retval == (ssize_t) __n) { + return retval; + } + if (retval < 0 && socket->catch_read_error(errno) != SW_WAIT) { + set_err(errno); + return retval; + } + total_bytes = retval > 0 ? retval : 0; + + retval = -1; + + 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_read_error(errno) == SW_WAIT) || + (retval > 0 && (total_bytes += retval) < __n); + }; + + recv_barrier = &barrier; + if (timer.start() && wait_event(SW_EVENT_READ)) { + check_return_value(retval); + } + recv_barrier = nullptr; + + return retval < 0 && total_bytes == 0 ? -1 : total_bytes; +} + +ssize_t Socket::send_all(const void *__buf, size_t __n) { + if (sw_unlikely(!is_available(SW_EVENT_WRITE))) { + return -1; + } + ssize_t retval = 0; + size_t total_bytes = 0; + TimerController timer(&write_timer, write_timeout, this, timer_callback); + + retval = socket->send(__buf, __n, 0); + + if (retval == 0 || retval == (ssize_t) __n) { + return retval; + } + if (retval < 0 && socket->catch_write_error(errno) != SW_WAIT) { + set_err(errno); + return retval; + } + total_bytes = retval > 0 ? retval : 0; + + retval = -1; + + 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_write_error(errno) == SW_WAIT) || + (retval > 0 && (total_bytes += retval) < __n); + }; + + send_barrier = &barrier; + if (timer.start() && wait_event(SW_EVENT_WRITE)) { + check_return_value(retval); + } + send_barrier = nullptr; + + return retval < 0 && total_bytes == 0 ? -1 : total_bytes; +} + +ssize_t Socket::recvmsg(struct msghdr *msg, int flags) { + if (sw_unlikely(!is_available(SW_EVENT_READ))) { return -1; } ssize_t retval; - timer_controller timer(&read_timer, read_timeout, this, timer_callback); + TimerController timer(&read_timer, read_timeout, this, timer_callback); do { retval = ::recvmsg(sock_fd, msg, flags); - } while (retval < 0 && swSocket_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; } /** * Notice: you must use non-global buffer here (or else it may be changed after yield) */ -ssize_t Socket::sendmsg(const struct msghdr *msg, int flags) -{ - if (sw_unlikely(!is_available(SW_EVENT_WRITE))) - { +ssize_t Socket::sendmsg(const struct msghdr *msg, int flags) { + if (sw_unlikely(!is_available(SW_EVENT_WRITE))) { return -1; } ssize_t retval; - timer_controller timer(&write_timer, write_timeout, this, timer_callback); + TimerController timer(&write_timer, write_timeout, this, timer_callback); do { retval = ::sendmsg(sock_fd, msg, flags); - } while (retval < 0 && swSocket_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; } -bool Socket::bind(std::string address, int port) -{ - if (sw_unlikely(!is_available(SW_EVENT_NULL))) - { +bool Socket::bind(const struct sockaddr *sa, socklen_t len) { + return ::bind(sock_fd, (struct sockaddr *) sa, len) == 0; +} + +bool Socket::bind(std::string address, int port) { + if (sw_unlikely(!is_available(SW_EVENT_NULL))) { return false; } - if ((sock_domain == AF_INET || sock_domain == AF_INET6) && (port < 0 || port > 65535)) - { - set_err(EINVAL, cpp_string::format("Invalid port [%d]", port)); + if ((sock_domain == AF_INET || sock_domain == AF_INET6) && (port < 0 || port > 65535)) { + set_err(EINVAL, std_string::format("Invalid port [%d]", port)); return false; } bind_address = address; bind_port = port; + bind_address_info.type = type; - struct sockaddr *sock_addr = (struct sockaddr*) &bind_address_info.addr; - - int option = 1; - if (::setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(int)) < 0) - { - swSysWarn("setsockopt(%d, SO_REUSEADDR) failed", sock_fd); - } - - int retval; - socklen_t len; - switch (sock_domain) - { - case AF_UNIX: - { - struct sockaddr_un *sa = (struct sockaddr_un *) sock_addr; - sa->sun_family = AF_UNIX; - - if (bind_address.size() >= sizeof(sa->sun_path)) - { - set_err( - EINVAL, - cpp_string::format( - "UNIXSocket bind path(%s) is too long, the maxium limit of bytes number is %zu", - bind_address.c_str(), sizeof(sa->sun_path) - ) - ); - return false; - } - memcpy(&sa->sun_path, bind_address.c_str(), bind_address.size()); - - retval = ::bind(sock_fd, (struct sockaddr *) sa, - offsetof(struct sockaddr_un, sun_path) + bind_address.size()); - break; - } - - case AF_INET: - { - struct sockaddr_in *sa = (struct sockaddr_in *) sock_addr; - sa->sin_family = AF_INET; - sa->sin_port = htons((unsigned short) bind_port); - if (!inet_aton(bind_address.c_str(), &sa->sin_addr)) - { - set_err(EINVAL); - return false; - } - retval = ::bind(sock_fd, (struct sockaddr *) sa, sizeof(struct sockaddr_in)); - if (retval == 0 && bind_port == 0) - { - len = sizeof(struct sockaddr_in); - if (::getsockname(sock_fd, (struct sockaddr *) sa, &len) != -1) - { - bind_port = ntohs(sa->sin_port); - } - } - break; - } - - case AF_INET6: - { - struct sockaddr_in6 *sa = (struct sockaddr_in6 *) sock_addr; - sa->sin6_family = AF_INET6; - sa->sin6_port = htons((unsigned short) bind_port); - - if (!inet_pton(AF_INET6, bind_address.c_str(), &sa->sin6_addr)) - { - return false; - } - retval = ::bind(sock_fd, (struct sockaddr *) sa, sizeof(struct sockaddr_in6)); - if (retval == 0 && bind_port == 0) - { - len = sizeof(struct sockaddr_in6); - if (::getsockname(sock_fd, (struct sockaddr *) sa, &len) != -1) - { - bind_port = ntohs(sa->sin6_port); - } - } - break; - } - default: - set_err(EINVAL); - return false; - } - - if (retval != 0) - { + if (socket->bind(address, &bind_port) != 0) { set_err(errno); return false; } @@ -1232,55 +1123,50 @@ bool Socket::bind(std::string address, int port) return true; } -bool Socket::listen(int backlog) -{ - if (sw_unlikely(!is_available(SW_EVENT_NULL))) - { +bool Socket::listen(int backlog) { + if (sw_unlikely(!is_available(SW_EVENT_NULL))) { return false; } this->backlog = backlog <= 0 ? SW_BACKLOG : backlog; - if (::listen(sock_fd, 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; } #ifdef SW_USE_OPENSSL ssl_is_server = true; - if (open_ssl) - { - return ssl_check_context(); - } #endif return true; } -Socket* Socket::accept(double timeout) -{ - if (sw_unlikely(!is_available(SW_EVENT_READ))) - { +Socket *Socket::accept(double timeout) { + if (sw_unlikely(!is_available(SW_EVENT_READ))) { return nullptr; } - swSocketAddress client_addr; - swSocket *conn = swSocket_accept(socket, &client_addr); - if (conn == nullptr && errno == EAGAIN) - { - timer_controller timer(&read_timer, timeout == 0 ? read_timeout : timeout, this, timer_callback); - if (!timer.start() || !wait_event(SW_EVENT_READ)) - { +#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); + if (!timer.start() || !wait_event(SW_EVENT_READ)) { return nullptr; } - conn = swSocket_accept(socket, &client_addr); + conn = socket->accept(); } - if (conn == nullptr) - { + if (conn == nullptr) { set_err(errno); return nullptr; } Socket *client_sock = new Socket(conn, this); - if (sw_unlikely(client_sock->get_fd() < 0)) - { - swSysWarn("new Socket() failed"); + if (sw_unlikely(client_sock->get_fd() < 0)) { + swoole_sys_warning("new Socket() failed"); set_err(errno); delete client_sock; return nullptr; @@ -1290,263 +1176,186 @@ Socket* Socket::accept(double timeout) } #ifdef SW_USE_OPENSSL -bool Socket::ssl_check_context() -{ - if (socket->ssl || ssl_context) - { - return true; - } - if (swSocket_is_dgram(sock_type)) - { +bool Socket::ssl_context_create() { + if (socket->is_dgram()) { #ifdef SW_SUPPORT_DTLS socket->dtls = 1; - ssl_option.dtls = 1; - ssl_option.method = SW_DTLS_CLIENT_METHOD; + 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 = swSSL_get_context(&ssl_option); - if (ssl_context == nullptr) - { - swWarn("swSSL_get_context() error"); + ssl_context->http_v2 = http2; + if (!ssl_context->create()) { return false; } - if (ssl_option.verify_peer) - { - if (swSSL_set_capath(&ssl_option, ssl_context) < 0) - { - return false; - } - } - socket->ssl_send = 1; -#if defined(SW_USE_HTTP2) && defined(SW_USE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10002000L - if (http2) - { -#ifndef OPENSSL_NO_NEXTPROTONEG - SSL_CTX_set_next_proto_select_cb(ssl_context, ssl_select_next_proto_cb, nullptr); -#endif - if (SSL_CTX_set_alpn_protos(ssl_context, (const unsigned char *) SW_STRL(SW_SSL_HTTP2_NPN_ADVERTISE)) < 0) - { - return false; - } - } -#endif + socket->ssl_send_ = 1; return true; } -bool Socket::ssl_create(SSL_CTX *ssl_context) -{ - if (socket->ssl) - { +bool Socket::ssl_create(SSLContext *ssl_context) { + if (socket->ssl) { return true; } - if (swSSL_create(socket, ssl_context, 0) < 0) - { + if (socket->ssl_create(ssl_context, 0) < 0) { return false; } #ifdef SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER SSL_set_mode(socket->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); #endif #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - if (ssl_option.tls_host_name) - { - SSL_set_tlsext_host_name(socket->ssl, ssl_option.tls_host_name); - } - else if (!ssl_option.disable_tls_host_name && !ssl_host_name.empty()) - { + if (!ssl_context->tls_host_name.empty()) { + SSL_set_tlsext_host_name(socket->ssl, ssl_context->tls_host_name.c_str()); + } else if (!ssl_context->disable_tls_host_name && !ssl_host_name.empty()) { SSL_set_tlsext_host_name(socket->ssl, ssl_host_name.c_str()); } #endif return true; } -bool Socket::ssl_handshake() -{ - if (ssl_handshaked) - { +bool Socket::ssl_handshake() { + if (ssl_handshaked) { return false; } - if (sw_unlikely(!is_available(SW_EVENT_RDWR))) - { + 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(ssl_context)) - { + 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 (swSSL_connect(socket) < 0) - { + while (true) { + if (socket->ssl_connect() < 0) { set_err(errno); return false; } - if (socket->ssl_state == SW_SSL_STATE_WAIT_STREAM) - { - timer_controller timer(&read_timer, read_timeout, this, timer_callback); - if (!timer.start() || !wait_event(SW_EVENT_READ)) - { + if (socket->ssl_state == SW_SSL_STATE_WAIT_STREAM) { + TimerController timer(&read_timer, read_timeout, this, timer_callback); + if (!timer.start() || !wait_event(SW_EVENT_READ)) { return false; } - } - else if (socket->ssl_state == SW_SSL_STATE_READY) - { + } else if (socket->ssl_state == SW_SSL_STATE_READY) { break; } } - } - else - { - enum swReturn_code retval; - timer_controller timer(&read_timer, read_timeout, this, timer_callback); + } else { + ReturnCode retval; + TimerController timer(&read_timer, read_timeout, this, timer_callback); - do - { - retval = swSSL_accept(socket); - } - while (retval == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); + do { + retval = socket->ssl_accept(); + } while (retval == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); - if (retval != SW_READY) - { + if (retval != SW_READY) { set_err(SW_ERROR_SSL_BAD_CLIENT); return false; } } - if (ssl_option.verify_peer) - { - if (!ssl_verify(ssl_option.allow_self_signed)) - { + if (ssl_context->verify_peer) { + if (!ssl_verify(ssl_context->allow_self_signed)) { return false; } } - open_ssl = true; ssl_handshaked = true; return true; } -bool Socket::ssl_verify(bool allow_self_signed) -{ - if (swSSL_verify(socket, allow_self_signed) < 0) - { +bool Socket::ssl_verify(bool allow_self_signed) { + if (!socket->ssl_verify(allow_self_signed)) { set_err(SW_ERROR_SSL_VERIFY_FAILED); return false; } #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - if (ssl_option.tls_host_name && swSSL_check_host(socket, ssl_option.tls_host_name) < 0) - { + if (!ssl_context->tls_host_name.empty() && !socket->ssl_check_host(ssl_context->tls_host_name.c_str())) { + set_err(SW_ERROR_SSL_VERIFY_FAILED); return false; } #endif return true; } -std::string Socket::ssl_get_peer_cert() -{ - if (!socket->ssl) - { - return ""; - } - int n = swSSL_get_peer_cert(socket->ssl, SwooleTG.buffer_stack->str, SwooleTG.buffer_stack->size); - if (n <= 0) - { +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 std::string(SwooleTG.buffer_stack->str, n); + } else { + return sw_tg_buffer()->to_std_string(); } } #endif -bool Socket::sendfile(const char *filename, off_t offset, size_t length) -{ - if (sw_unlikely(!is_available(SW_EVENT_WRITE))) - { +bool Socket::sendfile(const char *filename, off_t offset, size_t length) { + if (sw_unlikely(!is_available(SW_EVENT_WRITE))) { return false; } - int file_fd = ::open(filename, O_RDONLY); - if (file_fd < 0) - { - set_err(errno, cpp_string::format("open(%s) failed, %s", filename, strerror(errno))); + + File file(filename, O_RDONLY); + if (!file.ready()) { + set_err(errno, std_string::format("open(%s) failed, %s", filename, strerror(errno))); return false; } - if (length == 0) - { - struct stat file_stat; - if (::fstat(file_fd, &file_stat) < 0) - { - set_err(errno, cpp_string::format("fstat(%s) failed, %s", filename, strerror(errno))); - ::close(file_fd); + if (length == 0) { + FileStatus file_stat; + if (!file.stat(&file_stat)) { + set_err(errno, std_string::format("fstat(%s) failed, %s", filename, strerror(errno))); return false; } length = file_stat.st_size; - } - else - { + } else { // total length of the file length = offset + length; } - timer_controller timer(&write_timer, write_timeout, this, timer_callback); - int n, sendn; - while ((size_t) offset < length) - { - sendn = (length - offset > SW_SENDFILE_CHUNK_SIZE) ? SW_SENDFILE_CHUNK_SIZE : length - offset; + TimerController timer(&write_timer, write_timeout, this, timer_callback); + ssize_t n, sent_bytes; + while ((size_t) offset < length) { + sent_bytes = (length - offset > SW_SENDFILE_CHUNK_SIZE) ? SW_SENDFILE_CHUNK_SIZE : length - offset; #ifdef SW_USE_OPENSSL - if (socket->ssl) - { - n = swSSL_sendfile(socket, file_fd, &offset, sendn); - } - else + if (socket->ssl) { + n = socket->ssl_sendfile(file, &offset, sent_bytes); + } else #endif { - n = ::swoole_sendfile(sock_fd, file_fd, &offset, sendn); + n = ::swoole_sendfile(sock_fd, file.get_fd(), &offset, sent_bytes); } - if (n > 0) - { + if (n > 0) { continue; - } - else if (n == 0) - { + } else if (n == 0) { set_err(SW_ERROR_SYSTEM_CALL_FAIL, "sendfile return zero"); - ::close(file_fd); return false; - } - else if (errno != EAGAIN) - { - set_err(errno, cpp_string::format("sendfile(%d, %s) failed, %s", sock_fd, filename, strerror(errno))); - ::close(file_fd); + } else if (errno != EAGAIN) { + set_err(errno, std_string::format("sendfile(%d, %s) failed, %s", sock_fd, filename, strerror(errno))); return false; } - if (!timer.start() || !wait_event(SW_EVENT_WRITE)) - { - ::close(file_fd); + if (!timer.start() || !wait_event(SW_EVENT_WRITE)) { return false; } } - ::close(file_fd); return true; } -ssize_t Socket::sendto(const string &host, int port, const void *__buf, size_t __n) -{ - if (sw_unlikely(!is_available(SW_EVENT_WRITE))) - { +ssize_t Socket::sendto(const std::string &host, int port, const void *__buf, size_t __n) { + if (sw_unlikely(!is_available(SW_EVENT_WRITE))) { return -1; } ssize_t retval = 0; - union - { + union { struct sockaddr_in in; struct sockaddr_in6 in6; struct sockaddr_un un; @@ -1555,339 +1364,282 @@ ssize_t Socket::sendto(const string &host, int port, const void *__buf, size_t _ std::string ip = host; - for (size_t i = 0; i < 2; i++) - { - if (type == SW_SOCK_UDP) - { - if (::inet_aton(ip.c_str(), &addr.in.sin_addr) == 0) - { + for (size_t i = 0; i < 2; i++) { + if (type == SW_SOCK_UDP) { + 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; - if (ip.empty()) - { - set_err(SwooleG.error, swoole_strerror(SwooleG.error)); + if (ip.empty()) { + set_err(swoole_get_last_error(), swoole_strerror(swoole_get_last_error())); return -1; } continue; - } - else - { + } else { addr.in.sin_family = AF_INET; addr.in.sin_port = htons(port); addr_size = sizeof(addr.in); break; } - } - else if (type == SW_SOCK_UDP6) - { - if (::inet_pton(AF_INET6, ip.c_str(), &addr.in6.sin6_addr) == 0) - { + } else if (type == SW_SOCK_UDP6) { + if (::inet_pton(AF_INET6, ip.c_str(), &addr.in6.sin6_addr) == 0) { read_co = write_co = Coroutine::get_current_safe(); ip = System::gethostbyname(host, sock_domain, dns_timeout); read_co = write_co = nullptr; - if (ip.empty()) - { - set_err(SwooleG.error, swoole_strerror(SwooleG.error)); + if (ip.empty()) { + set_err(swoole_get_last_error(), swoole_strerror(swoole_get_last_error())); return -1; } continue; - } - else - { + } else { addr.in6.sin6_port = (uint16_t) htons(port); addr.in6.sin6_family = AF_INET6; addr_size = sizeof(addr.in6); break; } - } - else if (type == SW_SOCK_UNIX_DGRAM) - { + } else if (type == SW_SOCK_UNIX_DGRAM) { addr.un.sun_family = AF_UNIX; - strncpy(addr.un.sun_path, host.c_str(), sizeof(addr.un.sun_path) - 1); + swoole_strlcpy(addr.un.sun_path, host.c_str(), sizeof(addr.un.sun_path)); addr_size = sizeof(addr.un); break; - } - else - { + } else { set_err(EPROTONOSUPPORT); retval = -1; break; } } - if (addr_size > 0) - { - timer_controller timer(&write_timer, write_timeout, this, timer_callback); + if (addr_size > 0) { + 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 || (swSocket_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE, &__buf, __n)))); - set_err(retval < 0 ? errno : 0); + swoole_trace_log(SW_TRACE_SOCKET, "sendto %ld/%ld bytes, errno=%d", retval, __n, errno); + } while (retval < 0 && (errno == EINTR || (socket->catch_write_error(errno) == SW_WAIT && timer.start() && + wait_event(SW_EVENT_WRITE, &__buf, __n)))); + check_return_value(retval); } return retval; } -ssize_t Socket::recvfrom(void *__buf, size_t __n) -{ - if (sw_unlikely(!is_available(SW_EVENT_READ))) - { +ssize_t Socket::recvfrom(void *__buf, size_t __n) { + if (sw_unlikely(!is_available(SW_EVENT_READ))) { return -1; } socket->info.len = sizeof(socket->info.addr); - return recvfrom(__buf, __n, (struct sockaddr*) &socket->info.addr, &socket->info.len); + return recvfrom(__buf, __n, (struct sockaddr *) &socket->info.addr, &socket->info.len); } -ssize_t Socket::recvfrom(void *__buf, size_t __n, struct sockaddr* _addr, socklen_t *_socklen) -{ - if (sw_unlikely(!is_available(SW_EVENT_READ))) - { +ssize_t Socket::recvfrom(void *__buf, size_t __n, struct sockaddr *_addr, socklen_t *_socklen) { + if (sw_unlikely(!is_available(SW_EVENT_READ))) { return -1; } ssize_t retval; - timer_controller timer(&read_timer, read_timeout, this, timer_callback); + 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) || (swSocket_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; } -/** - * recv packet with protocol - */ -ssize_t Socket::recv_packet(double timeout) -{ - if (sw_unlikely(!is_available(SW_EVENT_READ))) - { - return -1; +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) { + if (read_buffer->length >= header_len || + (protocol.package_length_size == 0 && protocol.package_length_type == '\0') // custom package_length_func + ) { + goto _get_length; + } else { + goto _recv_header; + } } - ssize_t buf_len = SW_BUFFER_SIZE_STD; - ssize_t retval; - timer_controller timer(&read_timer, timeout == 0 ? read_timeout : timeout, this, timer_callback); +_recv_header: + retval = recv(read_buffer->str + read_buffer->length, header_len - read_buffer->length); + if (retval <= 0) { + return retval; + } else { + read_buffer->length += retval; + } - if (sw_unlikely(!timer.start())) - { +_get_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 (pl.header_len != 0) { + header_len = pl.header_len; + } + goto _recv_header; + } else if (packet_len > protocol.package_max_length) { + read_buffer->clear(); + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, + "packet length is too big, remote_addr=%s:%d, length=%zu", + socket->info.get_ip(), + socket->info.get_port(), + packet_len); + set_err(SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, sw_error); + return -1; } - get_read_buffer(); - //unprocessed data - if (read_buffer->offset > 0) - { - swString_sub(read_buffer, read_buffer->offset, read_buffer->length); + read_buffer->offset = packet_len; + + if ((size_t) packet_len <= read_buffer->length) { + return packet_len; } - if (open_length_check) - { - uint32_t header_len = protocol.package_length_offset + protocol.package_length_size; - if (read_buffer->length > 0) - { - if ( - read_buffer->length >= header_len || - (protocol.package_length_size == 0 && protocol.package_length_type == '\0') // custom package_length_func - ) - { - goto _get_length; - } - else - { - goto _recv_header; - } + if ((size_t) packet_len > read_buffer->size) { + if (!read_buffer->extend(packet_len)) { + read_buffer->clear(); + set_err(ENOMEM); + return -1; } + } - _recv_header: - retval = recv(read_buffer->str + read_buffer->length, header_len - read_buffer->length); - if (retval <= 0) - { - return retval; - } - else - { - read_buffer->length += retval; + retval = recv_all(read_buffer->str + read_buffer->length, packet_len - read_buffer->length); + if (retval > 0) { + read_buffer->length += retval; + if (read_buffer->length != (size_t) packet_len) { + retval = 0; + } else { + return packet_len; } + } - _get_length: - protocol.real_header_length = 0; - buf_len = protocol.get_package_length(&protocol, socket, read_buffer->str, (uint32_t) read_buffer->length); - swTraceLog(SW_TRACE_SOCKET, "packet_len=%ld, length=%ld", buf_len, read_buffer->length); - if (buf_len < 0) - { - set_err(SW_ERROR_PACKAGE_LENGTH_NOT_FOUND, "get package length failed"); - return 0; + return retval; +} + +ssize_t Socket::recv_packet_with_eof_protocol() { + ssize_t retval, eof = -1; + char *buf = nullptr; + size_t l_buf = 0; + + if (read_buffer->length > 0) { + goto _find_eof; + } + + while (1) { + buf = read_buffer->str + read_buffer->length; + l_buf = read_buffer->size - read_buffer->length; + + if (l_buf > SW_BUFFER_SIZE_BIG) { + l_buf = SW_BUFFER_SIZE_BIG; } - else if (buf_len == 0) - { - if (protocol.real_header_length != 0) - { - header_len = protocol.real_header_length; - } - goto _recv_header; + + retval = recv(buf, l_buf); + if (retval <= 0) { + read_buffer->clear(); + return retval; } - else if (buf_len > protocol.package_max_length) - { - set_err(SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, "remote packet is too big"); - return 0; + + read_buffer->length += retval; + + if (read_buffer->length < protocol.package_eof_len) { + continue; } - if ((size_t) buf_len == read_buffer->length) - { - read_buffer->length = 0; - return buf_len; + _find_eof: + eof = swoole_strnpos(read_buffer->str, read_buffer->length, protocol.package_eof, protocol.package_eof_len); + if (eof >= 0) { + return (read_buffer->offset = eof + protocol.package_eof_len); } - else if ((size_t) buf_len < read_buffer->length) - { - //unprocessed data (offset will always be zero) - read_buffer->length -= buf_len; - read_buffer->offset = buf_len; - return buf_len; + if (read_buffer->length == protocol.package_max_length) { + read_buffer->clear(); + set_err(SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, "no package eof, package_max_length exceeded"); + return -1; } - - if ((size_t) buf_len > read_buffer->size) - { - if (swString_extend(read_buffer, buf_len) < 0) - { - read_buffer->length = 0; + if (read_buffer->length == read_buffer->size && read_buffer->size < protocol.package_max_length) { + size_t new_size = read_buffer->size * 2; + if (new_size > protocol.package_max_length) { + new_size = protocol.package_max_length; + } + if (!read_buffer->extend(new_size)) { + read_buffer->clear(); set_err(ENOMEM); return -1; } } - - retval = recv_all(read_buffer->str + read_buffer->length, buf_len - read_buffer->length); - if (retval > 0) - { - read_buffer->length += retval; - if (read_buffer->length != (size_t) buf_len) - { - retval = 0; - } - else - { - read_buffer->length = 0; - return buf_len; - } - } } - else if (open_eof_check) - { - int eof = -1; - char *buf; + assert(0); + return -1; +} - if (read_buffer->length > 0) - { - goto _find_eof; - } +/** + * Recv packet with protocol + * Returns the length of the packet, [return value == read_buffer->offset] + * ---------------------------------------Usage--------------------------------------------- + * ssize_t l = sock.recv_packet(); + * String *pkt = sock.get_read_buffer(); + * a) memcpy(result_buf, pkt->str, l); //copy data to new buffer + * b) result_buf = sock.pop_packet(); //pop packet data, create a new buffer memory + * ---------------------------------------read_buffer--------------------------------------- + * [read_buffer->length > read_buffer->offset] : may be unprocessed data in the buffer + * [read_buffer->length == read_buffer->offset] : no data in the buffer + */ +ssize_t Socket::recv_packet(double timeout) { + if (sw_unlikely(!is_available(SW_EVENT_READ))) { + return -1; + } - while (1) - { - buf = read_buffer->str + read_buffer->length; - buf_len = read_buffer->size - read_buffer->length; + TimerController timer(&read_timer, timeout == 0 ? read_timeout : timeout, this, timer_callback); + if (sw_unlikely(!timer.start())) { + return 0; + } - if (buf_len > SW_BUFFER_SIZE_BIG) - { - buf_len = SW_BUFFER_SIZE_BIG; - } + get_read_buffer(); - retval = recv(buf, buf_len); - if (retval < 0) - { - read_buffer->length = 0; - return -1; - } - else if (retval == 0) - { - read_buffer->length = 0; - return 0; - } + // unprocessed data + if (read_buffer->offset > 0) { + read_buffer->reduce(read_buffer->offset); + } - read_buffer->length += retval; + ssize_t recv_bytes; - if (read_buffer->length < protocol.package_eof_len) - { - continue; - } - - _find_eof: - eof = swoole_strnpos(read_buffer->str, read_buffer->length, protocol.package_eof, protocol.package_eof_len); - if (eof >= 0) - { - eof += protocol.package_eof_len; - if (read_buffer->length > (uint32_t) eof) - { - read_buffer->length -= eof; - read_buffer->offset += eof; - } - else - { - read_buffer->length = 0; - } - return eof; - } - else - { - if (read_buffer->length == protocol.package_max_length) - { - read_buffer->length = 0; - set_err(EPROTO, "no package eof"); - return -1; - } - else if (read_buffer->length == read_buffer->size) - { - if (read_buffer->size < protocol.package_max_length) - { - size_t new_size = read_buffer->size * 2; - if (new_size > protocol.package_max_length) - { - new_size = protocol.package_max_length; - } - if (swString_extend(read_buffer, new_size) < 0) - { - read_buffer->length = 0; - set_err(ENOMEM); - return -1; - } - } - } - } + if (open_length_check) { + recv_bytes = recv_packet_with_length_protocol(); + } else if (open_eof_check) { + recv_bytes = recv_packet_with_eof_protocol(); + } else { + recv_bytes = recv(read_buffer->str, read_buffer->size); + if (recv_bytes > 0) { + read_buffer->length = read_buffer->offset = recv_bytes; } - read_buffer->length = 0; } - else - { - retval = recv(read_buffer->str, read_buffer->size); + if (recv_bytes <= 0) { + read_buffer->clear(); } - - return retval; + return recv_bytes; } -bool Socket::shutdown(int __how) -{ +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 - { + } else { #ifdef SW_USE_OPENSSL - if (socket->ssl) - { - SSL_set_quiet_shutdown(socket->ssl, 1); - SSL_shutdown(socket->ssl); + if (socket->ssl) { + socket->ssl_shutdown(); } #endif - if (::shutdown(sock_fd, __how) == 0 || errno == ENOTCONN) - { - if (errno == ENOTCONN) - { + if (::shutdown(sock_fd, __how) == 0 || errno == ENOTCONN) { + if (errno == ENOTCONN) { // connection reset by server side __how = SHUT_RDWR; } - switch (__how) - { + switch (__how) { case SHUT_RD: shutdown_read = true; break; @@ -1898,9 +1650,8 @@ bool Socket::shutdown(int __how) shutdown_read = shutdown_write = true; break; } - if (shutdown_read && shutdown_write) - { - activated = false; + if (shutdown_read && shutdown_write) { + connected = false; } return true; } @@ -1910,85 +1661,60 @@ bool Socket::shutdown(int __how) } #ifdef SW_USE_OPENSSL -bool Socket::ssl_shutdown() -{ - if (socket->ssl) - { - swSSL_close(socket); - } - if (ssl_context) - { - swSSL_free_context(ssl_context); - ssl_context = nullptr; +bool Socket::ssl_shutdown() { + if (socket->ssl) { + socket->ssl_close(); } return true; } #endif -bool Socket::cancel(const enum swEvent_type event) -{ - if (!has_bound(event)) - { +bool Socket::cancel(const EventType event) { + if (!has_bound(event)) { return false; } - if (event == SW_EVENT_READ) - { + if (event == SW_EVENT_READ) { set_err(ECANCELED); read_co->resume(); return true; - } - else if (event == SW_EVENT_WRITE) - { + } else if (event == SW_EVENT_WRITE) { set_err(ECANCELED); write_co->resume(); return true; - } - else - { + } 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) - { +bool Socket::close() { + if (is_closed()) { set_err(EBADF); - return true; - } - if (sw_unlikely(has_bound())) - { - if (closed) - { - // close operation is in processing - set_err(EINPROGRESS); - return false; - } - if (activated) - { - shutdown(); - } - closed = true; - if (write_co) - { - set_err(ECONNRESET); - write_co->resume(); - } - if (read_co) - { - set_err(ECONNRESET); - read_co->resume(); - } return false; } - else - { - sock_fd = -1; - closed = true; + if (connected) { + shutdown(); + } + if (sw_unlikely(has_bound())) { + 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 = SW_BAD_SOCKET; + if (dtor_ != nullptr) { + auto dtor = dtor_; + dtor_ = nullptr; + dtor(this); + } return true; } } @@ -2000,73 +1726,43 @@ bool Socket::close() * 2. called close() and it return true * 3. called close() and it return false but it will not be accessed anywhere else */ -Socket::~Socket() -{ - if (socket == nullptr) - { - return; - } +Socket::~Socket() { #ifdef SW_DEBUG - if (SwooleG.running) - { + if (SwooleG.running) { SW_ASSERT(!has_bound() && socket->removed); } #endif - if (read_buffer) - { - swString_free(read_buffer); + if (read_buffer) { + delete read_buffer; } - if (write_buffer) - { - swString_free(write_buffer); + if (write_buffer) { + delete write_buffer; } - /* {{{ release socket resources */ -#ifdef SW_USE_OPENSSL - ssl_shutdown(); - if (ssl_option.cert_file) - { - sw_free(ssl_option.cert_file); + if (socks5_proxy) { + delete socks5_proxy; } - if (ssl_option.key_file) - { - sw_free(ssl_option.key_file); + if (http_proxy) { + delete http_proxy; } - if (ssl_option.passphrase) - { - sw_free(ssl_option.passphrase); - } -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - if (ssl_option.tls_host_name) - { - sw_free(ssl_option.tls_host_name); - } -#endif - if (ssl_option.cafile) - { - sw_free(ssl_option.cafile); - } - if (ssl_option.capath) - { - sw_free(ssl_option.capath); + if (socket == nullptr) { + return; } - ssl_option = {}; + /* {{{ release socket resources */ +#ifdef SW_USE_OPENSSL + ssl_shutdown(); #endif - if (socket->in_buffer) - { - swBuffer_free(socket->in_buffer); - } - if (socket->out_buffer) - { - swBuffer_free(socket->out_buffer); - } - if (sock_domain == AF_UNIX && !bind_address.empty()) - { + if (sock_domain == AF_UNIX && !bind_address.empty()) { ::unlink(bind_address_info.addr.un.sun_path); bind_address_info = {}; } - if (sock_type == SW_SOCK_UNIX_DGRAM) - { + if (socket->socket_type == SW_SOCK_UNIX_DGRAM) { ::unlink(socket->info.addr.un.sun_path); } - swSocket_free(socket); + if (dtor_ != nullptr) { + dtor_(this); + } + socket->free(); } + +} // namespace coroutine +} // namespace swoole diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index 13dd39f6aca..cc6d5856eb8 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -10,368 +10,311 @@ | to obtain it through the world-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 "coroutine.h" -#include "coroutine_system.h" -#include "lru_cache.h" +#include "swoole_coroutine_system.h" +#include "swoole_coroutine_socket.h" +#include "swoole_lru_cache.h" +#include "swoole_signal.h" -using namespace std; -using namespace swoole; -using swoole::coroutine::System; +#ifdef SW_USE_IOURING +#include "swoole_iouring.h" +using swoole::Iouring; +#endif -struct AsyncTask -{ - Coroutine *co; - swAio_event *original_event; -}; +namespace swoole { +namespace coroutine { static size_t dns_cache_capacity = 1000; static time_t dns_cache_expire = 60; static LRUCache *dns_cache = nullptr; +static std::unordered_map async_resource_map; -void System::set_dns_cache_expire(time_t expire) -{ +void System::set_dns_cache_expire(time_t expire) { dns_cache_expire = expire; } -void System::set_dns_cache_capacity(size_t capacity) -{ +void System::set_dns_cache_capacity(size_t capacity) { dns_cache_capacity = capacity; delete dns_cache; dns_cache = nullptr; } -void System::clear_dns_cache() -{ - if (dns_cache) - { +void System::clear_dns_cache() { + if (dns_cache) { dns_cache->clear(); } } -static void aio_onReadFileCompleted(swAio_event *event) -{ - AsyncTask *task = (AsyncTask *) event->object; - task->original_event->buf = event->buf; - task->original_event->nbytes = event->ret; - task->original_event->error = event->error; - ((Coroutine *) task->co)->resume(); -} - -static void aio_onWriteFileCompleted(swAio_event *event) -{ - AsyncTask *task = (AsyncTask *) event->object; - task->original_event->ret = event->ret; - task->original_event->error = event->error; - ((Coroutine *) task->co)->resume(); -} - -static void aio_onDNSCompleted(swAio_event *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(); + delete canceled; } -static void aio_onDNSTimeout(swTimer *timer, swTimer_node *tnode) -{ - swAio_event *event = (swAio_event *) 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(); -} +int System::sleep(double sec) { + Coroutine *co = Coroutine::get_current_safe(); -static void sleep_timeout(swTimer *timer, swTimer_node *tnode) -{ - ((Coroutine *) tnode->data)->resume(); -} + bool *canceled = new bool(false); + TimerNode *tnode = nullptr; -int System::sleep(double sec) -{ - Coroutine* co = Coroutine::get_current_safe(); - if (swoole_timer_add((long) (sec * 1000), SW_FALSE, sleep_timeout, co) == NULL) - { - return -1; + if (sec < SW_TIMER_MIN_SEC) { + 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; + } + } + 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; } - co->yield(); - return 0; + return SW_OK; } -swString* System::read_file(const char *file, bool lock) -{ - AsyncTask task; - - swAio_event ev; - bzero(&ev, sizeof(swAio_event)); - - task.co = Coroutine::get_current_safe(); - task.original_event = &ev; - - ev.lock = lock ? 1 : 0; - ev.object = (void*) &task; - ev.handler = swAio_handler_read_file; - ev.callback = aio_onReadFileCompleted; - ev.req = (void*) file; +std::shared_ptr System::read_file(const char *file, bool lock) { + std::shared_ptr result; + async([&result, file, lock]() { + File fp(file, O_RDONLY); + if (!fp.ready()) { + swoole_sys_warning("open(%s, O_RDONLY) failed", file); + return; + } + if (lock && !fp.lock(LOCK_SH)) { + swoole_sys_warning("flock(%s, LOCK_SH) failed", file); + return; + } + ssize_t filesize = fp.get_size(); + if (filesize > 0) { + auto content = make_string(filesize + 1); + content->length = fp.read_all(content->str, filesize); + content->str[content->length] = 0; + result = std::shared_ptr(content); + } else { + result = fp.read_content(); + } + if (lock && !fp.unlock()) { + swoole_sys_warning("flock(%s, LOCK_UN) failed", file); + } + }); + return result; +} - ssize_t ret = swAio_dispatch(&ev); - if (ret < 0) - { - return NULL; - } - task.co->yield(); - if (ev.error == 0) - { - swString *str = (swString *) sw_malloc(sizeof(swString)); - if (!str) - { - return NULL; +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; + async([&]() { + File _file(file, file_flags, 0644); + if (!_file.ready()) { + swoole_sys_warning("open(%s, %d) failed", file, file_flags); + return; } - str->str = (char*) ev.buf; - str->length = ev.nbytes; - return str; - } - else - { - SwooleG.error = ev.error; - return NULL; - } + if (lock && !_file.lock(LOCK_EX)) { + 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()) { + swoole_sys_warning("fsync(%s) failed", file); + } + if (lock && !_file.unlock()) { + swoole_sys_warning("flock(%s, LOCK_UN) failed", file); + } + retval = bytes; + }); + return retval; } -ssize_t System::write_file(const char *file, char *buf, size_t length, bool lock, int flags) -{ - AsyncTask task; +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; - swAio_event ev; - bzero(&ev, sizeof(swAio_event)); + coroutine::async(async::handler_gethostbyname, ev, timeout); - task.co = Coroutine::get_current_safe(); - task.original_event = &ev; - - ev.lock = lock ? 1 : 0; - ev.buf = buf; - ev.nbytes = length; - ev.object = (void*) &task; - ev.handler = swAio_handler_write_file; - ev.callback = aio_onWriteFileCompleted; - ev.req = (void*) file; - ev.flags = flags; - - ssize_t ret = swAio_dispatch(&ev); - if (ret < 0) - { - return -1; - } - task.co->yield(); - if (ev.error != 0) - { - SwooleG.error = ev.error; + 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; } - return ev.ret; } -string System::gethostbyname(const string &hostname, int domain, double timeout) -{ - if (dns_cache == nullptr && dns_cache_capacity != 0) - { +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); } - string cache_key; - if (dns_cache) - { + std::string cache_key; + std::string result; + + if (dns_cache) { cache_key.append(domain == AF_INET ? "4_" : "6_"); cache_key.append(hostname); auto cache = dns_cache->get(cache_key); - if (cache) - { - return *(string *)cache.get(); + if (cache) { + return *(std::string *) cache.get(); } } - swAio_event ev; - AsyncTask task; - - bzero(&ev, sizeof(swAio_event)); - if (hostname.size() < SW_IP_MAX_LENGTH) - { - ev.nbytes = SW_IP_MAX_LENGTH + 1; +#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 - { - ev.nbytes = hostname.size() + 1; +#else + result = gethostbyname_impl_with_async(hostname, domain, timeout); +#endif + + if (dns_cache && !result.empty()) { + dns_cache->set(cache_key, std::make_shared(result), dns_cache_expire); } - task.co = Coroutine::get_current_safe(); - task.original_event = &ev; + return result; +} - ev.buf = sw_malloc(ev.nbytes); - if (!ev.buf) - { - return ""; - } +std::vector System::getaddrinfo( + const std::string &hostname, int family, int socktype, int protocol, const std::string &service, double timeout) { + assert(!hostname.empty()); + assert(family == AF_INET || family == AF_INET6); - memcpy(ev.buf, hostname.c_str(), hostname.size()); - ((char *) ev.buf)[hostname.size()] = 0; - ev.flags = domain; - ev.object = (void*) &task; - ev.handler = swAio_handler_gethostbyname; - ev.callback = aio_onDNSCompleted; - /* TODO: find a better way */ - ev.ret = 1; + AsyncEvent ev{}; + auto req = new GetaddrinfoRequest(hostname, family, socktype, protocol, service); + ev.data = std::shared_ptr(req); - swAio_event *event = swAio_dispatch2(&ev); - swTimer_node *timer = nullptr; - if (timeout > 0) - { - timer = swoole_timer_add((long) (timeout * 1000), SW_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 (ev.ret == -1) - { - SwooleG.error = ev.error; - return ""; - } - else - { - if (dns_cache) - { - string *addr = new string((char *) ev.buf); - dns_cache->set(cache_key, shared_ptr(addr), dns_cache_expire); - sw_free(ev.buf); - return *addr; - } + coroutine::async(async::handler_getaddrinfo, ev, timeout); - string addr((char *) ev.buf); - sw_free(ev.buf); - return addr; + std::vector retval; + + 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); } + + return retval; } -vector System::getaddrinfo(const string &hostname, int family, int socktype, int protocol, - const string &service, double timeout) -{ - assert(!hostname.empty()); - assert(family == AF_INET || family == AF_INET6); +struct SignalListener { + Coroutine *co; + int signo; +}; - swAio_event ev; - bzero(&ev, sizeof(swAio_event)); +/** + * Only the main thread should listen for signals, + * without modifying it to a thread-local variable. + */ +static SignalListener *listeners[SW_SIGNO_MAX]; - swRequest_getaddrinfo req; - bzero(&req, sizeof(swRequest_getaddrinfo)); +int System::wait_signal(int signal, double timeout) { + std::vector signals = {signal}; + return wait_signal(signals, timeout); +} - AsyncTask task; +/** + * @error: swoole_get_last_error() + */ +int System::wait_signal(const std::vector &signals, double timeout) { + SignalListener listener = { + Coroutine::get_current_safe(), + -1, + }; - task.co = Coroutine::get_current_safe(); - task.original_event = &ev; + if (SwooleTG.signal_listener_num > 0) { + swoole_set_last_error(EBUSY); + return -1; + } - ev.object = &task; - ev.handler = swAio_handler_getaddrinfo; - ev.callback = aio_onDNSCompleted; - ev.req = &req; + auto callback_fn = [](int signo) { + auto listener = listeners[signo]; + if (listener) { + listeners[signo] = nullptr; + listener->signo = signo; + listener->co->resume(); + } + }; - struct sockaddr_in6 result_buffer[SW_DNS_HOST_BUFFER_SIZE]; + for (auto &signo : signals) { + if (signo < 0 || signo >= SW_SIGNO_MAX || signo == SIGCHLD) { + swoole_set_last_error(EINVAL); + return -1; + } - 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; + /* resgiter signal */ + listeners[signo] = &listener; - swAio_event *event = swAio_dispatch2(&ev); - swTimer_node *timer = nullptr; - if (timeout > 0) - { - timer = swoole_timer_add((long) (timeout * 1000), SW_FALSE, aio_onDNSTimeout, event); +#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 } - task.co->yield(); - if (timer) - { - swoole_timer_del(timer); - } - - vector retval; - if (ev.ret == -1) - { - SwooleG.error = ev.error; + // exit condition + if (!sw_reactor()->isset_exit_condition(Reactor::EXIT_CONDITION_CO_SIGNAL_LISTENER)) { + sw_reactor()->set_exit_condition( + Reactor::EXIT_CONDITION_CO_SIGNAL_LISTENER, + [](Reactor *reactor, size_t &event_num) -> bool { return SwooleTG.co_signal_listener_num == 0; }); } - struct sockaddr_in *addr_v4; - struct sockaddr_in6 *addr_v6; + SwooleTG.co_signal_listener_num++; - if (req.error == 0) - { - int i; - char tmp[INET6_ADDRSTRLEN]; - const char *r; + bool retval = listener.co->yield_ex(timeout); - for (i = 0; i < req.count; i++) - { - if (req.family == AF_INET) - { - addr_v4 = (struct sockaddr_in *) ((char*) req.result + (i * sizeof(struct sockaddr_in))); - r = inet_ntop(AF_INET, (const void*) &addr_v4->sin_addr, tmp, sizeof(tmp)); - } - else - { - addr_v6 = (struct sockaddr_in6 *) ((char*) req.result + (i * sizeof(struct sockaddr_in6))); - r = inet_ntop(AF_INET6, (const void*) &addr_v6->sin6_addr, tmp, sizeof(tmp)); - } - if (r) - { - retval.push_back(tmp); - } - } - } - else - { - SwooleG.error = ev.error; + 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; } - return retval; + SwooleTG.co_signal_listener_num--; + + return retval ? listener.signo : -1; } -struct coro_poll_task -{ - std::unordered_map *fds; +struct CoroPollTask { + std::unordered_map *fds; Coroutine *co = nullptr; - swTimer_node *timer = nullptr; + TimerNode *timer = nullptr; bool success = false; bool wait = true; }; -static inline void socket_poll_clean(coro_poll_task *task) -{ - for (auto i = task->fds->begin(); i != task->fds->end(); i++) - { - swSocket *socket = i->second.socket; - if (!socket) - { +static inline void socket_poll_clean(CoroPollTask *task) { + for (auto i = task->fds->begin(); i != task->fds->end(); i++) { + network::Socket *socket = i->second.socket; + if (!socket) { continue; } int retval = swoole_event_del(i->second.socket); @@ -379,18 +322,16 @@ static inline void socket_poll_clean(coro_poll_task *task) * Temporary socket, fd marked -1, skip close */ socket->fd = -1; - swSocket_free(socket); + socket->free(); i->second.socket = nullptr; - if (retval < 0) - { + if (retval < 0) { continue; } } } -static void socket_poll_timeout(swTimer *timer, swTimer_node *tnode) -{ - coro_poll_task *task = (coro_poll_task *) tnode->data; +static void socket_poll_timeout(Timer *timer, TimerNode *tnode) { + CoroPollTask *task = (CoroPollTask *) tnode->data; task->timer = nullptr; task->success = false; task->wait = false; @@ -398,122 +339,97 @@ static void socket_poll_timeout(swTimer *timer, swTimer_node *tnode) task->co->resume(); } -static void socket_poll_completed(void *data) -{ - coro_poll_task *task = (coro_poll_task *) data; +static void socket_poll_completed(void *data) { + CoroPollTask *task = (CoroPollTask *) data; socket_poll_clean(task); task->co->resume(); } -static inline void socket_poll_trigger_event(swReactor *reactor, coro_poll_task *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) - { + if (event == SW_EVENT_ERROR && !(i->second.events & SW_EVENT_ERROR)) { + if (i->second.events & SW_EVENT_READ) { i->second.revents |= SW_EVENT_READ; } - if (i->second.events & SW_EVENT_WRITE) - { + if (i->second.events & SW_EVENT_WRITE) { i->second.revents |= SW_EVENT_WRITE; } - } - else - { + } else { i->second.revents |= event; } - if (task->wait) - { + if (task->wait) { task->wait = false; task->success = true; - if (task->timer) - { + if (task->timer) { swoole_timer_del(task->timer); task->timer = nullptr; } - reactor->defer(reactor, socket_poll_completed, task); + reactor->defer(socket_poll_completed, task); } } -static int socket_poll_read_callback(swReactor *reactor, swEvent *event) -{ - socket_poll_trigger_event(reactor, (coro_poll_task *) event->socket->object, event->fd, SW_EVENT_READ); +static int socket_poll_read_callback(Reactor *reactor, Event *event) { + socket_poll_trigger_event(reactor, (CoroPollTask *) event->socket->object, event->fd, SW_EVENT_READ); return SW_OK; } -static int socket_poll_write_callback(swReactor *reactor, swEvent *event) -{ - socket_poll_trigger_event(reactor, (coro_poll_task *) event->socket->object, event->fd, SW_EVENT_WRITE); +static int socket_poll_write_callback(Reactor *reactor, Event *event) { + socket_poll_trigger_event(reactor, (CoroPollTask *) event->socket->object, event->fd, SW_EVENT_WRITE); return SW_OK; } -static int socket_poll_error_callback(swReactor *reactor, swEvent *event) -{ - socket_poll_trigger_event(reactor, (coro_poll_task *) event->socket->object, event->fd, SW_EVENT_ERROR); +static int socket_poll_error_callback(Reactor *reactor, Event *event) { + socket_poll_trigger_event(reactor, (CoroPollTask *) event->socket->object, event->fd, SW_EVENT_ERROR); 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) - { + if (events & SW_EVENT_READ) { poll_events |= POLLIN; } - if (events & SW_EVENT_WRITE) - { + if (events & SW_EVENT_WRITE) { poll_events |= POLLOUT; } 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) - { + if (events & POLLIN) { sw_events |= SW_EVENT_READ; } - if (events & POLLOUT) - { + if (events & POLLOUT) { sw_events |= SW_EVENT_WRITE; } - //ignore ERR and HUP, because event is already processed at IN and OUT handler. - if ((((events & POLLERR) || (events & POLLHUP)) && !((events & POLLIN) || (events & POLLOUT)))) - { + // ignore ERR and HUP, because event is already processed at IN and OUT handler. + if ((((events & POLLERR) || (events & POLLHUP)) && !((events & POLLIN) || (events & POLLOUT)))) { sw_events |= SW_EVENT_ERROR; } return sw_events; } -bool System::socket_poll(std::unordered_map &fds, double timeout) -{ - if (timeout == 0) - { +bool System::socket_poll(std::unordered_map &fds, double timeout) { + if (timeout == 0) { struct pollfd *event_list = (struct pollfd *) sw_calloc(fds.size(), sizeof(struct pollfd)); - if (!event_list) - { - swWarn("calloc() failed"); + if (!event_list) { + swoole_warning("calloc() failed"); return false; } int n = 0; - for (auto i = fds.begin(); i != fds.end(); i++, n++) - { + for (auto i = fds.begin(); i != fds.end(); i++, n++) { event_list[n].fd = i->first; event_list[n].events = translate_events_to_poll(i->second.events); event_list[n].revents = 0; } int retval = ::poll(event_list, n, 0); - if (retval > 0) - { + if (retval > 0) { int n = 0; - for (auto i = fds.begin(); i != fds.end(); i++, n++) - { + for (auto i = fds.begin(); i != fds.end(); i++, n++) { i->second.revents = translate_events_from_poll(event_list[n].revents); } } @@ -522,33 +438,26 @@ bool System::socket_poll(std::unordered_map &fds, double ti } size_t tasked_num = 0; - coro_poll_task task; + CoroPollTask task; task.fds = &fds; task.co = Coroutine::get_current_safe(); - for (auto i = fds.begin(); i != fds.end(); i++) - { - i->second.socket = swSocket_new(i->first, SW_FD_CORO_POLL); - if (i->second.socket == nullptr) - { - continue; - } - if (swoole_event_add(i->second.socket, i->second.events) < 0) - { + for (auto i = fds.begin(); i != fds.end(); i++) { + 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; } i->second.socket->object = &task; tasked_num++; } - if (sw_unlikely(tasked_num == 0)) - { + if (sw_unlikely(tasked_num == 0)) { return false; } - if (timeout > 0) - { - task.timer = swoole_timer_add((long) (timeout * 1000), SW_FALSE, socket_poll_timeout, &task); + if (timeout > 0) { + task.timer = swoole_timer_add(timeout, false, socket_poll_timeout, &task); } task.co->yield(); @@ -556,127 +465,123 @@ bool System::socket_poll(std::unordered_map &fds, double ti return task.success; } -struct event_waiter -{ - swSocket *socket; - swTimer_node *timer; +struct EventWaiter { + network::Socket *socket; + TimerNode *timer; Coroutine *co; int revents; + int error_; - event_waiter(int fd, int events, double timeout) - { - revents = 0; - if (!(socket = swSocket_new(fd, SW_FD_CORO_EVENT))) - { - SwooleG.error = errno; - return; - } + EventWaiter(int fd, int events, double timeout) { + error_ = revents = 0; + socket = swoole::make_socket(fd, SW_FD_CO_EVENT); socket->object = this; - if (swoole_event_add(socket, events) < 0) - { - SwooleG.error = errno; + timer = 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); goto _done; } - if (timeout > 0) - { - timer = swoole_timer_add((long) (timeout * 1000), SW_FALSE, [](swTimer *timer, swTimer_node *tnode){ - event_waiter *waiter = (event_waiter *) tnode->data; - waiter->timer = nullptr; - waiter->co->resume(); - }, this); - } - else - { - timer = nullptr; + + if (timeout > 0) { + 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) - { + if (timer != nullptr) { swoole_timer_del(timer); } - else if (timeout > 0) - { - SwooleG.error = ETIMEDOUT; + if (error_) { + swoole_set_last_error(error_); } swoole_event_del(socket); - _done: + _done: socket->fd = -1; /* skip close */ - swSocket_free(socket); + socket->free(); } - }; -static inline void event_waiter_callback(swReactor *reactor, event_waiter *waiter, enum swEvent_type event) -{ +static inline void event_waiter_callback(Reactor *reactor, EventWaiter *waiter, EventType event) { if (waiter->revents == 0) { - reactor->defer(reactor, [](void *data) { - event_waiter *waiter = (event_waiter *) data; - waiter->co->resume(); - }, waiter); + reactor->defer([waiter](void *data) { waiter->co->resume(); }); } waiter->revents |= event; } -static int event_waiter_read_callback(swReactor *reactor, swEvent *event) -{ - event_waiter_callback(reactor, (event_waiter *) event->socket->object, SW_EVENT_READ); +static int event_waiter_read_callback(Reactor *reactor, Event *event) { + event_waiter_callback(reactor, (EventWaiter *) event->socket->object, SW_EVENT_READ); return SW_OK; } -static int event_waiter_write_callback(swReactor *reactor, swEvent *event) -{ - event_waiter_callback(reactor, (event_waiter *) event->socket->object, SW_EVENT_WRITE); +static int event_waiter_write_callback(Reactor *reactor, Event *event) { + event_waiter_callback(reactor, (EventWaiter *) event->socket->object, SW_EVENT_WRITE); return SW_OK; } -static int event_waiter_error_callback(swReactor *reactor, swEvent *event) -{ - event_waiter_callback(reactor, (event_waiter *) event->socket->object, SW_EVENT_ERROR); +static int event_waiter_error_callback(Reactor *reactor, Event *event) { + event_waiter_callback(reactor, (EventWaiter *) event->socket->object, SW_EVENT_ERROR); return SW_OK; } -int System::wait_event(int fd, int events, double timeout) -{ +/** + * @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) - { - SwooleG.error = EINVAL; + if (events == 0) { + swoole_set_last_error(EINVAL); return 0; } - if (timeout == 0) - { + if (timeout == 0) { struct pollfd pfd; pfd.fd = fd; pfd.events = translate_events_to_poll(events); pfd.revents = 0; int retval = ::poll(&pfd, 1, 0); - if (retval == 1) - { + if (retval == 1) { return translate_events_from_poll(pfd.revents); } - if (retval < 0) - { - SwooleG.error = errno; + if (retval < 0) { + swoole_set_last_error(errno); } return 0; } - int revents = event_waiter(fd, events, timeout).revents; + EventWaiter waiter(fd, events, timeout); + if (waiter.error_) { + errno = swoole_get_last_error(); + return SW_ERR; + } - if (revents & SW_EVENT_ERROR) - { + int revents = waiter.revents; + if (revents & SW_EVENT_ERROR) { revents ^= SW_EVENT_ERROR; - if (events & SW_EVENT_READ) - { + if (events & SW_EVENT_READ) { revents |= SW_EVENT_READ; } - if (events & SW_EVENT_WRITE) - { + if (events & SW_EVENT_WRITE) { revents |= SW_EVENT_WRITE; } } @@ -684,133 +589,157 @@ int System::wait_event(int fd, int events, double timeout) return revents; } -void System::init_reactor(swReactor *reactor) -{ - swReactor_set_handler(reactor, SW_FD_CORO_POLL | SW_EVENT_READ, socket_poll_read_callback); - swReactor_set_handler(reactor, SW_FD_CORO_POLL | SW_EVENT_WRITE, socket_poll_write_callback); - swReactor_set_handler(reactor, SW_FD_CORO_POLL | SW_EVENT_ERROR, socket_poll_error_callback); +bool System::exec(const char *command, bool get_error_stream, std::shared_ptr buffer, int *status) { + Coroutine::get_current_safe(); - swReactor_set_handler(reactor, SW_FD_CORO_EVENT | SW_EVENT_READ, event_waiter_read_callback); - swReactor_set_handler(reactor, SW_FD_CORO_EVENT | SW_EVENT_WRITE, event_waiter_write_callback); - swReactor_set_handler(reactor, SW_FD_CORO_EVENT | SW_EVENT_ERROR, event_waiter_error_callback); - - swReactor_set_handler(reactor, SW_FD_AIO | SW_EVENT_READ, swAio_callback); -} + 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; + } -static void async_task_completed(swAio_event *event) -{ - if (event->canceled) - { - return; + 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; + } } - AsyncTask *task = (AsyncTask *) event->object; - task->original_event->error = event->error; - task->original_event->ret = event->ret; - task->co->resume(); + socket.close(); + + return System::waitpid_safe(pid, status, 0) == pid; } -static void async_task_timeout(swTimer *timer, swTimer_node *tnode) -{ - swAio_event *event = (swAio_event *) tnode->data; - event->canceled = 1; - AsyncTask *task = (AsyncTask *) event->object; - task->original_event->error = SW_ERROR_AIO_TIMEOUT; - task->co->resume(); +void System::init_reactor(Reactor *reactor) { + reactor->set_handler(SW_FD_CO_POLL | SW_EVENT_READ, socket_poll_read_callback); + reactor->set_handler(SW_FD_CO_POLL | SW_EVENT_WRITE, socket_poll_write_callback); + reactor->set_handler(SW_FD_CO_POLL | SW_EVENT_ERROR, socket_poll_error_callback); + + reactor->set_handler(SW_FD_CO_EVENT | SW_EVENT_READ, event_waiter_read_callback); + reactor->set_handler(SW_FD_CO_EVENT | SW_EVENT_WRITE, event_waiter_write_callback); + reactor->set_handler(SW_FD_CO_EVENT | SW_EVENT_ERROR, event_waiter_error_callback); + + reactor->set_handler(SW_FD_AIO | SW_EVENT_READ, AsyncThreads::callback); +#ifdef SW_USE_IOURING + reactor->set_handler(SW_FD_IOURING | SW_EVENT_READ, Iouring::callback); +#endif } -bool coroutine::async(swAio_handler handler, swAio_event &event, double timeout) -{ - AsyncTask task; - swTimer_node *timer = nullptr; +static void async_task_completed(AsyncEvent *event) { + if (event->canceled) { + return; + } + Coroutine *co = (Coroutine *) event->object; + co->resume(); +} - task.co = Coroutine::get_current_safe(); - task.original_event = &event; +/** + * @error: swoole_get_last_error() + */ +bool async(async::Handler handler, AsyncEvent &event, double timeout) { + Coroutine *co = Coroutine::get_current_safe(); - event.object = (void*) &task; + event.object = co; event.handler = handler; event.callback = async_task_completed; - swAio_event *_ev = swAio_dispatch2(&event); - if (_ev == nullptr) - { + AsyncEvent *_ev = async::dispatch(&event); + if (_ev == nullptr) { return false; } - if (timeout > 0) - { - timer = swoole_timer_add((long) (timeout * 1000), SW_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); - } + } else { + event.canceled = _ev->canceled; + event.error = errno = _ev->error; + event.retval = _ev->retval; return true; } } -struct AsyncLambdaTask -{ +struct AsyncLambdaTask { Coroutine *co; std::function fn; }; -static void async_lambda_handler(swAio_event *event) -{ +static void async_lambda_handler(AsyncEvent *event) { AsyncLambdaTask *task = reinterpret_cast(event->object); task->fn(); - event->error = 0; - event->ret = 0; + event->error = errno; + event->retval = 0; } -static void async_lambda_callback(swAio_event *event) -{ - if (event->canceled) - { - return; - } +static void async_lambda_callback(AsyncEvent *event) { AsyncLambdaTask *task = reinterpret_cast(event->object); task->co->resume(); } -bool coroutine::async(const std::function &fn, double timeout) -{ - swTimer_node *timer = nullptr; - swAio_event event = {}; - - AsyncLambdaTask task; - task.co = Coroutine::get_current_safe(); - task.fn = fn; +bool async(const std::function &fn) { + AsyncEvent event{}; + AsyncLambdaTask task{Coroutine::get_current_safe(), fn}; event.object = &task; event.handler = async_lambda_handler; event.callback = async_lambda_callback; - swAio_event *_ev = swAio_dispatch2(&event); - if (_ev == nullptr) - { + AsyncEvent *_ev = async::dispatch(&event); + if (_ev == nullptr) { return false; } - if (timeout > 0) - { - timer = swoole_timer_add((long) (timeout * 1000), SW_FALSE, async_task_timeout, _ev); - } + task.co->yield(); - if (event.error == SW_ERROR_AIO_TIMEOUT) - { - return false; + errno = _ev->error; + return true; +} + +AsyncLock::AsyncLock(void *resource) { + resource_ = resource; + async_resource_map.emplace(resource, Coroutine::get_current_cid()); +} + +AsyncLock::~AsyncLock() { + async_resource_map.erase(resource_); +} + +std::shared_ptr async_lock(void *resource) { + auto iter = async_resource_map.find(resource); + if (iter != async_resource_map.end()) { + swoole_fatal_error(SW_ERROR_CO_HAS_BEEN_BOUND, + "resource(%p) has already been bound to another coroutine#%ld, " + "%s of the same resource in coroutine#%ld at the same time is not allowed", + resource, + *iter, + Coroutine::get_current_cid()); + return nullptr; } - else - { - if (timer) - { - swoole_timer_del(timer); + 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 +} // namespace swoole diff --git a/src/coroutine/thread_context.cc b/src/coroutine/thread_context.cc index f2a6b6d404b..b670d495719 100644 --- a/src/coroutine/thread_context.cc +++ b/src/coroutine/thread_context.cc @@ -10,77 +10,97 @@ | to obtain it through the world-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 "context.h" +#include "swoole_async.h" +#include "swoole_signal.h" +#include "swoole_coroutine_context.h" #ifdef SW_USE_THREAD_CONTEXT -using namespace swoole; -using namespace std; +namespace swoole { +namespace coroutine { -static mutex global_lock; -static swReactor *g_reactor = nullptr; -static swTimer *g_timer = nullptr; -static mutex *current_lock = nullptr; +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(swTimer *timer, swTimer_node *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, 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(); - thread_ = thread(Context::context_func, this); + swap_lock_ = nullptr; + thread_ = std::thread(Context::context_func, this); } -Context::~Context() -{ +Context::~Context() { thread_.join(); } -bool Context::swap_in() -{ +bool Context::swap_in() { swap_lock_ = current_lock; current_lock = &lock_; lock_.unlock(); swap_lock_->lock(); + return true; } -bool Context::swap_out() -{ +bool Context::swap_out() { current_lock = swap_lock_; swap_lock_->unlock(); lock_.lock(); + 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 #endif diff --git a/src/coroutine/ucontext.cc b/src/coroutine/ucontext.cc deleted file mode 100644 index b4c05e09de5..00000000000 --- a/src/coroutine/ucontext.cc +++ /dev/null @@ -1,101 +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 "swoole.h" -#include "context.h" - -#if USE_UCONTEXT - -using namespace swoole; - -Context::Context(size_t stack_size, coroutine_func_t fn, void* private_data) : - fn_(fn), stack_size_(stack_size), private_data_(private_data) -{ - if (-1 == getcontext(&ctx_)) - { - swoole_throw_error(SW_ERROR_CO_GETCONTEXT_FAILED); - return; - } - -#ifdef SW_CONTEXT_PROTECT_STACK_PAGE - protect_page_ = 0; -#endif - end_ = false; - - stack_ = (char*) sw_malloc(stack_size); - swTraceLog(SW_TRACE_COROUTINE, "alloc stack: size=%lu, ptr=%p", stack_size, stack_); - - ctx_.uc_stack.ss_sp = stack_; - ctx_.uc_stack.ss_size = stack_size; - ctx_.uc_link = NULL; - -#if defined(USE_VALGRIND) - valgrind_stack_id = VALGRIND_STACK_REGISTER(static_cast(stack_) + stack_size, stack_); -#endif - - makecontext(&ctx_, (void (*)(void))&context_func, 1, this); - -#ifdef SW_CONTEXT_PROTECT_STACK_PAGE - uint32_t protect_page = get_protect_stack_page(); - if (protect_page) - { - if (protect_stack(stack_, stack_size, protect_page)) - { - protect_page_ = protect_page; - } - } -#endif -} - -Context::~Context() -{ - if (stack_) - { - swTraceLog(SW_TRACE_COROUTINE, "free stack: ptr=%p", stack_); -#ifdef SW_CONTEXT_PROTECT_STACK_PAGE - if (protect_page_) - { - unprotect_stack(stack_, protect_page_); - } -#endif - -#if defined(USE_VALGRIND) - VALGRIND_STACK_DEREGISTER(valgrind_stack_id); -#endif - sw_free(stack_); - stack_ = NULL; - } -} - -bool Context::swap_in() -{ - return 0 == swapcontext(&swap_ctx_, &ctx_); -} - -bool Context::swap_out() -{ - return 0 == swapcontext(&ctx_, &swap_ctx_); -} - -void Context::context_func(void *arg) -{ - Context *_this = (Context *) arg; - _this->fn_(_this->private_data_); - _this->end_ = true; - _this->swap_out(); -} - -#endif diff --git a/src/lock/atomic.cc b/src/lock/atomic.cc deleted file mode 100644 index 5cab5e184e6..00000000000 --- a/src/lock/atomic.cc +++ /dev/null @@ -1,49 +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 "swoole.h" - -static int swAtomicLock_lock(swLock *lock); -static int swAtomicLock_unlock(swLock *lock); -static int swAtomicLock_trylock(swLock *lock); - -int swAtomicLock_create(swLock *lock, int spin) -{ - bzero(lock, sizeof(swLock)); - lock->type = SW_ATOMLOCK; - lock->object.atomlock.spin = spin; - lock->lock = swAtomicLock_lock; - lock->unlock = swAtomicLock_unlock; - lock->trylock = swAtomicLock_trylock; - return SW_OK; -} - -static int swAtomicLock_lock(swLock *lock) -{ - sw_spinlock(&lock->object.atomlock.lock_t); - return SW_OK; -} - -static int swAtomicLock_unlock(swLock *lock) -{ - return lock->object.atomlock.lock_t = 0; -} - -static int swAtomicLock_trylock(swLock *lock) -{ - sw_atomic_t *atomic = &lock->object.atomlock.lock_t; - return (*(atomic) == 0 && sw_atomic_cmp_set(atomic, 0, 1)); -} 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/cond.cc b/src/lock/cond.cc deleted file mode 100644 index df4e730a5e5..00000000000 --- a/src/lock/cond.cc +++ /dev/null @@ -1,89 +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 "swoole.h" - -static int swCond_notify(swCond *cond); -static int swCond_broadcast(swCond *cond); -static int swCond_timewait(swCond *cond, long sec, long nsec); -static int swCond_wait(swCond *cond); -static int swCond_lock(swCond *cond); -static int swCond_unlock(swCond *cond); -static void swCond_free(swCond *cond); - -int swCond_create(swCond *cond) -{ - if (pthread_cond_init(&cond->_cond, NULL) < 0) - { - swSysWarn("pthread_cond_init fail"); - return SW_ERR; - } - if (swMutex_create(&cond->_lock, 0) < 0) - { - return SW_ERR; - } - - cond->notify = swCond_notify; - cond->broadcast = swCond_broadcast; - cond->timewait = swCond_timewait; - cond->wait = swCond_wait; - cond->lock = swCond_lock; - cond->unlock = swCond_unlock; - cond->free = swCond_free; - - return SW_OK; -} - -static int swCond_notify(swCond *cond) -{ - return pthread_cond_signal(&cond->_cond); -} - -static int swCond_broadcast(swCond *cond) -{ - return pthread_cond_broadcast(&cond->_cond); -} - -static int swCond_timewait(swCond *cond, long sec, long nsec) -{ - struct timespec timeo; - - timeo.tv_sec = sec; - timeo.tv_nsec = nsec; - - return pthread_cond_timedwait(&cond->_cond, &cond->_lock.object.mutex._lock, &timeo); -} - -static int swCond_wait(swCond *cond) -{ - return pthread_cond_wait(&cond->_cond, &cond->_lock.object.mutex._lock); -} - -static int swCond_lock(swCond *cond) -{ - return cond->_lock.lock(&cond->_lock); -} - -static int swCond_unlock(swCond *cond) -{ - return cond->_lock.unlock(&cond->_lock); -} - -static void swCond_free(swCond *cond) -{ - pthread_cond_destroy(&cond->_cond); - cond->_lock.free(&cond->_lock); -} 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/file_lock.cc b/src/lock/file_lock.cc deleted file mode 100644 index eca6a6f5519..00000000000 --- a/src/lock/file_lock.cc +++ /dev/null @@ -1,77 +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 "swoole.h" - -#ifdef F_WRLCK - -static int swFileLock_lock_rd(swLock *lock); -static int swFileLock_lock_rw(swLock *lock); -static int swFileLock_unlock(swLock *lock); -static int swFileLock_trylock_rw(swLock *lock); -static int swFileLock_trylock_rd(swLock *lock); -static int swFileLock_free(swLock *lock); - -int swFileLock_create(swLock *lock, int fd) -{ - bzero(lock, sizeof(swLock)); - lock->type = SW_FILELOCK; - lock->object.filelock.fd = fd; - lock->lock_rd = swFileLock_lock_rd; - lock->lock = swFileLock_lock_rw; - lock->trylock_rd = swFileLock_trylock_rd; - lock->trylock = swFileLock_trylock_rw; - lock->unlock = swFileLock_unlock; - lock->free = swFileLock_free; - return 0; -} - -static int swFileLock_lock_rd(swLock *lock) -{ - lock->object.filelock.lock_t.l_type = F_RDLCK; - return fcntl(lock->object.filelock.fd, F_SETLKW, &lock->object.filelock); -} - -static int swFileLock_lock_rw(swLock *lock) -{ - lock->object.filelock.lock_t.l_type = F_WRLCK; - return fcntl(lock->object.filelock.fd, F_SETLKW, &lock->object.filelock); -} - -static int swFileLock_unlock(swLock *lock) -{ - lock->object.filelock.lock_t.l_type = F_UNLCK; - return fcntl(lock->object.filelock.fd, F_SETLKW, &lock->object.filelock); -} - -static int swFileLock_trylock_rw(swLock *lock) -{ - lock->object.filelock.lock_t.l_type = F_WRLCK; - return fcntl(lock->object.filelock.fd, F_SETLK, &lock->object.filelock); -} - -static int swFileLock_trylock_rd(swLock *lock) -{ - lock->object.filelock.lock_t.l_type = F_RDLCK; - return fcntl(lock->object.filelock.fd, F_SETLK, &lock->object.filelock); -} - -static int swFileLock_free(swLock *lock) -{ - return close(lock->object.filelock.fd); -} - -#endif diff --git a/src/lock/mutex.cc b/src/lock/mutex.cc index c48920758fa..afaecb3d53f 100644 --- a/src/lock/mutex.cc +++ b/src/lock/mutex.cc @@ -10,81 +10,105 @@ | to obtain it through the world-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_lock.h" -static int swMutex_lock(swLock *lock); -static int swMutex_unlock(swLock *lock); -static int swMutex_trylock(swLock *lock); -static int swMutex_free(swLock *lock); - -int swMutex_create(swLock *lock, int use_in_process) -{ - int ret; - bzero(lock, sizeof(swLock)); - lock->type = SW_MUTEX; - pthread_mutexattr_init(&lock->object.mutex.attr); - if (use_in_process == 1) - { - pthread_mutexattr_setpshared(&lock->object.mutex.attr, PTHREAD_PROCESS_SHARED); +namespace swoole { + +struct MutexImpl { + pthread_mutex_t lock_; + pthread_mutexattr_t attr_; +}; + +Mutex::Mutex(int flags) : Lock() { + flags_ = flags; + + if (flags & PROCESS_SHARED) { + impl = (MutexImpl *) sw_mem_pool()->alloc(sizeof(*impl)); + if (impl == nullptr) { + throw std::bad_alloc(); + } + shared_ = true; + } else { + impl = new MutexImpl(); + shared_ = false; } - if ((ret = pthread_mutex_init(&lock->object.mutex._lock, &lock->object.mutex.attr)) < 0) - { - return SW_ERR; + + type_ = MUTEX; + 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 + swoole_warning("PTHREAD_MUTEX_ROBUST is not supported"); +#endif } - lock->lock = swMutex_lock; - lock->unlock = swMutex_unlock; - lock->trylock = swMutex_trylock; - lock->free = swMutex_free; - return SW_OK; + + 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 && (flags_ & ROBUST)) { + retval = pthread_mutex_consistent(&impl->lock_); + } +#endif + + return retval; +} + +int Mutex::lock_rd() { + return lock(); } -static int swMutex_lock(swLock *lock) -{ - return pthread_mutex_lock(&lock->object.mutex._lock); +int Mutex::unlock() { + return pthread_mutex_unlock(&impl->lock_); } -static int swMutex_unlock(swLock *lock) -{ - return pthread_mutex_unlock(&lock->object.mutex._lock); +int Mutex::trylock() { + return pthread_mutex_trylock(&impl->lock_); } -static int swMutex_trylock(swLock *lock) -{ - return pthread_mutex_trylock(&lock->object.mutex._lock); +int Mutex::trylock_rd() { + return trylock(); } #ifdef HAVE_MUTEX_TIMEDLOCK -int swMutex_lockwait(swLock *lock, int timeout_msec) -{ - struct timespec timeo; - timeo.tv_sec = timeout_msec / 1000; - timeo.tv_nsec = (timeout_msec - timeo.tv_sec * 1000) * 1000 * 1000; - return pthread_mutex_timedlock(&lock->object.mutex._lock, &timeo); +int Mutex::lock_wait(int timeout_msec) { + struct timespec timeo = swoole_time_until(timeout_msec); + return pthread_mutex_timedlock(&impl->lock_, &timeo); } #else -int swMutex_lockwait(swLock *lock, int timeout_msec) -{ +int Mutex::lock_wait(int timeout_msec) { int sub = 1; int sleep_ms = 1000; - if (timeout_msec > 100) - { + if (timeout_msec > 100) { sub = 10; sleep_ms = 10000; } - while( timeout_msec > 0) - { - if (pthread_mutex_trylock(&lock->object.mutex._lock) == 0) - { + while (timeout_msec > 0) { + if (pthread_mutex_trylock(&impl->lock_) == 0) { return 0; - } - else - { + } else { usleep(sleep_ms); timeout_msec -= sub; } @@ -93,8 +117,14 @@ int swMutex_lockwait(swLock *lock, int timeout_msec) } #endif -static int swMutex_free(swLock *lock) -{ - pthread_mutexattr_destroy(&lock->object.mutex.attr); - return pthread_mutex_destroy(&lock->object.mutex._lock); +Mutex::~Mutex() { + pthread_mutexattr_destroy(&impl->attr_); + pthread_mutex_destroy(&impl->lock_); + if (shared_) { + sw_mem_pool()->free(impl); + } else { + delete impl; + } } + +} // namespace swoole diff --git a/src/lock/rw_lock.cc b/src/lock/rw_lock.cc index d19003cf32a..5689f45145c 100644 --- a/src/lock/rw_lock.cc +++ b/src/lock/rw_lock.cc @@ -10,72 +10,74 @@ | to obtain it through the world-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_lock.h" #ifdef HAVE_RWLOCK -static int swRWLock_lock_rd(swLock *lock); -static int swRWLock_lock_rw(swLock *lock); -static int swRWLock_unlock(swLock *lock); -static int swRWLock_trylock_rw(swLock *lock); -static int swRWLock_trylock_rd(swLock *lock); -static int swRWLock_free(swLock *lock); +namespace swoole { -int swRWLock_create(swLock *lock, int use_in_process) -{ - int ret; - bzero(lock, sizeof(swLock)); - lock->type = SW_RWLOCK; - pthread_rwlockattr_init(&lock->object.rwlock.attr); - if (use_in_process == 1) - { - pthread_rwlockattr_setpshared(&lock->object.rwlock.attr, PTHREAD_PROCESS_SHARED); +struct RWLockImpl { + pthread_rwlock_t _lock; + pthread_rwlockattr_t attr; +}; + +RWLock::RWLock(int use_in_process) : Lock() { + if (use_in_process) { + impl = (RWLockImpl *) sw_mem_pool()->alloc(sizeof(*impl)); + if (impl == nullptr) { + throw std::bad_alloc(); + } + shared_ = true; + } else { + impl = new RWLockImpl(); + shared_ = false; + } + + type_ = RW_LOCK; + pthread_rwlockattr_init(&impl->attr); + if (use_in_process == 1) { + pthread_rwlockattr_setpshared(&impl->attr, PTHREAD_PROCESS_SHARED); } - if ((ret = pthread_rwlock_init(&lock->object.rwlock._lock, &lock->object.rwlock.attr)) < 0) - { - return SW_ERR; + if (pthread_rwlock_init(&impl->_lock, &impl->attr) != 0) { + throw std::system_error(errno, std::generic_category(), "pthread_rwlock_init() failed"); } - lock->lock_rd = swRWLock_lock_rd; - lock->lock = swRWLock_lock_rw; - lock->unlock = swRWLock_unlock; - lock->trylock = swRWLock_trylock_rw; - lock->trylock_rd = swRWLock_trylock_rd; - lock->free = swRWLock_free; - return SW_OK; } -static int swRWLock_lock_rd(swLock *lock) -{ - return pthread_rwlock_rdlock(&lock->object.rwlock._lock); +int RWLock::lock_rd() { + return pthread_rwlock_rdlock(&impl->_lock); } -static int swRWLock_lock_rw(swLock *lock) -{ - return pthread_rwlock_wrlock(&lock->object.rwlock._lock); +int RWLock::lock() { + return pthread_rwlock_wrlock(&impl->_lock); } -static int swRWLock_unlock(swLock *lock) -{ - return pthread_rwlock_unlock(&lock->object.rwlock._lock); +int RWLock::unlock() { + return pthread_rwlock_unlock(&impl->_lock); } -static int swRWLock_trylock_rd(swLock *lock) -{ - return pthread_rwlock_tryrdlock(&lock->object.rwlock._lock); +int RWLock::trylock_rd() { + return pthread_rwlock_tryrdlock(&impl->_lock); } -static int swRWLock_trylock_rw(swLock *lock) -{ - return pthread_rwlock_trywrlock(&lock->object.rwlock._lock); +int RWLock::trylock() { + return pthread_rwlock_trywrlock(&impl->_lock); } -static int swRWLock_free(swLock *lock) -{ - return pthread_rwlock_destroy(&lock->object.rwlock._lock); +RWLock::~RWLock() { + pthread_rwlockattr_destroy(&impl->attr); + pthread_rwlock_destroy(&impl->_lock); + if (shared_) { + sw_mem_pool()->free(impl); + } else { + delete impl; + } } +} // namespace swoole + #endif diff --git a/src/lock/semaphore.cc b/src/lock/semaphore.cc deleted file mode 100644 index 4cdee236ac4..00000000000 --- a/src/lock/semaphore.cc +++ /dev/null @@ -1,71 +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 "swoole.h" - -#ifdef SEM_UNDO -#include - -static int swSem_lock(swLock *lock); -static int swSem_unlock(swLock *lock); -static int swSem_free(swLock *lock); - -int swSem_create(swLock *lock, key_t key) -{ - int ret; - lock->type = SW_SEM; - if ((ret = semget(key, 1, IPC_CREAT | 0666)) < 0) - { - return SW_ERR; - } - - if (semctl(ret, 0, SETVAL, 1) == -1) - { - swWarn("semctl(SETVAL) failed"); - return SW_ERR; - } - lock->object.sem.semid = ret; - - lock->lock = swSem_lock; - lock->unlock = swSem_unlock; - lock->free = swSem_free; - - return SW_OK; -} - -static int swSem_unlock(swLock *lock) -{ - struct sembuf sem; - sem.sem_flg = SEM_UNDO; - sem.sem_num = 0; - sem.sem_op = 1; - return semop(lock->object.sem.semid, &sem, 1); -} - -static int swSem_lock(swLock *lock) -{ - struct sembuf sem; - sem.sem_flg = SEM_UNDO; - sem.sem_num = 0; - sem.sem_op = -1; - return semop(lock->object.sem.semid, &sem, 1); -} - -static int swSem_free(swLock *lock) -{ - return semctl(lock->object.sem.semid, 0, IPC_RMID); -} -#endif diff --git a/src/lock/spin_lock.cc b/src/lock/spin_lock.cc index 6f083011e95..c073366d218 100644 --- a/src/lock/spin_lock.cc +++ b/src/lock/spin_lock.cc @@ -10,53 +10,62 @@ | to obtain it through the world-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_lock.h" #ifdef HAVE_SPINLOCK -static int swSpinLock_lock(swLock *lock); -static int swSpinLock_unlock(swLock *lock); -static int swSpinLock_trylock(swLock *lock); -static int swSpinLock_free(swLock *lock); - -int swSpinLock_create(swLock *lock, int use_in_process) -{ - int ret; - bzero(lock, sizeof(swLock)); - lock->type = SW_SPINLOCK; - if ((ret = pthread_spin_init(&lock->object.spinlock.lock_t, use_in_process)) < 0) - { - return -1; +namespace swoole { + +SpinLock::SpinLock(int use_in_process) : Lock() { + if (use_in_process) { + impl = (pthread_spinlock_t *) sw_mem_pool()->alloc(sizeof(*impl)); + if (impl == nullptr) { + throw std::bad_alloc(); + } + shared_ = true; + } else { + impl = new pthread_spinlock_t(); + shared_ = false; + } + + type_ = SPIN_LOCK; + if (pthread_spin_init(impl, use_in_process) != 0) { + throw std::system_error(errno, std::generic_category(), "pthread_spin_init() failed"); } - lock->lock = swSpinLock_lock; - lock->unlock = swSpinLock_unlock; - lock->trylock = swSpinLock_trylock; - lock->free = swSpinLock_free; - return 0; } -static int swSpinLock_lock(swLock *lock) -{ - return pthread_spin_lock(&lock->object.spinlock.lock_t); +int SpinLock::lock() { + return pthread_spin_lock(impl); } -static int swSpinLock_unlock(swLock *lock) -{ - return pthread_spin_unlock(&lock->object.spinlock.lock_t); +int SpinLock::lock_rd() { + return lock(); } -static int swSpinLock_trylock(swLock *lock) -{ - return pthread_spin_trylock(&lock->object.spinlock.lock_t); +int SpinLock::unlock() { + return pthread_spin_unlock(impl); } -static int swSpinLock_free(swLock *lock) -{ - return pthread_spin_destroy(&lock->object.spinlock.lock_t); +int SpinLock::trylock() { + return pthread_spin_trylock(impl); } +int SpinLock::trylock_rd() { + return trylock(); +} + +SpinLock::~SpinLock() { + pthread_spin_destroy(impl); + if (shared_) { + sw_mem_pool()->free((void *) impl); + } else { + delete impl; + } +} +} // namespace swoole #endif diff --git a/src/memory/buffer.cc b/src/memory/buffer.cc index 09bdf32a012..a6df5959d8f 100644 --- a/src/memory/buffer.cc +++ b/src/memory/buffer.cc @@ -10,182 +10,136 @@ | to obtain it through the world-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 "buffer.h" - -/** - * create new buffer - */ -swBuffer* swBuffer_new(uint32_t chunk_size) -{ - swBuffer *buffer = (swBuffer *) sw_malloc(sizeof(swBuffer)); - if (buffer == NULL) - { - swSysWarn("malloc for buffer failed"); - return NULL; - } +#include "swoole_buffer.h" - bzero(buffer, sizeof(swBuffer)); - buffer->chunk_size = chunk_size == 0 ? INT_MAX : chunk_size; +namespace swoole { - return buffer; +BufferChunk::BufferChunk(Type type, uint32_t size) : type(type), size(size) { + if (type == TYPE_DATA && size > 0) { + value.str = new char[size]; + } } -/** - * create new chunk - */ -swBuffer_chunk *swBuffer_new_chunk(swBuffer *buffer, uint32_t type, uint32_t size) -{ - swBuffer_chunk *chunk = (swBuffer_chunk *) sw_malloc(sizeof(swBuffer_chunk)); - if (chunk == NULL) - { - swSysWarn("malloc for chunk failed"); - return NULL; +BufferChunk::~BufferChunk() { + if (type == TYPE_DATA) { + delete[] value.str; } - - bzero(chunk, sizeof(swBuffer_chunk)); - - //require alloc memory - if (type == SW_CHUNK_DATA && size > 0) - { - void *buf = sw_malloc(size); - if (buf == NULL) - { - swSysWarn("malloc(%d) for data failed", size); - sw_free(chunk); - return NULL; - } - chunk->size = size; - chunk->store.ptr = buf; + if (destroy) { + destroy(this); } +} - chunk->type = type; - buffer->chunk_num ++; - - if (buffer->head == NULL) - { - buffer->tail = buffer->head = chunk; - } - else - { - buffer->tail->next = chunk; - buffer->tail = chunk; - } +Buffer::Buffer(uint32_t _chunk_size) { + chunk_size = _chunk_size == 0 ? INT_MAX : _chunk_size; +} +BufferChunk *Buffer::alloc(BufferChunk::Type type, uint32_t size) { + auto *chunk = new BufferChunk(type, size); + queue_.push(chunk); return chunk; } -/** - * pop the head chunk - */ -void swBuffer_pop_chunk(swBuffer *buffer, swBuffer_chunk *chunk) -{ - if (chunk->next == NULL) - { - buffer->head = NULL; - buffer->tail = NULL; - buffer->length = 0; - buffer->chunk_num = 0; - } - else - { - buffer->head = chunk->next; - buffer->length -= chunk->length; - buffer->chunk_num--; - } - if (chunk->type == SW_CHUNK_DATA) - { - sw_free(chunk->store.ptr); - } - if (chunk->destroy) - { - chunk->destroy(chunk); - } - sw_free(chunk); +void Buffer::pop() { + BufferChunk *chunk = queue_.front(); + total_length -= chunk->size; + delete chunk; + queue_.pop(); } -/** - * free buffer - */ -int swBuffer_free(swBuffer *buffer) -{ - swBuffer_chunk *chunk = buffer->head; - swBuffer_chunk *will_free_chunk; //free the point - while (chunk != NULL) - { - if (chunk->type == SW_CHUNK_DATA) - { - sw_free(chunk->store.ptr); - } - if (chunk->destroy) - { - chunk->destroy(chunk); - } - will_free_chunk = chunk; - chunk = chunk->next; - sw_free((void *) will_free_chunk); +Buffer::~Buffer() { + while (!queue_.empty()) { + pop(); } - sw_free(buffer); - return SW_OK; } -/** - * append to buffer queue - */ -int swBuffer_append(swBuffer *buffer, const void *data, uint32_t size) -{ +void Buffer::append(const void *data, uint32_t size) { uint32_t _length = size; - char* _pos = (char*) data; + char *_pos = (char *) data; uint32_t _n; - //buffer enQueue - while (_length > 0) - { - _n = _length >= buffer->chunk_size ? buffer->chunk_size : _length; + assert(size > 0); - swBuffer_chunk *chunk = swBuffer_new_chunk(buffer, SW_CHUNK_DATA, _n); - if (chunk == NULL) - { - return SW_ERR; - } + // buffer enQueue + while (_length > 0) { + _n = _length >= chunk_size ? chunk_size : _length; - buffer->length += _n; - chunk->length = _n; + BufferChunk *chunk = alloc(BufferChunk::TYPE_DATA, _n); - memcpy(chunk->store.ptr, _pos, _n); + total_length += _n; + + memcpy(chunk->value.str, _pos, _n); + chunk->length = _n; - swTraceLog(SW_TRACE_BUFFER, "chunk_n=%d|size=%d|chunk_len=%d|chunk=%p", buffer->chunk_num, _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; } - - return SW_OK; } -/** - * print buffer - */ -void swBuffer_debug(swBuffer *buffer, int print_data) -{ - int i = 0; - volatile swBuffer_chunk *chunk = buffer->head; - printf("%s\n%s\n", SW_START_LINE, __func__); - while (chunk != NULL) - { - i++; - printf("%d.\tlen=%d", i, chunk->length); - if (print_data) - { - printf("\tdata=%s", (char *) chunk->store.ptr); +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; + } + + char *pos = (char *) iov[0].iov_base; + BufferChunk *chunk = nullptr; + size_t iov_remain_len = iov[0].iov_len, chunk_remain_len; + size_t i = 0; + + while (true) { + if (chunk) { + if (chunk->size == chunk->length) { + chunk = nullptr; + continue; + } else { + chunk_remain_len = chunk->size - chunk->length; + } + } else { + if (offset > 0) { + if (offset >= (off_t) iov[i].iov_len) { + offset -= iov[i].iov_len; + i++; + continue; + } else { + pos = (char *) iov[i].iov_base + offset; + iov_remain_len = iov[i].iov_len - offset; + offset = 0; + } + } + chunk_remain_len = _length >= chunk_size ? chunk_size : _length; + chunk = alloc(BufferChunk::TYPE_DATA, chunk_remain_len); + } + + size_t _n = std::min(iov_remain_len, chunk_remain_len); + memcpy(chunk->value.str + chunk->length, pos, _n); + total_length += _n; + _length -= _n; + + 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; + + if (iov_remain_len == 0) { + i++; + if (i == iovcnt) { + break; + } + iov_remain_len = iov[i].iov_len; + pos = (char *) iov[i].iov_base; + } else { + pos += _n; } - printf("\n"); - chunk = chunk->next; } - printf("%s\n%s\n", SW_END_LINE, __func__); } +} // namespace swoole diff --git a/src/memory/fixed_pool.cc b/src/memory/fixed_pool.cc index a9272f8195c..7e7daa4d4ef 100644 --- a/src/memory/fixed_pool.cc +++ b/src/memory/fixed_pool.cc @@ -10,246 +10,227 @@ | to obtain it through the world-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" -static void swFixedPool_init(swFixedPool *object); -static void* swFixedPool_alloc(swMemoryPool *pool, uint32_t size); -static void swFixedPool_free(swMemoryPool *pool, void *ptr); -static void swFixedPool_destroy(swMemoryPool *pool); +namespace swoole { -void swFixedPool_debug_slice(swFixedPool_slice *slice); +struct FixedPoolSlice { + uint8_t lock; + FixedPoolSlice *next; + FixedPoolSlice *prev; + char data[0]; +}; -/** - * create new FixedPool, random alloc/free fixed size memory - */ -swMemoryPool* swFixedPool_new(uint32_t slice_num, uint32_t slice_size, uint8_t shared) -{ - slice_size = SW_MEM_ALIGNED_SIZE(slice_size); - size_t size = slice_size * slice_num + slice_num * sizeof(swFixedPool_slice); - size_t alloc_size = size + sizeof(swFixedPool) + sizeof(swMemoryPool); - void *memory = (shared == 1) ? sw_shm_malloc(alloc_size) : sw_malloc(alloc_size); - if (!memory) - { - swWarn("malloc(%ld) failed", size); - return NULL; - } +struct FixedPoolImpl { + void *memory; + size_t size; - swFixedPool *object = (swFixedPool *) memory; - memory = (char *) memory + sizeof(swFixedPool); - bzero(object, sizeof(swFixedPool)); + FixedPoolSlice *head; + FixedPoolSlice *tail; - object->shared = shared; - object->slice_num = slice_num; - object->slice_size = slice_size; - object->size = size; + // total memory size + uint32_t slice_num; - swMemoryPool *pool = (swMemoryPool *) memory; - memory = (char *) memory + sizeof(swMemoryPool); - pool->object = object; - pool->alloc = swFixedPool_alloc; - pool->free = swFixedPool_free; - pool->destroy = swFixedPool_destroy; + // memory usage + uint32_t slice_use; - object->memory = memory; + // Fixed slice size, not include the memory used by FixedPoolSlice + uint32_t slice_size; + bool shared; + bool allocated; - /** - * init linked list - */ - swFixedPool_init(object); + void init(); +}; + +/** + * 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); + void *memory = shared ? ::sw_shm_malloc(alloc_size) : ::sw_malloc(alloc_size); + if (!memory) { + throw std::bad_alloc(); + } - return pool; + impl = (FixedPoolImpl *) memory; + memory = (char *) memory + sizeof(*impl); + sw_memset_zero(impl, sizeof(*impl)); + + impl->shared = shared; + impl->slice_num = slice_num; + impl->slice_size = slice_size; + impl->size = size; + impl->memory = memory; + impl->allocated = true; + impl->init(); } /** * create new FixedPool, Using the given memory */ -swMemoryPool* swFixedPool_new2(uint32_t slice_size, void *memory, size_t size) -{ - swFixedPool *object = (swFixedPool *) memory; - memory = (char *) memory + sizeof(swFixedPool); - bzero(object, sizeof(swFixedPool)); - - object->slice_size = slice_size; - object->size = size - sizeof(swMemoryPool) - sizeof(swFixedPool); - object->slice_num = object->size / (slice_size + sizeof(swFixedPool_slice)); - - swMemoryPool *pool = (swMemoryPool *) memory; - memory = (char *) memory + sizeof(swMemoryPool); - bzero(pool, sizeof(swMemoryPool)); - - pool->object = object; - pool->alloc = swFixedPool_alloc; - pool->free = swFixedPool_free; - pool->destroy = swFixedPool_destroy; - - object->memory = memory; +FixedPool::FixedPool(uint32_t slice_size, void *memory, size_t size, bool shared) { + 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); + 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(); +} - /** - * init linked list - */ - swFixedPool_init(object); +size_t FixedPool::sizeof_struct_slice() { + return sizeof(FixedPoolSlice); +} - return pool; +size_t FixedPool::sizeof_struct_impl() { + return sizeof(FixedPoolImpl); } /** * linked list */ -static void swFixedPool_init(swFixedPool *object) -{ - swFixedPool_slice *slice; - void *cur = object->memory; - void *max = (char *) object->memory + object->size; - do - { - slice = (swFixedPool_slice *) cur; - bzero(slice, sizeof(swFixedPool_slice)); - - if (object->head != NULL) - { - object->head->pre = slice; - slice->next = object->head; - } - else - { - object->tail = slice; +void FixedPoolImpl::init() { + FixedPoolSlice *slice; + void *cur = memory; + void *max = (char *) memory + size; + do { + slice = (FixedPoolSlice *) cur; + sw_memset_zero(slice, sizeof(FixedPoolSlice)); + + if (head != nullptr) { + head->prev = slice; + slice->next = head; + } else { + tail = slice; } - object->head = slice; - cur = (char *) cur + (sizeof(swFixedPool_slice) + object->slice_size); + head = slice; + cur = (char *) cur + (sizeof(FixedPoolSlice) + slice_size); - if (cur < max) - { - slice->pre = (swFixedPool_slice *) cur; - } - else - { - slice->pre = NULL; + if (cur < max) { + slice->prev = (FixedPoolSlice *) cur; + } else { + slice->prev = nullptr; break; } } while (1); } -static void* swFixedPool_alloc(swMemoryPool *pool, uint32_t size) -{ - swFixedPool *object = (swFixedPool *) pool->object; - swFixedPool_slice *slice; - - slice = object->head; - - if (slice->lock == 0) - { - slice->lock = 1; - object->slice_use ++; - /** - * move next slice to head (idle list) - */ - object->head = slice->next; - slice->next->pre = NULL; - - /* - * move this slice to tail (busy list) - */ - object->tail->next = slice; - slice->next = NULL; - slice->pre = object->tail; - object->tail = slice; - - return slice->data; - } - else - { - return NULL; - } +uint32_t FixedPool::get_number_of_spare_slice() { + return impl->slice_num - impl->slice_use; } -static void swFixedPool_free(swMemoryPool *pool, void *ptr) -{ - swFixedPool *object = (swFixedPool *) pool->object; - swFixedPool_slice *slice; - - assert(ptr > object->memory && (char* )ptr < (char * ) object->memory + object->size); +uint32_t FixedPool::get_number_of_total_slice() { + return impl->slice_num; +} - slice = (swFixedPool_slice *) ((char *) ptr - sizeof(swFixedPool_slice)); +uint32_t FixedPool::get_slice_size() { + return impl->slice_size; +} - if (slice->lock) - { - object->slice_use--; +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 *) ((char *) ptr - sizeof(FixedPoolSlice)); + + assert(ptr > impl->memory && (char *) ptr < (char *) impl->memory + impl->size); + assert(slice->lock == 1); + + impl->slice_use--; slice->lock = 0; - //list head, AB - if (slice->pre == NULL) - { + if (slice == impl->head) { return; } - //list tail, DE - if (slice->next == NULL) - { - slice->pre->next = NULL; - object->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 = NULL; - slice->next = object->head; - object->head->pre = slice; - object->head = slice; + // move slice to head (idle) + slice->prev = nullptr; + slice->next = impl->head; + impl->head->prev = slice; + impl->head = slice; } -static void swFixedPool_destroy(swMemoryPool *pool) -{ - swFixedPool *object = (swFixedPool *) pool->object; - if (object->shared) - { - sw_shm_free(object); +FixedPool::~FixedPool() { + if (!impl->allocated) { + return; } - else - { - sw_free(object); + if (impl->shared) { + ::sw_shm_free(impl); + } else { + ::sw_free(impl); } } -void swFixedPool_debug(swMemoryPool *pool) -{ +void FixedPool::debug(int max_lines) { int line = 0; - swFixedPool *object = (swFixedPool *) pool->object; - swFixedPool_slice *slice = object->head; + FixedPoolSlice *slice = impl->head; printf("===============================%s=================================\n", __FUNCTION__); - while (slice != NULL) - { - if (slice->next == slice) - { + while (slice != nullptr) { + if (slice->next == slice) { printf("-------------------@@@@@@@@@@@@@@@@@@@@@@----------------\n"); - } + printf("#%d\t", line); - swFixedPool_debug_slice(slice); + 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) + if (line++ > max_lines) { break; + } } } -void swFixedPool_debug_slice(swFixedPool_slice *slice) -{ - printf("Slab[%p]\t", slice); - printf("pre=%p\t", slice->pre); - printf("next=%p\t", slice->next); - printf("tag=%d\t", slice->lock); - printf("data=%p\n", slice->data); -} +} // namespace swoole diff --git a/src/memory/global_memory.cc b/src/memory/global_memory.cc index a39d50cc313..d03837ea059 100644 --- a/src/memory/global_memory.cc +++ b/src/memory/global_memory.cc @@ -10,135 +10,126 @@ | to obtain it through the world-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" -#define SW_MIN_PAGE_SIZE 4096 +#include +#include -typedef struct _swMemoryGlobal_page -{ - struct _swMemoryGlobal_page *next; - char memory[0]; -} swMemoryGlobal_page; +#define SW_MIN_PAGE_SIZE 4096 + +namespace swoole { -typedef struct _swMemoryGlobal -{ - uint8_t shared; +struct GlobalMemoryImpl { + bool shared; uint32_t pagesize; - swLock lock; - swMemoryGlobal_page *root_page; - swMemoryGlobal_page *current_page; - uint32_t current_offset; -} swMemoryGlobal; - -static void *swMemoryGlobal_alloc(swMemoryPool *pool, uint32_t size); -static void swMemoryGlobal_free(swMemoryPool *pool, void *ptr); -static void swMemoryGlobal_destroy(swMemoryPool *poll); -static swMemoryGlobal_page* swMemoryGlobal_new_page(swMemoryGlobal *gm); - -swMemoryPool* swMemoryGlobal_new(uint32_t pagesize, uint8_t shared) -{ - swMemoryGlobal gm, *gm_ptr; + std::mutex lock; + std::vector pages; + uint32_t alloc_offset; + pid_t create_pid; + + public: + GlobalMemoryImpl(uint32_t _pagesize, bool _shared); + char *new_page(); +}; + +struct MemoryBlock { + uint32_t size; + uint32_t reserved; + char memory[0]; +}; + +/** + * After the memory is allocated, + * it will not be released until it is recycled by OS when the process exits + */ +GlobalMemory::GlobalMemory(uint32_t pagesize, bool shared) { assert(pagesize >= SW_MIN_PAGE_SIZE); - bzero(&gm, sizeof(swMemoryGlobal)); + impl = new GlobalMemoryImpl(pagesize, shared); +} - gm.shared = shared; - gm.pagesize = pagesize; +GlobalMemoryImpl::GlobalMemoryImpl(uint32_t _pagesize, bool _shared) { + shared = _shared; + pagesize = SW_MEM_ALIGNED_SIZE_EX(_pagesize, swoole_pagesize()); + create_pid = SwooleG.pid; - swMemoryGlobal_page *page = swMemoryGlobal_new_page(&gm); - if (page == NULL) - { - return NULL; - } - if (swMutex_create(&gm.lock, shared) < 0) - { - return NULL; + if (new_page() == nullptr) { + throw std::bad_alloc(); } +} - gm.root_page = page; +char *GlobalMemoryImpl::new_page() { + char *page = (char *) (shared ? sw_shm_malloc(pagesize) : sw_malloc(pagesize)); + if (page == nullptr) { + return nullptr; + } - gm_ptr = (swMemoryGlobal *) page->memory; - gm.current_offset += sizeof(swMemoryGlobal); + pages.push_back(page); + alloc_offset = 0; - swMemoryPool *allocator = (swMemoryPool *) (page->memory + gm.current_offset); - gm.current_offset += sizeof(swMemoryPool); + return page; +} - allocator->object = gm_ptr; - allocator->alloc = swMemoryGlobal_alloc; - allocator->destroy = swMemoryGlobal_destroy; - allocator->free = swMemoryGlobal_free; +/** + * The returned memory must be initialized to 0 + */ +void *GlobalMemory::alloc(uint32_t size) { + MemoryBlock *block; + size = SW_MEM_ALIGNED_SIZE(size); + uint32_t alloc_size = sizeof(*block) + size; + std::unique_lock lock(impl->lock); - memcpy(gm_ptr, &gm, sizeof(gm)); - return allocator; -} + if (alloc_size > impl->pagesize) { + swoole_warning("failed to alloc %d bytes, exceed the maximum size[%d]", size, impl->pagesize); + return nullptr; + } -static swMemoryGlobal_page* swMemoryGlobal_new_page(swMemoryGlobal *gm) -{ - swMemoryGlobal_page *page = (swMemoryGlobal_page *)((gm->shared == 1) ? sw_shm_malloc(gm->pagesize) : sw_malloc(gm->pagesize)); - if (page == NULL) - { - return NULL; + if (impl->shared and impl->create_pid != getpid()) { + GlobalMemoryImpl *old_impl = impl; + impl = new GlobalMemoryImpl(old_impl->pagesize, old_impl->shared); } - bzero(page, gm->pagesize); - page->next = NULL; - if (gm->current_page != NULL) - { - gm->current_page->next = page; + 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) { + swoole_warning("alloc memory error"); + return nullptr; + } } - gm->current_page = page; - gm->current_offset = 0; + block = (MemoryBlock *) (impl->pages.back() + impl->alloc_offset); + impl->alloc_offset += alloc_size; - return page; + block->size = size; + + sw_memset_zero(block->memory, size); + return block->memory; } -static void *swMemoryGlobal_alloc(swMemoryPool *pool, uint32_t size) -{ - swMemoryGlobal *gm = (swMemoryGlobal *) pool->object; - size = SW_MEM_ALIGNED_SIZE(size); - gm->lock.lock(&gm->lock); - if (size > gm->pagesize - sizeof(swMemoryGlobal_page)) - { - swWarn("failed to alloc %d bytes, exceed the maximum size[%d]", size, gm->pagesize - (int) sizeof(swMemoryGlobal_page)); - gm->lock.unlock(&gm->lock); - return NULL; - } - if (gm->current_offset + size > gm->pagesize - sizeof(swMemoryGlobal_page)) - { - swMemoryGlobal_page *page = swMemoryGlobal_new_page(gm); - if (page == NULL) - { - swWarn("swMemoryGlobal_alloc alloc memory error"); - gm->lock.unlock(&gm->lock); - return NULL; - } - gm->current_page = page; +void GlobalMemory::free(void *ptr) {} + +void GlobalMemory::destroy() { + for (auto page : impl->pages) { + impl->shared ? ::sw_shm_free(page) : ::sw_free(page); } - void *mem = gm->current_page->memory + gm->current_offset; - gm->current_offset += size; - gm->lock.unlock(&gm->lock); - return mem; } -static void swMemoryGlobal_free(swMemoryPool *pool, void *ptr) -{ - swWarn("swMemoryGlobal Allocator don't need to release"); +size_t GlobalMemory::capacity() { + return impl->pagesize - impl->alloc_offset; } -static void swMemoryGlobal_destroy(swMemoryPool *poll) -{ - swMemoryGlobal *gm = (swMemoryGlobal *) poll->object; - swMemoryGlobal_page *page = gm->root_page; - swMemoryGlobal_page *next; - - do - { - next = page->next; - sw_shm_free(page); - page = next; - } while (page); +size_t GlobalMemory::get_memory_size() { + return impl->pagesize * impl->pages.size(); } + +GlobalMemory::~GlobalMemory() { + delete impl; +} + +} // namespace swoole diff --git a/src/memory/malloc.cc b/src/memory/malloc.cc deleted file mode 100644 index b206a51f6c1..00000000000 --- a/src/memory/malloc.cc +++ /dev/null @@ -1,50 +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 "swoole.h" - -static void* swMalloc_alloc(swMemoryPool *pool, uint32_t size); -static void swMalloc_free(swMemoryPool *pool, void *ptr); -static void swMalloc_destroy(swMemoryPool *pool); - -swMemoryPool* swMalloc_new() -{ - swMemoryPool *pool = (swMemoryPool *) sw_malloc(sizeof(swMemoryPool)); - if (pool == NULL) - { - swSysWarn("mallc(%ld) failed", sizeof(swMemoryPool)); - return NULL; - } - pool->alloc = swMalloc_alloc; - pool->free = swMalloc_free; - pool->destroy = swMalloc_destroy; - return pool; -} - -static void* swMalloc_alloc(swMemoryPool *pool, uint32_t size) -{ - return sw_malloc(size); -} - -static void swMalloc_free(swMemoryPool *pool, void *ptr) -{ - sw_free(ptr); -} - -static void swMalloc_destroy(swMemoryPool *pool) -{ - sw_free(pool); -} diff --git a/src/memory/ring_buffer.cc b/src/memory/ring_buffer.cc index a51ada4f016..b53a3cae271 100644 --- a/src/memory/ring_buffer.cc +++ b/src/memory/ring_buffer.cc @@ -10,205 +10,160 @@ | to obtain it through the world-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" -typedef struct -{ - uint8_t shared; +namespace swoole { + +struct RingBufferImpl { + void *memory; + bool shared; uint8_t status; uint32_t size; uint32_t alloc_offset; uint32_t collect_offset; uint32_t alloc_count; sw_atomic_t free_count; - void *memory; -} swRingBuffer; -typedef struct -{ + void collect(); +}; + +struct RingBufferItem { uint16_t lock; uint16_t index; uint32_t length; char data[0]; -} swRingBuffer_item; - -static void swRingBuffer_destroy(swMemoryPool *pool); -static void* swRingBuffer_alloc(swMemoryPool *pool, uint32_t size); -static void swRingBuffer_free(swMemoryPool *pool, void *ptr); +}; #ifdef SW_RINGBUFFER_DEBUG static void swRingBuffer_print(swRingBuffer *object, char *prefix); -static void swRingBuffer_print(swRingBuffer *object, char *prefix) -{ - printf("%s: size=%d, status=%d, alloc_count=%d, free_count=%d, offset=%d, next_offset=%d\n", prefix, object->size, - object->status, object->alloc_count, object->free_count, object->alloc_offset, object->collect_offset); +static void swRingBuffer_print(swRingBuffer *object, char *prefix) { + printf("%s: size=%d, status=%d, alloc_count=%d, free_count=%d, offset=%d, next_offset=%d\n", + prefix, + impl->size, + impl->status, + impl->alloc_count, + impl->free_count, + impl->alloc_offset, + impl->collect_offset); } #endif -swMemoryPool *swRingBuffer_new(uint32_t size, uint8_t shared) -{ +RingBuffer::RingBuffer(uint32_t size, bool shared) { size = SW_MEM_ALIGNED_SIZE(size); void *mem = (shared == 1) ? sw_shm_malloc(size) : sw_malloc(size); - if (mem == NULL) - { - swWarn("malloc(%d) failed", size); - return NULL; + if (mem == nullptr) { + throw std::bad_alloc(); } - swRingBuffer *object = (swRingBuffer *) mem; - mem = (char *) mem + sizeof(swRingBuffer); - bzero(object, sizeof(swRingBuffer)); - - object->size = (size - sizeof(swRingBuffer) - sizeof(swMemoryPool)); - object->shared = shared; - - swMemoryPool *pool = (swMemoryPool *) mem; - mem = (char *) mem + sizeof(swMemoryPool); - - pool->object = object; - pool->destroy = swRingBuffer_destroy; - pool->free = swRingBuffer_free; - pool->alloc = swRingBuffer_alloc; + impl = (RingBufferImpl *) mem; + mem = (char *) mem + sizeof(*impl); + sw_memset_zero(impl, sizeof(*impl)); - object->memory = mem; + impl->size = size - sizeof(impl); + impl->shared = shared; + impl->memory = mem; - swDebug("memory: ptr=%p", mem); - - return pool; + swoole_debug("memory: ptr=%p", mem); } -static void swRingBuffer_collect(swRingBuffer *object) -{ - swRingBuffer_item *item; - sw_atomic_t *free_count = &object->free_count; - - int count = object->free_count; - int i; - uint32_t n_size; - - for (i = 0; i < count; i++) - { - item = (swRingBuffer_item *) ((char *) object->memory + object->collect_offset); - if (item->lock == 0) - { - n_size = item->length + sizeof(swRingBuffer_item); - - object->collect_offset += n_size; - - if (object->collect_offset + sizeof(swRingBuffer_item) > object->size - || object->collect_offset >= object->size) - { - object->collect_offset = 0; - object->status = 0; +void RingBufferImpl::collect() { + for (uint32_t i = 0; i < free_count; i++) { + RingBufferItem *item = (RingBufferItem*) ((char*) memory + collect_offset); + if (item->lock == 0) { + uint32_t n_size = item->length + sizeof(RingBufferItem); + collect_offset += n_size; + if (collect_offset + sizeof(RingBufferItem) > size || collect_offset >= size) { + collect_offset = 0; + status = 0; } - sw_atomic_fetch_sub(free_count, 1); - } - else - { + sw_atomic_fetch_sub(&free_count, 1); + } else { break; } } } -static void* swRingBuffer_alloc(swMemoryPool *pool, uint32_t size) -{ +void *RingBuffer::alloc(uint32_t size) { assert(size > 0); - swRingBuffer *object = (swRingBuffer *) pool->object; - swRingBuffer_item *item; + RingBufferItem *item; uint32_t capacity; size = SW_MEM_ALIGNED_SIZE(size); - uint32_t alloc_size = size + sizeof(swRingBuffer_item); + uint32_t alloc_size = size + sizeof(RingBufferItem); - if (object->free_count > 0) - { - swRingBuffer_collect(object); + if (impl->free_count > 0) { + impl->collect(); } - if (object->status == 0) - { - if (object->alloc_offset + alloc_size >= (object->size - sizeof(swRingBuffer_item))) - { - uint32_t skip_n = object->size - object->alloc_offset; - if (skip_n >= sizeof(swRingBuffer_item)) - { - item = (swRingBuffer_item *) ((char *) object->memory + object->alloc_offset); + if (impl->status == 0) { + if (impl->alloc_offset + alloc_size >= (impl->size - sizeof(RingBufferItem))) { + uint32_t skip_n = impl->size - impl->alloc_offset; + if (skip_n >= sizeof(RingBufferItem)) { + item = (RingBufferItem *) ((char *) impl->memory + impl->alloc_offset); item->lock = 0; - item->length = skip_n - sizeof(swRingBuffer_item); - sw_atomic_t *free_count = &object->free_count; + item->length = skip_n - sizeof(RingBufferItem); + sw_atomic_t *free_count = &impl->free_count; sw_atomic_fetch_add(free_count, 1); } - object->alloc_offset = 0; - object->status = 1; - capacity = object->collect_offset - object->alloc_offset; + impl->alloc_offset = 0; + impl->status = 1; + capacity = impl->collect_offset - impl->alloc_offset; + } else { + capacity = impl->size - impl->alloc_offset; } - else - { - capacity = object->size - object->alloc_offset; - } - } - else - { - capacity = object->collect_offset - object->alloc_offset; + } else { + capacity = impl->collect_offset - impl->alloc_offset; } - if (capacity < alloc_size) - { - return NULL; + if (capacity < alloc_size) { + return nullptr; } - item = (swRingBuffer_item *) ((char *) object->memory + object->alloc_offset); + item = (RingBufferItem *) ((char *) impl->memory + impl->alloc_offset); item->lock = 1; item->length = size; - item->index = object->alloc_count; + item->index = impl->alloc_count; - object->alloc_offset += alloc_size; - object->alloc_count ++; + impl->alloc_offset += alloc_size; + impl->alloc_count++; - swDebug("alloc: ptr=%p", (void * )(item->data - (char* )object->memory)); + swoole_debug("alloc: ptr=%p", (void *) (item->data - (char *) impl->memory)); return item->data; } -static void swRingBuffer_free(swMemoryPool *pool, void *ptr) -{ - swRingBuffer *object = (swRingBuffer *) pool->object; - swRingBuffer_item *item = (swRingBuffer_item *) ((char *) ptr - sizeof(swRingBuffer_item)); +void RingBuffer::free(void *ptr) { + RingBufferItem *item = (RingBufferItem *) ((char *) ptr - sizeof(RingBufferItem)); - assert(ptr >= object->memory); - assert((char* )ptr <= (char * ) object->memory + object->size); + assert(ptr >= impl->memory); + assert((char *) ptr <= (char *) impl->memory + impl->size); assert(item->lock == 1); - if (item->lock != 1) - { - swDebug("invalid free: index=%d, ptr=%p", item->index, (void * )(item->data - (char* )object->memory)); - } - else - { + if (item->lock != 1) { + 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* )object->memory)); + swoole_debug("free: ptr=%p", (void *) (item->data - (char *) impl->memory)); - sw_atomic_t *free_count = &object->free_count; + sw_atomic_t *free_count = &impl->free_count; sw_atomic_fetch_add(free_count, 1); } -static void swRingBuffer_destroy(swMemoryPool *pool) -{ - swRingBuffer *object = (swRingBuffer *) pool->object; - if (object->shared) - { - sw_shm_free(object); - } - else - { - sw_free(object); +RingBuffer::~RingBuffer() { + if (impl->shared) { + sw_shm_free(impl); + } else { + sw_free(impl); } } + +} diff --git a/src/memory/shared_memory.cc b/src/memory/shared_memory.cc index c457d40866e..a93873effd6 100644 --- a/src/memory/shared_memory.cc +++ b/src/memory/shared_memory.cc @@ -10,177 +10,103 @@ | to obtain it through the world-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 +#include "swoole_file.h" +#include "swoole_memory.h" -void* sw_shm_malloc(size_t size) -{ - size = SW_MEM_ALIGNED_SIZE(size); - swShareMemory object; - void *mem; - size += sizeof(swShareMemory); - mem = swShareMemory_mmap_create(&object, size, NULL); - if (mem == NULL) - { - return NULL; - } - else - { - memcpy(mem, &object, sizeof(swShareMemory)); - return (char *) mem + sizeof(swShareMemory); - } -} +#include -void* sw_shm_calloc(size_t num, size_t _size) -{ - swShareMemory object; - void *mem; - void *ret_mem; - size_t size = sizeof(swShareMemory) + (num * _size); - size = SW_MEM_ALIGNED_SIZE(size); +#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS) +#define MAP_ANONYMOUS MAP_ANON +#endif - mem = swShareMemory_mmap_create(&object, size, NULL); - if (mem == NULL) - { - return NULL; - } - else - { - memcpy(mem, &object, sizeof(swShareMemory)); - ret_mem = (char *) mem + sizeof(swShareMemory); - bzero(ret_mem, size - sizeof(swShareMemory)); - return ret_mem; - } -} +namespace swoole { -int sw_shm_protect(void *addr, int flags) -{ - swShareMemory *object = (swShareMemory *) ((char *) addr - sizeof(swShareMemory)); - return mprotect(object, object->size, flags); -} +struct SharedMemory { + size_t size_; -void sw_shm_free(void *ptr) -{ - swShareMemory *object = (swShareMemory *) ((char *) ptr - sizeof(swShareMemory)); - swShareMemory_mmap_free(object); -} + static void *alloc(size_t size); + static void free(void *ptr); -void* sw_shm_realloc(void *ptr, size_t new_size) -{ - swShareMemory *object = (swShareMemory *) ((char *) ptr - sizeof(swShareMemory)); - void *new_ptr; - new_ptr = sw_shm_malloc(new_size); - if (new_ptr == NULL) - { - return NULL; - } - else - { - memcpy(new_ptr, ptr, object->size); - sw_shm_free(ptr); - return new_ptr; + static SharedMemory *fetch_object(void *ptr) { + return (SharedMemory *) ((char *) ptr - sizeof(SharedMemory)); } -} +}; -void *swShareMemory_mmap_create(swShareMemory *object, size_t size, char *mapfile) -{ +void *SharedMemory::alloc(size_t size) { void *mem; int tmpfd = -1; - int flag = MAP_SHARED; - bzero(object, sizeof(swShareMemory)); + int flags = MAP_SHARED; + SharedMemory object; + + size = SW_MEM_ALIGNED_SIZE(size); + size += sizeof(SharedMemory); #ifdef MAP_ANONYMOUS - flag |= MAP_ANONYMOUS; + flags |= MAP_ANONYMOUS; #else - if (mapfile == NULL) - { - mapfile = "/dev/zero"; - } - if ((tmpfd = open(mapfile, O_RDWR)) < 0) - { - return NULL; + File zerofile("/dev/zero", O_RDWR); + if (!zerofile.ready()) { + return nullptr; } - strncpy(object->mapfile, mapfile, SW_SHM_MMAP_FILE_LEN); - object->tmpfd = tmpfd; + tmpfd = zerofile.get_fd(); #endif - -#if defined(SW_USE_HUGEPAGE) && defined(MAP_HUGE_PAGE) - if (size > 2 * 1024 * 1024) - { -#if defined(MAP_HUGETLD) - flag |= MAP_HUGETLB; -#elif defined(MAP_ALIGNED_SUPER) - flag &= ~MAP_ANONYMOUS; - flag |= MAP_ALIGNED_SUPER; -#endif - } -#endif - - mem = mmap(NULL, size, PROT_READ | PROT_WRITE, flag, tmpfd, 0); + mem = mmap(nullptr, size, PROT_READ | PROT_WRITE, flags, tmpfd, 0); #ifdef MAP_FAILED if (mem == MAP_FAILED) #else if (!mem) #endif { - swSysWarn("mmap(%ld) failed", size); - return NULL; + swoole_sys_warning("mmap(%lu) failed", size); + return nullptr; + } else { + object.size_ = size; + memcpy(mem, &object, sizeof(object)); + return (char *) mem + sizeof(object); } - else - { - object->size = size; - object->mem = mem; - return mem; +} + +void SharedMemory::free(void *ptr) { + SharedMemory *object = SharedMemory::fetch_object(ptr); + size_t size = object->size_; + if (munmap(object, size) < 0) { + swoole_sys_warning("munmap(%p, %lu) failed", object, size); } } -int swShareMemory_mmap_free(swShareMemory *object) -{ - return munmap(object->mem, object->size); +} // namespace swoole + +using swoole::SharedMemory; + +void *sw_shm_malloc(size_t size) { + return SharedMemory::alloc(size); } -void *swShareMemory_sysv_create(swShareMemory *object, size_t size, int key) -{ - int shmid; - void *mem; - bzero(object, sizeof(swShareMemory)); +void *sw_shm_calloc(size_t num, size_t _size) { + return SharedMemory::alloc(num * _size); +} - if (key == 0) - { - key = IPC_PRIVATE; - } - //SHM_R | SHM_W - if ((shmid = shmget(key, size, IPC_CREAT)) < 0) - { - swSysWarn("shmget(%d, %ld) failed", key, size); - return NULL; - } - if ((mem = shmat(shmid, NULL, 0)) == (void *) -1) - { - swSysWarn("shmat() failed"); - return NULL; - } - else - { - object->key = key; - object->shmid = shmid; - object->size = size; - object->mem = mem; - return mem; - } +int sw_shm_protect(void *ptr, int flags) { + SharedMemory *object = SharedMemory::fetch_object(ptr); + return mprotect(object, object->size_, flags); } -int swShareMemory_sysv_free(swShareMemory *object, int rm) -{ - int shmid = object->shmid; - int ret = shmdt(object->mem); - if (rm == 1) - { - shmctl(shmid, IPC_RMID, NULL); - } - return ret; +void sw_shm_free(void *ptr) { + SharedMemory::free(ptr); +} + +void *sw_shm_realloc(void *ptr, size_t new_size) { + SharedMemory *object = SharedMemory::fetch_object(ptr); + void *new_ptr = sw_shm_malloc(new_size); + if (new_ptr == nullptr) { + return nullptr; + } + memcpy(new_ptr, ptr, object->size_); + SharedMemory::free(ptr); + return new_ptr; } diff --git a/src/memory/table.cc b/src/memory/table.cc index 97f6c72d599..c09dc3e8dc1 100644 --- a/src/memory/table.cc +++ b/src/memory/table.cc @@ -10,60 +10,39 @@ | to obtain it through the world-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 "table.h" +#include "swoole_table.h" -//#define SW_TABLE_DEBUG 1 -#define SW_TABLE_USE_PHP_HASH +namespace swoole { -#ifdef SW_TABLE_DEBUG -static int conflict_count = 0; -static int insert_count = 0; -static int conflict_max_level = 0; -#endif - -swTable* swTable_new(uint32_t rows_size, float conflict_proportion) -{ - if (rows_size >= 0x80000000) - { +Table *Table::make(uint32_t rows_size, float conflict_proportion) { + if (rows_size >= 0x80000000) { rows_size = 0x80000000; - } - else - { + } else { uint32_t i = 6; - while ((1U << i) < rows_size) - { + while ((1U << i) < rows_size) { i++; } rows_size = 1 << i; } - if (conflict_proportion > 1.0) - { + if (conflict_proportion > 1.0) { conflict_proportion = 1.0; - } - else if (conflict_proportion < SW_TABLE_CONFLICT_PROPORTION) - { + } else if (conflict_proportion < SW_TABLE_CONFLICT_PROPORTION) { conflict_proportion = SW_TABLE_CONFLICT_PROPORTION; } - swTable *table = (swTable *) SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(swTable)); - if (table == NULL) - { - return NULL; + auto table = (Table *) sw_mem_pool()->alloc(sizeof(Table)); + if (table == nullptr) { + return nullptr; } - if (swMutex_create(&table->lock, 1) < 0) - { - swWarn("mutex create failed"); - return NULL; - } - table->iterator = new swTable_iterator; - table->column_map = new std::unordered_map; - table->column_list = new std::vector; + table->mutex = new Mutex(Mutex::PROCESS_SHARED); + table->iterator = nullptr; + table->column_map = new std::unordered_map; + table->column_list = new std::vector; table->size = rows_size; table->mask = rows_size - 1; table->conflict_proportion = conflict_proportion; @@ -73,202 +52,236 @@ swTable* swTable_new(uint32_t rows_size, float conflict_proportion) table->hash_func = swoole_hash_austin; #endif - bzero(table->iterator, sizeof(swTable_iterator)); - table->memory = NULL; - return table; } -bool swTableColumn_add(swTable *table, const std::string &name, enum swTableColumn_type type, size_t size) -{ - if (type < SW_TABLE_INT || type > SW_TABLE_STRING) - { - swWarn("unkown column type"); +bool Table::add_column(const std::string &_name, enum TableColumn::Type _type, size_t _size) { + if (_type < TableColumn::TYPE_INT || _type > TableColumn::TYPE_STRING) { + swoole_warning("unknown column type"); return false; } - swTableColumn *col = new swTableColumn(name, type, size); - col->index = table->item_size; - table->item_size += col->size; - table->column_map->emplace(name, col); - table->column_list->push_back(col); + auto col = new TableColumn(_name, _type, _size); + col->index = item_size; + item_size += col->size; + column_map->emplace(_name, col); + column_list->push_back(col); return true; } -size_t swTable_get_memory_size(swTable *table) -{ +size_t Table::calc_memory_size() const { /** * table size + conflict size */ - size_t row_num = table->size * (1 + table->conflict_proportion); + size_t _row_num = size * (1 + conflict_proportion); /* * header + data */ - size_t row_memory_size = sizeof(swTableRow) + table->item_size; + size_t _row_memory_size = sizeof(TableRow) + item_size; /** * row data & header */ - size_t memory_size = row_num * row_memory_size; + size_t _memory_size = _row_num * _row_memory_size; /** * memory pool for conflict rows */ - memory_size += sizeof(swMemoryPool) + sizeof(swFixedPool) + ((row_num - table->size) * sizeof(swFixedPool_slice)); + _memory_size += FixedPool::sizeof_struct_impl() + ((_row_num - size) * FixedPool::sizeof_struct_slice()); /** * for iterator, Iterate through all the elements */ - memory_size += table->size * sizeof(swTableRow *); + _memory_size += size * sizeof(TableRow *); + + 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; } -int swTable_create(swTable *table) -{ - size_t memory_size = swTable_get_memory_size(table); - size_t row_memory_size = sizeof(swTableRow) + table->item_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(); +} - void *memory = sw_shm_malloc(memory_size); - if (memory == NULL) - { - return SW_ERR; +bool Table::create() { + if (created) { + return false; } - table->memory_size = memory_size; - table->memory = memory; + size_t _memory_size = calc_memory_size(); + size_t _row_memory_size = sizeof(TableRow) + item_size; + + void *_memory = sw_shm_malloc(_memory_size); + if (_memory == nullptr) { + return false; + } + memory = _memory; - table->rows = (swTableRow **) memory; - memory = (char *) memory + table->size * sizeof(swTableRow *); - memory_size -= table->size * sizeof(swTableRow *); + rows = (TableRow **) _memory; + _memory = (char *) _memory + size * sizeof(TableRow *); + _memory_size -= size * sizeof(TableRow *); - for (size_t i = 0; i < table->size; i++) - { - table->rows[i] = (swTableRow *) ((char *) memory + (row_memory_size * i)); - memset(table->rows[i], 0, sizeof(swTableRow)); + for (size_t i = 0; i < size; i++) { + rows[i] = (TableRow *) ((char *) _memory + (_row_memory_size * i)); + memset(rows[i], 0, sizeof(TableRow)); } - memory = (char *) memory + row_memory_size * table->size; - memory_size -= row_memory_size * table->size; - table->pool = swFixedPool_new2(row_memory_size, memory, memory_size); + _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 SW_OK; + return true; } -void swTable_free(swTable *table) -{ +void Table::destroy() { #ifdef SW_TABLE_DEBUG - printf("swoole_table: size=%d, conflict_count=%d, conflict_max_level=%d, insert_count=%d\n", table->size, - conflict_count, conflict_max_level, insert_count); + printf("swoole_table: size=%ld, conflict_count=%d, conflict_max_level=%d, insert_count=%d\n", + size, + conflict_count, + conflict_max_level, + insert_count); #endif - auto i = table->column_map->begin(); - while (i != table->column_map->end()) - { + auto i = column_map->begin(); + while (i != column_map->end()) { delete i->second; - table->column_map->erase(i++); + column_map->erase(i++); } - delete table->column_map; - delete table->column_list; - delete table->iterator; - if (table->memory) - { - sw_shm_free(table->memory); + delete column_map; + delete column_list; + delete iterator; + delete pool; + if (memory) { + sw_shm_free(memory); } + memory = nullptr; + delete mutex; + sw_mem_pool()->free(this); } -static sw_inline swTableRow* swTable_hash(swTable *table, const char *key, int keylen) -{ - uint64_t hashv = table->hash_func(key, keylen); - uint64_t index = hashv & table->mask; - assert(index < table->size); - return table->rows[index]; -} - -void swTable_iterator_rewind(swTable *table) -{ - bzero(table->iterator, sizeof(swTable_iterator)); -} - -static sw_inline swTableRow* swTable_iterator_get(swTable *table, uint32_t index) -{ - swTableRow *row = table->rows[index]; - return row->active ? row : NULL; -} +void TableRow::lock() { + sw_atomic_t *lock = &lock_; + uint32_t i, n; + long t = 0; -swTableRow* swTable_iterator_current(swTable *table) -{ - return table->iterator->row; + while (1) { + if (*lock == 0 && sw_atomic_cmp_set(lock, 0, 1)) { + _success: + lock_pid = SwooleG.pid; + return; + } + if (SW_CPU_NUM > 1) { + for (n = 1; n < SW_SPINLOCK_LOOP_N; n <<= 1) { + for (i = 0; i < n; i++) { + sw_atomic_cpu_pause(); + } + if (*lock == 0 && sw_atomic_cmp_set(lock, 0, 1)) { + goto _success; + } + } + } + /** + * The process occupied by the resource no longer exists, + * indicating that OOM occurred during the locking process, + * forced to unlock + */ + if (kill(lock_pid, 0) < 0 && errno == ESRCH) { + *lock = 1; + swoole_warning("lock process[%d] not exists, force unlock", lock_pid); + goto _success; + } + /** + * Mark time + */ + if (t == 0) { + t = swoole::time(true); + } + /** + * The deadlock time exceeds 2 seconds (SW_TABLE_FORCE_UNLOCK_TIME), + * indicating that the lock process has OOM, + * and the PID has been reused, forcing the unlock + */ + else if ((swoole::time(true) - t) > SW_TABLE_FORCE_UNLOCK_TIME) { + *lock = 1; + swoole_warning("timeout, force unlock"); + goto _success; + } + sw_yield(); + } } -void swTable_iterator_forward(swTable *table) -{ - for (; table->iterator->absolute_index < table->size; table->iterator->absolute_index++) - { - swTableRow *row = swTable_iterator_get(table, table->iterator->absolute_index); - if (row == NULL) - { +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 == NULL) - { - table->iterator->absolute_index++; - table->iterator->row = row; + row->lock(); + if (row->next == nullptr) { + iterator->absolute_index++; + memcpy(iterator->current_, row, iterator->row_memory_size_); + row->unlock(); + iterator->unlock(); return; - } - else - { + } else { uint32_t i = 0; - for (;; i++) - { - if (row == NULL) - { - table->iterator->collision_index = 0; + TableRow *tmp_row = row; + for (;; i++) { + if (tmp_row == nullptr) { + iterator->collision_index = 0; break; } - if (i == table->iterator->collision_index) - { - table->iterator->collision_index++; - table->iterator->row = row; + if (i == iterator->collision_index) { + iterator->collision_index++; + memcpy(iterator->current_, tmp_row, iterator->row_memory_size_); + row->unlock(); + iterator->unlock(); return; } - row = row->next; + tmp_row = tmp_row->next; } } + row->unlock(); } - table->iterator->row = NULL; + sw_memset_zero(iterator->current_, sizeof(TableRow)); + iterator->unlock(); } -swTableRow* swTableRow_get(swTable *table, const char *key, int keylen, swTableRow** rowlock) -{ - if (keylen > SW_TABLE_KEY_SIZE) - { - keylen = SW_TABLE_KEY_SIZE; - } +TableRow *Table::get(const char *key, uint16_t keylen, TableRow **rowlock) { + check_key_length(&keylen); + + TableRow *row = hash(key, keylen); - swTableRow *row = swTable_hash(table, key, keylen); *rowlock = row; - swTableRow_lock(row); - - for (;;) - { - if (sw_mem_equal(row->key, row->key_len, key, keylen)) - { - if (!row->active) - { - row = NULL; + row->lock(); + + for (;;) { + if (sw_mem_equal(row->key, row->key_len, key, keylen)) { + if (!row->active) { + row = nullptr; } break; - } - else if (row->next == NULL) - { - row = NULL; + } else if (row->next == nullptr) { + row = nullptr; break; - } - else - { + } else { row = row->next; } } @@ -276,150 +289,163 @@ swTableRow* swTableRow_get(swTable *table, const char *key, int keylen, swTableR return row; } -swTableRow* swTableRow_set(swTable *table, const char *key, int keylen, swTableRow **rowlock) -{ - if (keylen >= SW_TABLE_KEY_SIZE) - { - keylen = SW_TABLE_KEY_SIZE - 1; - } +TableRow *Table::set(const char *key, uint16_t keylen, TableRow **rowlock, int *out_flags) { + check_key_length(&keylen); - swTableRow *row = swTable_hash(table, key, keylen); + TableRow *row = hash(key, keylen); *rowlock = row; - swTableRow_lock(row); + 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)) - { + if (row->active) { + for (;;) { + if (sw_mem_equal(row->key, row->key_len, key, keylen)) { break; - } - else if (row->next == NULL) - { - table->lock.lock(&table->lock); - swTableRow *new_row = (swTableRow *) table->pool->alloc(table->pool, 0); - -#ifdef SW_TABLE_DEBUG - conflict_count ++; - if (_conflict_level > conflict_max_level) - { + } else if (row->next == nullptr) { + conflict_count++; + if (_conflict_level > conflict_max_level) { conflict_max_level = _conflict_level; } - -#endif - table->lock.unlock(&table->lock); - - if (!new_row) - { - return NULL; + TableRow *new_row = alloc_row(); + if (!new_row) { + return nullptr; } - //add row_num - bzero(new_row, sizeof(swTableRow)); - sw_atomic_fetch_add(&(table->row_num), 1); + init_row(new_row, key, keylen); + _out_flags |= SW_TABLE_FLAG_NEW_ROW; row->next = new_row; row = new_row; break; - } - else - { + } else { row = row->next; -#ifdef SW_TABLE_DEBUG + _out_flags |= SW_TABLE_FLAG_CONFLICT; _conflict_level++; -#endif } } + } else { + init_row(row, key, keylen); + _out_flags |= SW_TABLE_FLAG_NEW_ROW; } - else - { -#ifdef SW_TABLE_DEBUG - insert_count ++; -#endif - sw_atomic_fetch_add(&(table->row_num), 1); + + if (out_flags) { + *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); } - memcpy(row->key, key, keylen); - row->key[keylen] = '\0'; - row->key_len = keylen; - row->active = 1; return row; } -int swTableRow_del(swTable *table, char *key, int keylen) -{ - if (keylen > SW_TABLE_KEY_SIZE) - { - keylen = SW_TABLE_KEY_SIZE; - } +bool Table::del(const char *key, uint16_t keylen) { + check_key_length(&keylen); - swTableRow *row = swTable_hash(table, key, keylen); - //no exists - if (!row->active) - { - return SW_ERR; + TableRow *row = hash(key, keylen); + // no exists + if (!row->active) { + return false; } - swTableRow *tmp = row; - swTableRow *prev = NULL; + TableRow *tmp, *prev = nullptr; - swTableRow_lock(row); - if (row->next == NULL) - { - if (sw_mem_equal(row->key, row->key_len, key, keylen)) - { - bzero(row, sizeof(swTableRow) + table->item_size); + row->lock(); + if (row->next == nullptr) { + if (sw_mem_equal(row->key, row->key_len, key, keylen)) { + row->clear(); goto _delete_element; - } - else - { + } else { goto _not_exists; } - } - else - { - while (tmp) - { - if (sw_mem_equal(row->key, row->key_len, key, keylen)) - { + } else { + tmp = row; + while (tmp) { + if (sw_mem_equal(tmp->key, tmp->key_len, key, keylen)) { break; } prev = tmp; tmp = tmp->next; } - if (tmp == NULL) - { - _not_exists: - swTableRow_unlock(row); - return SW_ERR; + if (tmp == nullptr) { + _not_exists: + row->unlock(); + + return false; } - //when the deleting element is root, we should move the first element's data to root, - //and remove the element from the collision list. - if (tmp == row) - { + // when the deleting element is root, should move the first element's data to root, + // and remove the element from the collision list. + if (tmp == row) { tmp = tmp->next; row->next = tmp->next; - memcpy(row->key, tmp->key, strlen(tmp->key) + 1); - memcpy(row->data, tmp->data, table->item_size); - } - if (prev) - { + memcpy(row->key, tmp->key, tmp->key_len + 1); + row->key_len = tmp->key_len; + memcpy(row->data, tmp->data, item_size); + } else { prev->next = tmp->next; } - table->lock.lock(&table->lock); - bzero(tmp, sizeof(swTableRow) + table->item_size); - table->pool->free(table->pool, tmp); - table->lock.unlock(&table->lock); + free_row(tmp); + } + +_delete_element: + sw_atomic_fetch_add(&(delete_count), 1); + sw_atomic_fetch_sub(&(row_num), 1); + row->unlock(); + + return true; +} + +void TableColumn::clear(TableRow *row) { + if (type == TYPE_STRING) { + row->set_value(this, nullptr, 0); + } else if (type == TYPE_FLOAT) { + double _value = 0; + row->set_value(this, &_value, 0); + } else { + long _value = 0; + row->set_value(this, &_value, 0); + } +} + +void TableRow::set_value(TableColumn *col, void *value, size_t vlen) { + switch (col->type) { + case TableColumn::TYPE_INT: + memcpy(data + col->index, value, sizeof(long)); + break; + case TableColumn::TYPE_FLOAT: + memcpy(data + col->index, value, sizeof(double)); + break; + default: + if (vlen > (col->size - sizeof(TableStringLength))) { + swoole_warning("[key=%s,field=%s]string value is too long", key, col->name.c_str()); + vlen = col->size - sizeof(TableStringLength); + } + if (value == nullptr) { + vlen = 0; + } + memcpy(data + col->index, &vlen, sizeof(TableStringLength)); + if (vlen > 0) { + memcpy(data + col->index + sizeof(TableStringLength), value, vlen); + } + break; } +} - _delete_element: - sw_atomic_fetch_sub(&(table->row_num), 1); - swTableRow_unlock(row); +void TableRow::get_value(TableColumn *col, double *dval) { + memcpy(dval, data + col->index, sizeof(*dval)); +} - return SW_OK; +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 new file mode 100644 index 00000000000..4a97dc27793 --- /dev/null +++ b/src/network/address.cc @@ -0,0 +1,115 @@ +/* + +----------------------------------------------------------------------+ + | 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_socket.h" + +#include + +namespace swoole { +namespace network { + +static thread_local char tmp_address[INET6_ADDRSTRLEN]; + +const char *Address::get_addr() { + if (type == SW_SOCK_TCP || type == SW_SOCK_UDP) { + 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; + } + } else if (type == SW_SOCK_UNIX_STREAM || type == SW_SOCK_UNIX_DGRAM) { + return addr.un.sun_path; + } + return "unknown"; +} + +int Address::get_port() { + if (type == SW_SOCK_TCP || type == SW_SOCK_UDP) { + return ntohs(addr.inet_v4.sin_port); + } else if (type == SW_SOCK_TCP6 || type == SW_SOCK_UDP6) { + return ntohs(addr.inet_v6.sin6_port); + } else { + return 0; + } +} + +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) { + 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, &addr.inet_v4.sin_addr.s_addr)) { + return true; + } + } else if (_type == SW_SOCK_TCP6 || _type == SW_SOCK_UDP6) { + 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, addr.inet_v6.sin6_addr.s6_addr)) { + return true; + } + } else if (_type == SW_SOCK_UNIX_STREAM || _type == SW_SOCK_UNIX_DGRAM) { + addr.un.sun_family = AF_UNIX; + swoole_strlcpy(addr.un.sun_path, host, sizeof(addr.un.sun_path)); + addr.un.sun_path[sizeof(addr.un.sun_path) - 1] = 0; + len = sizeof(addr.un.sun_path); + return true; + } + + 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 56e71c5db5f..4620fb43915 100644 --- a/src/network/client.cc +++ b/src/network/client.cc @@ -10,273 +10,325 @@ | to obtain it through the world-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 "client.h" -#include "socks5.h" -#include "async.h" - -static int swClient_inet_addr(swClient *cli, const char *host, int port); -static int swClient_tcp_connect_sync(swClient *cli, const char *host, int port, double _timeout, int udp_connect); -static int swClient_tcp_connect_async(swClient *cli, const char *host, int port, double timeout, int nonblock); - -static int swClient_tcp_send_sync(swClient *cli, const char *data, int length, int flags); -static int swClient_tcp_send_async(swClient *cli, const char *data, int length, int flags); -static int swClient_udp_send(swClient *cli, const char *data, int length, int flags); - -static int swClient_tcp_sendfile_sync(swClient *cli, const char *filename, off_t offset, size_t length); -static int swClient_tcp_sendfile_async(swClient *cli, const char *filename, off_t offset, size_t length); -static int swClient_tcp_recv_no_buffer(swClient *cli, char *data, int len, int flags); -static int swClient_udp_connect(swClient *cli, const char *host, int port, double _timeout, int udp_connect); -static int swClient_udp_recv(swClient *cli, char *data, int len, int waitall); -static int swClient_close(swClient *cli); - -static int swClient_onDgramRead(swReactor *reactor, swEvent *event); -static int swClient_onStreamRead(swReactor *reactor, swEvent *event); -static int swClient_onWrite(swReactor *reactor, swEvent *event); -static int swClient_onError(swReactor *reactor, swEvent *event); -static void swClient_onTimeout(swTimer *timer, swTimer_node *tnode); -static void swClient_onResolveCompleted(swAio_event *event); -static int swClient_onPackage(swProtocol *proto, swSocket *conn, char *data, uint32_t length); - -static sw_inline void execute_onConnect(swClient *cli) -{ - if (cli->timer) - { +#include "swoole_string.h" +#include "swoole_socket.h" +#include "swoole_reactor.h" +#include "swoole_timer.h" +#include "swoole_protocol.h" +#include "swoole_client.h" +#include "swoole_proxy.h" +#include "swoole_async.h" + +#include + +namespace swoole { +namespace network { + +static int Client_inet_addr(Client *cli, const char *host, int port); +static int Client_tcp_connect_sync(Client *cli, const char *host, int port, double _timeout, int udp_connect); +static int Client_tcp_connect_async(Client *cli, const char *host, int port, double timeout, int nonblock); +static int Client_udp_connect(Client *cli, const char *host, int port, double _timeout, int udp_connect); + +static ssize_t Client_tcp_send_sync(Client *cli, const char *data, size_t length, int flags); +static ssize_t Client_tcp_send_async(Client *cli, const char *data, size_t length, int flags); +static ssize_t Client_udp_send(Client *cli, const char *data, size_t length, int flags); + +static int Client_tcp_sendfile_sync(Client *cli, const char *filename, off_t offset, size_t length); +static int Client_tcp_sendfile_async(Client *cli, const char *filename, off_t offset, size_t length); + +static ssize_t Client_tcp_recv_no_buffer(Client *cli, char *data, size_t len, int flags); +static ssize_t Client_udp_recv(Client *cli, char *data, size_t len, int waitall); + +static int Client_onDgramRead(Reactor *reactor, Event *event); +static int Client_onStreamRead(Reactor *reactor, Event *event); +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(const Protocol *proto, Socket *conn, const RecvData *rdata); + +static sw_inline void execute_onConnect(Client *cli) { + if (cli->timer) { swoole_timer_del(cli->timer); - cli->timer = NULL; + cli->timer = nullptr; } cli->onConnect(cli); } -void swClient_init_reactor(swReactor *reactor) -{ - swReactor_set_handler(reactor, SW_FD_STREAM_CLIENT | SW_EVENT_READ, swClient_onStreamRead); - swReactor_set_handler(reactor, SW_FD_DGRAM_CLIENT | SW_EVENT_READ, swClient_onDgramRead); - swReactor_set_handler(reactor, SW_FD_STREAM_CLIENT | SW_EVENT_WRITE, swClient_onWrite); - swReactor_set_handler(reactor, SW_FD_STREAM_CLIENT | SW_EVENT_ERROR, swClient_onError); +void Client::init_reactor(Reactor *reactor) { + reactor->set_handler(SW_FD_STREAM_CLIENT | SW_EVENT_READ, Client_onStreamRead); + reactor->set_handler(SW_FD_DGRAM_CLIENT | SW_EVENT_READ, Client_onDgramRead); + reactor->set_handler(SW_FD_STREAM_CLIENT | SW_EVENT_WRITE, Client_onWrite); + reactor->set_handler(SW_FD_STREAM_CLIENT | SW_EVENT_ERROR, Client_onError); } -int swClient_create(swClient *cli, enum swSocket_type type, int async) -{ - bzero(cli, sizeof(swClient)); - - int sockfd = swSocket_create(type, async, 1); - if (sockfd < 0) - { - swSysWarn("socket() failed"); - return SW_ERR; - } - - cli->reactor_fdtype = swSocket_is_stream(type) ? SW_FD_STREAM_CLIENT: SW_FD_DGRAM_CLIENT; - cli->socket = swSocket_new(sockfd, cli->reactor_fdtype); - if (!cli->socket) - { - swWarn("malloc(%d) failed", (int ) sizeof(swConnection)); - close(sockfd); - return SW_ERR; +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) { + swoole_sys_warning("socket() failed"); + return; } - cli->socket->object = cli; - cli->input_buffer_size = SW_CLIENT_BUFFER_SIZE; - cli->socket->nonblock = async ? 1 : 0; - cli->socket->cloexec = 1; - cli->socket->chunk_size = SW_SEND_BUFFER_SIZE; + socket->object = this; + input_buffer_size = SW_CLIENT_BUFFER_SIZE; + socket->chunk_size = SW_SEND_BUFFER_SIZE; - if (swSocket_is_stream(type)) - { - cli->recv = swClient_tcp_recv_no_buffer; - if (async) - { - cli->connect = swClient_tcp_connect_async; - cli->send = swClient_tcp_send_async; - cli->sendfile = swClient_tcp_sendfile_async; - cli->socket->dontwait = SwooleG.socket_dontwait; - } - else - { - cli->connect = swClient_tcp_connect_sync; - cli->send = swClient_tcp_send_sync; - cli->sendfile = swClient_tcp_sendfile_sync; - } - } - else - { - cli->connect = swClient_udp_connect; - cli->recv = swClient_udp_recv; - cli->send = swClient_udp_send; + if (socket->is_stream()) { + recv = Client_tcp_recv_no_buffer; + if (async) { + connect = Client_tcp_connect_async; + send = Client_tcp_send_async; + sendfile = Client_tcp_sendfile_async; + socket->dontwait = SwooleG.socket_dontwait; + } else { + connect = Client_tcp_connect_sync; + send = Client_tcp_send_sync; + sendfile = Client_tcp_sendfile_sync; + } + } else { + connect = Client_udp_connect; + recv = Client_udp_recv; + send = Client_udp_send; } - swSocket_get_domain_and_type(type, &cli->_sock_domain, &cli->_sock_type); - - cli->close = swClient_close; - cli->type = type; - cli->async = async; + Socket::get_domain_and_type(_type, &_sock_domain, &_sock_type); - cli->protocol.package_length_type = 'N'; - cli->protocol.package_length_size = 4; - cli->protocol.package_body_offset = 0; - cli->protocol.package_max_length = SW_INPUT_BUFFER_SIZE; - cli->protocol.onPackage = swClient_onPackage; - - return SW_OK; + protocol.package_length_type = 'N'; + protocol.package_length_size = 4; + protocol.package_body_offset = 0; + protocol.package_max_length = SW_INPUT_BUFFER_SIZE; + protocol.onPackage = Client_onPackage; } -int swClient_sleep(swClient *cli) -{ +int Client::sleep() { int ret; - if (cli->socket->events & SW_EVENT_WRITE) - { - ret = swoole_event_set(cli->socket, SW_EVENT_WRITE); + if (socket->events & SW_EVENT_WRITE) { + ret = swoole_event_set(socket, SW_EVENT_WRITE); + } else { + ret = swoole_event_del(socket); } - else - { - ret = swoole_event_del(cli->socket); - } - if (ret == SW_OK) - { - cli->sleep = 1; + if (ret == SW_OK) { + sleep_ = true; } return ret; } -int swClient_wakeup(swClient *cli) -{ +int Client::wakeup() { int ret; - if (cli->socket->events & SW_EVENT_WRITE) - { - ret = swoole_event_set(cli->socket, SW_EVENT_READ | SW_EVENT_WRITE); + if (socket->events & SW_EVENT_WRITE) { + ret = swoole_event_set(socket, SW_EVENT_READ | SW_EVENT_WRITE); + } else { + ret = swoole_event_add(socket, SW_EVENT_READ); } - else - { - ret = swoole_event_add(cli->socket, SW_EVENT_READ); - } - if (ret == SW_OK) - { - cli->sleep = 0; + if (ret == SW_OK) { + sleep_ = false; } return ret; } -int swClient_shutdown(swClient *cli, int __how) -{ - if (!cli->socket || cli->closed) - { +int Client::shutdown(int __how) { + if (!socket || closed) { return SW_ERR; } - if (__how == SHUT_RD) - { - if (cli->shutdown_read || cli->shutdow_rw || shutdown(cli->socket->fd, SHUT_RD)) - { + if (__how == SHUT_RD) { + if (shutdown_read || shutdow_rw || ::shutdown(socket->fd, SHUT_RD)) { return SW_ERR; - } - else - { - cli->shutdown_read = 1; + } else { + shutdown_read = 1; return SW_OK; } - } - else if (__how == SHUT_WR) - { - if (cli->shutdown_write || cli->shutdow_rw || shutdown(cli->socket->fd, SHUT_RD) < 0) - { + } else if (__how == SHUT_WR) { + if (shutdown_write || shutdow_rw || ::shutdown(socket->fd, SHUT_WR) < 0) { return SW_ERR; + } else { + shutdown_write = 1; + return SW_OK; } - else - { - cli->shutdown_write = 1; + } else if (__how == SHUT_RDWR) { + if (shutdow_rw || ::shutdown(socket->fd, SHUT_RDWR) < 0) { + return SW_ERR; + } else { + shutdown_read = 1; return SW_OK; } + } else { + return SW_ERR; } - else if (__how == SHUT_RDWR) - { - if (cli->shutdow_rw || shutdown(cli->socket->fd, SHUT_RDWR) < 0) - { +} + +int Client::socks5_handshake(const char *recv_data, size_t length) { + Socks5Proxy *ctx = socks5_proxy; + char *buf = ctx->buf; + uchar version, status, result, method; + + if (ctx->state == SW_SOCKS5_STATE_HANDSHAKE) { + version = recv_data[0]; + method = recv_data[1]; + if (version != SW_SOCKS5_VERSION_CODE) { + swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SOCKS5_UNSUPPORT_VERSION, "SOCKS version is not supported"); + return SW_ERR; + } + if (method != ctx->method) { + swoole_error_log( + SW_LOG_NOTICE, SW_ERROR_SOCKS5_UNSUPPORT_METHOD, "SOCKS authentication method is not supported"); + return SW_ERR; + } + // authenticate request + if (method == SW_SOCKS5_METHOD_AUTH) { + buf[0] = 0x01; + buf[1] = ctx->username.length(); + + buf += 2; + memcpy(buf, ctx->username.c_str(), ctx->username.length()); + buf += ctx->username.length(); + buf[0] = ctx->password.length(); + memcpy(buf + 1, ctx->password.c_str(), ctx->password.length()); + + ctx->state = SW_SOCKS5_STATE_AUTH; + + return send(this, ctx->buf, ctx->username.length() + ctx->password.length() + 3, 0) > 0 ? SW_OK : SW_ERR; + } + // send connect request + else { + _send_connect_request: + buf[0] = SW_SOCKS5_VERSION_CODE; + buf[1] = 0x01; + buf[2] = 0x00; + + ctx->state = SW_SOCKS5_STATE_CONNECT; + + if (ctx->dns_tunnel) { + buf[3] = 0x03; + buf[4] = ctx->target_host.length(); + buf += 5; + 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) > 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) > 0 ? SW_OK : SW_ERR; + } + } + } else if (ctx->state == SW_SOCKS5_STATE_AUTH) { + version = recv_data[0]; + status = recv_data[1]; + if (version != 0x01) { + swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SOCKS5_UNSUPPORT_VERSION, "SOCKS version is not supported"); + return SW_ERR; + } + if (status != 0) { + swoole_error_log( + SW_LOG_NOTICE, SW_ERROR_SOCKS5_AUTH_FAILED, "SOCKS username/password authentication failed"); + return SW_ERR; + } + goto _send_connect_request; + } else if (ctx->state == SW_SOCKS5_STATE_CONNECT) { + version = recv_data[0]; + if (version != SW_SOCKS5_VERSION_CODE) { + swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SOCKS5_UNSUPPORT_VERSION, "SOCKS version is not supported"); return SW_ERR; } - else - { - cli->shutdown_read = 1; + result = recv_data[1]; +#if 0 + uchar reg = recv_data[2]; + uchar type = recv_data[3]; + uint32_t ip = *(uint32_t *) (recv_data + 4); + uint16_t port = *(uint16_t *) (recv_data + 8); +#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; } } - else - { - return SW_ERR; - } + return SW_OK; } #ifdef SW_USE_OPENSSL -int swClient_enable_ssl_encrypt(swClient *cli) -{ - cli->ssl_context = swSSL_get_context(&cli->ssl_option); - if (cli->ssl_context == NULL) - { +#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; } - - if (cli->ssl_option.verify_peer) - { - if (swSSL_set_capath(&cli->ssl_option, cli->ssl_context) < 0) - { - return SW_ERR; - } + ssl_context.reset(new swoole::SSLContext()); + open_ssl = true; +#ifdef SW_SUPPORT_DTLS + if (socket->is_dgram()) { + enable_dtls(); } - - cli->socket->ssl_send = 1; -#if defined(SW_USE_HTTP2) && defined(SW_USE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10002000L - if (cli->http2) +#else { - if (SSL_CTX_set_alpn_protos(cli->ssl_context, (const unsigned char *) "\x02h2", 3) < 0) - { - return SW_ERR; - } + swoole_warning("DTLS support require openssl-1.1 or later"); + return SW_ERR; } #endif return SW_OK; } -int swClient_ssl_handshake(swClient *cli) -{ - if (!cli->socket->ssl) - { - if (swSSL_create(cli->socket, cli->ssl_context, SW_SSL_CLIENT) < 0) - { +int Client::ssl_handshake() { + if (socket->ssl_state == SW_SSL_STATE_READY) { + 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 (cli->ssl_option.tls_host_name) - { - SSL_set_tlsext_host_name(cli->socket->ssl, cli->ssl_option.tls_host_name); + if (!ssl_context->tls_host_name.empty()) { + SSL_set_tlsext_host_name(socket->ssl, ssl_context->tls_host_name.c_str()); } #endif } - if (swSSL_connect(cli->socket) < 0) - { + if (socket->ssl_connect() < 0) { return SW_ERR; } - if (cli->socket->ssl_state == SW_SSL_STATE_READY && cli->ssl_option.verify_peer) - { - if (swClient_ssl_verify(cli, cli->ssl_option.allow_self_signed) < 0) - { + if (socket->ssl_state == SW_SSL_STATE_READY && ssl_context->verify_peer) { + if (ssl_verify(ssl_context->allow_self_signed) < 0) { return SW_ERR; } } return SW_OK; } -int swClient_ssl_verify(swClient *cli, int allow_self_signed) -{ - if (swSSL_verify(cli->socket, allow_self_signed) < 0) - { +int Client::ssl_verify(int allow_self_signed) { + if (!socket->ssl_verify(allow_self_signed)) { return SW_ERR; } #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - if (cli->ssl_option.tls_host_name && swSSL_check_host(cli->socket, cli->ssl_option.tls_host_name) < 0) - { + if (!ssl_context->tls_host_name.empty() && !socket->ssl_check_host(ssl_context->tls_host_name.c_str())) { return SW_ERR; } #endif @@ -285,431 +337,336 @@ int swClient_ssl_verify(swClient *cli, int allow_self_signed) #endif -static int swClient_inet_addr(swClient *cli, const char *host, int port) -{ - //enable socks5 proxy - if (cli->socks5_proxy) - { +static int Client_inet_addr(Client *cli, const char *host, int port) { + // enable socks5 proxy + if (cli->socks5_proxy) { cli->socks5_proxy->target_host = host; - cli->socks5_proxy->l_target_host = strlen(host); cli->socks5_proxy->target_port = port; - host = cli->socks5_proxy->host; + host = cli->socks5_proxy->host.c_str(); port = cli->socks5_proxy->port; } - //enable http proxy - if (cli->http_proxy) - { + // enable http proxy + if (cli->http_proxy) { cli->http_proxy->target_host = host; cli->http_proxy->target_port = port; - host = cli->http_proxy->proxy_host; + host = cli->http_proxy->proxy_host.c_str(); port = cli->http_proxy->proxy_port; } cli->server_host = host; cli->server_port = port; - void *addr = NULL; - if (cli->type == SW_SOCK_TCP || cli->type == SW_SOCK_UDP) - { + void *addr = nullptr; + if (cli->socket->is_inet4()) { cli->server_addr.addr.inet_v4.sin_family = AF_INET; cli->server_addr.addr.inet_v4.sin_port = htons(port); cli->server_addr.len = sizeof(cli->server_addr.addr.inet_v4); addr = &cli->server_addr.addr.inet_v4.sin_addr.s_addr; - if (inet_pton(AF_INET, host, addr)) - { + if (inet_pton(AF_INET, host, addr)) { return SW_OK; } - } - else if (cli->type == SW_SOCK_TCP6 || cli->type == SW_SOCK_UDP6) - { + } else if (cli->socket->is_inet6()) { cli->server_addr.addr.inet_v6.sin6_family = AF_INET6; cli->server_addr.addr.inet_v6.sin6_port = htons(port); cli->server_addr.len = sizeof(cli->server_addr.addr.inet_v6); addr = cli->server_addr.addr.inet_v6.sin6_addr.s6_addr; - if (inet_pton(AF_INET6, host, addr)) - { + if (inet_pton(AF_INET6, host, addr)) { return SW_OK; } - } - else if (cli->type == SW_SOCK_UNIX_STREAM || cli->type == SW_SOCK_UNIX_DGRAM) - { + } else if (cli->socket->is_local()) { cli->server_addr.addr.un.sun_family = AF_UNIX; - strncpy(cli->server_addr.addr.un.sun_path, host, sizeof(cli->server_addr.addr.un.sun_path) - 1); + swoole_strlcpy(cli->server_addr.addr.un.sun_path, host, sizeof(cli->server_addr.addr.un.sun_path)); cli->server_addr.addr.un.sun_path[sizeof(cli->server_addr.addr.un.sun_path) - 1] = 0; cli->server_addr.len = sizeof(cli->server_addr.addr.un.sun_path); return SW_OK; - } - else - { + } else { return SW_ERR; } - if (!cli->async) - { - if (swoole_gethostbyname(cli->_sock_domain, host, (char*) addr) < 0) - { - SwooleG.error = SW_ERROR_DNSLOOKUP_RESOLVE_FAILED; + if (!cli->async) { + if (swoole::network::gethostbyname(cli->_sock_domain, host, (char *) addr) < 0) { + swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); return SW_ERR; } - } - else - { + } else { cli->wait_dns = 1; } return SW_OK; } -void swClient_free(swClient *cli) -{ - assert(cli->socket->fd != 0); - //remove from reactor - if (!cli->closed) - { - cli->close(cli); +void Client::destroy() { + if (destroyed) { + return; } - if (cli->socket->out_buffer) - { - swBuffer_free(cli->socket->out_buffer); - cli->socket->out_buffer = NULL; + destroyed = true; + swoole_event_defer( + [](void *data) { + Client *object = (Client *) data; + delete object; + }, + this); +} + +Client::~Client() { + if (!socket) { + return; } - if (cli->socket->in_buffer) - { - swBuffer_free(cli->socket->in_buffer); - cli->socket->in_buffer = NULL; + assert(socket->fd != 0); + // remove from reactor + if (!closed) { + close(); } - if (cli->async) - { - swSocket_free(cli->socket); + // clear buffer + if (buffer) { + delete buffer; + buffer = nullptr; } - else - { - sw_free(cli->socket); + if (server_str) { + ::sw_free((void *) server_str); + } + if (socks5_proxy) { + delete socks5_proxy; + } + if (http_proxy) { + delete http_proxy; + } + if (async) { + socket->free(); + } else { + delete socket; } } -static int swClient_close(swClient *cli) -{ - if (cli->socket == NULL || cli->closed) - { +int Client::close() { + if (socket == nullptr || closed) { return SW_ERR; } - cli->closed = 1; + closed = 1; - int fd = cli->socket->fd; + int fd = socket->fd; assert(fd != 0); #ifdef SW_USE_OPENSSL - if (cli->open_ssl && cli->ssl_context) - { - if (cli->socket->ssl) - { - swSSL_close(cli->socket); - } - swSSL_free_context(cli->ssl_context); - if (cli->ssl_option.cert_file) - { - sw_free(cli->ssl_option.cert_file); - } - if (cli->ssl_option.key_file) - { - sw_free(cli->ssl_option.key_file); - } - if (cli->ssl_option.passphrase) - { - sw_free(cli->ssl_option.passphrase); - } -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - if (cli->ssl_option.tls_host_name) - { - sw_free(cli->ssl_option.tls_host_name); - } -#endif - if (cli->ssl_option.cafile) - { - sw_free(cli->ssl_option.cafile); - } - if (cli->ssl_option.capath) - { - sw_free(cli->ssl_option.capath); + if (open_ssl && ssl_context) { + if (socket->ssl) { + socket->ssl_close(); } } #endif - //clear buffer - if (cli->buffer) - { - swString_free(cli->buffer); - cli->buffer = NULL; - } - if (cli->type == SW_SOCK_UNIX_DGRAM) - { - unlink(cli->socket->info.addr.un.sun_path); + + if (socket->socket_type == SW_SOCK_UNIX_DGRAM) { + unlink(socket->info.addr.un.sun_path); } - if (cli->async) - { - //remove from reactor - if (!cli->socket->removed) - { - swoole_event_del(cli->socket); - } - if (cli->timer) - { - swoole_timer_del(cli->timer); - cli->timer = NULL; - } - //onClose callback - if (cli->active && cli->onClose) - { - cli->active = 0; - cli->onClose(cli); + if (async) { + // remove from reactor + if (!socket->removed) { + swoole_event_del(socket); } - } - else - { - cli->active = 0; + if (timer) { + swoole_timer_del(timer); + timer = nullptr; + } + // onClose callback + if (active) { + active = 0; + onClose(this); + } + } else { + active = 0; } /** * fd marked -1, prevent double close */ - cli->socket->fd = -1; + socket->fd = -1; - return close(fd); + return ::close(fd); } -static int swClient_tcp_connect_sync(swClient *cli, const char *host, int port, double timeout, int nonblock) -{ - int ret, n; +static int Client_tcp_connect_sync(Client *cli, const char *host, int port, double timeout, int nonblock) { + int ret; cli->timeout = timeout; - if (swClient_inet_addr(cli, host, port) < 0) - { + if (Client_inet_addr(cli, host, port) < 0) { return SW_ERR; } - if (nonblock) - { - swSocket_set_nonblock(cli->socket); - } - else - { - if (cli->timeout > 0) - { - swSocket_set_timeout(cli->socket, timeout); + if (nonblock) { + cli->socket->set_nonblock(); + } else { + if (cli->timeout > 0) { + cli->socket->set_timeout(timeout); } #ifndef HAVE_KQUEUE - swSocket_set_block(cli->socket); + cli->socket->set_block(); #endif } - while (1) - { + while (1) { #ifdef HAVE_KQUEUE - if (nonblock == 2) - { + if (nonblock == 2) { // special case on MacOS - ret = connect(cli->socket->fd, (struct sockaddr *) &cli->server_addr.addr, cli->server_addr.len); - } - else - { - swSocket_set_nonblock(cli->socket); - ret = connect(cli->socket->fd, (struct sockaddr *) &cli->server_addr.addr, cli->server_addr.len); - if (ret < 0) - { - if (errno != EINPROGRESS) - { + ret = cli->socket->connect(cli->server_addr); + } else { + cli->socket->set_nonblock(); + ret = cli->socket->connect(cli->server_addr); + if (ret < 0) { + if (errno != EINPROGRESS) { return SW_ERR; } - if (swSocket_wait(cli->socket->fd, timeout > 0 ? (int) (timeout * 1000) : timeout, SW_EVENT_WRITE) < 0) - { + if (cli->socket->wait_event(timeout > 0 ? (int) (timeout * 1000) : timeout, SW_EVENT_WRITE) < 0) { + swoole_set_last_error(ETIMEDOUT); return SW_ERR; } - else - { - swSocket_set_block(cli->socket); - ret = 0; + int err; + socklen_t len = sizeof(len); + ret = cli->socket->get_option(SOL_SOCKET, SO_ERROR, &err, &len); + if (ret < 0) { + swoole_set_last_error(errno); + return SW_ERR; + } + if (err != 0) { + swoole_set_last_error(err); + return SW_ERR; } + cli->socket->set_block(); + ret = 0; } } #else - ret = connect(cli->socket->fd, (struct sockaddr *) &cli->server_addr.addr, cli->server_addr.len); + ret = cli->socket->connect(cli->server_addr); #endif - if (ret < 0) - { - if (errno == EINTR) - { + 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); } break; } - if (ret >= 0) - { + if (ret >= 0) { cli->active = 1; - //socks5 proxy - if (cli->socks5_proxy) - { + if (cli->socks5_proxy) { char buf[1024]; - swSocks5_pack(buf, cli->socks5_proxy->username == NULL ? 0x00 : 0x02); - if (cli->send(cli, buf, 3, 0) < 0) - { + Socks5Proxy::pack(buf, cli->socks5_proxy->username.empty() ? 0x00 : 0x02); + if (cli->send(cli, buf, 3, 0) < 0) { return SW_ERR; } cli->socks5_proxy->state = SW_SOCKS5_STATE_HANDSHAKE; - while (1) - { - n = cli->recv(cli, buf, sizeof(buf), 0); - if (n > 0) - { - if (swSocks5_connect(cli, buf, n) < 0) - { + while (1) { + ssize_t n = cli->recv(cli, buf, sizeof(buf), 0); + if (n > 0) { + if (cli->socks5_handshake(buf, n) < 0) { return SW_ERR; } - else - { - if (cli->socks5_proxy->state == SW_SOCKS5_STATE_READY) - { - break; - } - else - { - continue; - } + if (cli->socks5_proxy->state == SW_SOCKS5_STATE_READY) { + break; + } else { + continue; } } return SW_ERR; } - } - -#ifdef SW_USE_OPENSSL - if (cli->open_ssl) - { - if (swClient_enable_ssl_encrypt(cli) < 0) - { + } 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; } - if (swClient_ssl_handshake(cli) < 0) - { + 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 && cli->ssl_handshake() < 0) { + return SW_ERR; + } #endif } return ret; } -static int swClient_tcp_connect_async(swClient *cli, const char *host, int port, double timeout, int nonblock) -{ +static int Client_tcp_connect_async(Client *cli, const char *host, int port, double timeout, int nonblock) { int ret; cli->timeout = timeout; - if (!cli->buffer) - { - //alloc input memory buffer - cli->buffer = swString_new(cli->input_buffer_size); - if (!cli->buffer) - { - return SW_ERR; - } + if (!cli->buffer) { + 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; } - if (cli->onBufferFull && cli->buffer_high_watermark == 0) - { + if (cli->onBufferFull && cli->buffer_high_watermark == 0) { cli->buffer_high_watermark = cli->socket->buffer_size * 0.8; } - if (swClient_inet_addr(cli, host, port) < 0) - { + if (Client_inet_addr(cli, host, port) < 0) { return SW_ERR; } - if (cli->wait_dns) - { - swAio_event ev; - bzero(&ev, sizeof(swAio_event)); - - int len = strlen(cli->server_host); - if (strlen(cli->server_host) < 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; + if (cli->wait_dns) { + AsyncEvent ev{}; + 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 = swAio_handler_gethostbyname; - ev.callback = swClient_onResolveCompleted; + ev.handler = async::handler_gethostbyname; + ev.callback = Client_onResolveCompleted; - if (swAio_dispatch(&ev) < 0) - { - sw_free(ev.buf); + if (swoole::async::dispatch(&ev) == nullptr) { return SW_ERR; - } - else - { + } else { return SW_OK; } } - while (1) - { - ret = connect(cli->socket->fd, (struct sockaddr *) &cli->server_addr.addr, cli->server_addr.len); - if (ret < 0) - { - if (errno == EINTR) - { + while (1) { + ret = cli->socket->connect(cli->server_addr); + if (ret < 0) { + if (errno == EINTR) { continue; } - SwooleG.error = errno; + swoole_set_last_error(errno); } break; } - if ((ret < 0 && errno == EINPROGRESS) || ret == 0) - { - if (swoole_event_add(cli->socket, SW_EVENT_WRITE) < 0) - { + if ((ret < 0 && errno == EINPROGRESS) || ret == 0) { + if (swoole_event_add(cli->socket, SW_EVENT_WRITE) < 0) { return SW_ERR; } - if (timeout > 0) - { - cli->timer = swoole_timer_add((long) (timeout * 1000), SW_FALSE, swClient_onTimeout, cli); + if (timeout > 0) { + cli->timer = swoole_timer_add(timeout, false, Client_onTimeout, cli); } return SW_OK; - } - else - { + } else { cli->active = 0; cli->socket->removed = 1; - cli->close(cli); - if (cli->onError) - { + cli->close(); + if (cli->onError) { cli->onError(cli); } } @@ -717,55 +674,41 @@ static int swClient_tcp_connect_async(swClient *cli, const char *host, int port, return ret; } -static int swClient_tcp_send_async(swClient *cli, const char *data, int length, int flags) -{ - int n = length; - if (swoole_event_write(cli->socket, data, length) < 0) - { - if (SwooleG.error == SW_ERROR_OUTPUT_BUFFER_OVERFLOW) - { +static ssize_t Client_tcp_send_async(Client *cli, const char *data, size_t length, int flags) { + 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; cli->high_watermark = 1; - } - else - { + } else { return SW_ERR; } } - if (cli->onBufferFull && cli->socket->out_buffer && cli->high_watermark == 0 - && cli->socket->out_buffer->length >= cli->buffer_high_watermark) - { + if (cli->onBufferFull && cli->socket->out_buffer && cli->high_watermark == 0 && + cli->socket->out_buffer->length() >= cli->buffer_high_watermark) { cli->high_watermark = 1; cli->onBufferFull(cli); } return n; } -static int swClient_tcp_send_sync(swClient *cli, const char *data, int length, int flags) -{ - int written = 0; - int n; +static ssize_t Client_tcp_send_sync(Client *cli, const char *data, size_t length, int flags) { + size_t written = 0; + ssize_t n; assert(length > 0); - assert(data != NULL); + assert(data != nullptr); - while (written < length) - { - n = swSocket_send(cli->socket, data, length - written, flags); - if (n < 0) - { - if (errno == EINTR) - { + while (written < length) { + n = cli->socket->send(data, length - written, flags); + if (n < 0) { + if (errno == EINTR) { continue; - } - else if (errno == EAGAIN) - { - swSocket_wait(cli->socket->fd, 1000, SW_EVENT_WRITE); + } else if (errno == EAGAIN) { + cli->socket->wait_event(1000, SW_EVENT_WRITE); continue; - } - else - { - SwooleG.error = errno; + } else { + swoole_set_last_error(errno); return SW_ERR; } } @@ -776,33 +719,21 @@ static int swClient_tcp_send_sync(swClient *cli, const char *data, int length, i return written; } -static int swClient_tcp_sendfile_sync(swClient *cli, const char *filename, off_t offset, size_t length) -{ - if (swSocket_sendfile_sync(cli->socket->fd, filename, offset, length, cli->timeout) < 0) - { - SwooleG.error = errno; +static int Client_tcp_sendfile_sync(Client *cli, const char *filename, off_t offset, size_t length) { + if (cli->socket->sendfile_blocking(filename, offset, length, cli->timeout) < 0) { + swoole_set_last_error(errno); return SW_ERR; } return SW_OK; } -static int swClient_tcp_sendfile_async(swClient *cli, const char *filename, off_t offset, size_t length) -{ - if (swSocket_sendfile(cli->socket, filename, offset, length) < 0) - { - SwooleG.error = errno; +static int Client_tcp_sendfile_async(Client *cli, const char *filename, off_t offset, size_t length) { + if (cli->socket->sendfile(filename, offset, length) < 0) { + 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; } @@ -810,50 +741,41 @@ static int swClient_tcp_sendfile_async(swClient *cli, const char *filename, off_ /** * Only for synchronous client */ -static int swClient_tcp_recv_no_buffer(swClient *cli, char *data, int len, int flag) -{ - int ret; +static ssize_t Client_tcp_recv_no_buffer(Client *cli, char *data, size_t len, int flag) { + ssize_t ret; - while (1) - { + while (1) { #ifdef HAVE_KQUEUE int timeout_ms = (int) (cli->timeout * 1000); - if (swSocket_wait(cli->socket->fd, 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 - ret = swSocket_recv(cli->socket, data, len, flag); - if (ret >= 0) - { + ret = cli->socket->recv(data, len, flag); + if (ret >= 0) { break; } - if (errno == EINTR) - { - if (cli->interrupt_time <= 0) - { - cli->interrupt_time = swoole_microtime(); + if (errno == EINTR) { + if (cli->interrupt_time <= 0) { + cli->interrupt_time = microtime(); continue; - } - else if (swoole_microtime() > cli->interrupt_time + cli->timeout) - { + } else if (microtime() > cli->interrupt_time + cli->timeout) { break; - } - else - { + } else { continue; } } #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 && swSocket_wait(cli->socket->fd, timeout_ms, SW_EVENT_READ) == SW_OK) - { + if (cli->socket->ssl_want_read && cli->socket->wait_event(timeout_ms, SW_EVENT_READ) == SW_OK) { continue; - } - else if (cli->socket->ssl_want_write && swSocket_wait(cli->socket->fd, timeout_ms, SW_EVENT_WRITE) == SW_OK) - { + } else if (cli->socket->ssl_want_write && cli->socket->wait_event(timeout_ms, SW_EVENT_WRITE) == SW_OK) { continue; } } @@ -864,32 +786,32 @@ static int swClient_tcp_recv_no_buffer(swClient *cli, char *data, int len, int f return ret; } -static int swClient_udp_connect(swClient *cli, const char *host, int port, double timeout, int udp_connect) -{ - if (swClient_inet_addr(cli, host, port) < 0) - { +static int Client_udp_connect(Client *cli, const char *host, int port, double timeout, int udp_connect) { + if (Client_inet_addr(cli, host, port) < 0) { + 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 = SwooleG.socket_buffer_size; + int bufsize = Socket::default_buffer_size; - if (timeout > 0) - { - swSocket_set_timeout(cli->socket, timeout); + if (timeout > 0) { + cli->socket->set_timeout(timeout); } - if (cli->type == SW_SOCK_UNIX_DGRAM) - { - struct sockaddr_un* client_addr = &cli->socket->info.addr.un; + if (cli->socket->socket_type == SW_SOCK_UNIX_DGRAM) { + struct sockaddr_un *client_addr = &cli->socket->info.addr.un; sprintf(client_addr->sun_path, "/tmp/swoole-client.%d.%d.sock", getpid(), cli->socket->fd); client_addr->sun_family = AF_UNIX; 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); + if (bind(cli->socket->fd, (struct sockaddr *) client_addr, sizeof(cli->socket->info.addr.un)) < 0) { + swoole_sys_warning("bind(%s) failed", client_addr->sun_path); return SW_ERR; } } @@ -899,348 +821,182 @@ static int swClient_udp_connect(swClient *cli, const char *host, int port, doubl #ifdef SW_SUPPORT_DTLS { udp_connect = 1; - cli->ssl_option.dtls = 1; - cli->ssl_option.method = SW_DTLS_CLIENT_METHOD; - cli->socket->dtls = 1; - cli->socket->chunk_size = SW_BUFFER_SIZE_STD; - cli->send = swClient_tcp_send_sync; - cli->recv = swClient_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 #endif - if (udp_connect != 1) - { + if (udp_connect != 1) { goto _connect_ok; } - if (connect(cli->socket->fd, (struct sockaddr *) (&cli->server_addr), cli->server_addr.len) == 0) - { - swSocket_clean(cli->socket->fd); - _connect_ok: + if (cli->socket->connect(cli->server_addr) == 0) { + cli->socket->clean(); + _connect_ok: - setsockopt(cli->socket->fd, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize)); - setsockopt(cli->socket->fd, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)); + cli->socket->set_option(SOL_SOCKET, SO_SNDBUF, bufsize); + cli->socket->set_option(SOL_SOCKET, SO_RCVBUF, bufsize); - if (cli->async && cli->onConnect) - { - if (swoole_event_add(cli->socket, SW_EVENT_READ) < 0) - { + if (cli->async && cli->onConnect) { + if (swoole_event_add(cli->socket, SW_EVENT_READ) < 0) { return SW_ERR; } execute_onConnect(cli); } #ifdef SW_USE_OPENSSL - if (cli->open_ssl) - { - if (swClient_enable_ssl_encrypt(cli) < 0) - { - return SW_ERR; - } - if (swClient_ssl_handshake(cli) < 0) - { - return SW_ERR; - } + if (cli->open_ssl && cli->ssl_handshake() < 0) { + return SW_ERR; } #endif return SW_OK; - } - else - { + } else { cli->active = 0; cli->socket->removed = 1; - cli->close(cli); - if (cli->async && cli->onError) - { + cli->close(); + if (cli->async && cli->onError) { cli->onError(cli); } return SW_ERR; } } -static int swClient_udp_send(swClient *cli, const char *data, int len, int flags) -{ - int n = sendto(cli->socket->fd, data, len, 0, (struct sockaddr *) &cli->server_addr.addr, cli->server_addr.len); - if (n < 0 || n < len) - { +static ssize_t Client_udp_send(Client *cli, const char *data, size_t len, int flags) { + ssize_t n = sendto(cli->socket->fd, data, len, 0, (struct sockaddr *) &cli->server_addr.addr, cli->server_addr.len); + if (n < 0 || n < (ssize_t) len) { return SW_ERR; - } - else - { + } else { return n; } } -static int swClient_udp_recv(swClient *cli, char *data, int length, int flags) -{ +static ssize_t Client_udp_recv(Client *cli, char *data, size_t length, int flags) { #ifdef HAVE_KQUEUE - if (!cli->async) - { + if (!cli->async) { int timeout_ms = (int) (cli->timeout * 1000); - if (swSocket_wait(cli->socket->fd, timeout_ms, SW_EVENT_READ) < 0) - { + if (cli->socket->wait_event(timeout_ms, SW_EVENT_READ) < 0) { return -1; } } #endif - cli->remote_addr.len = sizeof(cli->remote_addr.addr); - int ret = recvfrom(cli->socket->fd, data, length, flags, (struct sockaddr *) &cli->remote_addr.addr, &cli->remote_addr.len); - if (ret < 0) - { - if (errno == EINTR) - { - ret = recvfrom(cli->socket->fd, data, length, flags, (struct sockaddr *) &cli->remote_addr, &cli->remote_addr.len); - } - else - { + ssize_t ret = cli->socket->recvfrom(data, length, flags, &cli->remote_addr); + if (ret < 0) { + if (errno == EINTR) { + ret = cli->socket->recvfrom(data, length, flags, &cli->remote_addr); + } else { return SW_ERR; } } return ret; } -#ifdef SW_USE_OPENSSL -static int swClient_https_proxy_handshake(swClient *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 swClient_onPackage(swProtocol *proto, swSocket *conn, char *data, uint32_t length) -{ - swClient *cli = (swClient *) conn->object; - cli->onReceive(cli, data, length); +static int Client_onPackage(const Protocol *proto, Socket *conn, const RecvData *rdata) { + Client *cli = (Client *) conn->object; + cli->onReceive(cli, rdata->data, rdata->info.len); return conn->close_wait ? SW_ERR : SW_OK; } -static int swClient_onStreamRead(swReactor *reactor, swEvent *event) -{ +static int Client_onStreamRead(Reactor *reactor, Event *event) { ssize_t n = -1; - swClient *cli = (swClient *) event->socket->object; + Client *cli = (Client *) event->socket->object; char *buf = cli->buffer->str + cli->buffer->length; - long buf_size = cli->buffer->size - cli->buffer->length; - - if (cli->http_proxy && cli->http_proxy->state != SW_HTTP_PROXY_STATE_READY) - { + ssize_t buf_size = cli->buffer->size - cli->buffer->length; #ifdef SW_USE_OPENSSL - if (cli->open_ssl) - { - n = swSocket_recv(event->socket, 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 (swClient_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; - swString_clear(cli->buffer); - } - if (swClient_enable_ssl_encrypt(cli) < 0) - { - goto _connect_fail; - } - else - { - if (swClient_ssl_handshake(cli) < 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); + 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) { + 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 = swSocket_recv(event->socket, buf, buf_size, 0); - if (n <= 0) - { - goto __close; + + if (cli->socks5_proxy && cli->socks5_proxy->state != SW_SOCKS5_STATE_READY) { + n = event->socket->recv(buf, buf_size, 0); + if (n <= 0) { + goto _connect_fail; } - if (swSocks5_connect(cli, buf, buf_size) < 0) - { - goto __close; + cli->buffer->length += n; + if (cli->socks5_handshake(buf, buf_size) < 0) { + goto _connect_fail; } - if (cli->socks5_proxy->state != SW_SOCKS5_STATE_READY) - { + if (cli->socks5_proxy->state != SW_SOCKS5_STATE_READY) { return SW_OK; } -#ifdef SW_USE_OPENSSL - if (cli->open_ssl) - { - if (swClient_enable_ssl_encrypt(cli) < 0) - { - _connect_fail: - cli->active = 0; - cli->close(cli); - if (cli->onError) - { - cli->onError(cli); - } - } - else - { - if (swClient_ssl_handshake(cli) < 0) - { - goto _connect_fail; - } - 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 (swClient_ssl_handshake(cli) < 0) - { + 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) - { + 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 - if (cli->open_eof_check || cli->open_length_check) - { - swSocket *conn = cli->socket; - swProtocol *protocol = &cli->protocol; + if (cli->open_eof_check || cli->open_length_check) { + Socket *conn = cli->socket; + Protocol *protocol = &cli->protocol; - if (cli->open_eof_check) - { - n = swProtocol_recv_check_eof(protocol, conn, cli->buffer); - } - else - { - n = swProtocol_recv_check_length(protocol, conn, cli->buffer); + if (cli->open_eof_check) { + n = protocol->recv_with_eof_protocol(conn, cli->buffer); + } else { + n = protocol->recv_with_length_protocol(conn, cli->buffer); } - if (n < 0) - { - return cli->close(cli); - } - else - { - if (conn->removed == 0 && cli->remove_delay) - { - swClient_sleep(cli); + if (n < 0) { + if (!cli->closed) { + cli->close(); + } + return SW_OK; + } else { + if (conn->removed == 0 && cli->remove_delay) { + cli->sleep(); cli->remove_delay = 0; } return SW_OK; } } -#ifdef SW_CLIENT_RECV_AGAIN - _recv_again: -#endif - n = swSocket_recv(event->socket, buf, buf_size, 0); - if (n < 0) - { - switch (swSocket_error(errno)) - { + n = event->socket->recv(buf, buf_size, 0); + if (n < 0) { + 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; @@ -1249,223 +1005,160 @@ static int swClient_onStreamRead(swReactor *reactor, swEvent *event) default: return SW_OK; } - } - else if (n == 0) - { - __close: - return cli->close(cli); - } - else - { + } else if (n == 0) { + __close: + 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; } -static int swClient_onDgramRead(swReactor *reactor, swEvent *event) -{ - swClient *cli = (swClient *) event->socket->object; +static int Client_onDgramRead(Reactor *reactor, Event *event) { + Client *cli = (Client *) event->socket->object; char buffer[SW_BUFFER_SIZE_UDP]; - int n = swClient_udp_recv(cli, buffer, sizeof(buffer), 0); - if (n < 0) - { + int n = Client_udp_recv(cli, buffer, sizeof(buffer), 0); + if (n < 0) { return SW_ERR; - } - else - { + } else { cli->onReceive(cli, buffer, n); } return SW_OK; } -static int swClient_onError(swReactor *reactor, swEvent *event) -{ - swClient *cli = (swClient *) event->socket->object; - if (cli->active) - { - return cli->close(cli); - } - else - { - swClient_onWrite(reactor, event); +static int Client_onError(Reactor *reactor, Event *event) { + Client *cli = (Client *) event->socket->object; + if (cli->active) { + return cli->close(); + } else { + Client_onWrite(reactor, event); } return SW_OK; } -static void swClient_onTimeout(swTimer *timer, swTimer_node *tnode) -{ - swClient *cli = (swClient *) tnode->data; - SwooleG.error = ETIMEDOUT; +static void Client_onTimeout(Timer *timer, TimerNode *tnode) { + Client *cli = (Client *) tnode->data; + swoole_set_last_error(ETIMEDOUT); #ifdef SW_USE_OPENSSL - if (cli->open_ssl && cli->socket->ssl_state != SW_SSL_STATE_READY) - { + if (cli->open_ssl && cli->socket->ssl_state != SW_SSL_STATE_READY) { cli->active = 0; } #endif - if (cli->socks5_proxy && cli->socks5_proxy->state != SW_SOCKS5_STATE_READY) - { + if (cli->socks5_proxy && cli->socks5_proxy->state != SW_SOCKS5_STATE_READY) { cli->active = 0; - } - else if (cli->http_proxy && cli->http_proxy->state != SW_HTTP_PROXY_STATE_READY) - { + } else if (cli->http_proxy && cli->http_proxy->state != SW_HTTP_PROXY_STATE_READY) { cli->active = 0; } - cli->close(cli); - if (cli->onError) - { + cli->close(); + if (cli->onError) { cli->onError(cli); } } -static void swClient_onResolveCompleted(swAio_event *event) -{ - if (event->canceled) - { - sw_free(event->buf); - return; - } +static void Client_onResolveCompleted(AsyncEvent *event) { + GethostbynameRequest *req = dynamic_cast(event->data.get()); - swClient *cli = (swClient *) event->object; + Client *cli = (Client *) event->object; cli->wait_dns = 0; - if (event->error == 0) - { - swClient_tcp_connect_async(cli, (char*) event->buf, cli->server_port, cli->timeout, 1); - } - else - { - SwooleG.error = SW_ERROR_DNSLOOKUP_RESOLVE_FAILED; + if (event->error == 0) { + 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; - cli->close(cli); - if (cli->onError) - { + cli->close(); + if (cli->onError) { cli->onError(cli); } } - sw_free(event->buf); } -static int swClient_onWrite(swReactor *reactor, swEvent *event) -{ - swClient *cli = (swClient *) event->socket->object; - swSocket *_socket = cli->socket; - socklen_t len = sizeof(SwooleG.error); +static int Client_onWrite(Reactor *reactor, Event *event) { + Client *cli = (Client *) event->socket->object; + Socket *_socket = cli->socket; + int ret; + int err; - if (cli->active) - { + if (cli->active) { #ifdef SW_USE_OPENSSL - if (cli->open_ssl && _socket->ssl_state == SW_SSL_STATE_WAIT_STREAM) - { - if (swClient_ssl_handshake(cli) < 0) - { + if (cli->open_ssl && _socket->ssl_state == SW_SSL_STATE_WAIT_STREAM) { + if (cli->ssl_handshake() < 0) { goto _connect_fail; - } - else if (_socket->ssl_state == SW_SSL_STATE_READY) - { + } else if (_socket->ssl_state == SW_SSL_STATE_READY) { goto _connect_success; - } - else - { - if (_socket->ssl_want_read) - { + } else { + if (_socket->ssl_want_read) { swoole_event_set(event->socket, SW_EVENT_READ); } return SW_OK; } } #endif - if (swReactor_onWrite(reactor, event) < 0) - { + if (Reactor::_writable_callback(reactor, event) < 0) { return SW_ERR; } - if (cli->onBufferEmpty && cli->high_watermark && _socket->out_buffer->length <= cli->buffer_low_watermark) - { + if (cli->onBufferEmpty && cli->high_watermark && _socket->out_buffer->length() <= cli->buffer_low_watermark) { cli->high_watermark = 0; cli->onBufferEmpty(cli); } return SW_OK; } - if (getsockopt(event->fd, SOL_SOCKET, SO_ERROR, &SwooleG.error, &len) < 0) - { - swSysWarn("getsockopt(%d) failed", event->fd); + ret = _socket->get_option(SOL_SOCKET, SO_ERROR, &err); + swoole_set_last_error(err); + if (ret < 0) { + swoole_sys_warning("getsockopt(%d) failed", event->fd); return SW_ERR; } - //success - if (SwooleG.error == 0) - { - //listen read event + // success + if (swoole_get_last_error() == 0) { + // listen read event swoole_event_set(event->socket, SW_EVENT_READ); - //connected + // connected cli->active = 1; - //socks5 proxy - if (cli->socks5_proxy && cli->socks5_proxy->state == SW_SOCKS5_STATE_WAIT) - { + // socks5 proxy + if (cli->socks5_proxy && cli->socks5_proxy->state == SW_SOCKS5_STATE_WAIT) { char buf[3]; - swSocks5_pack(buf, cli->socks5_proxy->username == NULL ? 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, cli->http_proxy->target_port); - return cli->send(cli, cli->http_proxy->buf, n, 0); - } -#endif + // http proxy + if (cli->http_proxy && cli->http_proxy->state == SW_HTTP_PROXY_STATE_WAIT) { + 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) - { - if (swClient_enable_ssl_encrypt(cli) < 0) - { - goto _connect_fail; - } - if (swClient_ssl_handshake(cli) < 0) - { + if (cli->open_ssl) { + if (cli->ssl_handshake() < 0) { goto _connect_fail; - } - else - { + } else { _socket->ssl_state = SW_SSL_STATE_WAIT_STREAM; } return SW_OK; } - _connect_success: + _connect_success: #endif - if (cli->onConnect) - { - execute_onConnect(cli); - } - } - else - { + execute_onConnect(cli); + } else { #ifdef SW_USE_OPENSSL - _connect_fail: + _connect_fail: #endif cli->active = 0; - cli->close(cli); - if (cli->onError) - { - cli->onError(cli); - } + cli->close(); + cli->onError(cli); } return SW_OK; } +} // namespace network +} // namespace swoole diff --git a/src/network/dns.cc b/src/network/dns.cc index 34ac1e8acc6..22ac3ab9dd8 100644 --- a/src/network/dns.cc +++ b/src/network/dns.cc @@ -10,129 +10,254 @@ | to obtain it through the world-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 "coroutine_socket.h" +#include "swoole_coroutine_socket.h" +#include "swoole_coroutine_system.h" +#include "swoole_util.h" + #include +#include #include +#include +#include +#include + +#define SW_PATH_HOSTS "/etc/hosts" + +#ifdef SW_USE_CARES +#include +#endif -#define SW_DNS_SERVER_CONF "/etc/resolv.conf" -#define SW_DNS_SERVER_NUM 2 +using swoole::NameResolver; +using swoole::coroutine::System; + +SW_API bool swoole_load_resolv_conf() { + FILE *fp; + char line[100]; + char buf[16] = {}; -using namespace swoole::coroutine; -using namespace std; + 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; + } -enum swDNS_type -{ - 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 + 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 - SW_DNS_TIMEOUT, //Lookup time expired - SW_DNS_ERROR //No memory or other error +enum DNSError { + SW_DNS_NOT_EXIST, // Error: address does not exist + SW_DNS_TIMEOUT, // Lookup time expired + SW_DNS_ERROR // No memory or other error }; /* Struct for the DNS Header */ -typedef struct -{ +struct RecordHeader { uint16_t id; - uchar rd :1; - uchar tc :1; - uchar aa :1; - uchar opcode :4; - uchar qr :1; - uchar rcode :4; - uchar z :3; - uchar ra :1; + uchar rd : 1; + uchar tc : 1; + uchar aa : 1; + uchar opcode : 4; + uchar qr : 1; + uchar rcode : 4; + uchar z : 3; + uchar ra : 1; uint16_t qdcount; 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 std::string parse_ip_address(void *vaddr, int type); -static int get_dns_server() -{ - FILE *fp; - char line[100]; - char buf[16] = {}; - - if ((fp = fopen(SW_DNS_SERVER_CONF, "rt")) == NULL) - { - 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(NULL, "\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'; } - } - fclose(fp); - if (strlen(buf) == 0) - { - SwooleG.dns_server_v4 = sw_strdup(SW_DNS_DEFAULT_SERVER); - } - else - { - SwooleG.dns_server_v4 = sw_strdup(buf); + if (line[0] == '\n' || line[0] == '\0' || line[0] == '\r') { + continue; + } + + std::istringstream stream(line); + while (stream >> domain) { + domains.push_back(domain); + } + if (domains.empty() || domains.size() == 1) { + domains.clear(); + continue; + } + + txtaddr = domains[0]; + for (size_t i = 1; i < domains.size(); i++) { + result.insert(std::make_pair(domains[i], txtaddr)); + } + + auto iter = result.find(search_domain); + if (iter != result.end()) { + return iter->second; + } else { + result.clear(); + domains.clear(); + continue; + } } - return SW_OK; + 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 { + assert(0); + } + return ip_addr; } -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 = NULL; + Q_FLAGS *qflags = nullptr; char packet[SW_BUFFER_SIZE_STD]; - swDNSResolver_header *header = NULL; + RecordHeader *header = nullptr; int steps = 0; + std::vector result; - vector result; - if (SwooleG.dns_server_v4 == NULL) - { - 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; @@ -147,53 +272,42 @@ vector swoole::coroutine::dns_lookup(const char *domain, double timeout) 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); + if (domain_encode(domain, len, _domain_name) < 0) { + 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); Socket _sock(SW_SOCK_UDP); - if (timeout > 0) - { + 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; } /** * response */ - header = NULL; - qflags = NULL; - RR_FLAGS *rrflags = NULL; + header = nullptr; + qflags = nullptr; + RR_FLAGS *rrflags = nullptr; uchar rdata[10][254]; uint32_t type[10]; + sw_memset_zero(rdata, sizeof(rdata)); char *temp; steps = 0; @@ -201,15 +315,15 @@ vector swoole::coroutine::dns_lookup(const char *domain, double timeout) char name[10][254]; int i, j; - int ret = _sock.recv(packet, sizeof(packet) - 1); - if (ret <= 0) - { + 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); @@ -220,26 +334,20 @@ vector swoole::coroutine::dns_lookup(const char *domain, double timeout) steps = steps + sizeof(Q_FLAGS); int ancount = ntohs(header->ancount); - if (ancount > 10) - { + if (ancount > 10) { ancount = 10; } /* Parsing the RRs from the reply packet */ - for (i = 0; i < ancount; ++i) - { + for (i = 0; i < ancount; ++i) { type[i] = 0; /* Parsing the NAME portion of the RR */ temp = &packet[steps]; j = 0; - while (*temp != 0) - { - if ((uchar) (*temp) == 0xc0) - { + while (*temp != 0) { + if ((uchar) (*temp) == 0xc0) { ++temp; temp = &packet[(uint8_t) *temp]; - } - else - { + } else { name[i][j] = *temp; ++j; ++temp; @@ -255,29 +363,20 @@ vector swoole::coroutine::dns_lookup(const char *domain, double timeout) 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 */ - if (ntohs(rrflags->type) == 5) - { + if (ntohs(rrflags->type) == 5) { temp = &packet[steps]; j = 0; - while (*temp != 0) - { - if ((uchar) (*temp) == 0xc0) - { + while (*temp != 0) { + if ((uchar) (*temp) == 0xc0) { ++temp; temp = &packet[(uint8_t) *temp]; - } - else - { + } else { rdata[i][j] = *temp; ++j; ++temp; @@ -291,21 +390,19 @@ vector swoole::coroutine::dns_lookup(const char *domain, double timeout) } int request_id = ntohs(header->id); - //bad response - if (request_id != _request_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) - { + for (i = 0; i < ancount; i++) { + 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(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; } @@ -314,10 +411,8 @@ vector swoole::coroutine::dns_lookup(const char *domain, double timeout) * The function converts the dot-based hostname into the DNS format * (i.e. www.apple.com into 3www5apple3com0) */ -static int domain_encode(const char *src, int n, char *dest) -{ - if (src[n] == '.') - { +static int domain_encode(const char *src, int n, char *dest) { + if (src[n] == '.') { return SW_ERR; } @@ -330,10 +425,8 @@ static int domain_encode(const char *src, int n, char *dest) src = dest + 1; n++; - for (i = 0; i < n; i++) - { - if (src[i] == '.') - { + for (i = 0; i < n; i++) { + if (src[i] == '.') { len = i - pos; dest[pos] = len; pos += len + 1; @@ -347,14 +440,11 @@ static int domain_encode(const char *src, int n, char *dest) * This function converts a DNS-based hostname into dot-based format * (i.e. 3www5apple3com0 into www.apple.com) */ -static void domain_decode(char *str) -{ +static void domain_decode(char *str) { size_t i, j; - for (i = 0; i < strlen((const char*) str); i++) - { - unsigned int len = str[i]; - for (j = 0; j < len; j++) - { + for (i = 0; i < strlen(str); i++) { + uint32_t len = str[i]; + for (j = 0; j < len; j++) { str[i] = str[i + 1]; i++; } @@ -362,3 +452,374 @@ static void domain_decode(char *str) } str[i - 1] = '\0'; } + +#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 + +#ifdef HAVE_GETHOSTBYNAME2_R +int gethostbyname(int flags, const char *name, char *addr) { + int __af = flags & (~SW_DNS_LOOKUP_RANDOM); + int index = 0; + int rc, err; + int buf_len = 256; + struct hostent hbuf; + struct hostent *result; + + char *buf = (char *) sw_malloc(buf_len); + if (!buf) { + return SW_ERR; + } + memset(buf, 0, buf_len); + while ((rc = ::gethostbyname2_r(name, __af, &hbuf, buf, buf_len, &result, &err)) == ERANGE) { + buf_len *= 2; + char *tmp = (char *) sw_realloc(buf, buf_len); + if (nullptr == tmp) { + sw_free(buf); + return SW_ERR; + } else { + buf = tmp; + } + } + + if (0 != rc || nullptr == result) { + sw_free(buf); + return SW_ERR; + } + + union { + char v4[INET_ADDRSTRLEN]; + char v6[INET6_ADDRSTRLEN]; + } addr_list[SW_DNS_HOST_BUFFER_SIZE]{}; + + int i = 0; + for (i = 0; i < SW_DNS_HOST_BUFFER_SIZE; i++) { + if (hbuf.h_addr_list[i] == nullptr) { + break; + } + if (__af == AF_INET) { + memcpy(addr_list[i].v4, hbuf.h_addr_list[i], hbuf.h_length); + } else { + memcpy(addr_list[i].v6, hbuf.h_addr_list[i], hbuf.h_length); + } + } + if (__af == AF_INET) { + memcpy(addr, addr_list[index].v4, hbuf.h_length); + } else { + memcpy(addr, addr_list[index].v6, hbuf.h_length); + } + + sw_free(buf); + + return SW_OK; +} +#else +int gethostbyname(int flags, const char *name, char *addr) { + int __af = flags & (~SW_DNS_LOOKUP_RANDOM); + int index = 0; + + std::lock_guard _lock(g_gethostbyname2_lock); + + struct hostent *host_entry; + if (!(host_entry = ::gethostbyname2(name, __af))) { + return SW_ERR; + } + + union { + char v4[INET_ADDRSTRLEN]; + char v6[INET6_ADDRSTRLEN]; + } addr_list[SW_DNS_HOST_BUFFER_SIZE]; + + int i = 0; + for (i = 0; i < SW_DNS_HOST_BUFFER_SIZE; i++) { + if (host_entry->h_addr_list[i] == nullptr) { + break; + } + if (__af == AF_INET) { + memcpy(addr_list[i].v4, host_entry->h_addr_list[i], host_entry->h_length); + } else { + memcpy(addr_list[i].v6, host_entry->h_addr_list[i], host_entry->h_length); + } + } + if (__af == AF_INET) { + memcpy(addr, addr_list[index].v4, host_entry->h_length); + } else { + memcpy(addr, addr_list[index].v6, host_entry->h_length); + } + return SW_OK; +} +#endif + +int getaddrinfo(GetaddrinfoRequest *req) { + struct addrinfo *result = nullptr; + struct addrinfo *ptr = nullptr; + struct addrinfo hints {}; + + hints.ai_family = req->family; + hints.ai_socktype = req->socktype; + hints.ai_protocol = req->protocol; + + int ret = ::getaddrinfo(req->hostname.c_str(), req->service.c_str(), &hints, &result); + if (ret != 0) { + req->error = ret; + return SW_ERR; + } + + int i = 0; + 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(&req->results[i], ptr->ai_addr, sizeof(struct sockaddr_in)); + break; + case AF_INET6: + memcpy(&req->results[i], ptr->ai_addr, sizeof(struct sockaddr_in6)); + break; + default: + swoole_warning("unknown socket family[%d]", ptr->ai_family); + break; + } + if (i == SW_DNS_HOST_BUFFER_SIZE) { + break; + } + } + ::freeaddrinfo(result); + req->error = 0; + + return SW_OK; +} +} // namespace network + +void GetaddrinfoRequest::parse_result(std::vector &retval) { + struct sockaddr_in *addr_v4; + struct sockaddr_in6 *addr_v6; + + char tmp[INET6_ADDRSTRLEN]; + const char *r; + + for (auto &addr : results) { + if (family == AF_INET) { + 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 *) &addr; + r = inet_ntop(AF_INET6, (const void *) &addr_v6->sin6_addr, tmp, sizeof(tmp)); + } + if (r) { + retval.push_back(tmp); + } + } +} +} // namespace swoole diff --git a/src/network/process_pool.cc b/src/network/process_pool.cc deleted file mode 100644 index dde93b05a8b..00000000000 --- a/src/network/process_pool.cc +++ /dev/null @@ -1,900 +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 "server.h" -#include "client.h" - -/** - * call onTask - */ -static int swProcessPool_worker_loop(swProcessPool *pool, swWorker *worker); -/** - * call onMessage - */ -static int swProcessPool_worker_loop_ex(swProcessPool *pool, swWorker *worker); - -static void swProcessPool_free(swProcessPool *pool); - -static void swProcessPool_kill_timeout_worker(swTimer *timer, swTimer_node *tnode) -{ - uint32_t i; - pid_t reload_worker_pid = 0; - swProcessPool *pool = (swProcessPool *)tnode->data; - pool->reloading = 0; - - for (i = 0; i < pool->worker_num; i++) - { - if (i >= pool->reload_worker_i) - { - reload_worker_pid = pool->reload_workers[i].pid; - if (swoole_kill(reload_worker_pid, 0) == -1) - { - continue; - } - if (swoole_kill(reload_worker_pid, SIGKILL) < 0) - { - swSysWarn("swKill(%d, SIGKILL) [%d] failed", pool->reload_workers[i].pid, i); - } - else - { - swWarn("swKill(%d, SIGKILL) [%d]", pool->reload_workers[i].pid, i); - } - } - } - errno = 0; - pool->reload_worker_i = 0; - pool->reload_init = 0; -} -/** - * Process manager - */ -int swProcessPool_create(swProcessPool *pool, uint32_t worker_num, key_t msgqueue_key, int ipc_mode) -{ - bzero(pool, sizeof(swProcessPool)); - - uint32_t i; - - pool->worker_num = worker_num; - - /** - * Shared memory is used here - */ - pool->workers = (swWorker *) SwooleG.memory_pool->alloc(SwooleG.memory_pool, worker_num * sizeof(swWorker)); - if (pool->workers == nullptr) - { - swSysWarn("malloc[1] failed"); - return SW_ERR; - } - - if (ipc_mode == SW_IPC_MSGQUEUE) - { - pool->use_msgqueue = 1; - pool->msgqueue_key = msgqueue_key; - - pool->queue = (swMsgQueue *) sw_malloc(sizeof(swMsgQueue)); - if (pool->queue == nullptr) - { - swSysWarn("malloc[2] failed"); - return SW_ERR; - } - - if (swMsgQueue_create(pool->queue, 1, pool->msgqueue_key, 0) < 0) - { - return SW_ERR; - } - } - else if (ipc_mode == SW_IPC_UNIXSOCK) - { - pool->pipes = (swPipe *) sw_calloc(worker_num, sizeof(swPipe)); - if (pool->pipes == nullptr) - { - swWarn("malloc[2] failed"); - return SW_ERR; - } - - swPipe *pipe; - for (i = 0; i < worker_num; i++) - { - pipe = &pool->pipes[i]; - if (swPipeUnsock_create(pipe, 1, SOCK_DGRAM) < 0) - { - return SW_ERR; - } - pool->workers[i].pipe_master = pipe->getSocket(pipe, SW_PIPE_MASTER); - pool->workers[i].pipe_worker = pipe->getSocket(pipe, SW_PIPE_WORKER); - pool->workers[i].pipe_object = pipe; - } - } - else if (ipc_mode == SW_IPC_SOCKET) - { - pool->use_socket = 1; - pool->stream = (swStreamInfo *) sw_malloc(sizeof(swStreamInfo)); - if (pool->stream == nullptr) - { - swWarn("malloc[2] failed"); - return SW_ERR; - } - bzero(pool->stream, sizeof(swStreamInfo)); - } - else - { - ipc_mode = SW_IPC_NONE; - } - - pool->map = swHashMap_new(SW_HASHMAP_INIT_BUCKET_N, nullptr); - if (pool->map == nullptr) - { - swProcessPool_free(pool); - return SW_ERR; - } - - pool->ipc_mode = ipc_mode; - if (ipc_mode > SW_IPC_NONE) - { - pool->main_loop = swProcessPool_worker_loop; - } - - for (i = 0; i < worker_num; i++) - { - pool->workers[i].pool = pool; - } - - return SW_OK; -} - -int swProcessPool_create_unix_socket(swProcessPool *pool, char *socket_file, int blacklog) -{ - if (pool->ipc_mode != SW_IPC_SOCKET) - { - swWarn("ipc_mode is not SW_IPC_SOCKET"); - return SW_ERR; - } - pool->stream->socket_file = sw_strdup(socket_file); - if (pool->stream->socket_file == nullptr) - { - return SW_ERR; - } - pool->stream->socket = swSocket_create_server(SW_SOCK_UNIX_STREAM, pool->stream->socket_file, 0, blacklog); - if (!pool->stream->socket) - { - return SW_ERR; - } - return SW_OK; -} - -int swProcessPool_create_tcp_socket(swProcessPool *pool, char *host, int port, int blacklog) -{ - if (pool->ipc_mode != SW_IPC_SOCKET) - { - swWarn("ipc_mode is not SW_IPC_SOCKET"); - return SW_ERR; - } - pool->stream->socket_file = sw_strdup(host); - if (pool->stream->socket_file == nullptr) - { - return SW_ERR; - } - pool->stream->socket = swSocket_create_server(SW_SOCK_TCP, host, port, blacklog); - if (!pool->stream->socket) - { - return SW_ERR; - } - return SW_OK; -} - -/** - * start workers - */ -int swProcessPool_start(swProcessPool *pool) -{ - if (pool->ipc_mode == SW_IPC_SOCKET && (pool->stream == nullptr || pool->stream->socket == 0)) - { - swWarn("must first listen to an tcp port"); - return SW_ERR; - } - - uint32_t i; - pool->started = 1; - - for (i = 0; i < pool->worker_num; i++) - { - pool->workers[i].pool = pool; - pool->workers[i].id = pool->start_id + i; - pool->workers[i].type = pool->type; - } - - for (i = 0; i < pool->worker_num; i++) - { - if (swProcessPool_spawn(pool, &(pool->workers[i])) < 0) - { - return SW_ERR; - } - } - - return SW_OK; -} - -static sw_inline int swProcessPool_schedule(swProcessPool *pool) -{ - if (pool->dispatch_mode == SW_DISPATCH_QUEUE) - { - return 0; - } - - uint32_t i, target_worker_id = 0; - - for (i = 0; i < pool->worker_num + 1; i++) - { - target_worker_id = sw_atomic_fetch_add(&pool->round_id, 1) % pool->worker_num; - if (pool->workers[target_worker_id].status == SW_WORKER_IDLE) - { - break; - } - } - return target_worker_id; -} - -int swProcessPool_response(swProcessPool *pool, char *data, int length) -{ - if (pool->stream == nullptr || pool->stream->last_connection == nullptr || pool->stream->response_buffer == nullptr) - { - SwooleG.error = SW_ERROR_INVALID_PARAMS; - return SW_ERR; - } - return swString_append_ptr(pool->stream->response_buffer, data, length); -} - -/** - * dispatch data to worker - */ -int swProcessPool_dispatch(swProcessPool *pool, swEventData *data, int *dst_worker_id) -{ - int ret = 0; - swWorker *worker; - - if (pool->use_socket) - { - swStream *stream = swStream_new(pool->stream->socket_file, 0, SW_SOCK_UNIX_STREAM); - if (stream == nullptr) - { - return SW_ERR; - } - stream->response = nullptr; - if (swStream_send(stream, (char*) data, sizeof(data->info) + data->info.len) < 0) - { - stream->cancel = 1; - return SW_ERR; - } - return SW_OK; - } - - if (*dst_worker_id < 0) - { - *dst_worker_id = swProcessPool_schedule(pool); - } - - *dst_worker_id += pool->start_id; - worker = swProcessPool_get_worker(pool, *dst_worker_id); - - int sendn = sizeof(data->info) + data->info.len; - ret = swWorker_send2worker(worker, 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); - } - - return ret; -} - -/** - * dispatch data to worker - */ -int swProcessPool_dispatch_blocking(swProcessPool *pool, swEventData *data, int *dst_worker_id) -{ - int ret = 0; - int sendn = sizeof(data->info) + data->info.len; - - if (pool->use_socket) - { - swClient _socket; - if (swClient_create(&_socket, SW_SOCK_UNIX_STREAM, SW_SOCK_SYNC) < 0) - { - return SW_ERR; - } - if (_socket.connect(&_socket, pool->stream->socket_file, 0, -1, 0) < 0) - { - return SW_ERR; - } - if (_socket.send(&_socket, (char*) data, sendn, 0) < 0) - { - return SW_ERR; - } - _socket.close(&_socket); - return SW_OK; - } - - if (*dst_worker_id < 0) - { - *dst_worker_id = swProcessPool_schedule(pool); - } - - *dst_worker_id += pool->start_id; - swWorker *worker = swProcessPool_get_worker(pool, *dst_worker_id); - - ret = swWorker_send2worker(worker, 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); - } - - return ret; -} - -void swProcessPool_shutdown(swProcessPool *pool) -{ - uint32_t i; - int status; - swWorker *worker; - SwooleG.running = 0; - - swSignal_none(); - //concurrent kill - for (i = 0; i < pool->worker_num; i++) - { - worker = &pool->workers[i]; - if (swoole_kill(worker->pid, SIGTERM) < 0) - { - swSysWarn("swKill(%d) failed", worker->pid); - continue; - } - } - for (i = 0; i < pool->worker_num; i++) - { - worker = &pool->workers[i]; - if (swoole_waitpid(worker->pid, &status, 0) < 0) - { - swSysWarn("waitpid(%d) failed", worker->pid); - } - } - swProcessPool_free(pool); - pool->started = 0; -} - -pid_t swProcessPool_spawn(swProcessPool *pool, swWorker *worker) -{ - pid_t pid = swoole_fork(0); - int ret_code = 0; - - switch (pid) - { - //child - case 0: - /** - * Process start - */ - if (pool->onWorkerStart != nullptr) - { - pool->onWorkerStart(pool, worker->id); - } - /** - * Process main loop - */ - if (pool->main_loop) - { - ret_code = pool->main_loop(pool, worker); - } - /** - * Process stop - */ - if (pool->onWorkerStop != nullptr) - { - pool->onWorkerStop(pool, worker->id); - } - exit(ret_code); - break; - case -1: - swSysWarn("fork() failed"); - break; - //parent - default: - //remove old process - if (worker->pid) - { - swHashMap_del_int(pool->map, worker->pid); - } - worker->pid = pid; - //insert new process - swHashMap_add_int(pool->map, pid, worker); - break; - } - return pid; -} - -int swProcessPool_get_max_request(swProcessPool *pool) -{ - int task_n; - if (pool->max_request < 1) - { - return -1; - } - else - { - task_n = pool->max_request; - if (pool->max_request_grace > 0) - { - task_n += swoole_system_random(1, pool->max_request_grace); - } - } - return task_n; -} - -void swProcessPool_set_max_request(swProcessPool *pool, uint32_t max_request, uint32_t max_request_grace) -{ - pool->max_request = max_request; - pool->max_request_grace = max_request_grace; -} - -static int swProcessPool_worker_loop(swProcessPool *pool, swWorker *worker) -{ - struct - { - long mtype; - swEventData buf; - } out; - - int n = 0, ret, worker_task_always = 0; - int task_n = swProcessPool_get_max_request(pool); - if (task_n <= 0) - { - worker_task_always = 1; - task_n = 1; - } - - /** - * Use from_fd save the task_worker->id - */ - out.buf.info.server_fd = worker->id; - - if (pool->dispatch_mode == SW_DISPATCH_QUEUE) - { - out.mtype = 0; - } - else - { - out.mtype = worker->id + 1; - } - - while (SwooleG.running > 0 && task_n > 0) - { - /** - * fetch task - */ - if (pool->use_msgqueue) - { - n = swMsgQueue_pop(pool->queue, (swQueue_data *) &out, sizeof(out.buf)); - if (n < 0 && errno != EINTR) - { - swSysWarn("[Worker#%d] msgrcv() failed", worker->id); - break; - } - } - else if (pool->use_socket) - { - swSocketAddress sa; - swSocket *conn = swSocket_accept(pool->stream->socket, &sa); - if (conn == nullptr) - { - if (errno == EAGAIN || errno == EINTR) - { - continue; - } - else - { - swSysWarn("accept(%d) failed", pool->stream->socket); - break; - } - } - - n = swStream_recv_blocking(conn, (void*) &out.buf, sizeof(out.buf)); - if (n == SW_CLOSE) - { - swSocket_free(conn); - continue; - } - pool->stream->last_connection = conn; - } - else - { - n = read(worker->pipe_worker->fd, &out.buf, sizeof(out.buf)); - if (n < 0 && errno != EINTR) - { - swSysWarn("[Worker#%d] read(%d) failed", worker->id, worker->pipe_worker->fd); - } - } - - /** - * timer - */ - if (n < 0) - { - if (errno == EINTR && SwooleWG.signal_alarm && SwooleTG.timer) - { - _alarm_handler: - SwooleWG.signal_alarm = 0; - swTimer_select(SwooleTG.timer); - } - continue; - } - - /** - * do task - */ - worker->status = SW_WORKER_BUSY; - ret = pool->onTask(pool, &out.buf); - worker->status = SW_WORKER_IDLE; - - if (pool->use_socket && pool->stream->last_connection) - { - int _end = 0; - swSocket_write_blocking(pool->stream->last_connection, (void *) &_end, sizeof(_end)); - swSocket_free(pool->stream->last_connection); - pool->stream->last_connection = nullptr; - } - - /** - * timer - */ - if (SwooleWG.signal_alarm) - { - goto _alarm_handler; - } - - if (ret >= 0 && !worker_task_always) - { - task_n--; - } - } - return SW_OK; -} - -int swProcessPool_set_protocol(swProcessPool *pool, int task_protocol, uint32_t max_packet_size) -{ - if (task_protocol) - { - pool->main_loop = swProcessPool_worker_loop; - } - else - { - pool->packet_buffer = (char *) sw_malloc(max_packet_size); - if (pool->packet_buffer == nullptr) - { - swSysWarn("malloc(%d) failed", max_packet_size); - return SW_ERR; - } - if (pool->stream) - { - pool->stream->response_buffer = swString_new(SW_BUFFER_SIZE_STD); - if (pool->stream->response_buffer == nullptr) - { - sw_free(pool->packet_buffer); - return SW_ERR; - } - } - pool->max_packet_size = max_packet_size; - pool->main_loop = swProcessPool_worker_loop_ex; - } - - return SW_OK; -} - -static int swProcessPool_worker_loop_ex(swProcessPool *pool, swWorker *worker) -{ - uint32_t n; - char *data; - - swQueue_data *outbuf = (swQueue_data *) pool->packet_buffer; - outbuf->mtype = 0; - - while (SwooleG.running > 0) - { - /** - * fetch task - */ - if (pool->use_msgqueue) - { - n = swMsgQueue_pop(pool->queue, outbuf, SW_MSGMAX); - if (n < 0 && errno != EINTR) - { - swSysWarn("[Worker#%d] msgrcv() failed", worker->id); - break; - } - data = outbuf->mdata; - outbuf->mtype = 0; - } - else if (pool->use_socket) - { - swSocketAddress sa; - swSocket *conn = swSocket_accept(pool->stream->socket, &sa); - if (conn == nullptr) - { - if (errno == EAGAIN || errno == EINTR) - { - continue; - } - else - { - swSysWarn("accept(%d) failed", pool->stream->socket); - break; - } - } - int tmp = 0; - if (swSocket_recv_blocking(conn, &tmp, sizeof(tmp), MSG_WAITALL) <= 0) - { - goto _close; - } - n = ntohl(tmp); - if (n <= 0) - { - goto _close; - } - else if (n > pool->max_packet_size) - { - goto _close; - } - if (swSocket_recv_blocking(conn, pool->packet_buffer, n, MSG_WAITALL) <= 0) - { - _close: - swSocket_free(conn); - continue; - } - data = pool->packet_buffer; - pool->stream->last_connection = conn; - } - else - { - n = read(worker->pipe_worker->fd, pool->packet_buffer, pool->max_packet_size); - if (n < 0 && errno != EINTR) - { - swSysWarn("[Worker#%d] read(%d) failed", worker->id, worker->pipe_worker->fd); - } - data = pool->packet_buffer; - } - - /** - * timer - */ - if (n < 0) - { - if (errno == EINTR && SwooleWG.signal_alarm && SwooleTG.timer) - { - _alarm_handler: - SwooleWG.signal_alarm = 0; - swTimer_select(SwooleTG.timer); - } - continue; - } - - pool->onMessage(pool, data, n); - - if (pool->use_socket && pool->stream->last_connection) - { - swString *resp_buf = pool->stream->response_buffer; - if (resp_buf && resp_buf->length > 0) - { - int _l = htonl(resp_buf->length); - swSocket_write_blocking(pool->stream->last_connection, &_l, sizeof(_l)); - swSocket_write_blocking(pool->stream->last_connection, resp_buf->str, resp_buf->length); - swString_clear(resp_buf); - } - swSocket_free(pool->stream->last_connection); - pool->stream->last_connection = nullptr; - } - - /** - * timer - */ - if (SwooleWG.signal_alarm) - { - goto _alarm_handler; - } - } - return SW_OK; -} - -/** - * add a worker to pool - */ -int swProcessPool_add_worker(swProcessPool *pool, swWorker *worker) -{ - swHashMap_add_int(pool->map, worker->pid, worker); - return SW_OK; -} - -int swProcessPool_wait(swProcessPool *pool) -{ - int pid, new_pid; - pid_t reload_worker_pid = 0; - int ret; - int status; - - pool->reload_workers = (swWorker *) sw_calloc(pool->worker_num, sizeof(swWorker)); - if (pool->reload_workers == nullptr) - { - swError("malloc[reload_workers] failed"); - return SW_ERR; - } - - while (SwooleG.running) - { - pid = wait(&status); - if (SwooleWG.signal_alarm && SwooleTG.timer) - { - SwooleWG.signal_alarm = 0; - swTimer_select(SwooleTG.timer); - } - if (pid < 0) - { - if (SwooleG.running == 0) - { - break; - } - if (pool->reloading == 0) - { - if (errno > 0 && errno != EINTR) - { - swSysWarn("[Manager] wait failed"); - } - continue; - } - else - { - if (pool->reload_init == 0) - { - swInfo("reload workers"); - pool->reload_init = 1; - memcpy(pool->reload_workers, pool->workers, sizeof(swWorker) * pool->worker_num); - if (pool->max_wait_time) - { - swoole_timer_add((long) (pool->max_wait_time * 1000), SW_FALSE, swProcessPool_kill_timeout_worker, pool); - } - } - goto _kill_worker; - } - } - - if (SwooleG.running == 1) - { - swWorker *exit_worker = (swWorker *) swHashMap_find_int(pool->map, pid); - if (exit_worker == nullptr) - { - if (pool->onWorkerNotFound) - { - pool->onWorkerNotFound(pool, pid, status); - } - else - { - swWarn("[Manager]unknow worker[pid=%d]", pid); - } - continue; - } - 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 : "" - ); - } - new_pid = swProcessPool_spawn(pool, exit_worker); - if (new_pid < 0) - { - swSysWarn("Fork worker process failed"); - sw_free(pool->reload_workers); - return SW_ERR; - } - swHashMap_del_int(pool->map, pid); - if (pid == reload_worker_pid) - { - pool->reload_worker_i++; - } - } - //reload worker - _kill_worker: - if (pool->reloading == 1) - { - //reload finish - if (pool->reload_worker_i >= pool->worker_num) - { - pool->reloading = pool->reload_init = reload_worker_pid = pool->reload_worker_i = 0; - continue; - } - reload_worker_pid = pool->reload_workers[pool->reload_worker_i].pid; - ret = swoole_kill(reload_worker_pid, SIGTERM); - if (ret < 0) - { - if (errno == ECHILD) - { - pool->reload_worker_i++; - goto _kill_worker; - } - swSysWarn("[Manager]swKill(%d) failed", pool->reload_workers[pool->reload_worker_i].pid); - continue; - } - } - } - sw_free(pool->reload_workers); - pool->reload_workers = nullptr; - return SW_OK; -} - -static void swProcessPool_free(swProcessPool *pool) -{ - uint32_t i; - swPipe *_pipe; - - if (pool->pipes) - { - for (i = 0; i < pool->worker_num; i++) - { - _pipe = &pool->pipes[i]; - _pipe->close(_pipe); - } - sw_free(pool->pipes); - } - - if (pool->use_msgqueue == 1 && pool->msgqueue_key == 0) - { - swMsgQueue_free(pool->queue); - } - - if (pool->stream) - { - if (pool->stream->socket) - { - unlink(pool->stream->socket_file); - sw_free((void*) pool->stream->socket_file); - } - if (pool->stream->socket) - { - swSocket_free(pool->stream->socket); - pool->stream->socket = nullptr; - } - if (pool->stream->response_buffer) - { - swString_free(pool->stream->response_buffer); - } - sw_free(pool->stream); - } - - if (pool->packet_buffer) - { - sw_free(pool->packet_buffer); - } - - if (pool->map) - { - swHashMap_free(pool->map); - } -} diff --git a/src/network/socket.cc b/src/network/socket.cc new file mode 100644 index 00000000000..791195441dc --- /dev/null +++ b/src/network/socket.cc @@ -0,0 +1,1571 @@ +/* + +----------------------------------------------------------------------+ + | 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_socket.h" + +#include + +#include "swoole_api.h" +#include "swoole_util.h" +#include "swoole_string.h" + +namespace swoole { +namespace network { + +double Socket::default_dns_timeout = SW_SOCKET_DEFAULT_DNS_TIMEOUT; +double Socket::default_connect_timeout = SW_SOCKET_DEFAULT_CONNECT_TIMEOUT; +double Socket::default_read_timeout = SW_SOCKET_DEFAULT_READ_TIMEOUT; +double Socket::default_write_timeout = SW_SOCKET_DEFAULT_WRITE_TIMEOUT; +uint32_t Socket::default_buffer_size = SW_SOCKET_BUFFER_SIZE; + +IOVector::IOVector(struct iovec *_iov, int _iovcnt) { + iov = new iovec[_iovcnt + _iovcnt]; + iov_iterator = iov + _iovcnt; + count = remain_count = _iovcnt; + + memcpy(iov, _iov, sizeof(*_iov) * _iovcnt); + memcpy(iov_iterator, _iov, sizeof(*_iov) * _iovcnt); +} + +IOVector::~IOVector() { + delete[] iov; +} + +void IOVector::update_iterator(ssize_t __n) { + size_t total_bytes = 0; + size_t _offset_bytes = 0; + int _index = 0; + + if (__n <= 0 || remain_count == 0) { + return; + } + + SW_LOOP_N(remain_count) { + total_bytes += iov_iterator[i].iov_len; + if ((ssize_t) total_bytes >= __n) { + _offset_bytes = iov_iterator[i].iov_len - (total_bytes - __n); + _index = i; + + if (_offset_bytes == iov_iterator[i].iov_len) { + _index++; + _offset_bytes = 0; + } + // update remain_count, index, offset_bytes + remain_count -= _index; + index += _index; + offset_bytes = i > 0 ? 0 : offset_bytes; + offset_bytes += _offset_bytes; + if (remain_count == 0) { + // iov should not be modified, prevent valgrind from checking for invalid read + return; + } + iov_iterator += _index; + iov_iterator->iov_base = reinterpret_cast(iov_iterator->iov_base) + _offset_bytes; + iov_iterator->iov_len = iov_iterator->iov_len - _offset_bytes; + + return; + } + } + + // represents the length of __n greater than total_bytes + abort(); +} + +int Socket::sendfile_blocking(const char *filename, off_t offset, size_t length, double timeout) { + int timeout_ms = timeout < 0 ? -1 : timeout * 1000; + + File file(filename, O_RDONLY); + if (!file.ready()) { + swoole_sys_warning("open(%s) failed", filename); + return SW_ERR; + } + + if (length == 0) { + FileStatus file_stat; + if (!file.stat(&file_stat)) { + return SW_ERR; + } + length = file_stat.st_size; + } else { + length = offset + length; + } + + ssize_t n, sent_bytes; + while (offset < (off_t) length) { + if (wait_event(timeout_ms, SW_EVENT_WRITE) < 0) { + return SW_ERR; + } + 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; +} + +ssize_t Socket::writev_blocking(const struct iovec *iov, size_t iovcnt) { + while (1) { + ssize_t n = writev(iov, iovcnt); + if (n < 0) { + if (errno == EINTR) { + continue; + } else if (catch_write_error(errno) == SW_WAIT && + wait_event((int) (send_timeout_ * 1000), SW_EVENT_WRITE) == SW_OK) { + continue; + } else { + swoole_sys_warning("send %lu bytes failed", iov[1].iov_len); + return SW_ERR; + } + } else { + return n; + } + } + return -1; +} + +/** + * clear socket buffer. + */ +void Socket::clean() { + char buf[2048]; + while (::recv(fd, buf, sizeof(buf), MSG_DONTWAIT) > 0) { + }; +} + +/** + * Wait socket can read or write. + */ +int Socket::wait_event(int timeout_ms, int events) { + struct pollfd event; + event.fd = fd; + event.events = 0; + + if (timeout_ms < 0) { + timeout_ms = -1; + } + + if (events & SW_EVENT_READ) { + event.events |= POLLIN; + } + if (events & SW_EVENT_WRITE) { + event.events |= POLLOUT; + } + while (1) { + int ret = poll(&event, 1, timeout_ms); + if (ret == 0) { + swoole_set_last_error(SW_ERROR_SOCKET_POLL_TIMEOUT); + 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; + } + } + return SW_OK; +} + +ssize_t Socket::send_blocking(const void *__data, size_t __len) { + ssize_t n = 0; + ssize_t written = 0; + + while (written < (ssize_t) __len) { +#ifdef SW_USE_OPENSSL + if (ssl) { + n = ssl_send((char *) __data + written, __len - written); + } else +#endif + { + n = ::send(fd, (char *) __data + written, __len - written, 0); + } + if (n < 0) { + if (errno == EINTR) { + continue; + } else if (catch_write_error(errno) == SW_WAIT && + wait_event((int) (send_timeout_ * 1000), SW_EVENT_WRITE) == SW_OK) { + continue; + } else { + swoole_sys_warning("send %lu bytes failed", __len); + return SW_ERR; + } + } + written += n; + } + + return written; +} + +ssize_t Socket::recv_blocking(void *__data, size_t __len, int flags) { + ssize_t ret; + size_t read_bytes = 0; + + while (read_bytes != __len) { + errno = 0; + ret = ::recv(fd, (char *) __data + read_bytes, __len - read_bytes, flags); + if (ret > 0) { + read_bytes += ret; + } else if (ret == 0) { + return read_bytes; + } else if (ret < 0) { + if (errno == EINTR) { + continue; + } + if (catch_read_error(errno) == SW_WAIT && + wait_event((int) (recv_timeout_ * 1000), SW_EVENT_READ) == SW_OK) { + continue; + } + return ret; + } + } + + return read_bytes; +} + +Socket *Socket::accept() { + Socket *socket = new Socket(); + socket->removed = 1; + socket->socket_type = socket_type; + socket->info.len = sizeof(socket->info); +#ifdef HAVE_ACCEPT4 + int flags = SOCK_CLOEXEC; + if (nonblock) { + flags |= SOCK_NONBLOCK; + } + socket->fd = ::accept4(fd, (struct sockaddr *) &socket->info.addr, &socket->info.len, flags); +#else + socket->fd = ::accept(fd, (struct sockaddr *) &socket->info.addr, &socket->info.len); + if (socket->fd >= 0) { + set_fd_option(nonblock, 1); + } +#endif + if (socket->fd < 0) { + delete socket; + return nullptr; + } + socket->info.type = socket_type; + socket->nonblock = nonblock; + socket->cloexec = 1; + return socket; +} + +ssize_t Socket::sendto_blocking(const Address &sa, const void *__buf, size_t __n, int flags) { + ssize_t n = 0; + + for (int i = 0; i < SW_SOCKET_RETRY_COUNT; i++) { + n = sendto(sa, __buf, __n, flags); + if (n >= 0) { + break; + } + if (errno == EINTR) { + continue; + } + if (catch_write_error(errno) == SW_WAIT && wait_event((int) (send_timeout_ * 1000), SW_EVENT_WRITE) == SW_OK) { + continue; + } + break; + } + + return n; +} + +ssize_t Socket::recvfrom_blocking(char *__buf, size_t __len, int flags, Address *sa) { + ssize_t n = 0; + + for (int i = 0; i < SW_SOCKET_RETRY_COUNT; i++) { + n = recvfrom(__buf, __len, flags, sa); + if (n >= 0) { + break; + } + if (errno == EINTR) { + continue; + } + if (catch_read_error(errno) == SW_WAIT && wait_event((int) (recv_timeout_ * 1000), SW_EVENT_READ) == SW_OK) { + continue; + } + break; + } + + return n; +} + +static void socket_free_defer(void *ptr) { + Socket *sock = (Socket *) ptr; + if (sock->fd != -1 && close(sock->fd) != 0) { + swoole_sys_warning("close(%d) failed", sock->fd); + } + delete sock; +} + +void Socket::free() { + if (recv_timer) { + swoole_timer_del(recv_timer); + } + 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); + } else { + socket_free_defer(this); + } +} + +int Socket::bind(const std::string &_host, int *port) { + int ret; + Address address = {}; + size_t l_host = _host.length(); + const char *host = _host.c_str(); + + if (set_reuse_addr() < 0) { + 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) { + swoole_warning("bad unix socket file"); + errno = EINVAL; + return SW_ERR; + } + unlink(host); + address.addr.un.sun_family = AF_UNIX; + swoole_strlcpy(address.addr.un.sun_path, host, sizeof(address.addr.un.sun_path)); + ret = ::bind(fd, (struct sockaddr *) &address.addr.un, sizeof(address.addr.un)); + } + // IPv6 + else if (socket_type == SW_SOCK_TCP6 || socket_type == SW_SOCK_UDP6) { + if (l_host == 0) { + host = "::"; + } + if (inet_pton(AF_INET6, host, &address.addr.inet_v6.sin6_addr) < 0) { + swoole_sys_warning("inet_pton(AF_INET6, %s) failed", host); + return SW_ERR; + } + address.addr.inet_v6.sin6_port = htons(*port); + address.addr.inet_v6.sin6_family = AF_INET6; + ret = ::bind(fd, (struct sockaddr *) &address.addr.inet_v6, sizeof(address.addr.inet_v6)); + if (ret == 0 && *port == 0) { + address.len = sizeof(address.addr.inet_v6); + if (getsockname(fd, (struct sockaddr *) &address.addr.inet_v6, &address.len) != -1) { + *port = ntohs(address.addr.inet_v6.sin6_port); + } + } + } + // IPv4 + else if (socket_type == SW_SOCK_UDP || socket_type == SW_SOCK_TCP) { + if (l_host == 0) { + host = "0.0.0.0"; + } + if (inet_pton(AF_INET, host, &address.addr.inet_v4.sin_addr) < 0) { + swoole_sys_warning("inet_pton(AF_INET, %s) failed", host); + return SW_ERR; + } + address.addr.inet_v4.sin_port = htons(*port); + address.addr.inet_v4.sin_family = AF_INET; + ret = ::bind(fd, (struct sockaddr *) &address.addr.inet_v4, sizeof(address.addr.inet_v4)); + if (ret == 0 && *port == 0) { + address.len = sizeof(address.addr.inet_v4); + if (getsockname(fd, (struct sockaddr *) &address.addr.inet_v4, &address.len) != -1) { + *port = ntohs(address.addr.inet_v4.sin_port); + } + } + } else { + errno = EINVAL; + return -1; + } + + // bind failed + if (ret < 0) { + return SW_ERR; + } + + return ret; +} + +bool Socket::set_buffer_size(uint32_t _buffer_size) { + if (!set_send_buffer_size(_buffer_size)) { + return false; + } + if (!set_recv_buffer_size(_buffer_size)) { + return false; + } + return true; +} + +bool Socket::set_recv_buffer_size(uint32_t _buffer_size) { + if (set_option(SOL_SOCKET, SO_RCVBUF, _buffer_size) != 0) { + swoole_sys_warning("setsockopt(%d, SOL_SOCKET, SO_RCVBUF, %d) failed", fd, _buffer_size); + return false; + } + return true; +} + +bool Socket::set_send_buffer_size(uint32_t _buffer_size) { + if (set_option(SOL_SOCKET, SO_SNDBUF, _buffer_size) != 0) { + swoole_sys_warning("setsockopt(%d, SOL_SOCKET, SO_SNDBUF, %d) failed", fd, _buffer_size); + return false; + } + return true; +} + +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; + timeo.tv_sec = (int) timeout; + timeo.tv_usec = (int) ((timeout - timeo.tv_sec) * 1000 * 1000); + ret = setsockopt(fd, SOL_SOCKET, type, (void *) &timeo, sizeof(timeo)); + if (ret < 0) { + swoole_sys_warning("setsockopt(SO_SNDTIMEO, %s) failed", type == SO_SNDTIMEO ? "SEND" : "RECV"); + return false; + } else { + return true; + } +} + +static bool _fcntl_set_option(int sock, int nonblock, int cloexec) { + int opts, ret; + + if (nonblock >= 0) { + do { + opts = fcntl(sock, F_GETFL); + } while (opts < 0 && errno == EINTR); + + if (opts < 0) { + swoole_sys_warning("fcntl(%d, GETFL) failed", sock); + } + + if (nonblock) { + opts = opts | O_NONBLOCK; + } else { + opts = opts & ~O_NONBLOCK; + } + + do { + ret = fcntl(sock, F_SETFL, opts); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) { + swoole_sys_warning("fcntl(%d, SETFL, opts) failed", sock); + return false; + } + } + +#ifdef FD_CLOEXEC + if (cloexec >= 0) { + do { + opts = fcntl(sock, F_GETFD); + } while (opts < 0 && errno == EINTR); + + if (opts < 0) { + swoole_sys_warning("fcntl(%d, GETFL) failed", sock); + } + + if (cloexec) { + opts = opts | FD_CLOEXEC; + } else { + opts = opts & ~FD_CLOEXEC; + } + + do { + ret = fcntl(sock, F_SETFD, opts); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) { + swoole_sys_warning("fcntl(%d, SETFD, opts) failed", sock); + return false; + } + } +#endif + + return true; +} + +bool Socket::set_fd_option(int _nonblock, int _cloexec) { + if (_fcntl_set_option(fd, _nonblock, _cloexec)) { + nonblock = _nonblock; + cloexec = _cloexec; + return true; + } else { + return false; + } +} + +bool Socket::set_recv_timeout(double timeout) { + if (_set_timeout(fd, SO_SNDTIMEO, timeout)) { + send_timeout_ = timeout; + return true; + } else { + return false; + } +} + +bool Socket::set_send_timeout(double timeout) { + if (_set_timeout(fd, SO_RCVTIMEO, timeout)) { + recv_timeout_ = timeout; + return true; + } else { + return false; + } +} + +int Socket::handle_sendfile() { + int ret; + Buffer *buffer = out_buffer; + BufferChunk *chunk = buffer->front(); + SendfileRequest *task = (SendfileRequest *) chunk->value.ptr; + + if (task->offset == 0) { + cork(); + } + + size_t sendn = + (task->length - task->offset > SW_SENDFILE_CHUNK_SIZE) ? SW_SENDFILE_CHUNK_SIZE : task->length - task->offset; + +#ifdef SW_USE_OPENSSL + if (ssl) { + ret = ssl_sendfile(task->file, &task->offset, sendn); + } else +#endif + { + ret = ::swoole_sendfile(fd, task->file.get_fd(), &task->offset, sendn); + } + + swoole_trace("ret=%d|task->offset=%ld|sendn=%lu|filesize=%lu", ret, (long) task->offset, sendn, task->length); + + if (ret <= 0) { + switch (catch_write_error(errno)) { + case SW_ERROR: + swoole_sys_warning( + "sendfile(%s, %ld, %zu) failed", task->file.get_path().c_str(), (long) task->offset, sendn); + buffer->pop(); + return SW_OK; + case SW_CLOSE: + close_wait = 1; + return SW_ERR; + case SW_WAIT: + send_wait = 1; + return SW_ERR; + default: + break; + } + } else { + if (send_timer) { + last_sent_time = time(true); + } + } + + // sendfile completed + if ((size_t) task->offset >= task->length) { + buffer->pop(); + uncork(); + } + + return SW_OK; +} + +/** + * send buffer to client + */ +int Socket::handle_send() { + Buffer *buffer = out_buffer; + BufferChunk *chunk = buffer->front(); + uint32_t sendn = chunk->length - chunk->offset; + + if (sendn == 0) { + buffer->pop(); + return SW_OK; + } + + ssize_t ret = send(chunk->value.str + chunk->offset, sendn, 0); + if (ret < 0) { + switch (catch_write_error(errno)) { + case SW_ERROR: + swoole_sys_warning("send to fd[%d] failed", fd); + break; + case SW_CLOSE: + close_wait = 1; + return SW_ERR; + case SW_WAIT: + send_wait = 1; + return SW_ERR; + default: + break; + } + return SW_OK; + } + // chunk full send + else if (ret == sendn || sendn == 0) { + buffer->pop(); + } else { + chunk->offset += ret; + // kernel is not fully processing and socket buffer is full + if (ret < sendn) { + send_wait = 1; + return SW_ERR; + } + } + return SW_OK; +} + +static void Socket_sendfile_destructor(BufferChunk *chunk) { + 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()) { + swoole_sys_warning("open(%s) failed", filename); + return SW_OK; + } + + FileStatus file_stat; + if (!task->file.stat(&file_stat)) { + swoole_sys_warning("fstat(%s) failed", filename); + return SW_ERR; + } + + if (file_stat.st_size == 0) { + swoole_warning("empty file[%s]", filename); + return SW_ERR; + } + + if (out_buffer == nullptr) { + out_buffer = new Buffer(SW_SEND_BUFFER_SIZE); + if (out_buffer == nullptr) { + return SW_ERR; + } + } + + if (offset < 0 || (length + offset > (size_t) file_stat.st_size)) { + swoole_error_log(SW_LOG_WARNING, SW_ERROR_INVALID_PARAMS, "length or offset is invalid"); + return SW_OK; + } + if (length == 0) { + task->length = file_stat.st_size; + } else { + task->length = length + offset; + } + + BufferChunk *chunk = out_buffer->alloc(BufferChunk::TYPE_SENDFILE, 0); + chunk->value.ptr = task.release(); + chunk->destroy = Socket_sendfile_destructor; + + return SW_OK; +} + +ssize_t Socket::recv(void *__buf, size_t __n, int __flags) { + ssize_t total_bytes = 0; + + do { +#ifdef SW_USE_OPENSSL + if (ssl) { + ssize_t retval = 0; + while ((size_t) total_bytes < __n) { + retval = ssl_recv(((char *) __buf) + total_bytes, __n - total_bytes); + if (retval <= 0) { + if (total_bytes == 0) { + total_bytes = retval; + } + break; + } else { + total_bytes += retval; + if (!(nonblock || (__flags & MSG_WAITALL))) { + break; + } + } + } + } else +#endif + { + total_bytes = ::recv(fd, __buf, __n, __flags); + } + } while (total_bytes < 0 && errno == EINTR); + + if (total_bytes > 0) { + total_recv_bytes += total_bytes; + if (recv_timer) { + last_received_time = time(true); + } + } + + // The POLLHUP event is triggered, but Socket::recv returns EAGAIN + if (total_bytes < 0 && catch_read_error(errno) == SW_WAIT && event_hup) { + total_bytes = 0; + } + + swoole_trace_log(SW_TRACE_SOCKET, "recv %ld/%ld bytes, errno=%d", total_bytes, __n, errno); + + return total_bytes; +} + +ssize_t Socket::send(const void *__buf, size_t __n, int __flags) { + ssize_t retval; + + do { +#ifdef SW_USE_OPENSSL + if (ssl) { + retval = ssl_send(__buf, __n); + } else +#endif + { + retval = ::send(fd, __buf, __n, __flags); + } + } while (retval < 0 && errno == EINTR); + + if (retval > 0) { + total_send_bytes += retval; + if (send_timer) { + last_sent_time = time(true); + } + } + + swoole_trace_log(SW_TRACE_SOCKET, "send %ld/%ld bytes, errno=%d", retval, __n, errno); + + return retval; +} + +ssize_t Socket::send_async(const void *__buf, size_t __n) { + if (!swoole_event_is_available()) { + return send_blocking(__buf, __n); + } else { + return swoole_event_write(this, __buf, __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; + + do { +#ifdef SW_USE_OPENSSL + if (ssl) { + retval = ssl_readv(io_vector); + } else +#endif + { + retval = ::readv(fd, io_vector->get_iterator(), io_vector->get_remain_count()); + io_vector->update_iterator(retval); + } + } while (retval < 0 && errno == EINTR); + + return retval; +} + +ssize_t Socket::writev(IOVector *io_vector) { + ssize_t retval; + + do { +#ifdef SW_USE_OPENSSL + if (ssl) { + retval = ssl_writev(io_vector); + } else +#endif + { + retval = ::writev(fd, io_vector->get_iterator(), io_vector->get_remain_count()); + io_vector->update_iterator(retval); + } + } while (retval < 0 && errno == EINTR); + + return retval; +} + +ssize_t Socket::peek(void *__buf, size_t __n, int __flags) { + ssize_t retval; + __flags |= MSG_PEEK; + do { +#ifdef SW_USE_OPENSSL + if (ssl) { + retval = SSL_peek(ssl, __buf, __n); + } else +#endif + { + retval = ::recv(fd, __buf, __n, __flags); + } + } while (retval < 0 && errno == EINTR); + + swoole_trace_log(SW_TRACE_SOCKET, "peek %ld/%ld bytes, errno=%d", retval, __n, errno); + + return retval; +} + +#ifdef SW_USE_OPENSSL + +#ifndef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT +static int ssl_check_name(const char *name, ASN1_STRING *pattern) { + char *s, *end; + size_t slen, plen; + + s = (char *) name; + slen = strlen(name); + + uchar *p = ASN1_STRING_data(pattern); + plen = ASN1_STRING_length(pattern); + + if (swoole_strcaseeq(s, slen, (char *) p, plen)) { + return SW_OK; + } + + if (plen > 2 && p[0] == '*' && p[1] == '.') { + plen -= 1; + p += 1; + + end = s + slen; + s = swoole_strlchr(s, end, '.'); + + if (s == nullptr) { + return SW_ERR; + } + + slen = end - s; + + if (swoole_strcaseeq(s, slen, (char *) p, plen)) { + return SW_OK; + } + } + return SW_ERR; +} +#endif + +bool Socket::ssl_check_host(const char *tls_host_name) { + X509 *cert = ssl_get_peer_certificate(); + if (cert == nullptr) { + return false; + } +#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) { + swoole_warning("X509_check_host(): no match"); + goto _failed; + } + goto _found; +#else + int n, i; + X509_NAME *sname; + ASN1_STRING *str; + X509_NAME_ENTRY *entry; + GENERAL_NAME *altname; + STACK_OF(GENERAL_NAME) * altnames; + + /* + * As per RFC6125 and RFC2818, we check subjectAltName extension, + * and if it's not present - commonName in Subject is checked. + */ + altnames = (STACK_OF(GENERAL_NAME) *) X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr); + + if (altnames) { + n = sk_GENERAL_NAME_num(altnames); + + for (i = 0; i < n; i++) { + altname = sk_GENERAL_NAME_value(altnames, i); + + if (altname->type != GEN_DNS) { + continue; + } + + str = altname->d.dNSName; + swoole_trace("SSL subjectAltName: \"%.*s\"", ASN1_STRING_length(str), ASN1_STRING_data(str)); + + if (ssl_check_name(tls_host_name, str) == SW_OK) { + swoole_trace("SSL subjectAltName: match"); + GENERAL_NAMES_free(altnames); + goto _found; + } + } + + swoole_trace("SSL subjectAltName: no match"); + GENERAL_NAMES_free(altnames); + goto _failed; + } + + /* + * If there is no subjectAltName extension, check commonName + * in Subject. While RFC2818 requires to only check "most specific" + * CN, both Apache and OpenSSL check all CNs, and so do we. + */ + sname = X509_get_subject_name(cert); + + if (sname == nullptr) { + goto _failed; + } + + i = -1; + for (;;) { + i = X509_NAME_get_index_by_NID(sname, NID_commonName, i); + + if (i < 0) { + break; + } + + entry = X509_NAME_get_entry(sname, i); + str = X509_NAME_ENTRY_get_data(entry); + + swoole_trace("SSL commonName: \"%.*s\"", ASN1_STRING_length(str), ASN1_STRING_data(str)); + + if (ssl_check_name(tls_host_name, str) == SW_OK) { + swoole_trace("SSL commonName: match"); + goto _found; + } + } + swoole_trace("SSL commonName: no match"); +#endif + +_failed: + X509_free(cert); + return false; + +_found: + X509_free(cert); + return true; +} + +bool Socket::ssl_verify(bool allow_self_signed) { + long err = SSL_get_verify_result(ssl); + switch (err) { + case X509_V_OK: + break; + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + if (allow_self_signed) { + break; + } else { + swoole_error_log( + SW_LOG_NOTICE, SW_ERROR_SSL_VERIFY_FAILED, "self signed certificate from fd#%d is not allowed", fd); + return false; + } + default: + swoole_error_log(SW_LOG_NOTICE, + SW_ERROR_SSL_VERIFY_FAILED, + "can not verify peer from fd#%d with error#%ld: %s", + fd, + err, + X509_verify_cert_error_string(err)); + return false; + } + + return true; +} + +X509 *Socket::ssl_get_peer_certificate() { + if (!ssl) { + return NULL; + } + return SSL_get_peer_certificate(ssl); +} + +STACK_OF(X509) * Socket::ssl_get_peer_cert_chain() { + if (!ssl) { + return NULL; + } + return SSL_get_peer_cert_chain(ssl); +} + +static int _ssl_read_x509_file(X509 *cert, char *buffer, size_t length) { + long len; + BIO *bio = BIO_new(BIO_s_mem()); + ON_SCOPE_EXIT { + BIO_free(bio); + }; + + if (bio == nullptr) { + swoole_warning("BIO_new() failed"); + return -1; + } + + if (PEM_write_bio_X509(bio, cert) == 0) { + swoole_warning("PEM_write_bio_X509() failed"); + return -1; + } + + len = BIO_pending(bio); + if (len < 0 && len > (long) length) { + swoole_warning("certificate length[%ld] is too big", len); + return -1; + } + return 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); + +#ifdef OPENSSL_IS_BORINGSSL + n = std::min((int) n, limit); +#else + n = std::min(n, limit); +#endif + + SW_LOOP_N(n) { + X509 *cert = sk_X509_value(chain, i); + auto n = _ssl_read_x509_file(cert, sw_tg_buffer()->str, sw_tg_buffer()->size); + if (n > 0) { + list.emplace_back(sw_tg_buffer()->str, n); + } + } + return list; +} + +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; + } +} + +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); +} + +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); +} + +ReturnCode Socket::ssl_accept() { + ssl_clear_error(); + + int n = SSL_accept(ssl); + /** + * The TLS/SSL handshake was successfully completed + */ + if (n == 1) { + ssl_state = SW_SSL_STATE_READY; +#if OPENSSL_VERSION_NUMBER < 0x10100000L +#ifdef SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS + if (ssl->s3) { + ssl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS; + } +#endif +#endif + return SW_READY; + } + /** + * The TLS/SSL handshake was not successful but was shutdown. + */ + else if (n == 0) { + return SW_ERROR; + } + + long err = SSL_get_error(ssl, n); + if (err == SSL_ERROR_WANT_READ) { + ssl_want_read = 1; + ssl_want_write = 0; + return SW_WAIT; + } else if (err == SSL_ERROR_WANT_WRITE) { + ssl_want_read = 0; + ssl_want_write = 1; + return SW_WAIT; + } else if (err == SSL_ERROR_SSL) { + 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) { +#ifdef SW_SUPPORT_DTLS + if (dtls && errno == 0) { + ssl_want_read = 1; + return SW_WAIT; + } +#endif + return SW_ERROR; + } + swoole_warning("SSL_do_handshake() failed. Error: %s[%ld|%d]", strerror(errno), err, errno); + return SW_ERROR; +} + +int Socket::ssl_connect() { + ssl_clear_error(); + + int n = SSL_connect(ssl); + if (n == 1) { + ssl_state = SW_SSL_STATE_READY; + +#ifdef SW_LOG_TRACE_OPEN + const char *ssl_version = SSL_get_version(ssl); + const char *ssl_cipher = SSL_get_cipher_name(ssl); + swoole_trace_log(SW_TRACE_SSL, "connected (%s %s)", ssl_version, ssl_cipher); +#endif + + return SW_OK; + } + + long err = SSL_get_error(ssl, n); + if (err == SSL_ERROR_WANT_READ) { + ssl_want_read = 1; + ssl_want_write = 0; + ssl_state = SW_SSL_STATE_WAIT_STREAM; + return SW_OK; + } else if (err == SSL_ERROR_WANT_WRITE) { + ssl_want_read = 0; + ssl_want_write = 1; + ssl_state = SW_SSL_STATE_WAIT_STREAM; + return SW_OK; + } else if (err == SSL_ERROR_ZERO_RETURN) { + swoole_debug("SSL_connect(fd=%d) closed", fd); + return SW_ERR; + } else if (err == SSL_ERROR_SYSCALL) { + if (n) { + swoole_set_last_error(errno); + return SW_ERR; + } + } + + long err_code = ERR_get_error(); + char *msg = ERR_error_string(err_code, sw_tg_buffer()->str); + swoole_notice("Socket::ssl_connect(fd=%d) to server[%s:%d] failed. Error: %s[%ld|%d]", + fd, + info.get_ip(), + info.get_port(), + msg, + err, + ERR_GET_REASON(err_code)); + + return SW_ERR; +} + +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; + + ssize_t n = fp.pread(buf, readn, *_offset); + if (n > 0) { + ssize_t ret = ssl_send(buf, n); + if (ret < 0) { + if (catch_write_error(errno) == SW_ERROR) { + swoole_sys_warning("write() failed"); + } + } else { + *_offset += ret; + } + swoole_trace_log(SW_TRACE_REACTOR, "fd=%d, readn=%ld, n=%ld, ret=%ld", fd, readn, n, ret); + return ret; + } else { + swoole_sys_warning("pread() failed"); + return SW_ERR; + } +} + +bool Socket::ssl_shutdown() { + if (ssl_closed_) { + return false; + } + if (SSL_in_init(ssl)) { + 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. + */ + if (ssl_quiet_shutdown) { + SSL_set_quiet_shutdown(ssl, 1); + } + + int mode = SSL_get_shutdown(ssl); + SSL_set_shutdown(ssl, mode | SSL_RECEIVED_SHUTDOWN | SSL_SENT_SHUTDOWN); + + 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); + swoole_trace("SSL_get_error: %d", sslerr); + } + + if (!(n == 1 || sslerr == 0 || sslerr == SSL_ERROR_ZERO_RETURN)) { + int reason; + const char *error_string = ssl_get_error_reason(&reason); + 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; +} + +void Socket::ssl_catch_error() { + int level = SW_LOG_NOTICE; + int reason = ERR_GET_REASON(ERR_peek_error()); + +#if 0 + /* handshake failures */ + switch (reason) + { + case SSL_R_BAD_CHANGE_CIPHER_SPEC: /* 103 */ + case SSL_R_BLOCK_CIPHER_PAD_IS_WRONG: /* 129 */ + case SSL_R_DIGEST_CHECK_FAILED: /* 149 */ + case SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST: /* 151 */ + case SSL_R_EXCESSIVE_MESSAGE_SIZE: /* 152 */ + case SSL_R_LENGTH_MISMATCH:/* 159 */ + case SSL_R_NO_CIPHERS_PASSED:/* 182 */ + case SSL_R_NO_CIPHERS_SPECIFIED:/* 183 */ + case SSL_R_NO_COMPRESSION_SPECIFIED: /* 187 */ + case SSL_R_NO_SHARED_CIPHER:/* 193 */ + case SSL_R_RECORD_LENGTH_MISMATCH: /* 213 */ +#ifdef SSL_R_PARSE_TLSEXT + case SSL_R_PARSE_TLSEXT:/* 227 */ +#endif + case SSL_R_UNEXPECTED_MESSAGE:/* 244 */ + case SSL_R_UNEXPECTED_RECORD:/* 245 */ + case SSL_R_UNKNOWN_ALERT_TYPE: /* 246 */ + case SSL_R_UNKNOWN_PROTOCOL:/* 252 */ + case SSL_R_WRONG_VERSION_NUMBER:/* 267 */ + case SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC: /* 281 */ +#ifdef SSL_R_RENEGOTIATE_EXT_TOO_LONG + case SSL_R_RENEGOTIATE_EXT_TOO_LONG:/* 335 */ + case SSL_R_RENEGOTIATION_ENCODING_ERR:/* 336 */ + case SSL_R_RENEGOTIATION_MISMATCH:/* 337 */ +#endif +#ifdef SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED + case SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED: /* 338 */ +#endif +#ifdef SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING + case SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING:/* 345 */ +#endif +#ifdef SSL_R_INAPPROPRIATE_FALLBACK + case SSL_R_INAPPROPRIATE_FALLBACK: /* 373 */ +#endif + case 1000:/* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */ + case SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE:/* 1010 */ + case SSL_R_SSLV3_ALERT_BAD_RECORD_MAC:/* 1020 */ + case SSL_R_TLSV1_ALERT_DECRYPTION_FAILED:/* 1021 */ + case SSL_R_TLSV1_ALERT_RECORD_OVERFLOW:/* 1022 */ + case SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE:/* 1030 */ + case SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE:/* 1040 */ + case SSL_R_SSLV3_ALERT_NO_CERTIFICATE:/* 1041 */ + case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE:/* 1042 */ + case SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE: /* 1043 */ + case SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED:/* 1044 */ + case SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED:/* 1045 */ + case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN:/* 1046 */ + case SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER:/* 1047 */ + case SSL_R_TLSV1_ALERT_UNKNOWN_CA:/* 1048 */ + case SSL_R_TLSV1_ALERT_ACCESS_DENIED:/* 1049 */ + case SSL_R_TLSV1_ALERT_DECODE_ERROR:/* 1050 */ + case SSL_R_TLSV1_ALERT_DECRYPT_ERROR:/* 1051 */ + case SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION:/* 1060 */ + case SSL_R_TLSV1_ALERT_PROTOCOL_VERSION:/* 1070 */ + case SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY:/* 1071 */ + case SSL_R_TLSV1_ALERT_INTERNAL_ERROR:/* 1080 */ + case SSL_R_TLSV1_ALERT_USER_CANCELLED:/* 1090 */ + case SSL_R_TLSV1_ALERT_NO_RENEGOTIATION: /* 1100 */ + level = SW_LOG_WARNING; + break; +#endif + + swoole_error_log(level, + SW_ERROR_SSL_BAD_PROTOCOL, + "SSL connection#%d[%s:%d] protocol error[%d]", + fd, + info.get_ip(), + info.get_port(), + reason); +} + +ssize_t Socket::ssl_recv(void *__buf, size_t __n) { + ssl_clear_error(); + + int n = SSL_read(ssl, __buf, __n); + if (n < 0) { + int _errno = SSL_get_error(ssl, n); + switch (_errno) { + case SSL_ERROR_WANT_READ: + ssl_want_read = 1; + errno = EAGAIN; + return SW_ERR; + + case SSL_ERROR_WANT_WRITE: + ssl_want_write = 1; + errno = EAGAIN; + return SW_ERR; + + case SSL_ERROR_SYSCALL: + return errno == 0 ? 0 : SW_ERR; + + case SSL_ERROR_SSL: + ssl_catch_error(); + errno = SW_ERROR_SSL_BAD_CLIENT; + return SW_ERR; + + default: + break; + } + } + return n; +} + +ssize_t Socket::ssl_send(const void *__buf, size_t __n) { + ssl_clear_error(); + +#ifdef SW_SUPPORT_DTLS + if (dtls && chunk_size && __n > chunk_size) { + __n = chunk_size; + } +#endif + + int n = SSL_write(ssl, __buf, __n); + if (n < 0) { + int _errno = SSL_get_error(ssl, n); + switch (_errno) { + case SSL_ERROR_WANT_READ: + ssl_want_read = 1; + errno = EAGAIN; + return SW_ERR; + + case SSL_ERROR_WANT_WRITE: + ssl_want_write = 1; + errno = EAGAIN; + return SW_ERR; + + case SSL_ERROR_SYSCALL: + errno = SW_ERROR_SSL_RESET; + return SW_ERR; + + case SSL_ERROR_SSL: + ssl_catch_error(); + errno = SW_ERROR_SSL_BAD_CLIENT; + return SW_ERR; + + default: + break; + } + } + return n; +} + +ssize_t Socket::ssl_readv(IOVector *io_vector) { + ssize_t retval, total_bytes = 0; + + do { + retval = ssl_recv(io_vector->get_iterator()->iov_base, io_vector->get_iterator()->iov_len); + total_bytes += retval > 0 ? retval : 0; + io_vector->update_iterator(retval); + } while (retval > 0 && io_vector->get_remain_count() > 0); + + return total_bytes > 0 ? total_bytes : retval; +} + +ssize_t Socket::ssl_writev(IOVector *io_vector) { + ssize_t retval, total_bytes = 0; + + do { + retval = ssl_send(io_vector->get_iterator()->iov_base, io_vector->get_iterator()->iov_len); + total_bytes += retval > 0 ? retval : 0; + io_vector->update_iterator(retval); + } while (retval > 0 && io_vector->get_remain_count() > 0); + + return total_bytes > 0 ? total_bytes : retval; +} + +int Socket::ssl_create(SSLContext *ssl_context, int _flags) { + ssl_clear_error(); + + ssl = SSL_new(ssl_context->get_context()); + if (ssl == nullptr) { + swoole_warning("SSL_new() failed"); + return SW_ERR; + } + if (!SSL_set_fd(ssl, fd)) { + long err = ERR_get_error(); + swoole_warning("SSL_set_fd() failed. Error: %s[%ld]", ERR_reason_error_string(err), err); + return SW_ERR; + } + if (_flags & SW_SSL_CLIENT) { + SSL_set_connect_state(ssl); + } else if (_flags & SW_SSL_SERVER) { + SSL_set_accept_state(ssl); + } + 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; +} + +#endif + +} // namespace network + +using network::Socket; + +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) { + 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; + +#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) + int sock_flags = 0; + if (nonblock) { + sock_flags |= SOCK_NONBLOCK; + } + if (cloexec) { + sock_flags |= SOCK_CLOEXEC; + } + int sockfd = ::socket(sock_domain, sock_type | sock_flags, socket_protocol); + if (sockfd < 0) { + return sockfd; + } +#else + int sockfd = ::socket(sock_domain, sock_type, socket_protocol); + if (sockfd < 0) { + return sockfd; + } + if (nonblock || cloexec) { + if (!network::_fcntl_set_option(sockfd, nonblock ? 1 : -1, cloexec ? 1 : -1)) { + close(sockfd); + return sockfd; + } + } +#endif + return sockfd; +} + +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) { + swoole_sys_warning("socket() failed"); + return nullptr; + } + if (sock->bind(address, &port) < 0) { + sock->free(); + return nullptr; + } + if (sock->is_stream() && sock->listen(backlog) < 0) { + swoole_sys_warning("listen(%s:%d, %d) failed", address, port, backlog); + sock->free(); + return nullptr; + } + return sock; +} + +Socket *make_socket(int fd, FdType fd_type) { + Socket *socket = new Socket(); + socket->fd = fd; + socket->fd_type = fd_type; + socket->removed = 1; + return socket; +} + +} // namespace swoole diff --git a/src/network/stream.cc b/src/network/stream.cc index 9d0a07ca671..189ee317eca 100644 --- a/src/network/stream.cc +++ b/src/network/stream.cc @@ -10,169 +10,137 @@ | to obtain it through the world-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 "client.h" +#include "swoole_api.h" +#include "swoole_string.h" +#include "swoole_socket.h" +#include "swoole_protocol.h" +#include "swoole_client.h" -static void swStream_free(swStream *stream); +namespace swoole { +namespace network { -static void swStream_onConnect(swClient *cli) -{ - swStream *stream = (swStream*) cli->object; - if (stream->cancel) - { - cli->close(cli); +static void Stream_onConnect(Client *cli) { + Stream *stream = (Stream *) cli->object; + if (stream->cancel) { + cli->close(); } *((uint32_t *) stream->buffer->str) = ntohl(stream->buffer->length - 4); - if (cli->send(cli, stream->buffer->str, stream->buffer->length, 0) < 0) - { - cli->close(cli); - } - else - { - swString_free(stream->buffer); - stream->buffer = NULL; + if (cli->send(cli, stream->buffer->str, stream->buffer->length, 0) < 0) { + cli->close(); + } else { + delete stream->buffer; + stream->buffer = nullptr; } } -static void swStream_onError(swClient *cli) -{ - swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_CONNECT_FAIL, - " connect() failed (%d: %s) while connecting to worker process", errno, strerror(errno)); - swStream_free((swStream *) cli->object); -} +static void Stream_onError(Client *cli) { + Stream *stream = (Stream *) cli->object; + stream->errCode = swoole_get_last_error(); -static void swStream_onReceive(swClient *cli, char *data, uint32_t length) -{ - swStream *stream = (swStream*) cli->object; - if (length == 4) - { - cli->socket->close_wait = 1; - } - else - { - stream->response(stream, data + 4, length - 4); + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_CONNECT_FAIL, + " connect() failed (%d: %s) while connecting to worker process", + stream->errCode, + swoole_strerror(stream->errCode)); + + if (!stream->response) { + return; } -} -static void swStream_onClose(swClient *cli) -{ - swoole_event_defer([](void *data) { - swClient *cli = (swClient *) data; - swClient_free(cli); - swStream_free((swStream *) cli->object); - }, cli); + stream->response(stream, nullptr, 0); + delete stream; } -static void swStream_free(swStream *stream) -{ - if (stream->buffer) - { - swString_free(stream->buffer); +static void Stream_onReceive(Client *cli, const char *data, uint32_t length) { + Stream *stream = (Stream *) cli->object; + if (length == 4) { + cli->socket->close_wait = 1; + } else { + stream->response(stream, data + 4, length - 4); } - sw_free(stream); } -swStream* swStream_new(char *dst_host, int dst_port, enum swSocket_type type) -{ - swStream *stream = (swStream*) sw_malloc(sizeof(swStream)); - if (!stream) - { - return NULL; - } - bzero(stream, sizeof(swStream)); +static void Stream_onClose(Client *cli) { + swoole_event_defer( + [](void *data) { + Client *cli = (Client *) data; + delete (Stream *) cli->object; + }, + cli); +} - swClient *cli = &stream->client; - if (swClient_create(cli, type, 1) < 0) - { - swStream_free(stream); - return NULL; +Stream::Stream(const char *dst_host, int dst_port, SocketType type) : client(type, true) { + if (client.socket == nullptr) { + return; } - cli->onConnect = swStream_onConnect; - cli->onReceive = swStream_onReceive; - cli->onError = swStream_onError; - cli->onClose = swStream_onClose; - cli->object = stream; + client.onConnect = Stream_onConnect; + client.onReceive = Stream_onReceive; + client.onError = Stream_onError; + client.onClose = Stream_onClose; + client.object = this; - cli->open_length_check = 1; - swStream_set_protocol(&cli->protocol); + client.open_length_check = 1; + set_protocol(&client.protocol); - if (cli->connect(cli, dst_host, dst_port, -1, 0) < 0) - { - swSysWarn("failed to connect to [%s:%d]", dst_host, dst_port); - return NULL; + if (client.connect(&client, dst_host, dst_port, -1, 0) < 0) { + swoole_sys_warning("failed to connect to [%s:%d]", dst_host, dst_port); + return; } - else - { - return stream; + connected = true; +} + +Stream::~Stream() { + if (buffer) { + delete buffer; } } /** * Stream Protocol: Length(32bit/Network Byte Order) + Body */ -void swStream_set_protocol(swProtocol *protocol) -{ - protocol->get_package_length = swProtocol_get_package_length; - protocol->package_length_size = 4; +void Stream::set_protocol(Protocol *protocol) { + protocol->get_package_length = Protocol::default_length_func; 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; } -void swStream_set_max_length(swStream *stream, uint32_t max_length) -{ - stream->client.protocol.package_max_length = max_length; +void Stream::set_max_length(uint32_t max_length) { + client.protocol.package_max_length = max_length; } -int swStream_send(swStream *stream, char *data, size_t length) -{ - if (stream->buffer == NULL) - { - stream->buffer = swString_new(swoole_size_align(length + 4, SwooleG.pagesize)); - if (stream->buffer == NULL) - { - return SW_ERR; - } - stream->buffer->length = 4; +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, swoole_pagesize())); + buffer->length = 4; } - if (swString_append_ptr(stream->buffer, data, length) < 0) - { + if (buffer->append(data, length) < 0) { return SW_ERR; } return SW_OK; } -int swStream_recv_blocking(swSocket *sock, void *__buf, size_t __len) -{ +ssize_t Stream::recv_blocking(Socket *sock, void *__buf, size_t __len) { int tmp = 0; - ssize_t ret = swSocket_recv_blocking(sock, &tmp, sizeof(tmp), MSG_WAITALL); - - if (ret <= 0) - { - return SW_CLOSE; + ssize_t ret = sock->recv_blocking(&tmp, sizeof(tmp), MSG_WAITALL); + if (ret <= 0) { + return SW_ERR; } int length = (int) ntohl(tmp); - if (length <= 0) - { - return SW_CLOSE; - } - else if (length > (int) __len) - { - return SW_CLOSE; - } - - ret = swSocket_recv_blocking(sock, __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 +} // namespace swoole diff --git a/src/network/thread_pool.cc b/src/network/thread_pool.cc deleted file mode 100644 index 7f9dea4c139..00000000000 --- a/src/network/thread_pool.cc +++ /dev/null @@ -1,199 +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 "swoole.h" - -#define swThreadPool_thread(p,id) (&p->threads[id]) -static void* swThreadPool_loop(void *arg); - -int swThreadPool_create(swThreadPool *pool, int thread_num) -{ - bzero(pool, sizeof(swThreadPool)); - - pool->threads = (swThread *) sw_calloc(thread_num, sizeof(swThread)); - if (!pool->threads) - { - swWarn("malloc[1] failed"); - return SW_ERR; - } - - pool->params = (swThreadParam *) sw_calloc(thread_num, sizeof(swThreadParam)); - if (!pool->params) - { - sw_free(pool->threads); - swWarn("malloc[2] failed"); - return SW_ERR; - } - - swTrace("threads=%p|params=%p", pool->threads, pool->params); - -#ifdef SW_THREADPOOL_USE_CHANNEL - pool->chan = swChannel_create(1024 * 256, 512, 0); - if (pool->chan == NULL) - { - sw_free(pool->threads); - sw_free(pool->params); - swWarn("swThreadPool_create create channel failed"); - return SW_ERR; - } -#else - int size = SW_MAX(SwooleG.max_sockets + 1, SW_THREADPOOL_QUEUE_LEN); - if (swRingQueue_init(&pool->queue, size) < 0) - { - sw_free(pool->threads); - sw_free(pool->params); - return SW_ERR; - } -#endif - if (swCond_create(&pool->cond) < 0) - { - sw_free(pool->threads); - sw_free(pool->params); - return SW_ERR; - } - pool->thread_num = thread_num; - return SW_OK; -} - -int swThreadPool_dispatch(swThreadPool *pool, void *task, int task_len) -{ - int ret; - - pool->cond.lock(&pool->cond); -#ifdef SW_THREADPOOL_USE_CHANNEL - ret = swChannel_in(pool->chan, task, task_len); -#else - ret = swRingQueue_push(&pool->queue, task); -#endif - pool->cond.unlock(&pool->cond); - - if (ret < 0) - { - swoole_error_log(SW_LOG_ERROR, SW_ERROR_QUEUE_FULL, "the queue of thread pool is full"); - return SW_ERR; - } - - sw_atomic_t *task_num = &pool->task_num; - sw_atomic_fetch_add(task_num, 1); - - return pool->cond.notify(&pool->cond); -} - -int swThreadPool_run(swThreadPool *pool) -{ - int i; - for (i = 0; i < pool->thread_num; i++) - { - pool->params[i].pti = i; - pool->params[i].object = pool; - if (pthread_create(&(swThreadPool_thread(pool,i)->tid), NULL, swThreadPool_loop, &pool->params[i]) < 0) - { - swSysWarn("pthread_create failed"); - return SW_ERR; - } - } - return SW_OK; -} - -int swThreadPool_free(swThreadPool *pool) -{ - int i; - if (pool->shutdown) - { - return -1; - } - pool->shutdown = 1; - - //broadcast all thread - pool->cond.broadcast(&(pool->cond)); - - for (i = 0; i < pool->thread_num; i++) - { - pthread_join((swThreadPool_thread(pool,i)->tid), NULL); - } - -#ifdef SW_THREADPOOL_USE_CHANNEL - swChannel_free(pool->chan); -#else - swRingQueue_free(&pool->queue); -#endif - - pool->cond.free(&pool->cond); - - return SW_OK; -} - -static void* swThreadPool_loop(void *arg) -{ - swThreadParam *param = (swThreadParam *) arg; - swThreadPool *pool = (swThreadPool *) param->object; - - int id = param->pti; - int ret; - void *task; - - SwooleTG.buffer_stack = swString_new(SW_STACK_BUFFER_SIZE); - if (SwooleTG.buffer_stack == NULL) - { - return NULL; - } - - swSignal_none(); - - if (pool->onStart) - { - pool->onStart(pool, id); - } - - while (SwooleG.running) - { - pool->cond.lock(&pool->cond); - - if (pool->shutdown) - { - pool->cond.unlock(&pool->cond); - swTrace("thread [%d] will exit", id); - pthread_exit(NULL); - } - - if (pool->task_num == 0) - { - pool->cond.wait(&pool->cond); - } - - swTrace("thread [%d] is starting to work", id); - - ret = swRingQueue_pop(&pool->queue, &task); - pool->cond.unlock(&pool->cond); - - if (ret >= 0) - { - sw_atomic_t *task_num = &pool->task_num; - sw_atomic_fetch_sub(task_num, 1); - - pool->onTask(pool, (void *) task, ret); - } - } - - if (pool->onStop) - { - pool->onStop(pool, id); - } - - swString_free(SwooleTG.buffer_stack); - pthread_exit(NULL); - return NULL; -} diff --git a/src/network/timer.cc b/src/network/timer.cc deleted file mode 100644 index 597164d6932..00000000000 --- a/src/network/timer.cc +++ /dev/null @@ -1,295 +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 "swoole_api.h" - -int swTimer_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"); - return SW_ERR; - } - time->tv_sec = _now.tv_sec; - time->tv_usec = _now.tv_nsec / 1000; -#else - if (gettimeofday(time, NULL) < 0) - { - swSysWarn("gettimeofday() failed"); - return SW_ERR; - } -#endif - return SW_OK; -} - -static int swReactorTimer_set(swTimer *timer, long exec_msec) -{ - timer->reactor->timeout_msec = exec_msec; - return SW_OK; -} - -static void swReactorTimer_close(swTimer *timer) -{ - timer->reactor->check_timer = SW_FALSE; - swReactorTimer_set(timer, -1); -} - -static void swReactorTimer_free(swTimer *timer) -{ - swoole_timer_free(); -} - -static int swReactorTimer_init(swReactor *reactor, swTimer *timer, long exec_msec) -{ - reactor->check_timer = SW_TRUE; - reactor->timeout_msec = exec_msec; - reactor->timer = timer; - timer->reactor = reactor; - timer->set = swReactorTimer_set; - timer->close = swReactorTimer_close; - - swReactor_add_destroy_callback(reactor, (swCallback) swReactorTimer_free, timer); - - return SW_OK; -} - -int swTimer_init(swTimer *timer, long msec) -{ - bzero(timer, sizeof(swTimer)); - if (swTimer_now(&timer->basetime) < 0) - { - return SW_ERR; - } - - timer->heap = swHeap_new(1024, SW_MIN_HEAP); - if (!timer->heap) - { - return SW_ERR; - } - - timer->map = swHashMap_new(SW_HASHMAP_INIT_BUCKET_N, NULL); - if (!timer->map) - { - swHeap_free(timer->heap); - timer->heap = NULL; - return SW_ERR; - } - - timer->_current_id = -1; - timer->_next_msec = msec; - timer->_next_id = 1; - - int ret; - if (SwooleTG.reactor) - { - ret = swReactorTimer_init(SwooleTG.reactor, timer, msec); - } - else - { - ret = swSystemTimer_init(timer, msec); - } - if (sw_likely(ret != SW_OK)) - { - swTimer_free(timer); - } - return ret; -} - -void swTimer_reinit(swTimer *timer, swReactor *reactor) -{ - swReactorTimer_init(reactor, timer, timer->_next_msec); -} - -static void swTimer_node_dtor(void *data) -{ - swTimer_node *tnode = (swTimer_node *) data; - sw_free(tnode); -} - -void swTimer_free(swTimer *timer) -{ - if (timer->close) - { - timer->close(timer); - } - if (timer->heap) - { - swHeap_free(timer->heap); - } - if (timer->map) - { - timer->map->dtor = swTimer_node_dtor; - swHashMap_free(timer->map); - } - memset(timer, 0, sizeof(swTimer)); -} - -swTimer_node* swTimer_add(swTimer *timer, long _msec, int interval, void *data, swTimerCallback callback) -{ - if (sw_unlikely(_msec <= 0)) - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_INVALID_PARAMS, "msec value[%ld] is invalid", _msec); - return NULL; - } - - swTimer_node *tnode = (swTimer_node *) sw_malloc(sizeof(swTimer_node)); - if (sw_unlikely(!tnode)) - { - swSysWarn("malloc(%ld) failed", sizeof(swTimer_node)); - return NULL; - } - - int64_t now_msec = swTimer_get_relative_msec(); - if (sw_unlikely(now_msec < 0)) - { - sw_free(tnode); - return NULL; - } - - tnode->data = data; - tnode->type = SW_TIMER_TYPE_KERNEL; - tnode->exec_msec = now_msec + _msec; - tnode->interval = interval ? _msec : 0; - tnode->removed = 0; - tnode->callback = callback; - tnode->round = timer->round; - tnode->dtor = NULL; - - if (timer->_next_msec < 0 || timer->_next_msec > _msec) - { - timer->set(timer, _msec); - timer->_next_msec = _msec; - } - - tnode->id = timer->_next_id++; - if (sw_unlikely(tnode->id < 0)) - { - tnode->id = 1; - timer->_next_id = 2; - } - - tnode->heap_node = swHeap_push(timer->heap, tnode->exec_msec, tnode); - if (sw_unlikely(tnode->heap_node == NULL)) - { - sw_free(tnode); - return NULL; - } - if (sw_unlikely(swHashMap_add_int(timer->map, tnode->id, tnode) != SW_OK)) - { - sw_free(tnode); - return NULL; - } - timer->num++; - swTraceLog(SW_TRACE_TIMER, "id=%ld, exec_msec=%" PRId64 ", msec=%ld, round=%" PRIu64 ", exist=%u", tnode->id, tnode->exec_msec, _msec, tnode->round, timer->num); - return tnode; -} - -enum swBool_type swTimer_del(swTimer *timer, swTimer_node *tnode) -{ - if (sw_unlikely(!tnode || tnode->removed)) - { - return SW_FALSE; - } - if (sw_unlikely(timer->_current_id > 0 && tnode->id == timer->_current_id)) - { - tnode->removed = 1; - swTraceLog(SW_TRACE_TIMER, "set-remove: id=%ld, exec_msec=%" PRId64 ", round=%" PRIu64 ", exist=%u", tnode->id, tnode->exec_msec, tnode->round, timer->num); - return SW_TRUE; - } - if (sw_unlikely(swHashMap_del_int(timer->map, tnode->id) < 0)) - { - return SW_FALSE; - } - if (tnode->heap_node) - { - swHeap_remove(timer->heap, tnode->heap_node); - sw_free(tnode->heap_node); - } - if (tnode->dtor) - { - tnode->dtor(tnode); - } - timer->num--; - swTraceLog(SW_TRACE_TIMER, "id=%ld, exec_msec=%" PRId64 ", round=%" PRIu64 ", exist=%u", tnode->id, tnode->exec_msec, tnode->round, timer->num); - sw_free(tnode); - return SW_TRUE; -} - -int swTimer_select(swTimer *timer) -{ - int64_t now_msec = swTimer_get_relative_msec(); - if (sw_unlikely(now_msec < 0)) - { - return SW_ERR; - } - - swTimer_node *tnode = NULL; - swHeap_node *tmp; - - swTraceLog(SW_TRACE_TIMER, "timer msec=%" PRId64 ", round=%" PRId64, now_msec, timer->round); - while ((tmp = swHeap_top(timer->heap))) - { - tnode = (swTimer_node *) tmp->data; - if (tnode->exec_msec > now_msec || tnode->round == timer->round) - { - break; - } - - timer->_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, timer->num - 1); - tnode->callback(timer, tnode); - } - timer->_current_id = -1; - - //persistent timer - if (tnode->interval > 0 && !tnode->removed) - { - while (tnode->exec_msec <= now_msec) - { - tnode->exec_msec += tnode->interval; - } - swHeap_change_priority(timer->heap, tnode->exec_msec, tmp); - continue; - } - - timer->num--; - swHeap_pop(timer->heap); - swHashMap_del_int(timer->map, tnode->id); - sw_free(tnode); - } - - if (!tnode || !tmp) - { - timer->_next_msec = -1; - timer->set(timer, -1); - } - else - { - long next_msec = tnode->exec_msec - now_msec; - if (next_msec <= 0) - { - next_msec = 1; - } - timer->set(timer, next_msec); - } - timer->round++; - - return SW_OK; -} diff --git a/src/os/async_thread.cc b/src/os/async_thread.cc index 4a624473a3f..63f7dc90f51 100644 --- a/src/os/async_thread.cc +++ b/src/os/async_thread.cc @@ -10,12 +10,18 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "swoole_api.h" -#include "async.h" +#include "swoole_socket.h" +#include "swoole_reactor.h" +#include "swoole_string.h" +#include "swoole_signal.h" +#include "swoole_pipe.h" +#include "swoole_async.h" +#include "swoole_util.h" #include #include @@ -24,96 +30,79 @@ #include #include #include +#include +#include -using namespace std; +static std::mutex async_thread_lock; +static std::shared_ptr async_thread_pool; -typedef swAio_event AsyncEvent; - -static mutex init_lock; -static atomic refcount(0); - -static void aio_thread_release(AsyncEvent *event); - -namespace swoole { namespace async { +namespace swoole { +namespace async { //------------------------------------------------------------------------------- -class EventQueue -{ -public: - inline void push(AsyncEvent *event) - { +class EventQueue { + public: + inline void push(AsyncEvent *event) { _queue.push(event); } - inline AsyncEvent* pop() - { - if (_queue.empty()) - { + inline AsyncEvent *pop() { + if (_queue.empty()) { return nullptr; } - AsyncEvent* retval = _queue.front(); + AsyncEvent *retval = _queue.front(); _queue.pop(); return retval; } - inline double get_max_wait_time() - { - if (_queue.empty()) - { + inline double get_max_wait_time() { + if (_queue.empty()) { return 0; - } - else - { - AsyncEvent* event = _queue.front(); - return swoole_microtime() - event->timestamp; + } else { + AsyncEvent *event = _queue.front(); + return microtime() - event->timestamp; } } - inline size_t count() - { + inline size_t count() { return _queue.size(); } -private: - queue _queue; + private: + std::queue _queue; }; -class ThreadPool -{ -public: - ThreadPool(size_t _core_worker_num, size_t _worker_num, double _max_wait_time, double _max_idle_time) - { +class ThreadPool { + public: + ThreadPool(size_t _core_worker_num, size_t _worker_num, double _max_wait_time, double _max_idle_time) { running = false; core_worker_num = _core_worker_num == 0 ? SW_CPU_NUM : SW_MAX(1, _core_worker_num); 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; - - current_pid = getpid(); } - ~ThreadPool() - { + ~ThreadPool() { shutdown(); } - bool start() - { + bool is_running() { + return running; + } + + bool start() { running = true; current_task_id = 0; n_waiting = 0; n_closing = 0; - for (size_t i = 0; i < core_worker_num; i++) - { + for (size_t i = 0; i < core_worker_num; i++) { create_thread(true); } return true; } - bool shutdown() - { - if (!running) - { + bool shutdown() { + if (!running) { return false; } @@ -122,91 +111,82 @@ class ThreadPool _cv.notify_all(); event_mutex.unlock(); - for (auto &i : threads) - { - thread *_thread = i.second; - if (_thread->joinable()) - { + for (auto &i : threads) { + std::thread *_thread = i.second; + if (_thread->joinable()) { _thread->join(); } delete _thread; } + threads.clear(); return true; } - void schedule() - { - if (n_waiting == 0 && threads.size() < worker_num && max_wait_time > 0) - { - event_mutex.lock(); + void schedule() { + if (n_waiting == 0 && threads.size() < worker_num && max_wait_time > 0) { double _max_wait_time = _queue.get_max_wait_time(); - event_mutex.unlock(); - - if (_max_wait_time > max_wait_time) - { + if (_max_wait_time > max_wait_time) { size_t n = 1; /** * maybe we can find a better strategy */ - if (threads.size() + n > worker_num) - { + 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); - while (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(); } } } } - AsyncEvent* dispatch(const AsyncEvent *request) - { - if (SwooleTG.aio_schedule) - { - schedule(); - } + AsyncEvent *dispatch(const AsyncEvent *request) { auto _event_copy = new AsyncEvent(*request); - _event_copy->task_id = current_task_id++; - _event_copy->timestamp = swoole_microtime(); - _event_copy->pipe_fd = SwooleTG.aio_write_socket->fd; event_mutex.lock(); + schedule(); + _event_copy->task_id = current_task_id++; + _event_copy->timestamp = microtime(); + _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", swoole_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() - { - unique_lock lock(event_mutex); + inline size_t get_queue_size() { + std::unique_lock lock(event_mutex); return _queue.count(); } - pid_t current_pid; + static std::string get_thread_id(std::thread::id id) { + std::stringstream ss; + ss << id; + return ss.str(); + } - void release_thread(thread::id tid) - { + void release_thread(std::thread::id tid) { auto i = threads.find(tid); - if (i == threads.end()) - { - swWarn("AIO thread#%zu is missing", tid); + if (i == threads.end()) { + swoole_warning("AIO thread#%s is missing", get_thread_id(tid).c_str()); return; - } - else - { - thread *_thread = i->second; - swTraceLog(SW_TRACE_AIO, "release idle thread#%zu, we have %zu now", tid, threads.size() - 1); - if (_thread->joinable()) - { + } else { + std::thread *_thread = i->second; + swoole_trace_log(SW_TRACE_AIO, + "release idle thread#%s, we have %zu now", + get_thread_id(tid).c_str(), + threads.size() - 1); + if (_thread->joinable()) { _thread->join(); } threads.erase(i); @@ -214,15 +194,21 @@ class ThreadPool } } -#ifdef SW_DEBUG - void notify_one() - { + 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: + 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; @@ -231,286 +217,228 @@ class ThreadPool bool running; - atomic n_waiting; - atomic n_closing; + std::atomic n_waiting; + std::atomic n_closing; size_t current_task_id = 0; - - unordered_map threads; + std::unordered_map threads; EventQueue _queue; - mutex event_mutex; - condition_variable _cv; + std::mutex event_mutex; + std::condition_variable _cv; }; -//------------------------------------------------------------------------------- -}}; - -static swoole::async::ThreadPool *pool = nullptr; - -void swoole::async::ThreadPool::create_thread(const bool is_core_worker) -{ - try - { - thread *_thread = new thread([this, is_core_worker]() - { - bool exit_flag = false; - - SwooleTG.buffer_stack = swString_new(SW_STACK_BUFFER_SIZE); - if (SwooleTG.buffer_stack == nullptr) - { - return; - } - - swSignal_none(); - while (running) - { - event_mutex.lock(); - AsyncEvent *event = _queue.pop(); - event_mutex.unlock(); +void ThreadPool::main_func(bool is_core_worker) { + bool exit_flag = false; + swoole_thread_init(); - swDebug("%s: %f", event ? "pop 1 event" : "no event", swoole_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) - { - int ret = write(event->pipe_fd, &event, sizeof(event)); - if (ret < 0) - { - if (errno == EAGAIN) - { - swSocket_wait(event->pipe_fd, 1000, SW_EVENT_WRITE); - continue; - } - else if (errno == EINTR) - { - continue; - } - else - { - 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 - { - 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; + } else { + delete event; + swoole_sys_warning("sendto swoole_aio_pipe_write failed"); } - if (!running) - { - break; - } - ++n_waiting; - if (is_core_worker || max_idle_time <= 0) - { - _cv.wait(lock); - } - else - { - while (true) - { - if (_cv.wait_for(lock, chrono::microseconds((size_t) (max_idle_time * 1000 * 1000))) == 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 thread::id(this_thread::get_id()); - event->callback = aio_thread_release; - event->pipe_fd = SwooleG.aio_default_socket->fd; - event->canceled = false; - - --n_waiting; - ++n_closing; - exit_flag = true; - goto _send_event; - } - break; + } + break; + } + + // exit + if (exit_flag) { + n_closing--; + break; + } + } else { + std::unique_lock lock(event_mutex); + if (_queue.count() > 0) { + continue; + } + if (!running) { + break; + } + ++n_waiting; + if (is_core_worker || max_idle_time <= 0) { + _cv.wait(lock); + } else { + while (true) { + if (_cv.wait_for(lock, std::chrono::microseconds((size_t) (max_idle_time * 1000 * 1000))) == + std::cv_status::timeout) { + if (running && n_closing != 0) { + // wait for the next round + continue; } + /* notifies the main thread to release this thread */ + event = new AsyncEvent; + event->object = new std::thread::id(std::this_thread::get_id()); + event->callback = release_callback; + event->pipe_socket = SwooleG.aio_default_socket; + event->canceled = false; + + --n_waiting; + ++n_closing; + exit_flag = true; + goto _send_event; } - --n_waiting; + break; } } - }); - 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; + --n_waiting; + } } + swoole_thread_clean(); } -static void aio_thread_release(swAio_event *event) -{ - thread::id *tid = static_cast(event->object); - pool->release_thread(*tid); - delete tid; - // balance - SwooleTG.aio_task_num++; -} - -static void swAio_free(void *private_data) -{ - if (!SwooleTG.aio_init) - { +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 = 0; - swoole_event_del(SwooleTG.aio_read_socket); - - if (pool->current_pid == getpid()) - { - if ((--refcount) == 0) - { - delete pool; - pool = nullptr; - - SwooleTG.aio_pipe.close(&SwooleTG.aio_pipe); - SwooleTG.aio_read_socket = nullptr; - SwooleTG.aio_write_socket = nullptr; - } - } } -static int swAio_init() -{ - if (SwooleTG.aio_init) - { - swWarn("aio_thread_pool has already been initialized"); - return SW_ERR; +AsyncEvent *dispatch(const AsyncEvent *request) { + if (sw_unlikely(!SwooleTG.async_threads)) { + SwooleTG.async_threads = new AsyncThreads(); } - if (!SwooleTG.reactor) - { - swWarn("no event loop, cannot initialized"); - return SW_ERR; + AsyncEvent *event = SwooleTG.async_threads->pool->dispatch(request); + if (sw_likely(event)) { + SwooleTG.async_threads->task_num++; } + return event; +} - if (swPipeBase_create(&SwooleTG.aio_pipe, 0) < 0) - { - swoole_throw_error(SW_ERROR_SYSTEM_CALL_FAIL); - } +//------------------------------------------------------------------------------- +} // namespace async - SwooleTG.aio_read_socket = SwooleTG.aio_pipe.getSocket(&SwooleTG.aio_pipe, 0); - SwooleTG.aio_write_socket = SwooleTG.aio_pipe.getSocket(&SwooleTG.aio_pipe, 1); - SwooleTG.aio_read_socket->fdtype = SW_FD_AIO; - SwooleTG.aio_write_socket->fdtype = SW_FD_AIO; - - swoole_event_add(SwooleTG.aio_read_socket, SW_EVENT_READ); - swReactor_add_destroy_callback(SwooleTG.reactor, swAio_free, nullptr); - - init_lock.lock(); - if ((refcount++) == 0) - { - pool = new swoole::async::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; +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) { + swoole_sys_warning("read() aio events failed"); + return SW_ERR; + } + for (size_t i = 0; i < n / sizeof(AsyncEvent *); i++) { + AsyncEvent *event = events[i]; + if (!event->canceled) { + event->callback(event); + } + SwooleTG.async_threads->task_num--; + delete event; } - SwooleTG.aio_init = 1; - init_lock.unlock(); return SW_OK; } -size_t swAio_thread_count() -{ - return pool ? pool->worker_count() : 0; +size_t AsyncThreads::get_worker_num() { + return pool ? pool->get_worker_num() : 0; } -ssize_t swAio_dispatch(const swAio_event *request) -{ - AsyncEvent *event = swAio_dispatch2(request); - if (event == nullptr) - { - return -1; - } - return event->task_id; +size_t AsyncThreads::get_queue_size() { + return pool ? pool->get_queue_size() : 0; } -swAio_event* swAio_dispatch2(const swAio_event *request) -{ - if (sw_unlikely(!SwooleTG.aio_init)) - { - swAio_init(); - } - AsyncEvent *event = pool->dispatch(request); - if (sw_likely(event)) - { - SwooleTG.aio_task_num++; +void AsyncThreads::notify_one() { + if (pool) { + pool->notify_one(); } - return event; } -int swAio_callback(swReactor *reactor, swEvent *event) -{ - if (SwooleTG.aio_schedule) - { - pool->schedule(); +AsyncThreads::AsyncThreads() { + if (!SwooleTG.reactor) { + swoole_warning("no event loop, cannot initialized"); + throw swoole::Exception(SW_ERROR_WRONG_OPERATION); } - AsyncEvent *events[SW_AIO_EVENT_NUM]; - ssize_t n = read(event->fd, events, sizeof(AsyncEvent *) * SW_AIO_EVENT_NUM); - if (n < 0) - { - swSysWarn("read() aio events failed"); - return SW_ERR; + pipe = new Pipe(false); + if (!pipe->ready()) { + delete pipe; + pipe = nullptr; + swoole_throw_error(SW_ERROR_SYSTEM_CALL_FAIL); } - for (size_t i = 0; i < n / sizeof(AsyncEvent *); i++) - { - AsyncEvent *event = events[i]; - if (!event->canceled) - { - event->callback(event); + + 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; } - SwooleTG.aio_task_num--; - delete event; + 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(); - return SW_OK; + SwooleG.aio_default_socket = write_socket; + SwooleTG.async_threads = this; } -#ifdef SW_DEBUG -void swAio_notify_one() -{ - if (pool) - { - pool->notify_one(); +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; } -#endif +}; // namespace swoole diff --git a/src/os/base.cc b/src/os/base.cc index 5ba9954a42c..94e8e7ad64f 100644 --- a/src/os/base.cc +++ b/src/os/base.cc @@ -12,204 +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 "async.h" -#include -#include +#include "swoole_socket.h" +#include "swoole_async.h" -#if 0 -swAsyncIO SwooleAIO; - -static int swAio_onTask(swThreadPool *pool, void *task, int task_len); -static int swAio_onCompleted(swReactor *reactor, swEvent *event); - -static swThreadPool pool; -static swPipe _aio_pipe; -static int _pipe_read; -static int _pipe_write; - -int swAio_init(void) -{ - if (SwooleAIO.init) - { - swWarn("AIO has already been initialized"); - return SW_ERR; - } - if (!SwooleTG.reactor) - { - swWarn("No eventloop, cannot initialized"); - return SW_ERR; - } - if (swPipeBase_create(&_aio_pipe, 0) < 0) - { - return SW_ERR; - } - if (swMutex_create(&SwooleAIO.lock, 0) < 0) - { - swWarn("create mutex lock error"); - return SW_ERR; - } - if (SwooleAIO.thread_num <= 0) - { - SwooleAIO.thread_num = SW_AIO_THREAD_NUM_DEFAULT; - } - if (swThreadPool_create(&pool, SwooleAIO.thread_num) < 0) - { - return SW_ERR; - } - - pool.onTask = swAio_onTask; - - _pipe_read = _aio_pipe.getFd(&_aio_pipe, 0); - _pipe_write = _aio_pipe.getFd(&_aio_pipe, 1); - - SwooleTG.reactor->setHandle(SwooleTG.reactor, SW_FD_AIO, swAio_onCompleted); - swoole_event_add(_pipe_read, SW_FD_AIO); - - if (swThreadPool_run(&pool) < 0) - { - return SW_ERR; - } - - SwooleAIO.init = 1; - - return SW_OK; -} - -static int swAio_onCompleted(swReactor *reactor, swEvent *event) -{ - int i; - swAio_event *events[SW_AIO_EVENT_NUM]; - int n = read(event->fd, events, sizeof(swAio_event*) * SW_AIO_EVENT_NUM); - if (n < 0) - { - swSysWarn("read() failed"); - return SW_ERR; - } - for (i = 0; i < n / sizeof(swAio_event*); i++) - { - events[i]->callback(events[i]); - SwooleAIO.task_num--; - sw_free(events[i]); - } - return SW_OK; -} - - -static int swAio_onTask(swThreadPool *pool, void *task, int task_len) -{ - swAio_event *event = task; - if (event->handler == NULL) - { - event->error = SW_ERROR_AIO_BAD_REQUEST; - event->ret = -1; - goto _error; - } - - event->handler(event); - - swTrace("aio_thread ok. ret=%d, error=%d", event->ret, event->error); - - _error: - do - { - SwooleAIO.lock.lock(&SwooleAIO.lock); - int ret = write(_pipe_write, &task, sizeof(task)); - SwooleAIO.lock.unlock(&SwooleAIO.lock); - if (ret < 0) - { - if (errno == EAGAIN) - { - swYield(); - continue; - } - else if (errno == EINTR) - { - continue; - } - else - { - swSysWarn("sendto swoole_aio_pipe_write failed"); - } - } - break; - } while (1); - - return SW_OK; -} - -int swAio_dispatch(swAio_event *_event) -{ - if (SwooleAIO.init == 0) - { - swAio_init(); - } - - _event->task_id = SwooleAIO.current_id++; - - swAio_event *event = (swAio_event *) sw_malloc(sizeof(swAio_event)); - if (event == NULL) - { - swWarn("malloc failed"); - return SW_ERR; - } - memcpy(event, _event, sizeof(swAio_event)); - - if (swThreadPool_dispatch(&pool, event, sizeof(event)) < 0) - { - return SW_ERR; - } - else - { - SwooleAIO.task_num++; - return _event->task_id; - } -} - -void swAio_free(void) -{ - if (!SwooleAIO.init) - { - return; - } - swThreadPool_free(&pool); - if (SwooleTG.reactor) - { - SwooleTG.reactor->del(SwooleTG.reactor, _pipe_read); - } - _aio_pipe.close(&_aio_pipe); - SwooleAIO.init = 0; -} -#endif +#include #if __APPLE__ -int swoole_daemon(int nochdir, int noclose) -{ +int swoole_daemon(int nochdir, int noclose) { pid_t pid; - if (!nochdir && chdir("/") != 0) - { - swSysWarn("chdir() failed"); + if (!nochdir && chdir("/") != 0) { + swoole_sys_warning("chdir() failed"); return -1; } - if (!noclose) - { + if (!noclose) { int fd = open("/dev/null", O_RDWR); - if (fd < 0) - { - swSysWarn("open() failed"); + if (fd < 0) { + swoole_sys_warning("open() failed"); return -1; } - if (dup2(fd, 0) < 0 || dup2(fd, 1) < 0 || dup2(fd, 2) < 0) - { + 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; } @@ -217,25 +48,21 @@ int swoole_daemon(int nochdir, int noclose) } pid = swoole_fork(SW_FORK_DAEMON); - if (pid < 0) - { - swSysWarn("fork() failed"); + if (pid < 0) { + swoole_sys_warning("fork() failed"); return -1; } - if (pid > 0) - { + if (pid > 0) { _exit(0); } - if (setsid() < 0) - { - swSysWarn("setsid() failed"); + if (setsid() < 0) { + swoole_sys_warning("setsid() failed"); return -1; } return 0; } #else -int swoole_daemon(int nochdir, int noclose) -{ +int swoole_daemon(int nochdir, int noclose) { if (swoole_fork(SW_FORK_PRECHECK) < 0) { return -1; } @@ -243,314 +70,52 @@ int swoole_daemon(int nochdir, int noclose) } #endif -void swAio_handler_read(swAio_event *event) -{ - int ret = -1; - if (event->lock && flock(event->fd, LOCK_SH) < 0) - { - swSysWarn("flock(%d, LOCK_SH) failed", event->fd); - event->ret = -1; - event->error = errno; - return; - } - while (1) - { - ret = pread(event->fd, event->buf, event->nbytes, event->offset); - if (ret < 0 && errno == EINTR) - { - continue; - } - break; - } - if (event->lock && flock(event->fd, LOCK_UN) < 0) - { - swSysWarn("flock(%d, LOCK_UN) failed", event->fd); - } - if (ret < 0) - { - event->error = errno; - } - event->ret = ret; -} - -void swAio_handler_fread(swAio_event *event) -{ - int ret = -1; - if (event->lock && flock(event->fd, LOCK_SH) < 0) - { - swSysWarn("flock(%d, LOCK_SH) failed", event->fd); - event->ret = -1; - event->error = errno; - return; - } - while (1) - { - ret = read(event->fd, event->buf, event->nbytes); - if (ret < 0 && errno == EINTR) - { - continue; - } - break; - } - if (event->lock && flock(event->fd, LOCK_UN) < 0) - { - swSysWarn("flock(%d, LOCK_UN) failed", event->fd); - } - if (ret < 0) - { - event->error = errno; - } - event->ret = ret; -} - -void swAio_handler_fwrite(swAio_event *event) -{ - int ret = -1; - if (event->lock && flock(event->fd, LOCK_EX) < 0) - { - swSysWarn("flock(%d, LOCK_EX) failed", event->fd); - return; - } - while (1) - { - ret = write(event->fd, event->buf, event->nbytes); - if (ret < 0 && errno == EINTR) - { - continue; - } - break; - } - if (event->flags & SW_AIO_WRITE_FSYNC) - { - if (fsync(event->fd) < 0) - { - swSysWarn("fsync(%d) failed", event->fd); - } - } - if (event->lock && flock(event->fd, LOCK_UN) < 0) - { - swSysWarn("flock(%d, LOCK_UN) failed", event->fd); - } - if (ret < 0) - { - event->error = errno; - } - event->ret = ret; -} - -void swAio_handler_fgets(swAio_event *event) -{ - if (event->lock && flock(event->fd, LOCK_SH) < 0) - { - swSysWarn("flock(%d, LOCK_SH) failed", event->fd); - event->ret = -1; - event->error = errno; - return; - } - - FILE *file = (FILE *) event->req; - char *data = fgets((char*) event->buf, event->nbytes, file); - if (data == NULL) - { - event->ret = -1; - event->error = errno; - event->flags = SW_AIO_EOF; - } - - if (event->lock && flock(event->fd, LOCK_UN) < 0) - { - swSysWarn("flock(%d, LOCK_UN) failed", event->fd); - } -} - -void swAio_handler_read_file(swAio_event *event) -{ - int ret = -1; - int fd = open((char*) event->req, O_RDONLY); - if (fd < 0) - { - swSysWarn("open(%s, O_RDONLY) failed", (char * )event->req); - event->ret = ret; - event->error = errno; - return; - } - struct stat file_stat; - if (fstat(fd, &file_stat) < 0) - { - swSysWarn("fstat(%s) failed", (char * )event->req); - _error: - close(fd); - event->ret = ret; - event->error = errno; - return; - } - if ((file_stat.st_mode & S_IFMT) != S_IFREG) - { - errno = EISDIR; - goto _error; - } - - /** - * lock - */ - if (event->lock && flock(fd, LOCK_SH) < 0) - { - swSysWarn("flock(%d, LOCK_SH) failed", event->fd); - goto _error; - } - /** - * regular file - */ - if (file_stat.st_size == 0) - { - swString *data = swoole_sync_readfile_eof(fd); - if (data == NULL) - { - goto _error; - } - event->ret = data->length; - event->buf = data->str; - sw_free(data); - } - else - { - event->buf = sw_malloc(file_stat.st_size); - if (event->buf == NULL) - { - goto _error; - } - size_t readn = swoole_sync_readfile(fd, event->buf, file_stat.st_size); - event->ret = readn; - } - /** - * unlock - */ - if (event->lock && flock(fd, LOCK_UN) < 0) - { - swSysWarn("flock(%d, LOCK_UN) failed", event->fd); - } - close(fd); - event->error = 0; -} - -void swAio_handler_write_file(swAio_event *event) -{ - int ret = -1; - int fd = open((char*) event->req, event->flags, 0644); - if (fd < 0) - { - swSysWarn("open(%s, %d) failed", (char * )event->req, event->flags); - event->ret = ret; - event->error = errno; - return; - } - if (event->lock && flock(fd, LOCK_EX) < 0) - { - swSysWarn("flock(%d, LOCK_EX) failed", event->fd); - event->ret = ret; - event->error = errno; - close(fd); - return; - } - size_t written = swoole_sync_writefile(fd, event->buf, event->nbytes); - if (event->flags & SW_AIO_WRITE_FSYNC) - { - if (fsync(fd) < 0) - { - swSysWarn("fsync(%d) failed", event->fd); - } - } - if (event->lock && flock(fd, LOCK_UN) < 0) - { - swSysWarn("flock(%d, LOCK_UN) failed", event->fd); - } - close(fd); - event->ret = written; - event->error = 0; +#ifdef HAVE_CPU_AFFINITY +int swoole_set_cpu_affinity(cpu_set_t *set) { +#ifdef __FreeBSD__ + return cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(*set), set); +#else + return sched_setaffinity(getpid(), sizeof(*set), set); +#endif } -void swAio_handler_write(swAio_event *event) -{ - int ret = -1; - if (event->lock && flock(event->fd, LOCK_EX) < 0) - { - swSysWarn("flock(%d, LOCK_EX) failed", event->fd); - return; - } - while (1) - { - ret = pwrite(event->fd, event->buf, event->nbytes, event->offset); - if (ret < 0 && errno == EINTR) - { - continue; - } - break; - } - if (event->flags & SW_AIO_WRITE_FSYNC) - { - if (fsync(event->fd) < 0) - { - swSysWarn("fsync(%d) failed", event->fd); - } - } - if (event->lock && flock(event->fd, LOCK_UN) < 0) - { - swSysWarn("flock(%d, LOCK_UN) failed", event->fd); - } - if (ret < 0) - { - event->error = errno; - } - event->ret = ret; +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 -void swAio_handler_gethostbyname(swAio_event *event) -{ - struct in_addr addr_v4; - struct in6_addr addr_v6; - int ret; +namespace swoole { +namespace async { -#ifndef HAVE_GETHOSTBYNAME2_R - SwooleG.lock.lock(&SwooleG.lock); -#endif - if (event->flags == AF_INET6) - { - ret = swoole_gethostbyname(AF_INET6, (char*) event->buf, (char *) &addr_v6); - } - else - { - ret = swoole_gethostbyname(AF_INET, (char*) event->buf, (char *) &addr_v4); - } - bzero(event->buf, event->nbytes); -#ifndef HAVE_GETHOSTBYNAME2_R - SwooleG.lock.unlock(&SwooleG.lock); -#endif +void handler_gethostbyname(AsyncEvent *event) { + 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) - { + if (ret < 0) { event->error = SW_ERROR_DNSLOOKUP_RESOLVE_FAILED; - } - else - { - if (inet_ntop(event->flags == AF_INET6 ? AF_INET6 : AF_INET, - event->flags == AF_INET6 ? (void *) &addr_v6 : (void *) &addr_v4, (char*) event->buf, event->nbytes) == NULL) - { + } else { + if (inet_ntop(req->family, addr, req->addr, req->addr_len) == nullptr) { ret = -1; event->error = SW_ERROR_BAD_IPV6_ADDRESS; - } - else - { + } else { event->error = 0; ret = 0; } } - event->ret = ret; + event->retval = ret; } -void swAio_handler_getaddrinfo(swAio_event *event) -{ - swRequest_getaddrinfo *req = (swRequest_getaddrinfo *) event->req; - event->ret = swoole_getaddrinfo(req); +void handler_getaddrinfo(AsyncEvent *event) { + auto req = dynamic_cast(event->data.get()); + event->retval = network::getaddrinfo(req); event->error = req->error; } +} // namespace async +} // namespace swoole diff --git a/src/os/file.cc b/src/os/file.cc new file mode 100644 index 00000000000..612145a50ff --- /dev/null +++ b/src/os/file.cc @@ -0,0 +1,200 @@ +/* + +----------------------------------------------------------------------+ + | 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_file.h" + +int swoole_tmpfile(char *filename) { +#if defined(HAVE_MKOSTEMP) && defined(HAVE_EPOLL) + int tmp_fd = mkostemp(filename, O_WRONLY | O_CREAT); +#else + int tmp_fd = mkstemp(filename); +#endif + + if (tmp_fd < 0) { + swoole_sys_warning("mkstemp(%s) failed", filename); + return SW_ERR; + } else { + return tmp_fd; + } +} + +namespace swoole { + +ssize_t file_get_size(FILE *fp) { + fflush(fp); + return file_get_size(fileno(fp)); +} + +ssize_t file_get_size(const std::string &filename) { + File file(filename, File::READ); + if (!file.ready()) { + swoole_set_last_error(errno); + return -1; + } + return file.get_size(); +} + +ssize_t file_get_size(int fd) { + FileStatus file_stat; + if (fstat(fd, &file_stat) < 0) { + swoole_set_last_error(errno); + return -1; + } + if (!S_ISREG(file_stat.st_mode)) { + swoole_set_last_error(EISDIR); + return -1; + } + return file_stat.st_size; +} + +std::shared_ptr file_get_contents(const std::string &filename) { + File fp(filename, O_RDONLY); + if (!fp.ready()) { + swoole_sys_warning("open(%s) failed", filename.c_str()); + return nullptr; + } + + ssize_t filesize = fp.get_size(); + if (filesize < 0) { + return nullptr; + } else if (filesize == 0) { + swoole_error_log(SW_LOG_TRACE, SW_ERROR_FILE_EMPTY, "file[%s] is empty", filename.c_str()); + return nullptr; + } else if (filesize > SW_MAX_FILE_CONTENT) { + swoole_error_log(SW_LOG_WARNING, SW_ERROR_FILE_TOO_LARGE, "file[%s] is too large", filename.c_str()); + return nullptr; + } + + std::shared_ptr content = std::make_shared(filesize + 1); + ssize_t read_bytes = fp.read_all(content->str, filesize); + content->length = read_bytes; + content->str[read_bytes] = '\0'; + return content; +} + +File make_tmpfile() { + char *tmpfile = sw_tg_buffer()->str; + size_t l = swoole_strlcpy(tmpfile, SwooleG.task_tmpfile.c_str(), SW_TASK_TMP_PATH_SIZE); + int tmp_fd = swoole_tmpfile(tmpfile); + if (tmp_fd < 0) { + return File(-1); + } else { + return File(tmp_fd, std::string(tmpfile, l)); + } +} + +bool file_put_contents(const std::string &filename, const char *content, size_t length) { + if (length <= 0) { + swoole_error_log(SW_LOG_TRACE, SW_ERROR_FILE_EMPTY, "content is empty"); + return false; + } + if (length > SW_MAX_FILE_CONTENT) { + swoole_error_log(SW_LOG_WARNING, SW_ERROR_FILE_TOO_LARGE, "content is too large"); + return false; + } + File file(filename, O_WRONLY | O_TRUNC | O_CREAT, 0666); + if (!file.ready()) { + 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) { + ssize_t n; + if (flags_ & APPEND) { + n = write((char *) data + written_bytes, len - written_bytes); + } else { + n = pwrite((char *) data + written_bytes, len - written_bytes, written_bytes); + } + if (n > 0) { + written_bytes += n; + } else if (n == 0) { + break; + } else { + if (errno == EINTR) { + continue; + } else if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { + swoole_sys_warning("pwrite(%d, %p, %lu, %lu) failed", fd_, data, len - written_bytes, written_bytes); + } + break; + } + } + return written_bytes; +} + +size_t File::read_all(void *buf, size_t len) { + size_t read_bytes = 0; + while (read_bytes < len) { + ssize_t n = pread((char *) buf + read_bytes, len - read_bytes, read_bytes); + if (n > 0) { + read_bytes += n; + } else if (n == 0) { + break; + } else { + if (errno == EINTR) { + continue; + } else if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { + swoole_sys_warning("pread(%d, %p, %lu, %lu) failed", fd_, buf, len - read_bytes, read_bytes); + } + break; + } + } + 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); + while (1) { + n = read(data->str + data->length, data->size - data->length); + if (n <= 0) { + return data; + } else { + if (!data->grow((size_t) n)) { + return data; + } + } + } + return data; +} + +} // namespace swoole diff --git a/src/os/msg_queue.cc b/src/os/msg_queue.cc index 2531a2571a4..89d98448a07 100644 --- a/src/os/msg_queue.cc +++ b/src/os/msg_queue.cc @@ -10,136 +10,111 @@ | to obtain it through the world-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 -int swMsgQueue_free(swMsgQueue *q) -{ - if (msgctl(q->msg_id, IPC_RMID, 0) < 0) - { - swSysWarn("msgctl(%d, IPC_RMID) failed", q->msg_id); - return SW_ERR; +namespace swoole { + +bool MsgQueue::destroy() { + if (msgctl(msg_id_, IPC_RMID, 0) < 0) { + swoole_sys_warning("msgctl(%d, IPC_RMID) failed", msg_id_); + return false; } - return SW_OK; + msg_id_ = -1; + return true; } -void swMsgQueue_set_blocking(swMsgQueue *q, uint8_t blocking) -{ - if (blocking == 0) - { - q->flags = q->flags | IPC_NOWAIT; - } - else - { - q->flags = q->flags & (~IPC_NOWAIT); +void MsgQueue::set_blocking(bool blocking) { + if (blocking == 0) { + flags_ = flags_ | IPC_NOWAIT; + } else { + flags_ = flags_ & (~IPC_NOWAIT); } } -int swMsgQueue_create(swMsgQueue *q, int blocking, key_t msg_key, int perms) -{ - if (perms <= 0 || perms >= 01000) - { +MsgQueue::MsgQueue(key_t msg_key, bool blocking, int perms) { + if (perms <= 0 || perms >= 01000) { perms = 0666; } - int msg_id; - msg_id = msgget(msg_key, IPC_CREAT | perms); - if (msg_id < 0) - { - swSysWarn("msgget() failed"); - return SW_ERR; + msg_key_ = msg_key; + flags_ = 0; + perms_ = perms; + blocking_ = blocking; + msg_id_ = msgget(msg_key, IPC_CREAT | perms); + if (msg_id_ < 0) { + swoole_sys_warning("msgget() failed"); + } else { + set_blocking(blocking); } - else - { - bzero(q, sizeof(swMsgQueue)); - q->msg_id = msg_id; - q->perms = perms; - q->blocking = blocking; - swMsgQueue_set_blocking(q, blocking); +} + +MsgQueue::~MsgQueue() { + // private queue must be destroyed + if (msg_key_ == IPC_PRIVATE && msg_id_ >= 0) { + destroy(); } - return 0; } -int swMsgQueue_pop(swMsgQueue *q, swQueue_data *data, int length) -{ - int ret = msgrcv(q->msg_id, data, length, data->mtype, q->flags); - if (ret < 0) - { - SwooleG.error = errno; - if (errno != ENOMSG && errno != EINTR) - { - swSysWarn("msgrcv(%d, %d, %ld) failed", q->msg_id, length, data->mtype); +ssize_t MsgQueue::pop(QueueNode *data, size_t mdata_size) { + ssize_t ret = msgrcv(msg_id_, data, mdata_size, data->mtype, flags_); + if (ret < 0) { + swoole_set_last_error(errno); + if (errno != ENOMSG && errno != EINTR) { + swoole_sys_warning("msgrcv(%d, %zu, %ld) failed", msg_id_, mdata_size, data->mtype); } } return ret; } -int swMsgQueue_push(swMsgQueue *q, swQueue_data *in, int length) -{ - int ret; - - while (1) - { - ret = msgsnd(q->msg_id, in, length, q->flags); - if (ret < 0) - { - SwooleG.error = errno; - if (errno == EINTR) - { - continue; - } - else if (errno == EAGAIN) - { - return -1; - } - else - { - swSysWarn("msgsnd(%d, %d, %ld) failed", q->msg_id, length, in->mtype); - return -1; - } +bool MsgQueue::push(QueueNode *in, size_t mdata_length) { + while (1) { + if (msgsnd(msg_id_, in, mdata_length, flags_) == 0) { + return true; + } + if (errno == EINTR) { + continue; } - else - { - return ret; + if (errno != EAGAIN) { + swoole_sys_warning("msgsnd(%d, %lu, %ld) failed", msg_id_, mdata_length, in->mtype); } + swoole_set_last_error(errno); + break; } - return 0; + return false; } -int swMsgQueue_stat(swMsgQueue *q, int *queue_num, int *queue_bytes) -{ +bool MsgQueue::stat(size_t *queue_num, size_t *queue_bytes) { struct msqid_ds __stat; - if (msgctl(q->msg_id, IPC_STAT, &__stat) == 0) - { + if (msgctl(msg_id_, IPC_STAT, &__stat) == 0) { *queue_num = __stat.msg_qnum; +#ifndef __NetBSD__ *queue_bytes = __stat.msg_cbytes; - return 0; - } - else - { - return -1; +#else + *queue_bytes = __stat._msg_cbytes; +#endif + return true; + } else { + return false; } } -int swMsgQueue_set_capacity(swMsgQueue *q, int queue_bytes) -{ +bool MsgQueue::set_capacity(size_t queue_bytes) { struct msqid_ds __stat; - if (msgctl(q->msg_id, IPC_STAT, &__stat) != 0) - { - return -1; + if (msgctl(msg_id_, IPC_STAT, &__stat) != 0) { + return false; } __stat.msg_qbytes = queue_bytes; - if (msgctl(q->msg_id, IPC_SET, &__stat)) - { - swSysWarn("msgctl(msqid=%d, IPC_SET, msg_qbytes=%d) failed", q->msg_id, queue_bytes); - return -1; + if (msgctl(msg_id_, IPC_SET, &__stat)) { + swoole_sys_warning("msgctl(msqid=%d, IPC_SET, msg_qbytes=%lu) failed", msg_id_, queue_bytes); + return false; } - return 0; + return true; } - +} // namespace swoole diff --git a/src/os/pipe.cc b/src/os/pipe.cc new file mode 100644 index 00000000000..4a7dbf53499 --- /dev/null +++ b/src/os/pipe.cc @@ -0,0 +1,100 @@ +/* + +----------------------------------------------------------------------+ + | 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_pipe.h" +#include "swoole_socket.h" + +namespace swoole { +using network::Socket; + +bool SocketPair::init_socket(int master_fd, int worker_fd) { + master_socket = make_socket(master_fd, SW_FD_PIPE); + if (master_socket == nullptr) { + _error: + ::close(master_fd); + ::close(worker_fd); + return false; + } + worker_socket = make_socket(worker_fd, SW_FD_PIPE); + if (worker_socket == nullptr) { + master_socket->free(); + ::close(worker_fd); + goto _error; + } + set_blocking(blocking); + return true; +} + +Pipe::Pipe(bool _blocking) : SocketPair(_blocking) { + if (pipe(socks) < 0) { + swoole_sys_warning("pipe() failed"); + return; + } + if (!init_socket(socks[1], socks[0])) { + return; + } +} + +ssize_t SocketPair::read(void *data, size_t length) { + if (blocking && timeout > 0) { + if (worker_socket->wait_event(timeout * 1000, SW_EVENT_READ) < 0) { + return SW_ERR; + } + } + return worker_socket->read(data, 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_write_error(errno) == SW_WAIT) { + if (master_socket->wait_event(timeout * 1000, SW_EVENT_READ) < 0) { + return SW_ERR; + } + n = master_socket->write(data, length); + } + return n; +} + +bool SocketPair::close(int which) { + if (which == SW_PIPE_CLOSE_MASTER) { + if (master_socket == nullptr) { + return false; + } + master_socket->free(); + master_socket = nullptr; + } else if (which == SW_PIPE_CLOSE_WORKER) { + if (worker_socket == nullptr) { + return false; + } + worker_socket->free(); + worker_socket = nullptr; + } else { + close(SW_PIPE_CLOSE_MASTER); + close(SW_PIPE_CLOSE_WORKER); + } + return true; +} + +SocketPair::~SocketPair() { + if (master_socket) { + close(SW_PIPE_CLOSE_MASTER); + } + if (worker_socket) { + close(SW_PIPE_CLOSE_WORKER); + } +} + +} // namespace swoole diff --git a/src/os/process_pool.cc b/src/os/process_pool.cc new file mode 100644 index 00000000000..634be9ac1ac --- /dev/null +++ b/src/os/process_pool.cc @@ -0,0 +1,1102 @@ +/* + +----------------------------------------------------------------------+ + | 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_memory.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; + +static inline swReturnCode catch_system_error(int error) { + switch (error) { + case SW_SUCCESS: + case EAGAIN: + case EINTR: + return SW_CONTINUE; + default: + return SW_ERROR; + } +} + +void ProcessPool::kill_timeout_worker(Timer *timer, TimerNode *tnode) { + uint32_t i; + pid_t reload_worker_pid = 0; + ProcessPool *pool = (ProcessPool *) tnode->data; + pool->reloading = false; + + for (i = 0; i < pool->worker_num; i++) { + if (i >= pool->reload_worker_i) { + reload_worker_pid = pool->reload_workers[i].pid; + if (swoole_kill(reload_worker_pid, 0) == -1) { + continue; + } + if (swoole_kill(reload_worker_pid, SIGKILL) < 0) { + swoole_sys_warning( + "failed to force kill worker process(pid=%d, id=%d)", pool->reload_workers[i].pid, i); + } else { + swoole_warning("force kill worker process(pid=%d, id=%d)", pool->reload_workers[i].pid, i); + } + } + } + errno = 0; + pool->reload_worker_i = 0; + pool->reload_init = false; +} + +/** + * Process manager + */ +int ProcessPool::create(uint32_t _worker_num, key_t _msgqueue_key, swIPCMode _ipc_mode) { + worker_num = _worker_num; + /** + * Shared memory is used here + */ + workers = (Worker *) sw_mem_pool()->alloc(_worker_num * sizeof(Worker)); + if (workers == nullptr) { + swoole_sys_warning("malloc[1] failed"); + return SW_ERR; + } + + 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) { + pipes = new std::vector>; + SW_LOOP_N(_worker_num) { + auto sock = new UnixSocket(true, SOCK_DGRAM); + if (!sock->ready()) { + delete sock; + delete pipes; + pipes = nullptr; + return SW_ERR; + } + 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; + } + + 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; + + SW_LOOP_N(_worker_num) { + workers[i].pool = this; + } + + return SW_OK; +} + +int ProcessPool::create_message_box(size_t memory_size) { + message_box = Channel::make(memory_size, sizeof(EventData), SW_CHAN_LOCK | SW_CHAN_SHM); + if (message_box == nullptr) { + return SW_ERR; + } + return SW_OK; +} + +int ProcessPool::create_message_bus() { + if (ipc_mode != SW_IPC_UNIXSOCK) { + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_OPERATION_NOT_SUPPORT, "not support, ipc_mode must be SW_IPC_UNIXSOCK"); + return SW_ERR; + } + if (message_bus) { + swoole_error_log(SW_LOG_WARNING, SW_ERROR_WRONG_OPERATION, "the message bus has been created"); + return SW_ERR; + } + sw_atomic_long_t *msg_id = (sw_atomic_long_t *) sw_mem_pool()->alloc(sizeof(sw_atomic_long_t)); + if (msg_id == nullptr) { + swoole_sys_warning("malloc[1] failed"); + return SW_ERR; + } + *msg_id = 1; + message_bus = new MessageBus(); + message_bus->set_id_generator([msg_id]() { return sw_atomic_fetch_add(msg_id, 1); }); + size_t ipc_max_size; +#ifndef __linux__ + ipc_max_size = SW_IPC_MAX_SIZE; +#else + int bufsize; + /** + * Get the maximum ipc[unix socket with dgram] transmission length + */ + if (workers[0].pipe_master->get_option(SOL_SOCKET, SO_SNDBUF, &bufsize) != 0) { + bufsize = SW_IPC_MAX_SIZE; + } + ipc_max_size = SW_MIN(bufsize, SW_IPC_BUFFER_MAX_SIZE) - SW_DGRAM_HEADER_SIZE; +#endif + message_bus->set_buffer_size(ipc_max_size); + if (!message_bus->alloc_buffer()) { + return SW_ERR; + } + return SW_OK; +} + +int ProcessPool::listen(const char *socket_file, int blacklog) { + if (ipc_mode != SW_IPC_SOCKET) { + 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; + } + return SW_OK; +} + +int ProcessPool::listen(const char *host, int port, int blacklog) { + if (ipc_mode != 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; + } + return SW_OK; +} + +void ProcessPool::set_protocol(enum ProtocolType _protocol_type) { + switch (_protocol_type) { + case SW_PROTOCOL_TASK: + main_loop = run_with_task_protocol; + break; + case SW_PROTOCOL_STREAM: + main_loop = run_with_stream_protocol; + break; + case SW_PROTOCOL_MESSAGE: + main_loop = run_with_message_protocol; + break; + default: + abort(); + break; + } + protocol_type_ = _protocol_type; +} + +int ProcessPool::start_check() { + if (ipc_mode == SW_IPC_SOCKET && (stream_info_ == nullptr || stream_info_->socket == 0)) { + swoole_warning("must first listen to an tcp port"); + return SW_ERR; + } + + running = started = true; + master_pid = getpid(); + reload_workers = new Worker[worker_num](); + swoole_set_process_type(SW_PROCESS_MASTER); + + 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; + } + } + + 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; + } + } + + return SW_OK; +} + +int ProcessPool::schedule() { + // schedule by system message queue + if (schedule_by_sysvmsg) { + return 0; + } + + uint32_t i, target_worker_id = 0; + uint8_t found = 0; + + 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].is_idle()) { + found = 1; + break; + } + } + if (found == 0) { + scheduler_warning = 1; + } + return target_worker_id; +} + +int ProcessPool::response(const char *data, int length) { + if (stream_info_ == nullptr || stream_info_->last_connection == nullptr || + stream_info_->response_buffer == nullptr) { + swoole_set_last_error(SW_ERROR_INVALID_PARAMS); + return SW_ERR; + } + return stream_info_->response_buffer->append(data, length); +} + +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) { + Stream *stream = Stream::create(stream_info_->socket_file, 0, SW_SOCK_UNIX_STREAM); + if (!stream) { + return SW_ERR; + } + stream->response = nullptr; + if (stream->send((char *) data, data->size()) < 0) { + stream->cancel = 1; + delete stream; + return SW_ERR; + } + return SW_OK; + } + + if (*dst_worker_id < 0) { + *dst_worker_id = schedule(); + } + + *dst_worker_id += start_id; + worker = get_worker(*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 SW_OK; +} + +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) { + return dispatch_blocking((char *) data, data->size()); + } + + if (*dst_worker_id < 0) { + *dst_worker_id = schedule(); + } + + *dst_worker_id += start_id; + Worker *worker = get_worker(*dst_worker_id); + + 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; + } + }); + } +} + +void ProcessPool::kill_all_workers(int signo) { + SW_LOOP_N(worker_num) { + swoole_kill(workers[i].pid, signo); + } +} + +void ProcessPool::shutdown() { + uint32_t i; + int status; + Worker *worker; + running = 0; + + if (onShutdown) { + onShutdown(this); + } + + // concurrent kill + for (i = 0; i < worker_num; i++) { + worker = &workers[i]; + if (swoole_kill(worker->pid, SIGTERM) < 0) { + swoole_sys_warning("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]; + 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; +} + +pid_t ProcessPool::spawn(Worker *worker) { + pid_t pid = swoole_fork(0); + int ret_code = 0; + + switch (pid) { + // child + case 0: + 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); + } + if (main_loop) { + ret_code = main_loop(this, worker); + } + if (onWorkerStop != nullptr) { + onWorkerStop(this, worker); + } + exit(ret_code); + break; + case -1: + swoole_sys_warning("fork() failed"); + break; + // parent + default: + // remove old process + if (worker->pid) { + map_->erase(worker->pid); + } + worker->pid = pid; + // insert new process + map_->emplace(std::make_pair(pid, worker)); + break; + } + return pid; +} + +void ProcessPool::set_max_request(uint32_t _max_request, uint32_t _max_request_grace) { + max_request = _max_request; + max_request_grace = _max_request_grace; +} + +bool ProcessPool::is_worker_running(Worker *worker) { + return running && !worker->is_shutdown() && !worker->has_exceeded_max_request(); +} + +int ProcessPool::run_with_task_protocol(ProcessPool *pool, Worker *worker) { + struct { + long mtype; + EventData buf; + } out{}; + + ssize_t n = 0; + + /** + * Use from_fd save the task_worker->id + */ + out.buf.info.server_fd = worker->id; + + if (pool->schedule_by_sysvmsg) { + out.mtype = 0; + } else { + out.mtype = worker->id + 1; + } + + 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 && 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 (catch_system_error(errno) == SW_ERROR) { + swoole_sys_warning( + "[Worker#%d] accept(%d) failed", worker->id, pool->stream_info_->socket->get_fd()); + break; + } else { + goto _end; + } + } + n = Stream::recv_blocking(conn, (void *) &out.buf, sizeof(out.buf)); + if (n <= 0) { + conn->free(); + goto _end; + } + pool->stream_info_->last_connection = conn; + } else { + 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; + } + } + + if (n < 0) { + goto _end; + } + 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)); + } + if (pool->use_socket && pool->stream_info_->last_connection) { + int _end = 0; + pool->stream_info_->last_connection->send_blocking((void *) &_end, sizeof(_end)); + pool->stream_info_->last_connection->free(); + pool->stream_info_->last_connection = nullptr; + } + + _end: + if (sw_timer()) { + sw_timer()->select(); + } + } + + SwooleTG.timer_scheduler = nullptr; + + return SW_OK; +} + +static int ProcessPool_recv_packet(Reactor *reactor, Event *event) { + ProcessPool *pool = (ProcessPool *) reactor->ptr; + ssize_t n = event->socket->read(pool->packet_buffer, pool->max_packet_size_); + if (n < 0 && errno != EINTR) { + swoole_sys_warning("failed to read(%d) pipe", event->fd); + } + RecvData msg{}; + msg.info.reactor_id = -1; + msg.info.len = n; + msg.data = pool->packet_buffer; + pool->onMessage(pool, &msg); + return SW_OK; +} + +static int ProcessPool_recv_message(Reactor *reactor, Event *event) { + ProcessPool *pool = (ProcessPool *) reactor->ptr; + if (pool->message_bus->read(event->socket) <= 0) { + return SW_OK; + } + auto pipe_buffer = pool->message_bus->get_buffer(); + auto packet = pool->message_bus->get_packet(); + RecvData msg; + msg.info = pipe_buffer->info; + msg.info.len = packet.length; + msg.data = packet.data; + pool->onMessage(pool, &msg); + pool->message_bus->pop(); + return SW_OK; +} + +int ProcessPool::run_async(ProcessPool *pool, Worker *worker) { + if (pool->ipc_mode == SW_IPC_UNIXSOCK && pool->onMessage) { + swoole_event_add(worker->pipe_worker, SW_EVENT_READ); + if (pool->message_bus) { + swoole_event_set_handler(SW_FD_PIPE, ProcessPool_recv_message); + } else { + pool->packet_buffer = new char[pool->max_packet_size_]; + if (pool->stream_info_) { + pool->stream_info_->response_buffer = new String(SW_BUFFER_SIZE_STD); + } + swoole_event_set_handler(SW_FD_PIPE, ProcessPool_recv_packet); + } + } + return swoole_event_wait(); +} + +int ProcessPool::run_with_stream_protocol(ProcessPool *pool, Worker *worker) { + ssize_t n; + 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; + + 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); + /** + * 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; + } + msg.data = outbuf->mdata; + outbuf->mtype = 0; + } else if (pool->use_socket) { + Socket *conn = pool->stream_info_->socket->accept(); + if (conn == nullptr) { + 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; + } + } + uint32_t packet_len = 0; + if (conn->recv_blocking(&packet_len, sizeof(packet_len), MSG_WAITALL) <= 0) { + goto _close; + } + 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_) { + goto _close; + } + if (conn->recv_blocking(pool->packet_buffer, n, MSG_WAITALL) <= 0) { + _close: + conn->free(); + goto _end; + } + msg.data = pool->packet_buffer; + pool->stream_info_->last_connection = conn; + } else { + 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; + } + msg.data = pool->packet_buffer; + } + + if (n < 0) { + goto _end; + } + + 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; + if (resp_buf && resp_buf->length > 0) { + int _l = htonl(resp_buf->length); + pool->stream_info_->last_connection->send_blocking(&_l, sizeof(_l)); + pool->stream_info_->last_connection->send_blocking(resp_buf->str, resp_buf->length); + resp_buf->clear(); + } + pool->stream_info_->last_connection->free(); + pool->stream_info_->last_connection = nullptr; + } + + _end: + if (sw_timer()) { + sw_timer()->select(); + } + } + + SwooleTG.timer_scheduler = nullptr; + + return SW_OK; +} + +int ProcessPool::run_with_message_protocol(ProcessPool *pool, Worker *worker) { + if (pool->ipc_mode != SW_IPC_UNIXSOCK) { + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_OPERATION_NOT_SUPPORT, "not support, ipc_mode must be SW_IPC_UNIXSOCK"); + return SW_ERR; + } + + int read_timeout_ms = -1; + SwooleTG.timer_scheduler = [&read_timeout_ms](Timer *timer, long exec_msec) -> int { + read_timeout_ms = exec_msec; + return SW_OK; + }; + + auto fn = [&]() -> int { + if (worker->pipe_worker->wait_event(read_timeout_ms, SW_EVENT_READ) < 0) { + return errno == EINTR ? 0 : -1; + } + if (pool->message_bus->read(worker->pipe_worker) < 0) { + return errno == EINTR ? 0 : -1; + } + auto pipe_buffer = pool->message_bus->get_buffer(); + auto packet = pool->message_bus->get_packet(); + RecvData msg; + msg.info = pipe_buffer->info; + msg.info.len = packet.length; + msg.data = packet.data; + pool->onMessage(pool, &msg); + pool->message_bus->pop(); + return 1; + }; + + if (pool->message_bus == nullptr) { + pool->create_message_bus(); + } + + worker->pipe_worker->dont_restart = 1; + + while (pool->is_worker_running(worker)) { + switch (fn()) { + case 0: + if (sw_timer()) { + sw_timer()->select(); + } + break; + case 1: + break; + case -1: + default: + swoole_sys_warning("[Worker #%d]failed to read data from pipe", worker->id); + return SW_OK; + } + } + + SwooleTG.timer_scheduler = nullptr; + + return SW_OK; +} + +void ProcessPool::add_worker(Worker *worker) { + map_->emplace(std::make_pair(worker->pid, worker)); +} + +bool ProcessPool::detach() { + if (!running || !message_box) { + return false; + } + + 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) { + 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 (exit_status.get_pid() < 0) { + if (!running) { + break; + } + if (!reloading) { + if (errno > 0 && errno != EINTR) { + swoole_sys_warning("wait() failed"); + } + continue; + } else { + if (!reload_init) { + reload_init = true; + memcpy(reload_workers, workers, sizeof(Worker) * worker_num); + if (max_wait_time) { + swoole_timer_add((long) (max_wait_time * 1000), false, kill_timeout_worker, this); + } + } + goto _kill_worker; + } + } + + if (running) { + Worker *exit_worker = get_worker_by_pid(exit_status.get_pid()); + if (exit_worker == nullptr) { + if (onWorkerNotFound) { + onWorkerNotFound(this, exit_status); + } else { + swoole_warning("unknown worker[pid=%d]", exit_status.get_pid()); + } + continue; + } + + 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) { + swoole_sys_warning("Fork worker process failed"); + return SW_ERR; + } + map_->erase(exit_status.get_pid()); + if (exit_status.get_pid() == reload_worker_pid) { + reload_worker_i++; + } + } + // reload worker + _kill_worker: + if (reloading) { + // reload finish + if (reload_worker_i >= worker_num) { + reloading = reload_init = false; + reload_worker_pid = reload_worker_i = 0; + continue; + } + reload_worker_pid = reload_workers[reload_worker_i].pid; + if (swoole_kill(reload_worker_pid, SIGTERM) < 0) { + if (errno == ECHILD) { + reload_worker_i++; + goto _kill_worker; + } + swoole_sys_warning("kill(%d) failed", reload_workers[reload_worker_i].pid); + continue; + } + } + } + return SW_OK; +} + +void ProcessPool::destroy() { + if (pipes) { + delete pipes; + pipes = nullptr; + } + + if (queue) { + delete queue; + queue = nullptr; + } + + if (stream_info_) { + if (stream_info_->socket) { + unlink(stream_info_->socket_file); + sw_free((void *) stream_info_->socket_file); + } + if (stream_info_->socket) { + stream_info_->socket->free(); + stream_info_->socket = nullptr; + } + if (stream_info_->response_buffer) { + 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 421abce185b..aa16756ad2f 100644 --- a/src/os/sendfile.cc +++ b/src/os/sendfile.cc @@ -10,40 +10,38 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "swoole.h" - -#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) -{ - int ret; +ssize_t swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { + ssize_t ret; #ifdef __MACH__ struct sf_hdtr hdtr; - hdtr.headers = NULL; + hdtr.headers = nullptr; hdtr.hdr_cnt = 0; - hdtr.trailers = NULL; + hdtr.trailers = nullptr; hdtr.trl_cnt = 0; #else off_t sent_bytes; #endif - - _do_sendfile: +_do_sendfile: #ifdef __MACH__ ret = sendfile(in_fd, out_fd, *offset, (off_t *) &size, &hdtr, 0); #else ret = sendfile(in_fd, out_fd, *offset, size, 0, &sent_bytes, 0); #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); + // sent_bytes = (off_t)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; @@ -51,53 +49,36 @@ int swoole_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) *offset += sent_bytes; #endif - if (ret == -1) - { - if (errno == EINTR) - { + if (ret == -1) { + if (errno == EINTR) { goto _do_sendfile; - } - else - { + } else { return ret; } - } - else if (ret == 0) - { + } else if (ret == 0) { return size; - } - else - { - swSysWarn("sendfile failed"); + } else { + 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]; - int readn = size > sizeof(buf) ? sizeof(buf) : size; - - int ret; - int n = pread(in_fd, buf, readn, *offset); + size_t readn = size > sizeof(buf) ? sizeof(buf) : size; + ssize_t n = pread(in_fd, buf, readn, *offset); - if (n > 0) - { - ret = write(out_fd, buf, n); - if (ret < 0) - { - swSysWarn("write() failed"); - } - else - { + if (n > 0) { + ssize_t ret = write(out_fd, buf, n); + if (ret < 0) { + swoole_sys_warning("write() failed"); + } else { *offset += ret; } return ret; - } - else - { - swSysWarn("pread() failed"); + } else { + swoole_sys_warning("pread() failed"); return SW_ERR; } } diff --git a/src/os/signal.cc b/src/os/signal.cc index 59f79f02b3f..cab4a16e1fc 100644 --- a/src/os/signal.cc +++ b/src/os/signal.cc @@ -10,54 +10,67 @@ | to obtain it through the world-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 "coroutine.h" -#include "coroutine_system.h" +#include "swoole_signal.h" +#include "swoole_socket.h" +#include "swoole_reactor.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 -struct swSignal -{ - swSignalHandler handler; - uint16_t signo; - uint16_t active; -}; +using swoole::Event; +using swoole::Reactor; +using swoole::Signal; +using swoole::SignalHandler; +using swoole::network::Socket; #ifdef HAVE_SIGNALFD -static void swSignalfd_set(int signo, swSignalHandler handler); -static void swSignalfd_clear(); -static int swSignalfd_onSignal(swReactor *reactor, swEvent *event); - -static sigset_t signalfd_mask; -static int signal_fd = 0; -static swSocket *signal_socket = NULL; +static SignalHandler swoole_signalfd_set(int signo, SignalHandler handler); +static bool swoole_signalfd_create(); +static bool swoole_signalfd_setup(Reactor *reactor); +static int swoole_signalfd_event_callback(Reactor *reactor, Event *event); +static void swoole_signalfd_clear(); #endif #ifdef HAVE_KQUEUE -static void swKqueueSignal_set(int signo, swSignalHandler handler); +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) - { + if (strchr(buf, ':') == 0) { size_t len = strlen(buf); snprintf(buf + len, sizeof(buf) - len, ": %d", sig); } @@ -65,269 +78,267 @@ char* swSignal_str(int sig) } /** - * clear all singal + * 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, NULL); - if (ret < 0) - { - swSysWarn("pthread_sigmask() failed"); + int ret = pthread_sigmask(SIG_BLOCK, &mask, nullptr); + if (ret < 0) { + swoole_sys_warning("pthread_sigmask() failed"); } } /** - * setup signal + * set new signal handler and return origin signal handler */ -swSignalHandler swSignal_set(int sig, swSignalHandler func, int restart, int mask) -{ - //ignore - if (func == NULL) - { +SignalHandler swoole_signal_set(int signo, SignalHandler func, int restart, int mask) { + // ignore + if (func == nullptr) { func = SIG_IGN; } - //clear - else if ((long) func == -1) - { + // clear + else if ((long) func == -1) { func = SIG_DFL; } - struct sigaction act, oact; + if (func == SIG_IGN || func == SIG_DFL) { + signals[signo].handler = nullptr; + signals[signo].activated = false; + } + + struct sigaction act { + }, oact{}; act.sa_handler = func; - if (mask) - { + if (mask) { sigfillset(&act.sa_mask); - } - else - { + } else { sigemptyset(&act.sa_mask); } act.sa_flags = 0; - if (sigaction(sig, &act, &oact) < 0) - { - return NULL; + if (sigaction(signo, &act, &oact) < 0) { + return nullptr; } return oact.sa_handler; } -void swSignal_add(int signo, swSignalHandler handler) -{ +SW_API bool swoole_signal_isset(int signo) { + return signals[signo].handler && signals[signo].activated; +} + +/** + * set new signal handler and return origin signal handler + */ +SignalHandler swoole_signal_set(int signo, SignalHandler handler) { #ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd) - { - swSignalfd_set(signo, handler); - } - else + if (SwooleG.enable_signalfd && swoole_event_is_available()) { + return swoole_signalfd_set(signo, handler); + } else #endif { #ifdef HAVE_KQUEUE // SIGCHLD can not be monitored by kqueue, if blocked by SIG_IGN // see https://www.freebsd.org/cgi/man.cgi?kqueue // if there's no main reactor, signals cannot be monitored either - if (signo != SIGCHLD && SwooleTG.reactor) - { - swKqueueSignal_set(signo, handler); - } - else + if (signo != SIGCHLD && sw_reactor()) { + return swoole_signal_kqueue_set(signo, handler); + } else #endif { signals[signo].handler = handler; - signals[signo].active = 1; + signals[signo].activated = true; signals[signo].signo = signo; - 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) -{ - if (SwooleTG.reactor) - { - SwooleTG.reactor->singal_no = signo; - } - else - { - //discard signal - if (_lock) - { +static void swoole_signal_async_handler(int signo) { + if (sw_reactor()) { + sw_reactor()->singal_no = signo; + } else { + // discard signal + if (_lock || !SwooleG.init) { return; } _lock = 1; - swSignal_callback(signo); + swoole_signal_callback(signo); _lock = 0; } } -void swSignal_callback(int signo) -{ - if (signo >= SW_SIGNO_MAX) - { - swWarn("signal[%d] numberis invalid", signo); +void swoole_signal_callback(int signo) { + if (signo >= SW_SIGNO_MAX) { + swoole_warning("signal[%d] numberis invalid", signo); return; } - swSignalHandler callback = signals[signo].handler; - if (!callback) - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_UNREGISTERED_SIGNAL, SW_UNREGISTERED_SIGNAL_FMT, swSignal_str(signo)); + SignalHandler callback = signals[signo].handler; + if (!callback) { + 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) -{ - if (signo >= SW_SIGNO_MAX) - { - swWarn("signal[%d] numberis invalid", signo); - return NULL; - } - else - { +SignalHandler swoole_signal_get_handler(int signo) { + if (signo >= SW_SIGNO_MAX) { + 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(); - } - else + if (SwooleG.enable_signalfd && swoole_signalfd_is_available()) { + swoole_signalfd_clear(); + } else #endif { - int i; - for (i = 0; i < SW_SIGNO_MAX; i++) - { - if (signals[i].active) - { + SW_LOOP_N(SW_SIGNO_MAX) { + if (signals[i].activated) { #ifdef HAVE_KQUEUE - if (signals[i].signo != SIGCHLD && SwooleTG.reactor) - { - swKqueueSignal_set(signals[i].signo, NULL); - } - else + if (signals[i].signo != SIGCHLD && sw_reactor()) { + 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); } } } } - bzero(&signals, sizeof(signals)); + sw_memset_zero(&signals, sizeof(signals)); } #ifdef HAVE_SIGNALFD -void swSignalfd_init() -{ +void swoole_signalfd_init() { sigemptyset(&signalfd_mask); - bzero(&signals, sizeof(signals)); + sw_memset_zero(&signals, sizeof(signals)); } -static void swSignalfd_set(int signo, swSignalHandler handler) -{ - if (handler == NULL && signals[signo].active) - { +/** + * set new signal handler and return origin signal handler + */ +static SignalHandler swoole_signalfd_set(int signo, SignalHandler handler) { + SignalHandler origin_handler = nullptr; + + if (handler == nullptr && signals[signo].activated) { sigdelset(&signalfd_mask, signo); - bzero(&signals[signo], sizeof(swSignal)); - } - else - { + sw_memset_zero(&signals[signo], sizeof(Signal)); + } else { sigaddset(&signalfd_mask, signo); + origin_handler = signals[signo].handler; signals[signo].handler = handler; signals[signo].signo = signo; - signals[signo].active = 1; + signals[signo].activated = true; } - if (signal_fd > 0) - { - sigprocmask(SIG_SETMASK, &signalfd_mask, NULL); + + if (!swoole_event_is_available()) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_OPERATION_NOT_SUPPORT, + "The signalfd must only be used after event loop is initialized"); + return nullptr; + } + + if (swoole_signalfd_is_available()) { + sigprocmask(SIG_SETMASK, &signalfd_mask, nullptr); signalfd(signal_fd, &signalfd_mask, SFD_NONBLOCK | SFD_CLOEXEC); } + swoole_signalfd_setup(sw_reactor()); + + return origin_handler; } -int swSignalfd_setup(swReactor *reactor) -{ - if (signal_fd == 0) - { - signal_fd = signalfd(-1, &signalfd_mask, SFD_NONBLOCK | SFD_CLOEXEC); - if (signal_fd < 0) - { - swSysWarn("signalfd() failed"); - return SW_ERR; - } - signal_socket = swSocket_new(signal_fd, SW_FD_SIGNAL); - if (signal_socket == NULL) - { - close(signal_fd); - signal_fd = 0; - return SW_ERR; - } - SwooleG.signal_fd = signal_fd; - if (sigprocmask(SIG_BLOCK, &signalfd_mask, NULL) == -1) - { - swSysWarn("sigprocmask() failed"); - return SW_ERR; - } - swReactor_set_handler(reactor, SW_FD_SIGNAL, swSignalfd_onSignal); - if (swoole_event_add(signal_socket, SW_EVENT_READ) < 0) - { - return SW_ERR; - } - return SW_OK; +static bool swoole_signalfd_create() { + if (swoole_signalfd_is_available()) { + return false; } - else - { - swWarn("signalfd has been created"); - return SW_ERR; + + signal_fd = signalfd(-1, &signalfd_mask, SFD_NONBLOCK | SFD_CLOEXEC); + if (signal_fd < 0) { + 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) { + swoole_sys_warning("sigprocmask() failed"); + signal_socket->fd = -1; + signal_socket->free(); + close(signal_fd); + signal_socket = nullptr; + signal_fd = -1; + return false; + } + signalfd_create_pid = getpid(); + SwooleG.signal_fd = signal_fd; + + return true; } -static void swSignalfd_clear() -{ - if (signal_fd) - { - if (sigprocmask(SIG_UNBLOCK, &signalfd_mask, NULL) < 0) - { - swSysWarn("sigprocmask(SIG_UNBLOCK) failed"); +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 swoole_signalfd_clear() { + if (swoole_signalfd_is_available()) { + if (sigprocmask(SIG_UNBLOCK, &signalfd_mask, nullptr) < 0) { + swoole_sys_warning("sigprocmask(SIG_UNBLOCK) failed"); } - if (signal_socket) - { - swSocket_free(signal_socket); - signal_socket = NULL; + if (signal_socket) { + signal_socket->free(); + signal_socket = nullptr; } - bzero(&signalfd_mask, sizeof(signalfd_mask)); + 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(swReactor *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)); - if (n < 0) - { - swSysWarn("read from signalfd failed"); + ssize_t n = read(event->fd, &siginfo, sizeof(siginfo)); + if (n < 0) { + swoole_sys_warning("read from signalfd failed"); return SW_OK; } - if (siginfo.ssi_signo >= SW_SIGNO_MAX) - { - swWarn("unknown signal[%d]", siginfo.ssi_signo); + if (siginfo.ssi_signo >= SW_SIGNO_MAX) { + swoole_warning("unknown signal[%d]", siginfo.ssi_signo); return SW_OK; } - if (signals[siginfo.ssi_signo].active) - { - if (signals[siginfo.ssi_signo].handler) - { - signals[siginfo.ssi_signo].handler(siginfo.ssi_signo); - } - else - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_UNREGISTERED_SIGNAL, SW_UNREGISTERED_SIGNAL_FMT, swSignal_str(siginfo.ssi_signo)); + if (signals[siginfo.ssi_signo].activated) { + SignalHandler handler = signals[siginfo.ssi_signo].handler; + if (handler == SIG_IGN) { + return SW_OK; + } else if (handler) { + handler(siginfo.ssi_signo); + } else { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_UNREGISTERED_SIGNAL, + SW_UNREGISTERED_SIGNAL_FMT, + swoole_signal_to_str(siginfo.ssi_signo)); } } @@ -336,102 +347,40 @@ static int swSignalfd_onSignal(swReactor *reactor, swEvent *event) #endif #ifdef HAVE_KQUEUE -static void swKqueueSignal_set(int signo, swSignalHandler handler) -{ +/** + * set new signal handler and return origin signal handler + */ +static SignalHandler swoole_signal_kqueue_set(int signo, SignalHandler handler) { struct kevent ev; - swReactor *reactor = SwooleTG.reactor; - struct reactor_object - { - int fd; - }; - struct reactor_object *reactor_obj = (struct reactor_object *) reactor->object; + SignalHandler origin_handler = nullptr; + Reactor *reactor = sw_reactor(); + // clear signal - if (handler == NULL) - { + if (handler == nullptr) { signal(signo, SIG_DFL); - bzero(&signals[signo], sizeof(swSignal)); + sw_memset_zero(&signals[signo], sizeof(Signal)); EV_SET(&ev, signo, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL); } // add/update signal - else - { + else { signal(signo, SIG_IGN); + origin_handler = signals[signo].handler; signals[signo].handler = handler; signals[signo].signo = signo; - signals[signo].active = 1; - // save swSignal* as udata - EV_SET(&ev, signo, EVFILT_SIGNAL, EV_ADD, 0, 0, &signals[signo]); - } - int n = kevent(reactor_obj->fd, &ev, 1, NULL, 0, NULL); - if (n < 0 && sw_unlikely(handler)) - { - swSysWarn("kevent set signal[%d] error", 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 - -namespace swoole { namespace coroutine { - -bool System::wait_signal(int signo, double timeout) -{ - static Coroutine* listeners[SW_SIGNO_MAX]; - - if (SwooleTG.reactor->signal_listener_num > 0) - { - errno = EBUSY; - return false; - } - if (signo < 0 || signo >= SW_SIGNO_MAX || signo == SIGCHLD) - { - errno = EINVAL; - return false; - } - - Coroutine *co = Coroutine::get_current_safe(); - - /* resgiter signal */ - listeners[signo] = co; - // for swSignalfd_setup - SwooleTG.reactor->check_signalfd = 1; - /* always enable signalfd */ - SwooleG.use_signalfd = SwooleG.enable_signalfd = 1; - swSignal_add(signo, [](int signo) { - Coroutine *co = listeners[signo]; - if (co) - { - listeners[signo] = nullptr; - co->resume(); - } - }); - SwooleTG.reactor->co_signal_listener_num++; - - swTimer_node* timer = nullptr; - if (timeout > 0) - { - timer = swoole_timer_add(timeout * 1000, 0, [](swTimer *timer, swTimer_node *tnode) { - Coroutine *co = (Coroutine *) tnode->data; - co->resume(); - }, co); - } - - co->yield(); - - swSignal_add(signo, nullptr); - SwooleTG.reactor->co_signal_listener_num--; - - if (listeners[signo] != nullptr) - { - listeners[signo] = nullptr; - errno = ETIMEDOUT; - return false; + // save swSignal* as udata + EV_SET(&ev, signo, EVFILT_SIGNAL, EV_ADD, 0, 0, sigptr); } - - if (timer) - { - swoole_timer_del(timer); + int n = kevent(reactor->native_handle, &ev, 1, nullptr, 0, nullptr); + if (n < 0 && sw_unlikely(handler)) { + swoole_sys_warning("kevent set signal[%d] error", signo); } - return true; + return origin_handler; } - -}} +#endif diff --git a/src/os/timer.cc b/src/os/timer.cc index b462a1a024b..a4bca6641ad 100644 --- a/src/os/timer.cc +++ b/src/os/timer.cc @@ -10,93 +10,58 @@ | to obtain it through the world-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_timer.h" +#include "swoole_signal.h" -static int swSystemTimer_signal_set(swTimer *timer, long interval); -static int swSystemTimer_set(swTimer *timer, long new_interval); -static void swSystemTimer_close(swTimer *timer); +#include -/** - * create timer - */ -int swSystemTimer_init(swTimer *timer, long interval) -{ - timer->set = swSystemTimer_set; - timer->close = swSystemTimer_close; - timer->lasttime = interval; - if (swSystemTimer_signal_set(timer, interval) < 0) - { - return SW_ERR; - } - swSignal_add(SIGALRM, swSystemTimer_signal_handler); - return SW_OK; +namespace swoole { + +static int SystemTimer_set(Timer *timer, long next_msec); + +bool Timer::init_with_system_timer() { + set = SystemTimer_set; + close = [](Timer *timer) { SystemTimer_set(timer, -1); }; + swoole_signal_set(SIGALRM, [](int sig) { SwooleG.signal_alarm = true; }); + return true; } /** * setitimer */ -static int swSystemTimer_signal_set(swTimer *timer, long interval) -{ - struct itimerval timer_set = {}; - int sec = interval / 1000; - int msec = interval % 1000; - +static int SystemTimer_set(Timer *timer, long next_msec) { + struct itimerval timer_set; struct timeval now; - if (gettimeofday(&now, NULL) < 0) - { - swSysWarn("gettimeofday() failed"); + if (gettimeofday(&now, nullptr) < 0) { + swoole_sys_warning("gettimeofday() failed"); return SW_ERR; } - if (interval > 0) - { + if (next_msec > 0) { + int sec = next_msec / 1000; + int msec = next_msec % 1000; timer_set.it_interval.tv_sec = sec; timer_set.it_interval.tv_usec = msec * 1000; - timer_set.it_value.tv_sec = sec; timer_set.it_value.tv_usec = timer_set.it_interval.tv_usec; - if (timer_set.it_value.tv_usec > 1e6) - { + if (timer_set.it_value.tv_usec > 1e6) { timer_set.it_value.tv_usec = timer_set.it_value.tv_usec - 1e6; timer_set.it_value.tv_sec += 1; } + } else { + timer_set = {}; } - if (setitimer(ITIMER_REAL, &timer_set, NULL) < 0) - { - swSysWarn("setitimer() failed"); + if (setitimer(ITIMER_REAL, &timer_set, nullptr) < 0) { + swoole_sys_warning("setitimer() failed"); return SW_ERR; } return SW_OK; } -static void swSystemTimer_close(swTimer *timer) -{ - swSystemTimer_signal_set(timer, -1); -} - -static long _next_msec = 0; - -static int swSystemTimer_set(swTimer *timer, long exec_msec) -{ - if (exec_msec == _next_msec) - { - return SW_OK; - } - if (exec_msec == 0) - { - exec_msec = 1; - } - _next_msec = exec_msec; - return swSystemTimer_signal_set(timer, exec_msec); -} - -void swSystemTimer_signal_handler(int sig) -{ - SwooleWG.signal_alarm = 1; -} +} // namespace swoole diff --git a/src/os/unix_socket.cc b/src/os/unix_socket.cc new file mode 100644 index 00000000000..c1db228dedd --- /dev/null +++ b/src/os/unix_socket.cc @@ -0,0 +1,41 @@ +/* + +----------------------------------------------------------------------+ + | 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_pipe.h" +#include "swoole_socket.h" + +namespace swoole { +UnixSocket::UnixSocket(bool blocking, int _protocol) : SocketPair(blocking), protocol_(_protocol) { + if (socketpair(AF_UNIX, protocol_, 0, socks) < 0) { + swoole_sys_warning("socketpair() failed"); + return; + } + if (!init_socket(socks[1], socks[0])) { + return; + } + set_buffer_size(network::Socket::default_buffer_size); +} + +bool UnixSocket::set_buffer_size(size_t _size) { + if (!master_socket->set_buffer_size(_size)) { + return false; + } + if (!worker_socket->set_buffer_size(_size)) { + return false; + } + return true; +} +} // namespace swoole diff --git a/src/os/wait.cc b/src/os/wait.cc index 02c3fe9e5ec..ca02eee1388 100644 --- a/src/os/wait.cc +++ b/src/os/wait.cc @@ -10,217 +10,206 @@ | to obtain it through the world-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 "coroutine.h" -#include "coroutine_system.h" +#include "swoole_process_pool.h" +#include "swoole_coroutine.h" +#include "swoole_coroutine_system.h" +#include "swoole_signal.h" #include #include -using namespace std; using namespace swoole; using swoole::coroutine::System; -struct wait_task -{ +struct WaitTask { Coroutine *co; pid_t pid; int status; }; -static list wait_list; -static unordered_map waitpid_map; -static unordered_map child_processes; +/** + * 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; 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; - } - - wait_task *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(); - } +static void signal_handler(int signo) { + 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(); } } } -static void signal_free(void *nullopt) -{ - signal_ready = false; - swSignal_clear(); -} +static void signal_init() { + if (!signal_ready) { + Reactor *reactor = SwooleTG.reactor; + swoole_signal_set(SIGCHLD, signal_handler); + + 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; + swoole_signal_clear(); + }); -static void signal_init() -{ - if (!signal_ready) - { - swReactor *reactor = SwooleTG.reactor; - swSignal_add(SIGCHLD, signal_handler); -#ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd && !swReactor_isset_handler(reactor, SW_FD_SIGNAL)) - { - swSignalfd_setup(reactor); - } -#endif - swReactor_add_destroy_callback(reactor, (swCallback) signal_free, nullptr); signal_ready = true; } } -pid_t System::wait(int *__stat_loc , double timeout) -{ +pid_t System::wait(int *__stat_loc, double timeout) { return System::waitpid(-1, __stat_loc, 0, timeout); } -pid_t System::waitpid(pid_t __pid, int *__stat_loc, int __options, double timeout) -{ - if (__pid < 0) - { - if (!child_processes.empty()) - { +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()) { auto i = child_processes.begin(); pid_t __pid = i->first; *__stat_loc = i->second; child_processes.erase(i); return __pid; } - } - else - { + } else { auto i = child_processes.find(__pid); - if (i != child_processes.end()) - { + if (i != child_processes.end()) { *__stat_loc = i->second; child_processes.erase(i); return __pid; } } - if (sw_unlikely(SwooleTG.reactor == nullptr || !Coroutine::get_current() || (__options & WNOHANG))) - { + if (sw_unlikely(SwooleTG.reactor == nullptr || !Coroutine::get_current() || (__options & WNOHANG))) { return ::waitpid(__pid, __stat_loc, __options); } - /* try once if failed we init the task */ - wait_task task; + /* try once if failed we init the task, and we must register SIGCHLD before try waitpid, or we may lose the SIGCHLD + */ + WaitTask task; + signal_init(); task.pid = ::waitpid(__pid, __stat_loc, __options | WNOHANG); - if (task.pid > 0) - { + if (task.pid != 0) { return task.pid; } - signal_init(); - task.pid = -1; task.status = 0; task.co = Coroutine::get_current(); /* enqueue */ - if (__pid < 0) - { + if (__pid < 0) { wait_list.push_back(&task); - } - else - { + } else { waitpid_map[__pid] = &task; } /* timeout controller */ - swTimer_node* timer = nullptr; - if (timeout > 0) - { - timer = swoole_timer_add(timeout * 1000, 0, [](swTimer *timer, swTimer_node *tnode) { - Coroutine *co = (Coroutine *) tnode->data; - co->resume(); - }, task.co); + TimerNode *timer = nullptr; + if (timeout > 0) { + timer = swoole_timer_add( + timeout, + false, + [](Timer *timer, TimerNode *tnode) { + Coroutine *co = (Coroutine *) tnode->data; + co->resume(); + }, + 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) - { - if (task.pid > 0) - { + if (__pid < 0) { + if (task.pid > 0) { wait_list.pop_front(); - } - else - { + } else { /* timeout so we should remove it from the list */ wait_list.remove(&task); } - } - else - { + } else { waitpid_map.erase(__pid); } /* clear and assign result */ - if (task.pid > 0) - { - if (timer) - { + if (task.pid > 0) { + if (timer) { swoole_timer_del(timer); } *__stat_loc = task.status; - } - else - { - errno = ETIMEDOUT; + } else { + swoole_set_last_error(task.co->is_canceled() ? SW_ERROR_CO_CANCELED : ETIMEDOUT); + errno = swoole_get_last_error(); } return task.pid; } -extern "C" -{ +extern "C" { -size_t swoole_coroutine_wait_count() -{ +size_t swoole_coroutine_wait_count() { return wait_list.size() + waitpid_map.size(); } -pid_t swoole_coroutine_wait(int *__stat_loc) -{ +pid_t swoole_coroutine_wait(int *__stat_loc) { return System::wait(__stat_loc); } -pid_t swoole_coroutine_waitpid(pid_t __pid, int *__stat_loc, int __options) -{ +pid_t swoole_coroutine_waitpid(pid_t __pid, int *__stat_loc, int __options) { return System::waitpid(__pid, __stat_loc, __options); } - } diff --git a/src/pipe/base.cc b/src/pipe/base.cc deleted file mode 100644 index c810e2a7ac1..00000000000 --- a/src/pipe/base.cc +++ /dev/null @@ -1,124 +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 "swoole.h" - -static int swPipeBase_read(swPipe *p, void *data, int length); -static int swPipeBase_write(swPipe *p, void *data, int length); -static int swPipeBase_close(swPipe *p); - -typedef struct _swPipeBase -{ - int pipes[2]; -} swPipeBase; - -int swPipe_init_socket(swPipe *p, int master_fd, int worker_fd, int blocking) -{ - p->master_socket = swSocket_new(master_fd, SW_FD_PIPE); - if (p->master_socket == NULL) - { - _error: - close(master_fd); - close(worker_fd); - return SW_ERR; - } - p->worker_socket = swSocket_new(worker_fd, SW_FD_PIPE); - if (p->worker_socket == NULL) - { - swSocket_free(p->master_socket); - goto _error; - } - - if (blocking) - { - swSocket_set_block(p->worker_socket); - swSocket_set_block(p->master_socket); - } - else - { - swSocket_set_nonblock(p->worker_socket); - swSocket_set_nonblock(p->master_socket); - } - - return SW_OK; -} - -swSocket* swPipe_getSocket(swPipe *p, int master) -{ - return master ? p->master_socket : p->worker_socket; -} - -int swPipeBase_create(swPipe *p, int blocking) -{ - int ret; - swPipeBase *object = (swPipeBase *) sw_malloc(sizeof(swPipeBase)); - if (object == NULL) - { - return -1; - } - p->blocking = blocking; - ret = pipe(object->pipes); - if (ret < 0) - { - swSysWarn("pipe() failed"); - sw_free(object); - return -1; - } - else - { - if (swPipe_init_socket(p, object->pipes[1], object->pipes[0], blocking) < 0) - { - sw_free(object); - return SW_ERR; - } - - p->timeout = -1; - p->object = object; - p->read = swPipeBase_read; - p->write = swPipeBase_write; - p->getSocket = swPipe_getSocket; - p->close = swPipeBase_close; - } - return 0; -} - -static int swPipeBase_read(swPipe *p, void *data, int length) -{ - swPipeBase *object = (swPipeBase *) p->object; - if (p->blocking == 1 && p->timeout > 0) - { - if (swSocket_wait(object->pipes[0], p->timeout * 1000, SW_EVENT_READ) < 0) - { - return SW_ERR; - } - } - return read(object->pipes[0], data, length); -} - -static int swPipeBase_write(swPipe *p, void *data, int length) -{ - swPipeBase *object = (swPipeBase *) p->object; - return write(object->pipes[1], data, length); -} - -static int swPipeBase_close(swPipe *p) -{ - swPipeBase *object = (swPipeBase *) p->object; - swSocket_free(p->master_socket); - swSocket_free(p->worker_socket); - sw_free(object); - return SW_OK; -} diff --git a/src/pipe/eventfd.cc b/src/pipe/eventfd.cc deleted file mode 100644 index 25d588955e4..00000000000 --- a/src/pipe/eventfd.cc +++ /dev/null @@ -1,143 +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 "swoole.h" - -#ifdef HAVE_EVENTFD -#include - -static int swPipeEventfd_read(swPipe *p, void *data, int length); -static int swPipeEventfd_write(swPipe *p, void *data, int length); -static int swPipeEventfd_close(swPipe *p); - -typedef struct _swPipeEventfd -{ - int event_fd; -} swPipeEventfd; - -int swPipeEventfd_create(swPipe *p, int blocking, int semaphore, int timeout) -{ - int efd; - int flag = 0; - swPipeEventfd *object = (swPipeEventfd *) sw_malloc(sizeof(swPipeEventfd)); - if (object == NULL) - { - return -1; - } - - flag = EFD_NONBLOCK; - - if (blocking == 1) - { - if (timeout > 0) - { - flag = 0; - p->timeout = -1; - } - else - { - p->timeout = timeout; - } - } - -#ifdef EFD_SEMAPHORE - if (semaphore == 1) - { - flag |= EFD_SEMAPHORE; - } -#endif - - p->blocking = blocking; - efd = eventfd(0, flag); - if (efd < 0) - { - swSysWarn("eventfd create failed"); - sw_free(object); - return -1; - } - else - { - p->master_socket = swSocket_new(efd, SW_FD_PIPE); - if (p->master_socket == NULL) - { - close(efd); - sw_free(object); - return -1; - } - p->worker_socket = p->master_socket; - p->object = object; - p->read = swPipeEventfd_read; - p->write = swPipeEventfd_write; - p->getSocket = swPipe_getSocket; - p->close = swPipeEventfd_close; - object->event_fd = efd; - } - return 0; -} - -static int swPipeEventfd_read(swPipe *p, void *data, int length) -{ - int ret = -1; - swPipeEventfd *object = (swPipeEventfd *) p->object; - - //eventfd not support socket timeout - if (p->blocking == 1 && p->timeout > 0) - { - if (swSocket_wait(object->event_fd, p->timeout * 1000, SW_EVENT_READ) < 0) - { - return SW_ERR; - } - } - - while (1) - { - ret = read(object->event_fd, data, sizeof(uint64_t)); - if (ret < 0 && errno == EINTR) - { - continue; - } - break; - } - return ret; -} - -static int swPipeEventfd_write(swPipe *p, void *data, int length) -{ - int ret; - swPipeEventfd *object = (swPipeEventfd *) p->object; - while (1) - { - ret = write(object->event_fd, data, sizeof(uint64_t)); - if (ret < 0) - { - if (errno == EINTR) - { - continue; - } - } - break; - } - return ret; -} - -static int swPipeEventfd_close(swPipe *p) -{ - swSocket_free(p->master_socket); - sw_free(p->object); - return SW_OK; -} - -#endif diff --git a/src/pipe/unix_socket.cc b/src/pipe/unix_socket.cc deleted file mode 100644 index 9e1a9fbd157..00000000000 --- a/src/pipe/unix_socket.cc +++ /dev/null @@ -1,126 +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 "swoole.h" - -static int swPipeUnsock_read(swPipe *p, void *data, int length); -static int swPipeUnsock_write(swPipe *p, void *data, int length); -static int swPipeUnsock_close(swPipe *p); - -typedef struct _swPipeUnsock -{ - /** - * master : socks[1] - * worker : socks[0] - */ - int socks[2]; - /** - * master pipe is closed - */ - uint8_t pipe_master_closed; - /** - * worker pipe is closed - */ - uint8_t pipe_worker_closed; -} swPipeUnsock; - -static int swPipeUnsock_close(swPipe *p) -{ - swPipeUnsock *object = (swPipeUnsock *) p->object; - int ret = swPipeUnsock_close_ext(p, 0); - sw_free(object); - return ret; -} - -int swPipeUnsock_close_ext(swPipe *p, int which) -{ - swPipeUnsock *object = (swPipeUnsock *) p->object; - - if (which == SW_PIPE_CLOSE_MASTER) - { - if (object->pipe_master_closed) - { - return SW_ERR; - } - swSocket_free(p->master_socket); - object->pipe_master_closed = 1; - } - else if (which == SW_PIPE_CLOSE_WORKER) - { - if (object->pipe_worker_closed) - { - return SW_ERR; - } - swSocket_free(p->worker_socket);; - object->pipe_worker_closed = 1; - } - else - { - swPipeUnsock_close_ext(p, SW_PIPE_CLOSE_MASTER); - swPipeUnsock_close_ext(p, SW_PIPE_CLOSE_WORKER); - } - - return SW_OK; -} - -int swPipeUnsock_create(swPipe *p, int blocking, int protocol) -{ - int ret; - swPipeUnsock *object = (swPipeUnsock *) sw_malloc(sizeof(swPipeUnsock)); - if (object == NULL) - { - swWarn("malloc() failed"); - return SW_ERR; - } - bzero(object, sizeof(swPipeUnsock)); - p->blocking = blocking; - ret = socketpair(AF_UNIX, protocol, 0, object->socks); - if (ret < 0) - { - swSysWarn("socketpair() failed"); - sw_free(object); - return SW_ERR; - } - else - { - if (swPipe_init_socket(p, object->socks[1], object->socks[0], blocking) < 0) - { - sw_free(object); - return SW_ERR; - } - - uint32_t sbsize = SwooleG.socket_buffer_size; - swSocket_set_buffer_size(p->master_socket, sbsize); - swSocket_set_buffer_size(p->worker_socket, sbsize); - - p->object = object; - p->read = swPipeUnsock_read; - p->write = swPipeUnsock_write; - p->getSocket = swPipe_getSocket; - p->close = swPipeUnsock_close; - } - return 0; -} - -static int swPipeUnsock_read(swPipe *p, void *data, int length) -{ - return read(((swPipeUnsock *) p->object)->socks[0], data, length); -} - -static int swPipeUnsock_write(swPipe *p, void *data, int length) -{ - return write(((swPipeUnsock *) p->object)->socks[1], data, length); -} diff --git a/src/protocol/base.cc b/src/protocol/base.cc index 2bce09e012b..d948b97647f 100644 --- a/src/protocol/base.cc +++ b/src/protocol/base.cc @@ -12,95 +12,88 @@ | to obtain it through the world-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_cxx.h" - -using namespace swoole; +#include "swoole.h" +#include "swoole_string.h" +#include "swoole_socket.h" +#include "swoole_protocol.h" +namespace swoole { /** * return the package total length */ -ssize_t swProtocol_get_package_length(swProtocol *protocol, swSocket *conn, 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(conn) : protocol->package_length_size; + uint8_t package_length_size = + protocol->get_package_length_size ? protocol->get_package_length_size(socket) : protocol->package_length_size; int32_t body_length; + + if (package_length_size == 0) { + // protocol error + return SW_ERR; + } /** * 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); - //Length error - //Protocol length is not legitimate, out of bounds or exceed the allocated length - if (body_length < 0) - { - swWarn("invalid package, remote_addr=%s:%d, length=%d, size=%d", - swSocket_get_ip(conn->socket_type, &conn->info), - swSocket_get_port(conn->socket_type, &conn->info), body_length, size); + body_length = swoole_unpack(protocol->package_length_type, pl->buf + length_offset); + // Length error + // Protocol length is not legitimate, out of bounds or exceed the allocated length + if (body_length < 0) { + swoole_warning("invalid package (size=%d) from socket#%u<%s:%d>", + 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); - //total package 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; } -static sw_inline int swProtocol_split_package_by_eof(swProtocol *protocol, swSocket *conn, swString *buffer) -{ - if (buffer->length < protocol->package_eof_len) - { +int Protocol::recv_split_by_eof(network::Socket *socket, String *buffer) { + RecvData rdata{}; + + if (buffer->length < package_eof_len) { return SW_CONTINUE; } - int retval; - - size_t n = string_split(buffer, protocol->package_eof, protocol->package_eof_len, [&](char *data, size_t length) -> int { - if (protocol->onPackage(protocol, conn, data, length) < 0) - { - retval = SW_CLOSE; + ssize_t n = buffer->split(package_eof, package_eof_len, [&](const char *data, size_t length) -> int { + rdata.info.len = length; + rdata.data = data; + if (onPackage(this, socket, &rdata) < 0) { return false; } - if (conn->removed) - { + if (socket->removed) { return false; } return true; }); - if (conn->removed) - { + if (socket->removed || n < 0) { return SW_CLOSE; - } - - if (n < 0) - { - return retval; - } - else if (n == 0) - { + } else if (n == 0) { return SW_CONTINUE; - } - else if (n < buffer->length) - { + } else if (n < (ssize_t) buffer->length) { off_t offset; - swString_pop_front(buffer, n); - offset = buffer->length - protocol->package_eof_len; + buffer->reduce(n); + offset = buffer->length - package_eof_len; buffer->offset = offset > 0 ? offset : 0; - } - else - { - swString_clear(buffer); + } else { + buffer->clear(); } #ifdef SW_USE_OPENSSL - if (conn->ssl) - { + if (socket->ssl) { return SW_CONTINUE; } #endif @@ -112,137 +105,126 @@ static sw_inline int swProtocol_split_package_by_eof(swProtocol *protocol, swSoc * @return SW_ERR: close the connection * @return SW_OK: continue */ -int swProtocol_recv_check_length(swProtocol *protocol, swSocket *conn, swString *buffer) -{ +int Protocol::recv_with_length_protocol(network::Socket *socket, String *buffer) { + RecvData rdata{}; + PacketLength pl{}; ssize_t package_length; - uint8_t package_length_size = protocol->get_package_length_size ? protocol->get_package_length_size(conn) : protocol->package_length_size; - uint32_t recv_size; + uint8_t _package_length_size = get_package_length_size ? get_package_length_size(socket) : package_length_size; + uint32_t recv_size = 0; ssize_t recv_n = 0; - if (conn->skip_recv) - { - conn->skip_recv = 0; + // protocol error + if (get_package_length_size && _package_length_size == 0) { + return SW_ERR; + } + + if (socket->skip_recv) { + socket->skip_recv = 0; goto _do_get_length; } - _do_recv: - if (conn->removed) - { +_do_recv: + if (socket->removed) { return SW_OK; } - if (buffer->offset > 0) - { + if (buffer->offset > 0) { recv_size = buffer->offset - buffer->length; - } - else - { - recv_size = protocol->package_length_offset + package_length_size; + } else { + recv_size = package_length_offset + _package_length_size; } - recv_n = swSocket_recv(conn, buffer->str + buffer->length, recv_size, 0); - if (recv_n < 0) - { - switch (swSocket_error(errno)) - { + recv_n = socket->recv(buffer->str + buffer->length, recv_size, 0); + if (recv_n < 0) { + switch (socket->catch_read_error(errno)) { case SW_ERROR: - swSysWarn("recv(%d, %d) failed", conn->fd, recv_size); + swoole_sys_warning("recv(%d, %d) failed", socket->fd, recv_size); return SW_OK; case SW_CLOSE: return SW_ERR; default: return SW_OK; } - } - else if (recv_n == 0) - { + } else if (recv_n == 0) { return SW_ERR; - } - else - { + } else { buffer->length += recv_n; - if (conn->recv_wait) - { - if (buffer->length >= (size_t) buffer->offset) - { - _do_dispatch: - if (protocol->onPackage(protocol, conn, buffer->str, buffer->offset) < 0) - { + if (socket->recv_wait) { + if (buffer->length >= (size_t) buffer->offset) { + _do_dispatch: + rdata.info.len = buffer->offset; + rdata.data = buffer->str; + if (onPackage(this, socket, &rdata) < 0) { return SW_ERR; } - if (conn->removed) - { + if (socket->removed) { return SW_OK; } - conn->recv_wait = 0; + socket->recv_wait = 0; - if (buffer->length > (size_t) buffer->offset) - { - swString_pop_front(buffer, buffer->offset); + if (buffer->length > (size_t) buffer->offset) { + buffer->reduce(buffer->offset); goto _do_get_length; - } - else - { - swString_clear(buffer); + } else { + buffer->clear(); } } #ifdef SW_USE_OPENSSL - if (conn->ssl) - { + if (socket->ssl) { goto _do_recv; } #endif return SW_OK; - } - else - { - _do_get_length: - package_length = protocol->get_package_length(protocol, conn, buffer->str, buffer->length); - //invalid package, close connection. - if (package_length < 0) - { + } else { + _do_get_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 == protocol->package_length_offset + package_length_size) - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_PACKAGE_LENGTH_NOT_FOUND, "bad request, No length found in %ld bytes", buffer->length); + // no length + else if (package_length == 0) { + if (buffer->length == recv_size) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_PACKAGE_LENGTH_NOT_FOUND, + "bad request, no length found in %zu bytes", + buffer->length); return SW_ERR; - } - else - { + } else { return SW_OK; } - } - else if (package_length > protocol->package_max_length) - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, - "package is too big, remote_addr=%s:%d, length=%zu", - swSocket_get_ip(conn->socket_type, &conn->info), - swSocket_get_port(conn->socket_type, &conn->info), package_length); + } else if (package_length > package_max_length) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, + "package is too big, remote_addr=%s:%d, length=%zu", + socket->info.get_ip(), + socket->info.get_port(), + package_length); return SW_ERR; } - //get length success - else - { - if (buffer->size < (size_t) package_length) - { - if (swString_extend(buffer, package_length) < 0) - { + // get length success + else { + if (buffer->size < (size_t) package_length) { + if (!buffer->extend(package_length)) { return SW_ERR; } } - conn->recv_wait = 1; + socket->recv_wait = 1; buffer->offset = package_length; - if (buffer->length >= (size_t) package_length) - { + if (buffer->length >= (size_t) package_length) { goto _do_dispatch; - } - else - { + } else { goto _do_recv; } } @@ -255,112 +237,92 @@ int swProtocol_recv_check_length(swProtocol *protocol, swSocket *conn, swString * @return SW_ERR: close the connection * @return SW_OK: continue */ -int swProtocol_recv_check_eof(swProtocol *protocol, swSocket *conn, swString *buffer) -{ - int recv_again = SW_FALSE; - int buf_size; +int Protocol::recv_with_eof_protocol(network::Socket *socket, String *buffer) { + bool recv_again = false; + size_t buf_size; + RecvData rdata{}; - _recv_data: +_recv_data: buf_size = buffer->size - buffer->length; char *buf_ptr = buffer->str + buffer->length; - if (buf_size > SW_BUFFER_SIZE_STD) - { + if (buf_size > SW_BUFFER_SIZE_STD) { buf_size = SW_BUFFER_SIZE_STD; } - int n = swSocket_recv(conn, buf_ptr, buf_size, 0); - if (n < 0) - { - switch (swSocket_error(errno)) - { + ssize_t n = socket->recv(buf_ptr, buf_size, 0); + if (n < 0) { + 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", socket->fd); return SW_OK; case SW_CLOSE: return SW_ERR; default: return SW_OK; } - } - else if (n == 0) - { + } else if (n == 0) { return SW_ERR; - } - else - { + } else { buffer->length += n; - if (buffer->length < protocol->package_eof_len) - { + if (buffer->length < package_eof_len) { return SW_OK; } - if (protocol->split_by_eof) - { - int retval = swProtocol_split_package_by_eof(protocol, conn, buffer); - if (retval == SW_CONTINUE) - { - recv_again = SW_TRUE; - } - else if (retval == SW_CLOSE) - { + if (split_by_eof) { + int retval = recv_split_by_eof(socket, buffer); + if (retval == SW_CONTINUE) { + recv_again = true; + } else if (retval == SW_CLOSE) { return SW_ERR; - } - else - { + } else { return SW_OK; } - } - else if (memcmp(buffer->str + buffer->length - protocol->package_eof_len, protocol->package_eof, protocol->package_eof_len) == 0) - { - if (protocol->onPackage(protocol, conn, buffer->str, buffer->length) < 0) - { + } else if (memcmp(buffer->str + buffer->length - package_eof_len, package_eof, package_eof_len) == 0) { + buffer->offset = buffer->length; + rdata.info.len = buffer->length; + rdata.data = buffer->str; + if (onPackage(this, socket, &rdata) < 0) { return SW_ERR; } - if (conn->removed) - { + if (socket->removed) { return SW_OK; } - swString_clear(buffer); + buffer->clear(); #ifdef SW_USE_OPENSSL - if (conn->ssl && SSL_pending(conn->ssl) > 0) - { + if (socket->ssl && SSL_pending(socket->ssl) > 0) { goto _recv_data; } #endif return SW_OK; } - //over max length, will discard - if (buffer->length == protocol->package_max_length) - { - swWarn("Package is too big. package_length=%d", (int )buffer->length); + // over max length, will discard + if (buffer->length == package_max_length) { + swoole_warning("Package is too big. package_length=%d", (int) buffer->length); return SW_ERR; } - //buffer is full, may have not read data - if (buffer->length == buffer->size) - { - recv_again = SW_TRUE; - if (buffer->size < protocol->package_max_length) - { - uint32_t extend_size = swoole_size_align(buffer->size * 2, SwooleG.pagesize); - if (extend_size > protocol->package_max_length) - { - extend_size = protocol->package_max_length; + // buffer is full, may have not read data + if (buffer->length == buffer->size) { + recv_again = true; + if (buffer->size < package_max_length) { + uint32_t extend_size = swoole_size_align(buffer->size * 2, swoole_pagesize()); + if (extend_size > package_max_length) { + extend_size = package_max_length; } - if (swString_extend(buffer, extend_size) < 0) - { + if (!buffer->extend(extend_size)) { return SW_ERR; } } } - //no eof - if (recv_again) - { + // no eof + if (recv_again) { goto _recv_data; } } return SW_OK; } + +} // namespace swoole diff --git a/src/protocol/dtls.cc b/src/protocol/dtls.cc index a5599fcf11e..e9442529ece 100644 --- a/src/protocol/dtls.cc +++ b/src/protocol/dtls.cc @@ -1,68 +1,76 @@ -#include "server.h" - +/* + +----------------------------------------------------------------------+ + | 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 -namespace swoole { namespace dtls { +namespace swoole { +namespace dtls { //------------------------------------------------------------------------------- -int BIO_write(BIO *b, const char *data, int dlen) -{ - swTraceLog(SW_TRACE_SSL, "BIO_write(%d)", dlen); +int BIO_write(BIO *b, const char *data, int dlen) { + swoole_trace_log(SW_TRACE_SSL, "BIO_write(%d)", dlen); Session *session = (Session *) BIO_get_data(b); - return write(session->socket->fd, data, dlen); + return session->socket->write(data, dlen); } -int BIO_read(BIO *b, char *data, int len) -{ +int BIO_read(BIO *b, char *data, int len) { Session *session = (Session *) BIO_get_data(b); Buffer *buffer; BIO_clear_retry_flags(b); - if (!session->rxqueue.empty()) - { + 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); - if (!session->peek_mode) - { + if (!session->peek_mode) { session->rxqueue.pop_front(); sw_free(buffer); } return n; - } - else - { + } else { BIO_set_retry_read(b); return -1; } } -long BIO_ctrl(BIO *b, int cmd, long lval, void *ptrval) -{ +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) - { + switch (cmd) { case BIO_CTRL_EOF: - return session->rxqueue.empty(); - case BIO_CTRL_GET_CLOSE: - return BIO_get_shutdown(b); - case BIO_CTRL_SET_CLOSE: - BIO_set_shutdown(b, (int) lval); - break; - case BIO_CTRL_WPENDING: - return 0; - case BIO_CTRL_PENDING: - return (long) session->get_buffer_length(); + return session->rxqueue.empty(); + case BIO_CTRL_GET_CLOSE: + return BIO_get_shutdown(b); + case BIO_CTRL_SET_CLOSE: + BIO_set_shutdown(b, (int) lval); + break; + case BIO_CTRL_WPENDING: + return 0; + case BIO_CTRL_PENDING: + return (long) session->get_buffer_length(); case BIO_CTRL_FLUSH: case BIO_CTRL_DGRAM_SET_CONNECTED: @@ -70,8 +78,7 @@ long BIO_ctrl(BIO *b, int cmd, long lval, void *ptrval) retval = 1; break; case BIO_CTRL_DGRAM_GET_PEER: - if (ptrval) - { + if (ptrval) { memcpy(ptrval, &session->socket->info, sizeof(session->socket->info.addr)); } retval = 1; @@ -80,20 +87,30 @@ long BIO_ctrl(BIO *b, int cmd, long lval, void *ptrval) case BIO_CTRL_DGRAM_GET_FALLBACK_MTU: retval = 1500; break; +#ifdef BIO_CTRL_DGRAM_GET_MTU_OVERHEAD case BIO_CTRL_DGRAM_GET_MTU_OVERHEAD: - retval = 96; // random guess + retval = 96; // random guess break; +#endif +#ifdef BIO_CTRL_DGRAM_SET_PEEK_MODE case BIO_CTRL_DGRAM_SET_PEEK_MODE: ((Session *) BIO_get_data(b))->peek_mode = !!lval; retval = 1; break; +#endif case BIO_CTRL_PUSH: case BIO_CTRL_POP: 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,24 +118,20 @@ long BIO_ctrl(BIO *b, int cmd, long lval, void *ptrval) return retval; } -int BIO_create(BIO *b) -{ +int BIO_create(BIO *b) { return 1; } -int BIO_destroy(BIO *b) -{ - swTraceLog(SW_TRACE_SSL, "BIO_destroy(BIO[0x%016lX])\n", b); +int BIO_destroy(BIO *b) { + swoole_trace_log(SW_TRACE_SSL, "BIO_destroy(BIO[%p])\n", b); return 1; } static BIO_METHOD *_bio_methods = nullptr; static int dtls_session_index = 0; -BIO_METHOD *BIO_get_methods(void) -{ - if (_bio_methods) - { +BIO_METHOD *BIO_get_methods(void) { + if (_bio_methods) { return _bio_methods; } @@ -130,37 +143,40 @@ 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); - BIO_meth_set_callback_ctrl(_bio_methods, BIO_callback_ctrl); + +#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; } -void BIO_meth_free(void) -{ - if (_bio_methods) - { +void BIO_meth_free(void) { + if (_bio_methods) { BIO_meth_free(_bio_methods); } _bio_methods = nullptr; } -void Session::append(const char* data, ssize_t len) -{ +void Session::append(const char *data, ssize_t len) { Buffer *buffer = (Buffer *) sw_malloc(sizeof(*buffer) + len); buffer->length = len; memcpy(buffer->data, data, buffer->length); rxqueue.push_back(buffer); } -bool Session::init() -{ - if (socket->ssl) - { +bool Session::init() { + if (socket->ssl) { return false; } - if (swSSL_create(socket, ctx, SW_SSL_SERVER) < 0) - { + if (socket->ssl_create(ctx, SW_SSL_SERVER) < 0) { return false; } socket->dtls = 1; @@ -173,40 +189,37 @@ bool Session::init() return true; } -bool Session::listen() -{ - if (listened) - { +bool Session::listen() { +#ifdef OPENSSL_IS_BORINGSSL + return SSL_is_dtls(socket->ssl); +#else + + if (listened) { return false; } ERR_clear_error(); - int retval = DTLSv1_listen(socket->ssl, NULL); - if (retval == 0) - { + int retval = DTLSv1_listen(socket->ssl, nullptr); + if (retval == 0) { return true; - } - else if (retval < 0) - { + } else if (retval < 0) { int reason = ERR_GET_REASON(ERR_peek_error()); - swWarn( - "DTLSv1_listen() failed, client[%s:%d], reason=%d, error_string=%s", - swSocket_get_ip(socket->socket_type, &socket->info), - swSocket_get_port(socket->socket_type, &socket->info), - 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 - { + } else { listened = true; } - return true; +#endif } //------------------------------------------------------------------------------- -}} +} // namespace dtls +} // namespace swoole #endif diff --git a/src/protocol/http.cc b/src/protocol/http.cc index 24bc6c0c301..aa5d8c68335 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -10,84 +10,162 @@ | to obtain it through the world-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 "server.h" -#include "http.h" -#include "http2.h" -#include "websocket.h" -#include "static_handler.h" -#include "swoole_cxx.h" -#include -#include +#include "swoole_http.h" +#include "swoole_server.h" +#include "swoole_proxy.h" +#include "swoole_base64.h" #include +#include + +#include "swoole_util.h" +#include "swoole_http2.h" +#include "swoole_websocket.h" +#include "swoole_static_handler.h" + +#include "thirdparty/multipart_parser.h" using std::string; -using swoole::http::StaticHandler; +using swoole::http_server::Request; +using swoole::http_server::StaticHandler; +using swoole::network::SendfileTask; +using swoole::network::Socket; -static const char *method_strings[] = -{ +// clang-format off +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 + +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); +} -string swHttpRequest_get_date_if_modified_since(swHttpRequest *request); - -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; - } +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 NULL; +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; } -int swServer_http_static_handler_hit(swServer *serv, swHttpRequest *request, swConnection *conn) -{ - char *url = request->buffer->str + request->url_offset; - size_t url_length = request->url_length; +bool Server::select_static_handler(http_server::Request *request, Connection *conn) { + const char *url = request->buffer_->str + request->url_offset_; + size_t url_length = request->url_length_; - StaticHandler handler(serv, url, url_length); - if (!handler.hit()) - { + StaticHandler handler(this, url, url_length); + if (!handler.hit()) { return false; } char header_buffer[1024]; - swSendData response; + SendData response; response.info.fd = conn->session_id; response.info.type = SW_SERVER_EVENT_SEND_DATA; - if (handler.status_code == SW_HTTP_NOT_FOUND) - { - response.info.len = sw_snprintf( - header_buffer, sizeof(header_buffer), - "HTTP/1.1 %s\r\n" - "Server: " SW_HTTP_SERVER_SOFTWARE "\r\n" - "Content-Length: %zu\r\n" - "\r\n%s", - swHttp_get_status_message(SW_HTTP_NOT_FOUND), - sizeof(SW_HTTP_PAGE_404) - 1, SW_HTTP_PAGE_404 - ); + if (handler.status_code == SW_HTTP_NOT_FOUND) { + response.info.len = sw_snprintf(header_buffer, + sizeof(header_buffer), + "HTTP/1.1 %s\r\n" + "Server: " SW_HTTP_SERVER_SOFTWARE "\r\n" + "Content-Length: %zu\r\n" + "\r\n%s", + http_server::get_status_message(SW_HTTP_NOT_FOUND), + sizeof(SW_HTTP_PAGE_404) - 1, + SW_HTTP_PAGE_404); response.data = header_buffer; - swServer_master_send(serv, &response); + send_to_connection(&response); return true; } @@ -95,137 +173,353 @@ int swServer_http_static_handler_hit(swServer *serv, swHttpRequest *request, swC auto date_str = handler.get_date(); auto date_str_last_modified = handler.get_date_last_modified(); - string date_if_modified_since = swHttpRequest_get_date_if_modified_since(request); - 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" - "Date: %s\r\n" - "Last-Modified: %s\r\n" - "Server: %s\r\n\r\n", - request->keep_alive ? "Connection: keep-alive\r\n" : "", - date_str.c_str(), - date_str_last_modified.c_str(), - SW_HTTP_SERVER_SOFTWARE - ); + 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" + "Connection: %s\r\n" + "Date: %s\r\n" + "Last-Modified: %s\r\n" + "Server: %s\r\n\r\n", + request->keep_alive ? "keep-alive" : "close", + date_str.c_str(), + date_str_last_modified.c_str(), + SW_HTTP_SERVER_SOFTWARE); response.data = header_buffer; - swServer_master_send(serv, &response); + send_to_connection(&response); return true; } - const swSendFile_request *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 (serv->http_index_files && !serv->http_index_files->empty() && handler.is_dir()) - { - handler.get_dir_files(dir_files); - index_file = swoole::intersection(*serv->http_index_files, dir_files); - - if (index_file != "" && !handler.set_filename(index_file)) - { - return false; - } - else if (index_file == "" && !serv->http_autoindex) - { - return false; - } + if (!handler.hit_index_file()) { + return false; } + /** - * the index file was not found in the current directory, + * 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 == "" && serv->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, SwooleTG.buffer_stack->str, SwooleTG.buffer_stack->size); - - 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: 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" : "", - (long) body_length, - date_str.c_str(), - date_str_last_modified.c_str(), - SW_HTTP_SERVER_SOFTWARE - ); + 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" + "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 ? "keep-alive" : "close", + (long) body_length, + date_str.c_str(), + date_str_last_modified.c_str(), + SW_HTTP_SERVER_SOFTWARE); response.data = header_buffer; - swServer_master_send(serv, &response); + send_to_connection(&response); response.info.len = body_length; - response.data = SwooleTG.buffer_stack->str; - swServer_master_send(serv, &response); + response.data = sw_tg_buffer()->str; + send_to_connection(&response); 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(); - response.data = header_buffer; - -#ifdef HAVE_TCP_NOPUSH - if (conn->socket->tcp_nopush == 0) - { - if (swSocket_tcp_nopush(conn->fd, 1) == -1) - { - swSysWarn("swSocket_tcp_nopush() failed"); + 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"; } - conn->socket->tcp_nopush = 1; } -#endif - swServer_master_send(serv, &response); - if (task->length != 0) - { - response.info.type = SW_SERVER_EVENT_SEND_FILE; - response.info.len = sizeof(swSendFile_request) + task->length + 1; - response.data = (char *) task; + 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; + + // Use tcp_nopush to improve sending efficiency + conn->socket->cork(); + + // Send HTTP header + send_to_connection(&response); + + // Send HTTP body + 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); + } - swServer_master_send(serv, &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); + } } - if (!request->keep_alive) - { + // Close the connection if keepalive is not used + if (!request->keep_alive) { response.info.type = SW_SERVER_EVENT_CLOSE; response.info.len = 0; - response.data = NULL; - swServer_master_send(serv, &response); + response.data = nullptr; + send_to_connection(&response); } return true; } -const char *swHttp_get_status_message(int code) -{ - switch (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"; case 101: @@ -316,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: @@ -344,21 +640,101 @@ 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; - c = ((unsigned char *)s)[0]; - if (isupper(c)) - { + c = ((unsigned char *) s)[0]; + if (isupper(c)) { c = tolower(c); } value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16; - c = ((unsigned char *)s)[1]; - if (isupper(c)) - { + c = ((unsigned char *) s)[1]; + if (isupper(c)) { c = tolower(c); } value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10; @@ -367,17 +743,15 @@ 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; while (len--) { if (*data == '+') { *dest = ' '; - } - else if (*data == '%' && len >= 2 && isxdigit((int) *(data + 1)) && isxdigit((int) *(data + 2))) { - *dest = (char) sw_htoi(data + 1); + } else if (*data == '%' && len >= 2 && isxdigit((int) *(data + 1)) && isxdigit((int) *(data + 2))) { + *dest = (char) url_htoi(data + 1); data += 2; len -= 2; } else { @@ -391,21 +765,18 @@ size_t swHttp_url_decode(char *str, size_t len) return dest - str; } -char* swHttp_url_encode(char const *str, size_t len) -{ - static unsigned char hexchars[] = "0123456789ABCDEF"; +char *url_encode(char const *str, size_t len) { + static uchar hexchars[] = "0123456789ABCDEF"; - register size_t x, y; - char *ret = (char*) sw_malloc(len * 3); + size_t x, y; + char *ret = (char *) sw_malloc(len * 3); - for (x = 0, y = 0; len--; x++, y++) - { + for (x = 0, y = 0; len--; x++, y++) { char c = str[x]; ret[y] = c; - if ((c < '0' && c != '-' && c != '.') || (c < 'A' && c > '9') || (c > 'Z' && c < 'a' && c != '_') - || (c > 'z' && c != '~')) - { + if ((c < '0' && c != '-' && c != '.') || (c < 'A' && c > '9') || (c > 'Z' && c < 'a' && c != '_') || + (c > 'z' && c != '~')) { ret[y++] = '%'; ret[y++] = hexchars[(unsigned char) c >> 4]; ret[y] = hexchars[(unsigned char) c & 15]; @@ -413,10 +784,9 @@ char* swHttp_url_encode(char const *str, size_t len) } ret[y] = '\0'; - do - { + do { size_t size = y + 1; - char *tmp = (char*) sw_malloc(size); + char *tmp = (char *) sw_malloc(size); memcpy(tmp, ret, size); sw_free(ret); ret = tmp; @@ -428,390 +798,379 @@ char* swHttp_url_encode(char const *str, size_t len) /** * only GET/POST */ -int swHttpRequest_get_protocol(swHttpRequest *request) -{ - char *p = request->buffer->str; - char *pe = p + request->buffer->length; +int Request::get_protocol() { + char *p = buffer_->str; + char *pe = p + buffer_->length; - if (request->buffer->length < (sizeof("GET / HTTP/1.x\r\n") - 1)) - { + if (buffer_->length < (sizeof("GET / HTTP/1.x\r\n") - 1)) { return SW_ERR; } - //http method - if (memcmp(p, SW_STRL("GET")) == 0) - { - request->method = SW_HTTP_GET; + // http method + if (memcmp(p, SW_STRL("GET")) == 0) { + method = SW_HTTP_GET; p += 3; - } - else if (memcmp(p, SW_STRL("POST")) == 0) - { - request->method = SW_HTTP_POST; + } else if (memcmp(p, SW_STRL("POST")) == 0) { + method = SW_HTTP_POST; p += 4; - } - else if (memcmp(p, SW_STRL("PUT")) == 0) - { - request->method = SW_HTTP_PUT; + } else if (memcmp(p, SW_STRL("PUT")) == 0) { + method = SW_HTTP_PUT; p += 3; - } - else if (memcmp(p, SW_STRL("PATCH")) == 0) - { - request->method = SW_HTTP_PATCH; + } else if (memcmp(p, SW_STRL("PATCH")) == 0) { + method = SW_HTTP_PATCH; p += 5; - } - else if (memcmp(p, SW_STRL("DELETE")) == 0) - { - request->method = SW_HTTP_DELETE; + } else if (memcmp(p, SW_STRL("DELETE")) == 0) { + method = SW_HTTP_DELETE; p += 6; - } - else if (memcmp(p, SW_STRL("HEAD")) == 0) - { - request->method = SW_HTTP_HEAD; + } else if (memcmp(p, SW_STRL("HEAD")) == 0) { + method = SW_HTTP_HEAD; p += 4; - } - else if (memcmp(p, SW_STRL("OPTIONS")) == 0) - { - request->method = SW_HTTP_OPTIONS; + } else if (memcmp(p, SW_STRL("OPTIONS")) == 0) { + method = SW_HTTP_OPTIONS; p += 7; - } - else if (memcmp(p, SW_STRL("COPY")) == 0) - { - request->method = SW_HTTP_COPY; + } else if (memcmp(p, SW_STRL("COPY")) == 0) { + method = SW_HTTP_COPY; p += 4; - } - else if (memcmp(p, SW_STRL("LOCK")) == 0) - { - request->method = SW_HTTP_LOCK; + } else if (memcmp(p, SW_STRL("LOCK")) == 0) { + method = SW_HTTP_LOCK; p += 4; - } - else if (memcmp(p, SW_STRL("MKCOL")) == 0) - { - request->method = SW_HTTP_MKCOL; + } else if (memcmp(p, SW_STRL("MKCOL")) == 0) { + method = SW_HTTP_MKCOL; p += 5; - } - else if (memcmp(p, SW_STRL("MOVE")) == 0) - { - request->method = SW_HTTP_MOVE; + } else if (memcmp(p, SW_STRL("MOVE")) == 0) { + method = SW_HTTP_MOVE; p += 4; - } - else if (memcmp(p, SW_STRL("PROPFIND")) == 0) - { - request->method = SW_HTTP_PROPFIND; + } else if (memcmp(p, SW_STRL("PROPFIND")) == 0) { + method = SW_HTTP_PROPFIND; p += 8; - } - else if (memcmp(p, SW_STRL("PROPPATCH")) == 0) - { - request->method = SW_HTTP_PROPPATCH; + } else if (memcmp(p, SW_STRL("PROPPATCH")) == 0) { + method = SW_HTTP_PROPPATCH; p += 9; - } - else if (memcmp(p, SW_STRL("UNLOCK")) == 0) - { - request->method = SW_HTTP_UNLOCK; + } else if (memcmp(p, SW_STRL("UNLOCK")) == 0) { + method = SW_HTTP_UNLOCK; p += 6; - } - else if (memcmp(p, SW_STRL("REPORT")) == 0) - { - request->method = SW_HTTP_REPORT; + } else if (memcmp(p, SW_STRL("REPORT")) == 0) { + method = SW_HTTP_REPORT; p += 6; - } - else if (memcmp(p, SW_STRL("PURGE")) == 0) - { - request->method = SW_HTTP_PURGE; + } else if (memcmp(p, SW_STRL("PURGE")) == 0) { + method = SW_HTTP_PURGE; p += 5; } -#ifdef SW_USE_HTTP2 // HTTP2 Connection Preface - else if (memcmp(p, SW_STRL("PRI")) == 0) - { - request->method = SW_HTTP_PRI; - if (memcmp(p, SW_STRL(SW_HTTP2_PRI_STRING)) == 0) - { - request->buffer->offset = sizeof(SW_HTTP2_PRI_STRING) - 1; + else if (memcmp(p, SW_STRL("PRI")) == 0) { + method = SW_HTTP_PRI; + if (buffer_->length >= (sizeof(SW_HTTP2_PRI_STRING) - 1) && memcmp(p, SW_STRL(SW_HTTP2_PRI_STRING)) == 0) { + buffer_->offset = sizeof(SW_HTTP2_PRI_STRING) - 1; return SW_OK; - } - else - { + } else { goto _excepted; } - } -#endif - else - { - _excepted: - request->excepted = 1; + } else { + _excepted: + excepted = 1; return SW_ERR; } - //http version + // http version char state = 0; - for (; p < pe; p++) - { - switch(state) - { + for (; p < pe; p++) { + switch (state) { case 0: - if (isspace(*p)) - { + if (isspace(*p)) { continue; } state = 1; - request->url_offset = p - request->buffer->str; + url_offset_ = p - buffer_->str; break; case 1: - if (isspace(*p)) - { + if (isspace(*p)) { state = 2; - request->url_length = p - request->buffer->str - request->url_offset; + url_length_ = p - buffer_->str - url_offset_; continue; } break; case 2: - if (isspace(*p)) - { + 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) - { - request->version = SW_HTTP_VERSION_11; + if (memcmp(p, SW_STRL("HTTP/1.1")) == 0) { + version = SW_HTTP_VERSION_11; goto _end; - } - else if (memcmp(p, SW_STRL("HTTP/1.0")) == 0) - { - request->version = SW_HTTP_VERSION_10; + } else if (memcmp(p, SW_STRL("HTTP/1.0")) == 0) { + version = SW_HTTP_VERSION_10; goto _end; - } - else - { + } else { goto _excepted; } default: break; } } - _end: +_end: p += sizeof("HTTP/1.x") - 1; - request->request_line_length = request->buffer->offset = p - request->buffer->str; + request_line_length_ = buffer_->offset = p - buffer_->str; return SW_OK; } -void swHttpRequest_free(swConnection *conn) -{ - swHttpRequest *request = (swHttpRequest *) conn->object; - if (!request) - { - return; - } - if (request->buffer) - { - swString_free(request->buffer); - } - bzero(request, sizeof(swHttpRequest)); - sw_free(request); - conn->object = nullptr; -} - /** * simple get headers info */ -void swHttpRequest_parse_header_info(swHttpRequest *request) -{ - swString *buffer = request->buffer; +void Request::parse_header_info() { // header field start - char *p = buffer->str + request->request_line_length + (sizeof("\r\n") - 1); + char *p = buffer_->str + request_line_length_ + (sizeof("\r\n") - 1); // point-end: start + strlen(all-header) without strlen("\r\n\r\n") - char *pe = buffer->str + request->header_length - (sizeof("\r\n\r\n") - 1); - - for (; p < pe; p++) - { - if (*(p - 1) == '\n' && *(p - 2) == '\r') - { - if (SW_STRCASECT(p, pe - p, "Content-Length:")) - { - unsigned long long content_length; + char *pe = buffer_->str + header_length_ - (sizeof("\r\n\r\n") - 1); + + for (; p < pe; p++) { + if (*(p - 1) == '\n' && *(p - 2) == '\r') { + if (SW_STR_ISTARTS_WITH(p, pe - p, "Content-Length:")) { // strlen("Content-Length:") p += (sizeof("Content-Length:") - 1); // skip spaces - while (*p == ' ') - { + while (*p == ' ') { p++; } - content_length = strtoull(p, NULL, 10); - request->content_length = SW_MIN(content_length, UINT32_MAX); - request->known_length = 1; - } - else if (SW_STRCASECT(p, pe - p, "Connection:")) - { + content_length_ = strtoull(p, nullptr, 10); + known_length = 1; + } else if (SW_STR_ISTARTS_WITH(p, pe - p, "Connection:")) { // strlen("Connection:") p += (sizeof("Connection:") - 1); // skip spaces - while (*p == ' ') - { + while (*p == ' ') { p++; } - if (SW_STRCASECT(p, pe - p, "keep-alive")) - { - request->keep_alive = 1; + 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 == ' ') - { + while (*p == ' ') { + p++; + } + 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_STRCASECT(p, pe - p, "chunked")) - { - request->chunked = 1; + 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; } } } } - request->header_parsed = 1; - if (request->chunked && request->known_length && request->content_length == 0) - { - request->nobody_chunked = 1; + header_parsed = 1; + if (chunked && known_length && content_length_ == 0) { + nobody_chunked = 1; + } +} + +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; } -#ifdef SW_HTTP_100_CONTINUE -int swHttpRequest_has_expect_header(swHttpRequest *request) -{ - swString *buffer = request->buffer; - //char *buf = buffer->str + buffer->offset; - char *buf = buffer->str; - //int len = buffer->length - buffer->offset; - int len = buffer->length; +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; + 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")) - { + for (p = buf; p < pe; p++) { + 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")) - { - return 1; - } - else - { - return 0; + if (SW_STR_ISTARTS_WITH(p, pe - p, "100-continue")) { + return true; + } else { + return false; } - } - else - { + } else { p++; } } } - return 0; + return false; } -#endif -int swHttpRequest_get_header_length(swHttpRequest *request) -{ - swString *buffer = request->buffer; - char *p = buffer->str + buffer->offset; - char *pe = buffer->str + buffer->length; +int Request::get_header_length() { + char *p = buffer_->str + buffer_->offset; + char *pe = buffer_->str + buffer_->length; - for (; p <= pe - (sizeof("\r\n\r\n") - 1); p++) - { - if (memcmp(p, SW_STRL("\r\n\r\n")) == 0) - { + for (; p <= pe - (sizeof("\r\n\r\n") - 1); p++) { + if (memcmp(p, SW_STRL("\r\n\r\n")) == 0) { // strlen(header) + strlen("\r\n\r\n") - request->header_length = buffer->offset = p - buffer->str + (sizeof("\r\n\r\n") - 1); + header_length_ = buffer_->offset = p - buffer_->str + (sizeof("\r\n\r\n") - 1); return SW_OK; } } - buffer->offset = p - buffer->str; + buffer_->offset = p - buffer_->str; return SW_ERR; } -int swHttpRequest_get_chunked_body_length(swHttpRequest *request) -{ - swString *buffer = request->buffer; - char *p = buffer->str + buffer->offset; - char *pe = buffer->str + buffer->length; +int Request::get_chunked_body_length() { + char *p = buffer_->str + buffer_->offset; + char *pe = buffer_->str + buffer_->length; - while (1) - { - if ((size_t) (pe - p) < (1 + (sizeof("\r\n") - 1))) - { + while (1) { + if ((size_t) (pe - p) < (1 + (sizeof("\r\n") - 1))) { /* need the next chunk */ return SW_ERR; } char *head = p; - size_t chunk_length = swoole_hex2dec(&head); - if (*head != '\r') - { - request->excepted = 1; + size_t n_parsed; + size_t chunk_length = swoole_hex2dec(head, &n_parsed); + head += n_parsed; + if (*head != '\r') { + excepted = 1; return SW_ERR; } p = head + (sizeof("\r\n") - 1) + chunk_length + (sizeof("\r\n") - 1); /* used to check package_max_length */ - request->content_length = p - (buffer->str + request->header_length); - if (p > pe) - { + content_length_ = p - (buffer_->str + header_length_); + if (p > pe) { /* need recv chunk body again */ return SW_ERR; } - buffer->offset = p - buffer->str; - if (chunk_length == 0) - { + buffer_->offset = p - buffer_->str; + if (chunk_length == 0) { break; } } - request->known_length = 1; + known_length = 1; return SW_OK; } -string swHttpRequest_get_date_if_modified_since(swHttpRequest *request) -{ - char *p = request->buffer->str + request->url_offset + request->url_length + 10; - char *pe = request->buffer->str + request->header_length; - - string result; +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_; - char *date_if_modified_since = NULL; - size_t length_if_modified_since = 0; + char *buffer = nullptr; + char *colon = nullptr; int state = 0; - for (; p < pe; p++) - { - switch (state) - { + 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; + if (!isspace(*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: @@ -819,62 +1178,78 @@ string swHttpRequest_get_date_if_modified_since(swHttpRequest *request) } } - return string(""); + return string(); } - -#ifdef SW_USE_HTTP2 -ssize_t swHttpMix_get_package_length(swProtocol *protocol, swSocket *socket, char *data, uint32_t length) -{ - swConnection *conn = (swConnection *) socket->object; - if (conn->websocket_status == WEBSOCKET_STATUS_ACTIVE) - { - return swWebSocket_get_package_length(protocol, socket, data, length); +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; + } } - else if (conn->http2_stream) - { - return swHttp2_get_frame_length(protocol, socket, data, length); + return -1; +} + +const char *get_method_string(int method) { + if (method < 0 || method > SW_HTTP_PRI) { + return nullptr; } - else - { - abort(); + return method_strings[method - 1]; +} + +int dispatch_request(Server *serv, const Protocol *proto, Socket *_socket, const RecvData *rdata) { + if (serv->is_unavailable()) { + _socket->send(SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET), 0); return SW_ERR; } + return Server::dispatch_task(proto, _socket, rdata); +} + +//----------------------------------------------------------------- +static void protocol_status_error(Socket *socket, Connection *conn) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_PROTOCOL_ERROR, + "unexpected protocol status of session#%ld<%s:%d>", + conn->session_id, + conn->info.get_ip(), + conn->info.get_port()); } -uint8_t swHttpMix_get_package_length_size(swSocket *socket) -{ - swConnection *conn = (swConnection *) socket->object; - if (conn->websocket_status == WEBSOCKET_STATUS_ACTIVE) - { - return SW_WEBSOCKET_HEADER_LEN + SW_WEBSOCKET_MASK_LEN + sizeof(uint64_t); +ssize_t get_package_length(const Protocol *protocol, Socket *socket, PacketLength *pl) { + Connection *conn = (Connection *) socket->object; + if (conn->websocket_status >= websocket::STATUS_HANDSHAKE) { + return websocket::get_package_length(protocol, socket, pl); + } else if (conn->http2_stream) { + return http2::get_frame_length(protocol, socket, pl); + } else { + protocol_status_error(socket, conn); + return SW_ERR; } - else if (conn->http2_stream) - { +} + +uint8_t get_package_length_size(Socket *socket) { + Connection *conn = (Connection *) socket->object; + 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 - { - abort(); + } else { + protocol_status_error(socket, conn); return 0; } } -int swHttpMix_dispatch_frame(swProtocol *proto, swSocket *socket, char *data, uint32_t length) -{ - swConnection *conn = (swConnection *) socket->object; - if (conn->websocket_status == WEBSOCKET_STATUS_ACTIVE) - { - return swWebSocket_dispatch_frame(proto, socket, data, length); - } - else if (conn->http2_stream) - { - return swReactorThread_dispatch(proto, socket, data, length); - } - else - { - abort(); +int dispatch_frame(const Protocol *proto, Socket *socket, const RecvData *rdata) { + Connection *conn = (Connection *) socket->object; + if (conn->websocket_status >= websocket::STATUS_HANDSHAKE) { + return websocket::dispatch_frame(proto, socket, rdata); + } else if (conn->http2_stream) { + 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 e6bdc1e15cc..b870eda4eb4 100644 --- a/src/protocol/http2.cc +++ b/src/protocol/http2.cc @@ -12,41 +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 "http2.h" +#include "swoole_socket.h" +#include "swoole_http2.h" +#include "swoole_protocol.h" -int swHttp2_send_setting_frame(swProtocol *protocol, swSocket *_socket) -{ - char setting_frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_SETTING_OPTION_SIZE * 3]; - char *p = setting_frame; +using swoole::PacketLength; +using swoole::Protocol; +using swoole::network::Socket; + +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 swSocket_send(_socket, 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); } /** @@ -60,19 +138,15 @@ int swHttp2_send_setting_frame(swProtocol *protocol, swSocket *_socket) | Frame Payload (0...) ... +---------------------------------------------------------------+ */ -ssize_t swHttp2_get_frame_length(swProtocol *protocol, swSocket *conn, 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) -{ - switch(type) - { +const char *get_type(int type) { + switch (type) { case SW_HTTP2_TYPE_DATA: return "DATA"; case SW_HTTP2_TYPE_HEADERS: @@ -94,14 +168,12 @@ 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) -{ - switch(type) - { +int get_type_color(int type) { + switch (type) { case SW_HTTP2_TYPE_DATA: case SW_HTTP2_TYPE_WINDOW_UPDATE: return SW_COLOR_MAGENTA; @@ -119,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 94ac3b73542..177606b285a 100644 --- a/src/protocol/mime_type.cc +++ b/src/protocol/mime_type.cc @@ -12,439 +12,429 @@ | to obtain it through the world-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 "mime_type.h" +#include "swoole_mime_type.h" -using namespace std; +#include -namespace swoole { namespace mime_type { -unordered_map map({ - { "ez", "application/andrew-inset" }, - { "aw", "application/applixware" }, - { "atom", "application/atom+xml" }, - { "atomcat", "application/atomcat+xml" }, - { "atomsvc", "application/atomsvc+xml" }, - { "bdoc", "application/bdoc" }, - { "ccxml", "application/ccxml+xml" }, - { "cdmia", "application/cdmi-capability" }, - { "cdmic", "application/cdmi-container" }, - { "cdmid", "application/cdmi-domain" }, - { "cdmio", "application/cdmi-object" }, - { "cdmiq", "application/cdmi-queue" }, - { "cu", "application/cu-seeme" }, - { "mpd", "application/dash+xml" }, - { "davmount", "application/davmount+xml" }, - { "dbk", "application/docbook+xml" }, - { "dssc", "application/dssc+der" }, - { "xdssc", "application/dssc+xml" }, - { "ecma", "application/ecmascript" }, - { "emma", "application/emma+xml" }, - { "epub", "application/epub+zip" }, - { "exi", "application/exi" }, - { "pfr", "application/font-tdpfr" }, - { "woff", "application/font-woff" }, - { "geojson", "application/geo+json" }, - { "gml", "application/gml+xml" }, - { "gpx", "application/gpx+xml" }, - { "gxf", "application/gxf" }, - { "gz", "application/gzip" }, - { "hjson", "application/hjson" }, - { "stk", "application/hyperstudio" }, - { "ink", "application/inkml+xml" }, - { "inkml", "application/inkml+xml" }, - { "ipfix", "application/ipfix" }, - { "jar", "application/java-archive" }, - { "war", "application/java-archive" }, - { "ear", "application/java-archive" }, - { "apk", "application/vnd.android.package-archive" }, - { "ser", "application/java-serialized-object" }, - { "class", "application/java-vm" }, - { "js", "application/javascript" }, - { "mjs", "application/javascript" }, - { "json", "application/json" }, - { "map", "application/json" }, - { "json5", "application/json5" }, - { "jsonml", "application/jsonml+json" }, - { "jsonld", "application/ld+json" }, - { "lostxml", "application/lost+xml" }, - { "hqx", "application/mac-binhex40" }, - { "cpt", "application/mac-compactpro" }, - { "mads", "application/mads+xml" }, - { "webmanifest", "application/manifest+json" }, - { "mrc", "application/marc" }, - { "mrcx", "application/marcxml+xml" }, - { "ma", "application/mathematica" }, - { "nb", "application/mathematica" }, - { "mb", "application/mathematica" }, - { "mathml", "application/mathml+xml" }, - { "mbox", "application/mbox" }, - { "mscml", "application/mediaservercontrol+xml" }, - { "metalink", "application/metalink+xml" }, - { "meta4", "application/metalink4+xml" }, - { "mets", "application/mets+xml" }, - { "mods", "application/mods+xml" }, - { "m21", "application/mp21" }, - { "mp21", "application/mp21" }, - { "mp4s", "application/mp4" }, - { "m4p", "application/mp4" }, - { "doc", "application/msword" }, - { "docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }, - { "dot", "application/msword" }, - { "mxf", "application/mxf" }, - { "bin", "application/octet-stream" }, - { "dms", "application/octet-stream" }, - { "lrf", "application/octet-stream" }, - { "mar", "application/octet-stream" }, - { "so", "application/octet-stream" }, - { "dist", "application/octet-stream" }, - { "distz", "application/octet-stream" }, - { "pkg", "application/octet-stream" }, - { "bpk", "application/octet-stream" }, - { "dump", "application/octet-stream" }, - { "elc", "application/octet-stream" }, - { "deploy", "application/octet-stream" }, - { "exe", "application/octet-stream" }, - { "dll", "application/octet-stream" }, - { "deb", "application/octet-stream" }, - { "dmg", "application/octet-stream" }, - { "iso", "application/octet-stream" }, - { "img", "application/octet-stream" }, - { "msi", "application/octet-stream" }, - { "msp", "application/octet-stream" }, - { "msm", "application/octet-stream" }, - { "buffer", "application/octet-stream" }, - { "oda", "application/oda" }, - { "opf", "application/oebps-package+xml" }, - { "ogx", "application/ogg" }, - { "omdoc", "application/omdoc+xml" }, - { "onetoc", "application/onenote" }, - { "onetoc2", "application/onenote" }, - { "onetmp", "application/onenote" }, - { "onepkg", "application/onenote" }, - { "oxps", "application/oxps" }, - { "xer", "application/patch-ops-error+xml" }, - { "pdf", "application/pdf" }, - { "pgp", "application/pgp-encrypted" }, - { "asc", "application/pgp-signature" }, - { "sig", "application/pgp-signature" }, - { "prf", "application/pics-rules" }, - { "p10", "application/pkcs10" }, - { "p7m", "application/pkcs7-mime" }, - { "p7c", "application/pkcs7-mime" }, - { "p7s", "application/pkcs7-signature" }, - { "p8", "application/pkcs8" }, - { "ac", "application/pkix-attr-cert" }, - { "cer", "application/pkix-cert" }, - { "crl", "application/pkix-crl" }, - { "pkipath", "application/pkix-pkipath" }, - { "pki", "application/pkixcmp" }, - { "pls", "application/pls+xml" }, - { "ai", "application/postscript" }, - { "eps", "application/postscript" }, - { "ps", "application/postscript" }, - { "pskcxml", "application/pskc+xml" }, - { "raml", "application/raml+yaml" }, - { "rdf", "application/rdf+xml" }, - { "rif", "application/reginfo+xml" }, - { "rnc", "application/relax-ng-compact-syntax" }, - { "rl", "application/resource-lists+xml" }, - { "rld", "application/resource-lists-diff+xml" }, - { "rs", "application/rls-services+xml" }, - { "gbr", "application/rpki-ghostbusters" }, - { "mft", "application/rpki-manifest" }, - { "roa", "application/rpki-roa" }, - { "rsd", "application/rsd+xml" }, - { "rss", "application/rss+xml" }, - { "rtf", "application/rtf" }, - { "sbml", "application/sbml+xml" }, - { "scq", "application/scvp-cv-request" }, - { "scs", "application/scvp-cv-response" }, - { "spq", "application/scvp-vp-request" }, - { "spp", "application/scvp-vp-response" }, - { "sdp", "application/sdp" }, - { "setpay", "application/set-payment-initiation" }, - { "setreg", "application/set-registration-initiation" }, - { "shf", "application/shf+xml" }, - { "smi", "application/smil+xml" }, - { "smil", "application/smil+xml" }, - { "rq", "application/sparql-query" }, - { "srx", "application/sparql-results+xml" }, - { "gram", "application/srgs" }, - { "grxml", "application/srgs+xml" }, - { "sru", "application/sru+xml" }, - { "ssdl", "application/ssdl+xml" }, - { "ssml", "application/ssml+xml" }, - { "tei", "application/tei+xml" }, - { "teicorpus", "application/tei+xml" }, - { "tfi", "application/thraud+xml" }, - { "tsd", "application/timestamped-data" }, - { "vxml", "application/voicexml+xml" }, - { "wasm", "application/wasm" }, - { "wgt", "application/widget" }, - { "hlp", "application/winhlp" }, - { "wsdl", "application/wsdl+xml" }, - { "wspolicy", "application/wspolicy+xml" }, - { "xaml", "application/xaml+xml" }, - { "xdf", "application/xcap-diff+xml" }, - { "xenc", "application/xenc+xml" }, - { "xhtml", "application/xhtml+xml" }, - { "xht", "application/xhtml+xml" }, - { "xml", "application/xml" }, - { "xsl", "application/xml" }, - { "xsd", "application/xml" }, - { "rng", "application/xml" }, - { "dtd", "application/xml-dtd" }, - { "xop", "application/xop+xml" }, - { "xpl", "application/xproc+xml" }, - { "xslt", "application/xslt+xml" }, - { "xspf", "application/xspf+xml" }, - { "mxml", "application/xv+xml" }, - { "xhvml", "application/xv+xml" }, - { "xvml", "application/xv+xml" }, - { "xvm", "application/xv+xml" }, - { "yang", "application/yang" }, - { "yin", "application/yin+xml" }, - { "zip", "application/zip" }, - { "*3gpp", "audio/3gpp" }, - { "adp", "audio/adpcm" }, - { "au", "audio/basic" }, - { "snd", "audio/basic" }, - { "mid", "audio/midi" }, - { "midi", "audio/midi" }, - { "kar", "audio/midi" }, - { "rmi", "audio/midi" }, - { "*mp3", "audio/mp3" }, - { "m4a", "audio/mp4" }, - { "mp4a", "audio/mp4" }, - { "mpga", "audio/mpeg" }, - { "mp2", "audio/mpeg" }, - { "mp2a", "audio/mpeg" }, - { "mp3", "audio/mpeg" }, - { "m2a", "audio/mpeg" }, - { "m3a", "audio/mpeg" }, - { "oga", "audio/ogg" }, - { "ogg", "audio/ogg" }, - { "spx", "audio/ogg" }, - { "s3m", "audio/s3m" }, - { "sil", "audio/silk" }, - { "wav", "audio/wav" }, - { "*wav", "audio/wave" }, - { "weba", "audio/webm" }, - { "xm", "audio/xm" }, - { "ttc", "font/collection" }, - { "otf", "font/otf" }, - { "ttf", "font/ttf" }, - { "*woff", "font/woff" }, - { "woff2", "font/woff2" }, - { "apng", "image/apng" }, - { "bmp", "image/bmp" }, - { "cgm", "image/cgm" }, - { "g3", "image/g3fax" }, - { "gif", "image/gif" }, - { "ief", "image/ief" }, - { "jp2", "image/jp2" }, - { "jpg2", "image/jp2" }, - { "jpeg", "image/jpeg" }, - { "jpg", "image/jpeg" }, - { "jpe", "image/jpeg" }, - { "jpm", "image/jpm" }, - { "jpx", "image/jpx" }, - { "jpf", "image/jpx" }, - { "ktx", "image/ktx" }, - { "png", "image/png" }, - { "sgi", "image/sgi" }, - { "svg", "image/svg+xml" }, - { "svgz", "image/svg+xml" }, - { "tiff", "image/tiff" }, - { "tif", "image/tiff" }, - { "webp", "image/webp" }, - { "disposition-notification", "message/disposition-notification" }, - { "u8msg", "message/global" }, - { "u8dsn", "message/global-delivery-status" }, - { "u8mdn", "message/global-disposition-notification" }, - { "u8hdr", "message/global-headers" }, - { "eml", "message/rfc822" }, - { "mime", "message/rfc822" }, - { "gltf", "model/gltf+json" }, - { "glb", "model/gltf-binary" }, - { "igs", "model/iges" }, - { "iges", "model/iges" }, - { "msh", "model/mesh" }, - { "mesh", "model/mesh" }, - { "silo", "model/mesh" }, - { "wrl", "model/vrml" }, - { "vrml", "model/vrml" }, - { "x3db", "model/x3d+binary" }, - { "x3dbz", "model/x3d+binary" }, - { "x3dv", "model/x3d+vrml" }, - { "x3dvz", "model/x3d+vrml" }, - { "x3d", "model/x3d+xml" }, - { "x3dz", "model/x3d+xml" }, - { "appcache", "text/cache-manifest" }, - { "manifest", "text/cache-manifest" }, - { "ics", "text/calendar" }, - { "ifb", "text/calendar" }, - { "coffee", "text/coffeescript" }, - { "litcoffee", "text/coffeescript" }, - { "css", "text/css" }, - { "csv", "text/csv" }, - { "html", "text/html" }, - { "htm", "text/html" }, - { "shtml", "text/html" }, - { "jade", "text/jade" }, - { "jsx", "text/jsx" }, - { "less", "text/less" }, - { "markdown", "text/markdown" }, - { "md", "text/markdown" }, - { "mml", "text/mathml" }, - { "n3", "text/n3" }, - { "txt", "text/plain" }, - { "text", "text/plain" }, - { "conf", "text/plain" }, - { "def", "text/plain" }, - { "list", "text/plain" }, - { "log", "text/plain" }, - { "in", "text/plain" }, - { "ini", "text/plain" }, - { "rtx", "text/richtext" }, - { "*rtf", "text/rtf" }, - { "sgml", "text/sgml" }, - { "sgm", "text/sgml" }, - { "shex", "text/shex" }, - { "slim", "text/slim" }, - { "slm", "text/slim" }, - { "stylus", "text/stylus" }, - { "styl", "text/stylus" }, - { "tsv", "text/tab-separated-values" }, - { "t", "text/troff" }, - { "tr", "text/troff" }, - { "roff", "text/troff" }, - { "man", "text/troff" }, - { "me", "text/troff" }, - { "ms", "text/troff" }, - { "ttl", "text/turtle" }, - { "uri", "text/uri-list" }, - { "uris", "text/uri-list" }, - { "urls", "text/uri-list" }, - { "vcard", "text/vcard" }, - { "vtt", "text/vtt" }, - { "*xml", "text/xml" }, - { "yaml", "text/yaml" }, - { "yml", "text/yaml" }, - { "3gp", "video/3gpp" }, - { "3gpp", "video/3gpp" }, - { "3g2", "video/3gpp2" }, - { "h261", "video/h261" }, - { "h263", "video/h263" }, - { "h264", "video/h264" }, - { "jpgv", "video/jpeg" }, - { "*jpm", "video/jpm" }, - { "jpgm", "video/jpm" }, - { "mj2", "video/mj2" }, - { "mjp2", "video/mj2" }, - { "ts", "video/mp2t" }, - { "mp4", "video/mp4" }, - { "mp4v", "video/mp4" }, - { "mpg4", "video/mp4" }, - { "mpeg", "video/mpeg" }, - { "mpg", "video/mpeg" }, - { "mpe", "video/mpeg" }, - { "m1v", "video/mpeg" }, - { "m2v", "video/mpeg" }, - { "ogv", "video/ogg" }, - { "qt", "video/quicktime" }, - { "mov", "video/quicktime" }, - { "webm", "video/webm" }, - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Complete_list_of_MIME_types - { "aac", "audio/aac" }, - { "abw", "application/x-abiword" }, - { "arc", "application/octet-stream" }, - { "avi", "video/x-msvideo" }, - { "azw", "application/vnd.amazon.ebook" }, - { "bz", "application/x-bzip" }, - { "bz2", "application/x-bzip2" }, - { "csh", "application/x-csh" }, - { "eot", "application/vnd.ms-fontobject" }, - { "es", "application/ecmascript" }, - { "ico", "image/x-icon" }, - { "mpkg", "application/vnd.apple.installer+xml" }, - { "odp", "application/vnd.oasis.opendocument.presentation" }, - { "ods", "application/vnd.oasis.opendocument.spreadsheet" }, - { "odt", "application/vnd.oasis.opendocument.text" }, - { "ppt", "application/vnd.ms-powerpoint" }, - { "pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation" }, - { "rar", "application/x-rar-compressed" }, - { "sh", "application/x-sh" }, - { "swf", "application/x-shockwave-flash" }, - { "tar", "application/x-tar" }, - { "vsd", "application/vnd.visio" }, - { "xls", "application/vnd.ms-excel" }, - { "xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }, - { "xul", "application/vnd.mozilla.xul+xml" }, - { "7z", "application/x-7z-compressed" } -}); +namespace swoole { +namespace mime_type { -static const string octet_stream("application/octet-stream"); +static std::unordered_map map_( + {{"ez", "application/andrew-inset"}, + {"aw", "application/applixware"}, + {"atom", "application/atom+xml"}, + {"atomcat", "application/atomcat+xml"}, + {"atomsvc", "application/atomsvc+xml"}, + {"bdoc", "application/bdoc"}, + {"ccxml", "application/ccxml+xml"}, + {"cdmia", "application/cdmi-capability"}, + {"cdmic", "application/cdmi-container"}, + {"cdmid", "application/cdmi-domain"}, + {"cdmio", "application/cdmi-object"}, + {"cdmiq", "application/cdmi-queue"}, + {"cu", "application/cu-seeme"}, + {"mpd", "application/dash+xml"}, + {"davmount", "application/davmount+xml"}, + {"dbk", "application/docbook+xml"}, + {"dssc", "application/dssc+der"}, + {"xdssc", "application/dssc+xml"}, + {"ecma", "application/ecmascript"}, + {"emma", "application/emma+xml"}, + {"epub", "application/epub+zip"}, + {"exi", "application/exi"}, + {"pfr", "application/font-tdpfr"}, + {"woff", "application/font-woff"}, + {"geojson", "application/geo+json"}, + {"gml", "application/gml+xml"}, + {"gpx", "application/gpx+xml"}, + {"gxf", "application/gxf"}, + {"gz", "application/gzip"}, + {"hjson", "application/hjson"}, + {"stk", "application/hyperstudio"}, + {"ink", "application/inkml+xml"}, + {"inkml", "application/inkml+xml"}, + {"ipfix", "application/ipfix"}, + {"jar", "application/java-archive"}, + {"war", "application/java-archive"}, + {"ear", "application/java-archive"}, + {"apk", "application/vnd.android.package-archive"}, + {"ser", "application/java-serialized-object"}, + {"class", "application/java-vm"}, + {"js", "application/javascript"}, + {"mjs", "application/javascript"}, + {"json", "application/json"}, + {"map", "application/json"}, + {"json5", "application/json5"}, + {"jsonml", "application/jsonml+json"}, + {"jsonld", "application/ld+json"}, + {"lostxml", "application/lost+xml"}, + {"hqx", "application/mac-binhex40"}, + {"cpt", "application/mac-compactpro"}, + {"mads", "application/mads+xml"}, + {"webmanifest", "application/manifest+json"}, + {"mrc", "application/marc"}, + {"mrcx", "application/marcxml+xml"}, + {"ma", "application/mathematica"}, + {"nb", "application/mathematica"}, + {"mb", "application/mathematica"}, + {"mathml", "application/mathml+xml"}, + {"mbox", "application/mbox"}, + {"mscml", "application/mediaservercontrol+xml"}, + {"metalink", "application/metalink+xml"}, + {"meta4", "application/metalink4+xml"}, + {"mets", "application/mets+xml"}, + {"mods", "application/mods+xml"}, + {"m21", "application/mp21"}, + {"mp21", "application/mp21"}, + {"mp4s", "application/mp4"}, + {"m4p", "application/mp4"}, + {"doc", "application/msword"}, + {"docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, + {"dot", "application/msword"}, + {"mxf", "application/mxf"}, + {"bin", "application/octet-stream"}, + {"dms", "application/octet-stream"}, + {"lrf", "application/octet-stream"}, + {"mar", "application/octet-stream"}, + {"so", "application/octet-stream"}, + {"dist", "application/octet-stream"}, + {"distz", "application/octet-stream"}, + {"pkg", "application/octet-stream"}, + {"bpk", "application/octet-stream"}, + {"dump", "application/octet-stream"}, + {"elc", "application/octet-stream"}, + {"deploy", "application/octet-stream"}, + {"exe", "application/octet-stream"}, + {"dll", "application/octet-stream"}, + {"deb", "application/octet-stream"}, + {"dmg", "application/octet-stream"}, + {"iso", "application/octet-stream"}, + {"img", "application/octet-stream"}, + {"msi", "application/octet-stream"}, + {"msp", "application/octet-stream"}, + {"msm", "application/octet-stream"}, + {"buffer", "application/octet-stream"}, + {"oda", "application/oda"}, + {"opf", "application/oebps-package+xml"}, + {"ogx", "application/ogg"}, + {"omdoc", "application/omdoc+xml"}, + {"onetoc", "application/onenote"}, + {"onetoc2", "application/onenote"}, + {"onetmp", "application/onenote"}, + {"onepkg", "application/onenote"}, + {"oxps", "application/oxps"}, + {"xer", "application/patch-ops-error+xml"}, + {"pdf", "application/pdf"}, + {"pgp", "application/pgp-encrypted"}, + {"asc", "application/pgp-signature"}, + {"sig", "application/pgp-signature"}, + {"prf", "application/pics-rules"}, + {"p10", "application/pkcs10"}, + {"p7m", "application/pkcs7-mime"}, + {"p7c", "application/pkcs7-mime"}, + {"p7s", "application/pkcs7-signature"}, + {"p8", "application/pkcs8"}, + {"ac", "application/pkix-attr-cert"}, + {"cer", "application/pkix-cert"}, + {"crl", "application/pkix-crl"}, + {"pkipath", "application/pkix-pkipath"}, + {"pki", "application/pkixcmp"}, + {"pls", "application/pls+xml"}, + {"ai", "application/postscript"}, + {"eps", "application/postscript"}, + {"ps", "application/postscript"}, + {"pskcxml", "application/pskc+xml"}, + {"raml", "application/raml+yaml"}, + {"rdf", "application/rdf+xml"}, + {"rif", "application/reginfo+xml"}, + {"rnc", "application/relax-ng-compact-syntax"}, + {"rl", "application/resource-lists+xml"}, + {"rld", "application/resource-lists-diff+xml"}, + {"rs", "application/rls-services+xml"}, + {"gbr", "application/rpki-ghostbusters"}, + {"mft", "application/rpki-manifest"}, + {"roa", "application/rpki-roa"}, + {"rsd", "application/rsd+xml"}, + {"rss", "application/rss+xml"}, + {"rtf", "application/rtf"}, + {"sbml", "application/sbml+xml"}, + {"scq", "application/scvp-cv-request"}, + {"scs", "application/scvp-cv-response"}, + {"spq", "application/scvp-vp-request"}, + {"spp", "application/scvp-vp-response"}, + {"sdp", "application/sdp"}, + {"setpay", "application/set-payment-initiation"}, + {"setreg", "application/set-registration-initiation"}, + {"shf", "application/shf+xml"}, + {"smi", "application/smil+xml"}, + {"smil", "application/smil+xml"}, + {"rq", "application/sparql-query"}, + {"srx", "application/sparql-results+xml"}, + {"gram", "application/srgs"}, + {"grxml", "application/srgs+xml"}, + {"sru", "application/sru+xml"}, + {"ssdl", "application/ssdl+xml"}, + {"ssml", "application/ssml+xml"}, + {"tei", "application/tei+xml"}, + {"teicorpus", "application/tei+xml"}, + {"tfi", "application/thraud+xml"}, + {"tsd", "application/timestamped-data"}, + {"vxml", "application/voicexml+xml"}, + {"wasm", "application/wasm"}, + {"wgt", "application/widget"}, + {"hlp", "application/winhlp"}, + {"wsdl", "application/wsdl+xml"}, + {"wspolicy", "application/wspolicy+xml"}, + {"xaml", "application/xaml+xml"}, + {"xdf", "application/xcap-diff+xml"}, + {"xenc", "application/xenc+xml"}, + {"xhtml", "application/xhtml+xml"}, + {"xht", "application/xhtml+xml"}, + {"xml", "application/xml"}, + {"xsl", "application/xml"}, + {"xsd", "application/xml"}, + {"rng", "application/xml"}, + {"dtd", "application/xml-dtd"}, + {"xop", "application/xop+xml"}, + {"xpl", "application/xproc+xml"}, + {"xslt", "application/xslt+xml"}, + {"xspf", "application/xspf+xml"}, + {"mxml", "application/xv+xml"}, + {"xhvml", "application/xv+xml"}, + {"xvml", "application/xv+xml"}, + {"xvm", "application/xv+xml"}, + {"yang", "application/yang"}, + {"yin", "application/yin+xml"}, + {"zip", "application/zip"}, + {"*3gpp", "audio/3gpp"}, + {"adp", "audio/adpcm"}, + {"au", "audio/basic"}, + {"snd", "audio/basic"}, + {"mid", "audio/midi"}, + {"midi", "audio/midi"}, + {"kar", "audio/midi"}, + {"rmi", "audio/midi"}, + {"*mp3", "audio/mp3"}, + {"m4a", "audio/mp4"}, + {"mp4a", "audio/mp4"}, + {"mpga", "audio/mpeg"}, + {"mp2", "audio/mpeg"}, + {"mp2a", "audio/mpeg"}, + {"mp3", "audio/mpeg"}, + {"m2a", "audio/mpeg"}, + {"m3a", "audio/mpeg"}, + {"oga", "audio/ogg"}, + {"ogg", "audio/ogg"}, + {"spx", "audio/ogg"}, + {"s3m", "audio/s3m"}, + {"sil", "audio/silk"}, + {"wav", "audio/wav"}, + {"*wav", "audio/wave"}, + {"weba", "audio/webm"}, + {"xm", "audio/xm"}, + {"ttc", "font/collection"}, + {"otf", "font/otf"}, + {"ttf", "font/ttf"}, + {"*woff", "font/woff"}, + {"woff2", "font/woff2"}, + {"apng", "image/apng"}, + {"bmp", "image/bmp"}, + {"cgm", "image/cgm"}, + {"g3", "image/g3fax"}, + {"gif", "image/gif"}, + {"ief", "image/ief"}, + {"jp2", "image/jp2"}, + {"jpg2", "image/jp2"}, + {"jpeg", "image/jpeg"}, + {"jpg", "image/jpeg"}, + {"jpe", "image/jpeg"}, + {"jpm", "image/jpm"}, + {"jpx", "image/jpx"}, + {"jpf", "image/jpx"}, + {"ktx", "image/ktx"}, + {"png", "image/png"}, + {"sgi", "image/sgi"}, + {"svg", "image/svg+xml"}, + {"svgz", "image/svg+xml"}, + {"tiff", "image/tiff"}, + {"tif", "image/tiff"}, + {"webp", "image/webp"}, + {"disposition-notification", "message/disposition-notification"}, + {"u8msg", "message/global"}, + {"u8dsn", "message/global-delivery-status"}, + {"u8mdn", "message/global-disposition-notification"}, + {"u8hdr", "message/global-headers"}, + {"eml", "message/rfc822"}, + {"mime", "message/rfc822"}, + {"gltf", "model/gltf+json"}, + {"glb", "model/gltf-binary"}, + {"igs", "model/iges"}, + {"iges", "model/iges"}, + {"msh", "model/mesh"}, + {"mesh", "model/mesh"}, + {"silo", "model/mesh"}, + {"wrl", "model/vrml"}, + {"vrml", "model/vrml"}, + {"x3db", "model/x3d+binary"}, + {"x3dbz", "model/x3d+binary"}, + {"x3dv", "model/x3d+vrml"}, + {"x3dvz", "model/x3d+vrml"}, + {"x3d", "model/x3d+xml"}, + {"x3dz", "model/x3d+xml"}, + {"appcache", "text/cache-manifest"}, + {"manifest", "text/cache-manifest"}, + {"ics", "text/calendar"}, + {"ifb", "text/calendar"}, + {"coffee", "text/coffeescript"}, + {"litcoffee", "text/coffeescript"}, + {"css", "text/css"}, + {"csv", "text/csv"}, + {"html", "text/html"}, + {"htm", "text/html"}, + {"shtml", "text/html"}, + {"jade", "text/jade"}, + {"jsx", "text/jsx"}, + {"less", "text/less"}, + {"markdown", "text/markdown"}, + {"md", "text/markdown"}, + {"mml", "text/mathml"}, + {"n3", "text/n3"}, + {"txt", "text/plain"}, + {"text", "text/plain"}, + {"conf", "text/plain"}, + {"def", "text/plain"}, + {"list", "text/plain"}, + {"log", "text/plain"}, + {"in", "text/plain"}, + {"ini", "text/plain"}, + {"rtx", "text/richtext"}, + {"*rtf", "text/rtf"}, + {"sgml", "text/sgml"}, + {"sgm", "text/sgml"}, + {"shex", "text/shex"}, + {"slim", "text/slim"}, + {"slm", "text/slim"}, + {"stylus", "text/stylus"}, + {"styl", "text/stylus"}, + {"tsv", "text/tab-separated-values"}, + {"t", "text/troff"}, + {"tr", "text/troff"}, + {"roff", "text/troff"}, + {"man", "text/troff"}, + {"me", "text/troff"}, + {"ms", "text/troff"}, + {"ttl", "text/turtle"}, + {"uri", "text/uri-list"}, + {"uris", "text/uri-list"}, + {"urls", "text/uri-list"}, + {"vcard", "text/vcard"}, + {"vtt", "text/vtt"}, + {"*xml", "text/xml"}, + {"yaml", "text/yaml"}, + {"yml", "text/yaml"}, + {"3gp", "video/3gpp"}, + {"3gpp", "video/3gpp"}, + {"3g2", "video/3gpp2"}, + {"h261", "video/h261"}, + {"h263", "video/h263"}, + {"h264", "video/h264"}, + {"jpgv", "video/jpeg"}, + {"*jpm", "video/jpm"}, + {"jpgm", "video/jpm"}, + {"mj2", "video/mj2"}, + {"mjp2", "video/mj2"}, + {"ts", "video/mp2t"}, + {"mp4", "video/mp4"}, + {"mp4v", "video/mp4"}, + {"mpg4", "video/mp4"}, + {"mpeg", "video/mpeg"}, + {"mpg", "video/mpeg"}, + {"mpe", "video/mpeg"}, + {"m1v", "video/mpeg"}, + {"m2v", "video/mpeg"}, + {"ogv", "video/ogg"}, + {"qt", "video/quicktime"}, + {"mov", "video/quicktime"}, + {"webm", "video/webm"}, + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Complete_list_of_MIME_types + {"aac", "audio/aac"}, + {"abw", "application/x-abiword"}, + {"arc", "application/octet-stream"}, + {"avi", "video/x-msvideo"}, + {"azw", "application/vnd.amazon.ebook"}, + {"bz", "application/x-bzip"}, + {"bz2", "application/x-bzip2"}, + {"csh", "application/x-csh"}, + {"eot", "application/vnd.ms-fontobject"}, + {"es", "application/ecmascript"}, + {"ico", "image/x-icon"}, + {"mpkg", "application/vnd.apple.installer+xml"}, + {"odp", "application/vnd.oasis.opendocument.presentation"}, + {"ods", "application/vnd.oasis.opendocument.spreadsheet"}, + {"odt", "application/vnd.oasis.opendocument.text"}, + {"ppt", "application/vnd.ms-powerpoint"}, + {"pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, + {"rar", "application/x-rar-compressed"}, + {"sh", "application/x-sh"}, + {"swf", "application/x-shockwave-flash"}, + {"tar", "application/x-tar"}, + {"vsd", "application/vnd.visio"}, + {"xls", "application/vnd.ms-excel"}, + {"xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, + {"xul", "application/vnd.mozilla.xul+xml"}, + {"7z", "application/x-7z-compressed"}}); -static string get_suffix(const string &filename) -{ - return string(filename).substr(filename.find_last_of('.') + 1); +static const std::string octet_stream("application/octet-stream"); + +static std::string get_suffix(const std::string &filename) { + std::string suffix = std::string(filename).substr(filename.find_last_of('.') + 1); + std::transform(suffix.begin(), suffix.end(), suffix.begin(), ::tolower); + return suffix; } -const unordered_map& list() -{ - return map; +const std::unordered_map &list() { + return map_; } -const string& get(const string &filename) -{ - string suffix = get_suffix(filename); - auto i = map.find(suffix); - if (i != map.end()) - { +const std::string &get(const std::string &filename) { + std::string suffix = get_suffix(filename); + auto i = map_.find(suffix); + if (i != map_.end()) { return i->second; } return octet_stream; } -bool add(const string &suffix, const string &mime_type) -{ - if (map.find(suffix) == map.end()) - { - map[suffix] = mime_type; +bool add(const std::string &suffix, const std::string &mime_type) { + if (map_.find(suffix) == map_.end()) { + map_[suffix] = mime_type; return true; - } - else - { + } else { return false; } } -void set(const string &suffix, const string &mime_type) -{ - map[suffix] = mime_type; +void set(const std::string &suffix, const std::string &mime_type) { + map_[suffix] = mime_type; } -bool del(const string &suffix) -{ - if (map.find(suffix) == map.end()) - { +bool del(const std::string &suffix) { + if (map_.find(suffix) == map_.end()) { return false; - } - else - { - map.erase(suffix); + } else { + map_.erase(suffix); return true; } } -bool exists(const string &filename) -{ - string suffix = get_suffix(filename); - return map.find(suffix) != map.end(); +bool exists(const std::string &filename) { + std::string suffix = get_suffix(filename); + return map_.find(suffix) != map_.end(); } -}} +} // namespace mime_type +} // namespace swoole diff --git a/src/protocol/mqtt.cc b/src/protocol/mqtt.cc index 1b97a161b34..f2ce9de04fc 100644 --- a/src/protocol/mqtt.cc +++ b/src/protocol/mqtt.cc @@ -12,62 +12,67 @@ | to obtain it through the world-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 | +----------------------------------------------------------------------+ */ -#include "mqtt.h" +#include "swoole_mqtt.h" +#include "swoole_protocol.h" -void swMqtt_print_package(swMqtt_packet *pkg) -{ +using swoole::network::Socket; + +namespace swoole { +namespace mqtt { + +void print_package(Packet *pkg) { printf("type=%d, length=%d\n", pkg->type, pkg->length); } -static sw_inline ssize_t swMqtt_get_length(char *data, uint32_t size, ssize_t *count) -{ +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 = 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 SW_MQTT_RECV_LEN_AGAIN 0 + +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 = (pl->buf_size - 1); + if (recv_variable_header_size < SW_MQTT_MIN_LENGTH_SIZE) { // recv continue + return SW_MQTT_RECV_LEN_AGAIN; + } + uint8_t byte; int mul = 1; ssize_t length = 0; - - *count = 0; - do - { - byte = data[*count + 1]; + ssize_t variable_header_byte_count = 0; + while (1) { + variable_header_byte_count++; + byte = pl->buf[variable_header_byte_count]; length += (byte & 127) * mul; mul *= 128; - (*count)++; - } while ((byte & 128) != 0); - - return length; -} - -#if 0 -int swMqtt_unpack(swMqtt_packet *pkg, char *data, uint32_t size) -{ - uint8_t byte = data[0]; - off_t offset; - - pkg->type = (byte & 0xF0) >> 4; - pkg->dup = (byte & 0x08) >> 3; - pkg->qos = (byte & 0x06) >> 1; - pkg->retain = byte & 0x01; - - offset += 1; - - int count = 0; - pkg->length = swMqtt_get_length(data, size, &count); - offset += count + 1; - return 0; -} -#endif - -ssize_t swMqtt_get_package_length(swProtocol *protocol, swSocket *conn, char *data, uint32_t size) -{ - if (size < SW_MQTT_MIN_LENGTH) - { - return 0; + if ((byte & 128) == 0) { // done! there is no surplus length byte + break; + } + if (variable_header_byte_count >= SW_MQTT_MAX_LENGTH_SIZE) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, + "bad request, the variable header size is larger than %d", + SW_MQTT_MAX_LENGTH_SIZE); + return SW_ERR; + } + if (variable_header_byte_count >= recv_variable_header_size) { // length not enough + return SW_MQTT_RECV_LEN_AGAIN; + } } - ssize_t count = 0; - ssize_t length = swMqtt_get_length(data, size, &count); - return length + count + 1; + // 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 50d41dafd6d..c625f0c717a 100644 --- a/src/protocol/redis.cc +++ b/src/protocol/redis.cc @@ -12,15 +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 "swoole.h" -#include "redis.h" +#include "swoole_string.h" +#include "swoole_socket.h" +#include "swoole_protocol.h" +#include "swoole_server.h" +#include "swoole_redis.h" -typedef struct -{ +namespace swoole { +namespace redis { + +struct Request { uint8_t state; int n_lines_total; @@ -30,79 +36,61 @@ typedef struct int n_bytes_received; int offset; +}; -} swRedis_request; - -int swRedis_recv(swProtocol *protocol, swConnection *conn, swString *buffer) -{ - char *p, *pe; +int recv_packet(Protocol *protocol, Connection *conn, String *buffer) { + const char *p, *pe; int ret; char *buf_ptr; size_t buf_size; + RecvData rdata{}; + Request *request; + network::Socket *socket = conn->socket; - swRedis_request *request; - swSocket *socket = conn->socket; - - if (conn->object == NULL) - { - request = (swRedis_request *) sw_malloc(sizeof(swRedis_request)); - if (!request) - { - swWarn("malloc(%ld) failed", sizeof(swRedis_request)); + if (conn->object == nullptr) { + request = (Request *) sw_malloc(sizeof(Request)); + if (!request) { + swoole_warning("malloc(%ld) failed", sizeof(Request)); return SW_ERR; } - bzero(request, sizeof(swRedis_request)); + sw_memset_zero(request, sizeof(Request)); conn->object = request; - } - else - { - request = (swRedis_request *) conn->object; + } else { + request = (Request *) conn->object; } - _recv_data: +_recv_data: buf_ptr = buffer->str + buffer->length; buf_size = buffer->size - buffer->length; - int n = swSocket_recv(socket, buf_ptr, buf_size, 0); - if (n < 0) - { - switch (swSocket_error(errno)) - { + int n = socket->recv(buf_ptr, buf_size, 0); + if (n < 0) { + 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; default: return SW_OK; } - } - else if (n == 0) - { + } else if (n == 0) { return SW_ERR; - } - else - { + } else { buffer->length += n; - 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); - if (extend_size > protocol->package_max_length) - { + 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, swoole_pagesize()); + if (extend_size > protocol->package_max_length) { extend_size = protocol->package_max_length; } - if (swString_extend(buffer, extend_size) < 0) - { + if (!buffer->extend(extend_size)) { return SW_ERR; } - } - else if (buffer->length == buffer->size) - { - _package_too_big: - swWarn("Package is too big. package_length=%ld", buffer->length); + } else if (buffer->length == buffer->size) { + _package_too_big: + swoole_warning("Package is too big. package_length=%ld", buffer->length); return SW_ERR; } goto _recv_data; @@ -111,66 +99,65 @@ int swRedis_recv(swProtocol *protocol, swConnection *conn, swString *buffer) p = buffer->str; pe = p + buffer->length; - do - { - switch(request->state) - { - case SW_REDIS_RECEIVE_TOTAL_LINE: - if (*p == '*' && (p = swRedis_get_number(p, &ret))) - { + do { + switch (request->state) { + 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; } /* no break */ - case SW_REDIS_RECEIVE_LENGTH: - if (*p == '$' && (p = swRedis_get_number(p, &ret))) - { - if (ret < 0) - { + case STATE_RECEIVE_LENGTH: + if (*p == '$') { + if ((p = get_number(p, &ret)) == nullptr) { + goto _failed; + } + if (ret < 0) { break; } - if (ret + (p - buffer->str) > protocol->package_max_length) - { + if (ret + (p - buffer->str) > protocol->package_max_length) { 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))) - { + // integer + else if (*p == ':') { + if ((p = get_number(p, &ret)) == nullptr) { + goto _failed; + } break; } /* no break */ - case SW_REDIS_RECEIVE_STRING: - if (pe - p < request->n_bytes_total - request->n_bytes_received) - { + case STATE_RECEIVE_STRING: + if (pe - p < request->n_bytes_total - request->n_bytes_received) { request->n_bytes_received += pe - p; return SW_OK; - } - else - { + } else { 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) - { + if (request->n_lines_received == request->n_lines_total) { + rdata.info.len = buffer->length; + rdata.data = buffer->str; + if (protocol->onPackage(protocol, socket, &rdata) < 0) { return SW_ERR; } - if (socket->removed) - { + if (socket->removed) { return SW_OK; } - swString_clear(buffer); - bzero(request, sizeof(swRedis_request)); + buffer->clear(); + sw_memset_zero(request, sizeof(Request)); return SW_OK; } } @@ -179,9 +166,95 @@ int swRedis_recv(swProtocol *protocol, swConnection *conn, swString *buffer) default: goto _failed; } - } while(p < pe); + } while (p < pe); } - _failed: - swWarn("redis protocol error"); +_failed: + swoole_warning("redis protocol error"); return SW_ERR; } + +bool format(String *buf) { + return buf->append(SW_STRL(SW_REDIS_RETURN_NIL)) == SW_OK; +} + +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 == 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 == REPLY_STRING) { + if (value.empty() or value.length() > SW_REDIS_MAX_STRING_SIZE) { + return false; + } else { + if (buf->format("$%zu\r\n", value.length()) == 0) { + return false; + } + buf->append(value); + buf->append(SW_CRLF, SW_CRLF_LEN); + return true; + } + } + return false; +} + +bool format(String *buf, enum ReplyType type, long value) { + return buf->format(":%" PRId64 "\r\n", value) > 0; +} + +std::vector parse(const char *data, size_t len) { + int state = STATE_RECEIVE_TOTAL_LINE; + + const char *p = data; + const char *pe = p + len; + int ret; + int length = 0; + + std::vector result; + do { + switch (state) { + case STATE_RECEIVE_TOTAL_LINE: + if (*p == '*' && (p = get_number(p, &ret))) { + state = STATE_RECEIVE_LENGTH; + break; + } + /* no break */ + + case STATE_RECEIVE_LENGTH: + if (*p == '$' && (p = get_number(p, &ret))) { + if (ret == -1) { + break; + } + length = ret; + state = STATE_RECEIVE_STRING; + break; + } + // integer + else if (*p == ':' && (p = get_number(p, &ret))) { + result.push_back(std::to_string(ret)); + break; + } + /* no break */ + + case STATE_RECEIVE_STRING: + result.push_back(std::string(p, length)); + p += length + SW_CRLF_LEN; + state = STATE_RECEIVE_LENGTH; + break; + + default: + break; + } + } while (p < pe); + + return result; +} +} // namespace redis +} // namespace swoole diff --git a/src/protocol/sha1.cc b/src/protocol/sha1.cc deleted file mode 100644 index be7088f0dd8..00000000000 --- a/src/protocol/sha1.cc +++ /dev/null @@ -1,150 +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 - -#include "sha1.h" - -#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - -/* blk0() and blk() perform the initial expand. */ -/* I got the idea of expanding during the round function from SSLeay */ - -#ifdef LITTLE_ENDIAN -#define blk0(i) (block->l[i] = (rol(block->l[i],24)&(sha1_quadbyte)0xFF00FF00) \ - |(rol(block->l[i],8)&(sha1_quadbyte)0x00FF00FF)) -#else -#define blk0(i) block->l[i] -#endif - -#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ - ^block->l[(i+2)&15]^block->l[i&15],1)) - -/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ -#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); -#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); -#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); - -typedef union _BYTE64QUAD16 { - sha1_byte c[64]; - sha1_quadbyte l[16]; -} BYTE64QUAD16; -static void swSha1_transform(sha1_quadbyte state[5], sha1_byte buffer[64]); -/* Hash a single 512-bit block. This is the core of the algorithm. */ -static void swSha1_transform(sha1_quadbyte state[5], sha1_byte buffer[64]) { - sha1_quadbyte a, b, c, d, e; - BYTE64QUAD16 src; - BYTE64QUAD16 *block; - - /* slow but cast-align */ - memcpy(src.c, buffer, sizeof(sha1_byte) * 64); - block = &src; - /* Copy context->state[] to working vars */ - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - /* 4 rounds of 20 operations each. Loop unrolled. */ - R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); - R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); - R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); - R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); - R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); - R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); - R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); - R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); - R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); - R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); - R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); - R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); - R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); - R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); - R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); - R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); - R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); - R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); - R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); - R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); - /* Add the working vars back into context.state[] */ - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - /* Wipe variables */ - a = b = c = d = e = 0; -} - - -/* swSha1_init - Initialize new context */ -void swSha1_init(SHA_CTX* context) { - /* SHA1 initialization constants */ - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; -} - -/* Run your data through this. */ -void swSha1_update(SHA_CTX *context, sha1_byte *data, unsigned int len) { - unsigned int i, j; - - j = (context->count[0] >> 3) & 63; - if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; - context->count[1] += (len >> 29); - if ((j + len) > 63) { - memcpy(&context->buffer[j], data, (i = 64-j)); - swSha1_transform(context->state, context->buffer); - for ( ; i + 63 < len; i += 64) { - swSha1_transform(context->state, &data[i]); - } - j = 0; - } - else i = 0; - memcpy(&context->buffer[j], &data[i], len - i); -} - - -/* Add padding and return the message digest. */ -void swSha1_final(sha1_byte digest[SHA1_DIGEST_LENGTH], SHA_CTX *context) { - sha1_quadbyte i, j; - sha1_byte finalcount[8]; - - for (i = 0; i < 8; i++) { - finalcount[i] = (sha1_byte)((context->count[(i >= 4 ? 0 : 1)] - >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ - } - swSha1_update(context, (sha1_byte *)"\200", 1); - while ((context->count[0] & 504) != 448) { - swSha1_update(context, (sha1_byte *)"\0", 1); - } - /* Should cause a swSha1_Transform() */ - swSha1_update(context, finalcount, 8); - for (i = 0; i < SHA1_DIGEST_LENGTH; i++) { - digest[i] = (sha1_byte) - ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); - } - /* Wipe variables */ - i = j = 0; - memset(context->buffer, 0, SHA1_BLOCK_LENGTH); - memset(context->state, 0, SHA1_DIGEST_LENGTH); - memset(context->count, 0, 8); - memset(&finalcount, 0, 8); -} diff --git a/src/protocol/socks5.cc b/src/protocol/socks5.cc index 8f684ff95d9..f58939666c8 100644 --- a/src/protocol/socks5.cc +++ b/src/protocol/socks5.cc @@ -12,18 +12,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 "swoole.h" -#include "client.h" -#include "socks5.h" +#include "swoole_proxy.h" -const char* swSocks5_strerror(int code) -{ - switch (code) - { +namespace swoole { +const char *Socks5Proxy::strerror(int code) { + switch (code) { case 0x01: return "General failure"; case 0x02: @@ -44,114 +41,4 @@ const char* swSocks5_strerror(int code) return "Unknown error"; } } - -int swSocks5_connect(swClient *cli, char *recv_data, int length) -{ - swSocks5 *ctx = cli->socks5_proxy; - char *buf = ctx->buf; - uchar version, status, result, method; - - if (ctx->state == SW_SOCKS5_STATE_HANDSHAKE) - { - version = recv_data[0]; - method = recv_data[1]; - if (version != SW_SOCKS5_VERSION_CODE) - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SOCKS5_UNSUPPORT_VERSION, "SOCKS version is not supported"); - return SW_ERR; - } - if (method != ctx->method) - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SOCKS5_UNSUPPORT_METHOD, "SOCKS authentication method not supported"); - return SW_ERR; - } - //authenticate request - if (method == SW_SOCKS5_METHOD_AUTH) - { - buf[0] = 0x01; - buf[1] = ctx->l_username; - - buf += 2; - memcpy(buf, ctx->username, ctx->l_username); - buf += ctx->l_username; - buf[0] = ctx->l_password; - memcpy(buf + 1, ctx->password, ctx->l_password); - - ctx->state = SW_SOCKS5_STATE_AUTH; - - return cli->send(cli, ctx->buf, ctx->l_username + ctx->l_password + 3, 0); - } - //send connect request - else - { - _send_connect_request: - buf[0] = SW_SOCKS5_VERSION_CODE; - buf[1] = 0x01; - buf[2] = 0x00; - - ctx->state = SW_SOCKS5_STATE_CONNECT; - - if (ctx->dns_tunnel) - { - buf[3] = 0x03; - buf[4] = ctx->l_target_host; - buf += 5; - memcpy(buf, ctx->target_host, ctx->l_target_host); - buf += ctx->l_target_host; - *(uint16_t *) buf = htons(ctx->target_port); - return cli->send(cli, ctx->buf, ctx->l_target_host + 7, 0); - } - else - { - buf[3] = 0x01; - buf += 4; - *(uint32_t *) buf = htons(ctx->l_target_host); - buf += 4; - *(uint16_t *) buf = htons(ctx->target_port); - return cli->send(cli, ctx->buf, ctx->l_target_host + 7, 0); - } - } - } - else if (ctx->state == SW_SOCKS5_STATE_AUTH) - { - version = recv_data[0]; - status = recv_data[1]; - if (version != 0x01) - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SOCKS5_UNSUPPORT_VERSION, "SOCKS version is not supported"); - return SW_ERR; - } - if (status != 0) - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SOCKS5_AUTH_FAILED, "SOCKS username/password authentication failed"); - return SW_ERR; - } - goto _send_connect_request; - } - else if (ctx->state == SW_SOCKS5_STATE_CONNECT) - { - version = recv_data[0]; - if (version != SW_SOCKS5_VERSION_CODE) - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SOCKS5_UNSUPPORT_VERSION, "SOCKS version is not supported"); - return SW_ERR; - } - result = recv_data[1]; -#if 0 - uchar reg = recv_data[2]; - uchar type = recv_data[3]; - uint32_t ip = *(uint32_t *) (recv_data + 4); - uint16_t port = *(uint16_t *) (recv_data + 8); -#endif - if (result == 0) - { - ctx->state = SW_SOCKS5_STATE_READY; - } - else - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SOCKS5_SERVER_ERROR, "Socks5 server error, reason :%s", swSocks5_strerror(result)); - } - return result; - } - return SW_OK; -} +} // namespace swoole diff --git a/src/protocol/ssl.cc b/src/protocol/ssl.cc index d296d1d843d..72cd5592d24 100644 --- a/src/protocol/ssl.cc +++ b/src/protocol/ssl.cc @@ -10,328 +10,190 @@ | to obtain it through the world-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 "ssl.h" +#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; + #if OPENSSL_VERSION_NUMBER < 0x10000000L #error "require openssl version 1.0 or later" #endif -#include -#include -#include -#include - -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 const SSL_METHOD *swSSL_get_method(int method); -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); -#endif -static int swSSL_set_dhparam(SSL_CTX* ssl_context, const char *file); -static int swSSL_set_ecdh_curve(SSL_CTX* ssl_context, const char *ecdh_curve); - -#ifdef TLSEXT_TYPE_next_proto_neg -static int swSSL_npn_advertised(SSL *ssl, const uchar **out, uint32_t *outlen, void *arg); -#endif - -#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation -static int swSSL_alpn_advertised(SSL *ssl, const uchar **out, uchar *outlen, const uchar *in, uint32_t inlen, void *arg); +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__ - #define MAYBE_UNUSED __attribute__((used)) +#define MAYBE_UNUSED __attribute__((used)) #else - #define MAYBE_UNUSED +#define MAYBE_UNUSED #endif -static void MAYBE_UNUSED swSSL_lock_callback(int mode, int type, const char *file, int line); - -static const SSL_METHOD *swSSL_get_method(int method) -{ - switch (method) - { -#ifndef OPENSSL_NO_SSL3_METHOD - case SW_SSLv3_METHOD: - return SSLv3_method(); - case SW_SSLv3_SERVER_METHOD: - return SSLv3_server_method(); - case SW_SSLv3_CLIENT_METHOD: - return SSLv3_client_method(); -#endif - case SW_SSLv23_SERVER_METHOD: - return SSLv23_server_method(); - case SW_SSLv23_CLIENT_METHOD: - return SSLv23_client_method(); -/** - * openssl 1.1.0 - */ -#if OPENSSL_VERSION_NUMBER < 0x10100000L - case SW_TLSv1_METHOD: - return TLSv1_method(); - case SW_TLSv1_SERVER_METHOD: - return TLSv1_server_method(); - case SW_TLSv1_CLIENT_METHOD: - return TLSv1_client_method(); -#ifdef TLS1_1_VERSION - case SW_TLSv1_1_METHOD: - return TLSv1_1_method(); - case SW_TLSv1_1_SERVER_METHOD: - return TLSv1_1_server_method(); - case SW_TLSv1_1_CLIENT_METHOD: - return TLSv1_1_client_method(); -#endif -#ifdef TLS1_2_VERSION - case SW_TLSv1_2_METHOD: - return TLSv1_2_method(); - case SW_TLSv1_2_SERVER_METHOD: - return TLSv1_2_server_method(); - case SW_TLSv1_2_CLIENT_METHOD: - return TLSv1_2_client_method(); -#endif -#endif -/** - * DTLS supports - */ -#ifdef SW_SUPPORT_DTLS - case SW_DTLS_CLIENT_METHOD: - return DTLS_client_method(); - case SW_DTLS_SERVER_METHOD: - return DTLS_server_method(); -#endif - case SW_SSLv23_METHOD: - default: - return SSLv23_method(); - } - return SSLv23_method(); +std::string swoole_ssl_get_version_message() { + return swoole::std_string::format("OPENSSL_VERSION: %s\n", OPENSSL_VERSION_TEXT); } -void swSSL_init(void) -{ - if (openssl_init) - { +static void MAYBE_UNUSED swoole_ssl_lock_callback(int mode, int type, const char *file, int line); + +void swoole_ssl_init(void) { + if (openssl_init) { return; } #if OPENSSL_VERSION_NUMBER >= 0x10100003L && !defined(LIBRESSL_VERSION_NUMBER) - OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG | OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG | OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, + nullptr); #else - OPENSSL_config(NULL); + OPENSSL_config(nullptr); SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); #endif - ssl_connection_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); - if (ssl_connection_index < 0) - { - swError("SSL_get_ex_new_index() failed"); + ssl_connection_index = SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr); + if (ssl_connection_index < 0) { + 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) { + swoole_error("SSL_get_ex_new_index() failed"); return; } - openssl_init = 1; + openssl_init = true; } -void swSSL_destroy() -{ - if (!openssl_init) - { +int swoole_ssl_get_ex_connection_index() { + return ssl_connection_index; +} + +int swoole_ssl_get_ex_port_index() { + return ssl_port_index; +} + +void swoole_ssl_destroy() { + if (!openssl_init) { return; } - CRYPTO_set_locking_callback(NULL); - 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(NULL); + (void) CRYPTO_THREADID_set_callback(nullptr); #else - CRYPTO_set_id_callback(NULL); + CRYPTO_set_id_callback(nullptr); #endif - CRYPTO_set_locking_callback(NULL); + 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) -{ - if (mode & CRYPTO_LOCK) - { +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 - { + } else { pthread_mutex_unlock(&(lock_array[type])); } } -static int ssl_error_cb(const char *str, size_t len, void *buf) -{ +static int ssl_error_cb(const char *str, size_t len, void *buf) { memcpy(buf, str, len); return 0; } -const char* swSSL_get_error() -{ - ERR_print_errors_cb(ssl_error_cb, SwooleTG.buffer_stack->str); - - return SwooleTG.buffer_stack->str; -} +const char *swoole_ssl_get_error() { + ERR_print_errors_cb(ssl_error_cb, sw_tg_buffer()->str); -static sw_inline void swSSL_clear_error(swSocket *conn) -{ - ERR_clear_error(); - conn->ssl_want_read = 0; - conn->ssl_want_write = 0; + 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() -{ - if (!openssl_init) - { +void swoole_ssl_init_thread_safety() { + if (!openssl_init) { return; } - int i; - lock_array = (pthread_mutex_t *) OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t)); - for (i = 0; i < CRYPTO_num_locks(); i++) - { - pthread_mutex_init(&(lock_array[i]), NULL); - } - -#if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_0_0 - (void) CRYPTO_THREADID_set_callback(swSSL_id_callback); -#else - CRYPTO_set_id_callback(swSSL_id_callback); -#endif - CRYPTO_set_locking_callback(swSSL_lock_callback); -} - -void swSSL_server_http_advise(SSL_CTX* ssl_context, swSSL_config *cfg) -{ -#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation - SSL_CTX_set_alpn_select_cb(ssl_context, swSSL_alpn_advertised, cfg); -#endif - -#ifdef TLSEXT_TYPE_next_proto_neg - SSL_CTX_set_next_protos_advertised_cb(ssl_context, swSSL_npn_advertised, cfg); -#endif - - if (cfg->http) - { - SSL_CTX_set_session_id_context(ssl_context, (const unsigned char *) "HTTP", sizeof("HTTP") - 1); - SSL_CTX_set_session_cache_mode(ssl_context, SSL_SESS_CACHE_SERVER); - SSL_CTX_sess_set_cache_size(ssl_context, 1); + if (openssl_thread_safety_init) { + return; } -} - -int swSSL_server_set_cipher(SSL_CTX* ssl_context, swSSL_config *cfg) -{ -#ifndef TLS1_2_VERSION - return SW_OK; -#endif - if (cfg->ciphers && strlen(cfg->ciphers) > 0) - { - if (SSL_CTX_set_cipher_list(ssl_context, cfg->ciphers) == 0) - { - swWarn("SSL_CTX_set_cipher_list(\"%s\") failed", cfg->ciphers); - return SW_ERR; - } - if (cfg->prefer_server_ciphers) - { - SSL_CTX_set_options(ssl_context, SSL_OP_CIPHER_SERVER_PREFERENCE); - } + lock_array = (pthread_mutex_t *) OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t)); + SW_LOOP_N(CRYPTO_num_locks()) { + pthread_mutex_init(&(lock_array[i]), nullptr); } -#ifndef OPENSSL_NO_RSA - SSL_CTX_set_tmp_rsa_callback(ssl_context, swSSL_rsa_key_callback); +#if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_0_0 + (void) CRYPTO_THREADID_set_callback(swoole_ssl_id_callback); +#else + CRYPTO_set_id_callback(swoole_ssl_id_callback); #endif - if (cfg->dhparam && strlen(cfg->dhparam) > 0) - { - swSSL_set_dhparam(ssl_context, cfg->dhparam); - } -#if OPENSSL_VERSION_NUMBER < 0x10100000L - else - { - swSSL_set_default_dhparam(ssl_context); - } -#endif - if (cfg->ecdh_curve && strlen(cfg->ecdh_curve) > 0) - { - swSSL_set_ecdh_curve(ssl_context, cfg->ecdh_curve); - } - return SW_OK; + CRYPTO_set_locking_callback(swoole_ssl_lock_callback); + openssl_thread_safety_init = true; } -static int swSSL_passwd_callback(char *buf, int num, int verify, void *data) -{ - swSSL_option *option = (swSSL_option *) data; - if (option->passphrase) - { - int len = strlen(option->passphrase); - if (len < num - 1) - { - memcpy(buf, option->passphrase, len + 1); - return (int) len; - } - } - return 0; +bool swoole_ssl_is_thread_safety() { + return openssl_thread_safety_init; } -static void swSSL_info_callback(const SSL *ssl, int where, int ret) -{ +static void swoole_ssl_info_callback(const SSL *ssl, int where, int ret) { BIO *rbio, *wbio; swSocket *sock; - if (where & SSL_CB_HANDSHAKE_START) - { + if (where & SSL_CB_HANDSHAKE_START) { sock = (swSocket *) SSL_get_ex_data(ssl, ssl_connection_index); - if (sock->ssl_state == SW_SSL_STATE_READY) - { + if (sock->ssl_state == SW_SSL_STATE_READY) { sock->ssl_renegotiation = 1; - swDebug("SSL renegotiation"); + swoole_debug("SSL renegotiation"); } } - if ((where & SSL_CB_ACCEPT_LOOP) == SSL_CB_ACCEPT_LOOP) - { + if ((where & SSL_CB_ACCEPT_LOOP) == SSL_CB_ACCEPT_LOOP) { sock = (swSocket *) SSL_get_ex_data(ssl, ssl_connection_index); - if (!sock->ssl_handshake_buffer_set) - { + if (!sock->ssl_handshake_buffer_set) { /* * By default OpenSSL uses 4k buffer during a handshake, * which is too low for long certificate chains and might @@ -346,8 +208,7 @@ static void swSSL_info_callback(const SSL *ssl, int where, int ret) rbio = SSL_get_rbio(ssl); wbio = SSL_get_wbio(ssl); - if (rbio != wbio) - { + if (rbio != wbio) { (void) BIO_set_write_buffer_size(wbio, SW_SSL_BUFFER_SIZE); sock->ssl_handshake_buffer_set = 1; } @@ -355,977 +216,621 @@ static void swSSL_info_callback(const SSL *ssl, int where, int ret) } } -SSL_CTX* swSSL_get_context(swSSL_option *option) -{ - if (!openssl_init) - { - swSSL_init(); +namespace swoole { + +#ifndef OPENSSL_NO_NEXTPROTONEG + +#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) { + if (std::equal(std::begin(key), std::end(key), p)) { + *out = p + 1; + *outlen = *p; + return true; + } + } + return false; +} + +static bool ssl_select_h2(const uchar **out, uchar *outlen, const uchar *in, uint inlen) { + return ssl_select_proto(out, outlen, in, inlen, HTTP2_H2_ALPN) || + ssl_select_proto(out, outlen, in, inlen, HTTP2_H2_16_ALPN) || + 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 protos_len; + const char *protos; + + SSLContext *cfg = (SSLContext *) arg; + if (cfg->http_v2) { + protos = HTTP2_H2_ALPN HTTP1_NPN; + protos_len = sizeof(HTTP2_H2_ALPN HTTP1_NPN) - 1; + } else { + protos = HTTP1_NPN; + protos_len = sizeof(HTTP1_NPN) - 1; + } + + 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; +} +#endif + +static int ssl_select_next_proto_cb(SSL *ssl, uchar **out, uchar *outlen, const uchar *in, uint inlen, void *arg) { +#ifdef SW_LOG_TRACE_OPEN + std::string info("[NPN] server offers:\n"); + for (unsigned int i = 0; i < inlen; i += in[i] + 1) { + info += " * " + std::string(reinterpret_cast(&in[i + 1]), in[i]); + } + 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)) { + swoole_warning("HTTP/2 protocol was not selected, expects [h2]"); + return SSL_TLSEXT_ERR_NOACK; + } else if (ctx->http) { + *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; + if (!ctx->passphrase.empty()) { + int len = ctx->passphrase.length(); + if (len < num - 1) { + memcpy(buf, ctx->passphrase.c_str(), len); + buf[len] = '\0'; + return (int) len; + } + } + return 0; +} + +bool SSLContext::create() { + if (!openssl_init) { + swoole_ssl_init(); } - SSL_CTX *ssl_context = SSL_CTX_new(swSSL_get_method(option->method)); - if (ssl_context == NULL) + const SSL_METHOD *method; +#ifdef SW_SUPPORT_DTLS + if (protocols & SW_SSL_DTLS) { + method = DTLS_method(); + } else +#endif { + method = SSLv23_method(); + } + if (protocols == 0) { + protocols = SW_SSL_ALL; + } + 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); - return NULL; + swoole_warning("SSL_CTX_new() failed, Error: %s[%d]", ERR_reason_error_string(error), error); + return false; } #ifdef SSL_OP_MICROSOFT_SESS_ID_BUG - SSL_CTX_set_options(ssl_context, SSL_OP_MICROSOFT_SESS_ID_BUG); + SSL_CTX_set_options(context, SSL_OP_MICROSOFT_SESS_ID_BUG); #endif #ifdef SSL_OP_NETSCAPE_CHALLENGE_BUG - SSL_CTX_set_options(ssl_context, SSL_OP_NETSCAPE_CHALLENGE_BUG); + SSL_CTX_set_options(context, SSL_OP_NETSCAPE_CHALLENGE_BUG); #endif /* server side options */ #ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG - SSL_CTX_set_options(ssl_context, SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG); + SSL_CTX_set_options(context, SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG); #endif #ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER - SSL_CTX_set_options(ssl_context, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER); + SSL_CTX_set_options(context, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER); #endif #ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING /* this option allow a potential SSL 2.0 rollback (CAN-2005-2969) */ - SSL_CTX_set_options(ssl_context, SSL_OP_MSIE_SSLV2_RSA_PADDING); + SSL_CTX_set_options(context, SSL_OP_MSIE_SSLV2_RSA_PADDING); #endif #ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG - SSL_CTX_set_options(ssl_context, SSL_OP_SSLEAY_080_CLIENT_DH_BUG); + SSL_CTX_set_options(context, SSL_OP_SSLEAY_080_CLIENT_DH_BUG); #endif #ifdef SSL_OP_TLS_D5_BUG - SSL_CTX_set_options(ssl_context, SSL_OP_TLS_D5_BUG); + SSL_CTX_set_options(context, SSL_OP_TLS_D5_BUG); #endif #ifdef SSL_OP_TLS_BLOCK_PADDING_BUG - SSL_CTX_set_options(ssl_context, SSL_OP_TLS_BLOCK_PADDING_BUG); + SSL_CTX_set_options(context, SSL_OP_TLS_BLOCK_PADDING_BUG); #endif #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS - SSL_CTX_set_options(ssl_context, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); + SSL_CTX_set_options(context, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); #endif - if (option->disable_protocols & SW_SSL_SSLv2) - { - SSL_CTX_set_options(ssl_context, SSL_OP_NO_SSLv2); +#if OPENSSL_VERSION_NUMBER >= 0x009080dfL + /* only in 0.9.8m+ */ + SSL_CTX_clear_options(context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1); +#endif + +#ifdef SSL_OP_NO_SSLv2 + if (!(protocols & SW_SSL_SSLv2)) { + SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); } - if (option->disable_protocols & SW_SSL_SSLv3) - { - SSL_CTX_set_options(ssl_context, SSL_OP_NO_SSLv3); +#endif +#ifdef SSL_OP_NO_SSLv3 + if (!(protocols & SW_SSL_SSLv3)) { + SSL_CTX_set_options(context, SSL_OP_NO_SSLv3); } - if (option->disable_protocols & SW_SSL_TLSv1) - { - SSL_CTX_set_options(ssl_context, SSL_OP_NO_TLSv1); +#endif +#ifdef SSL_OP_NO_TLSv1 + if (!(protocols & SW_SSL_TLSv1)) { + SSL_CTX_set_options(context, SSL_OP_NO_TLSv1); } +#endif #ifdef SSL_OP_NO_TLSv1_1 - SSL_CTX_clear_options(ssl_context, SSL_OP_NO_TLSv1_1); - if (option->disable_protocols & SW_SSL_TLSv1_1) - { - SSL_CTX_set_options(ssl_context, SSL_OP_NO_TLSv1_1); + SSL_CTX_clear_options(context, SSL_OP_NO_TLSv1_1); + if (!(protocols & SW_SSL_TLSv1_1)) { + SSL_CTX_set_options(context, SSL_OP_NO_TLSv1_1); } #endif #ifdef SSL_OP_NO_TLSv1_2 - SSL_CTX_clear_options(ssl_context, SSL_OP_NO_TLSv1_2); - if (option->disable_protocols & SW_SSL_TLSv1_2) - { - SSL_CTX_set_options(ssl_context, SSL_OP_NO_TLSv1_2); + SSL_CTX_clear_options(context, SSL_OP_NO_TLSv1_2); + if (!(protocols & SW_SSL_TLSv1_2) && !(protocols & SW_SSL_DTLS)) { + SSL_CTX_set_options(context, SSL_OP_NO_TLSv1_2); + } +#endif +#ifdef SSL_OP_NO_TLSv1_3 + SSL_CTX_clear_options(context, SSL_OP_NO_TLSv1_3); + if (!(protocols & SW_SSL_TLSv1_3)) { + SSL_CTX_set_options(context, SSL_OP_NO_TLSv1_3); } #endif #ifdef SSL_OP_NO_COMPRESSION - if (option->disable_compress) - { - SSL_CTX_set_options(ssl_context, SSL_OP_NO_COMPRESSION); + if (disable_compress) { + SSL_CTX_set_options(context, SSL_OP_NO_COMPRESSION); } #endif #ifdef SSL_MODE_RELEASE_BUFFERS - SSL_CTX_set_mode(ssl_context, SSL_MODE_RELEASE_BUFFERS); + SSL_CTX_set_mode(context, SSL_MODE_RELEASE_BUFFERS); #endif #ifdef SSL_MODE_NO_AUTO_CHAIN - SSL_CTX_set_mode(ssl_context, SSL_MODE_NO_AUTO_CHAIN); + SSL_CTX_set_mode(context, SSL_MODE_NO_AUTO_CHAIN); #endif - SSL_CTX_set_read_ahead(ssl_context, 1); - SSL_CTX_set_info_callback(ssl_context, swSSL_info_callback); + SSL_CTX_set_read_ahead(context, 1); + SSL_CTX_set_info_callback(context, swoole_ssl_info_callback); - if (option->passphrase) - { - SSL_CTX_set_default_passwd_cb_userdata(ssl_context, option); - SSL_CTX_set_default_passwd_cb(ssl_context, swSSL_passwd_callback); + if (!passphrase.empty()) { + SSL_CTX_set_default_passwd_cb_userdata(context, this); + SSL_CTX_set_default_passwd_cb(context, ssl_passwd_callback); } - if (option->cert_file) - { + if (!cert_file.empty()) { /* * set the local certificate from CertFile */ - if (SSL_CTX_use_certificate_file(ssl_context, option->cert_file, SSL_FILETYPE_PEM) <= 0) - { + 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); - return NULL; + swoole_warning("SSL_CTX_use_certificate_file(%s) failed, Error: %s[%d]", + cert_file.c_str(), + ERR_reason_error_string(error), + error); + return true; } /* * if the crt file have many certificate entry ,means certificate chain * we need call this function */ - if (SSL_CTX_use_certificate_chain_file(ssl_context, option->cert_file) <= 0) - { + 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); - return NULL; + 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(ssl_context, option->key_file, SSL_FILETYPE_PEM) <= 0) - { + 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); - return NULL; + 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(ssl_context)) - { - swWarn("Private key does not match the public certificate"); - return NULL; + if (!SSL_CTX_check_private_key(context)) { + swoole_warning("Private key does not match the public certificate"); + return false; } } #ifdef SW_SUPPORT_DTLS - if (option->dtls) - { - SSL_CTX_set_cookie_generate_cb(ssl_context, swSSL_generate_cookie); - SSL_CTX_set_cookie_verify_cb(ssl_context, swSSL_verify_cookie); + if (protocols & SW_SSL_DTLS) { +#ifndef OPENSSL_IS_BORINGSSL + SSL_CTX_set_cookie_generate_cb(context, swoole_ssl_generate_cookie); + SSL_CTX_set_cookie_verify_cb(context, swoole_ssl_verify_cookie); +#endif } #endif - return ssl_context; -} - -static int swSSL_verify_callback(int ok, X509_STORE_CTX *x509_store) -{ -#if 0 - char *subject, *issuer; - int err, depth; - X509 *cert; - X509_NAME *sname, *iname; - X509_STORE_CTX_get_ex_data(x509_store, SSL_get_ex_data_X509_STORE_CTX_idx()); - cert = X509_STORE_CTX_get_current_cert(x509_store); - err = X509_STORE_CTX_get_error(x509_store); - depth = X509_STORE_CTX_get_error_depth(x509_store); + if (verify_peer && !set_capath()) { + return false; + } else { + SSL_CTX_set_verify(context, SSL_VERIFY_NONE, nullptr); + } - sname = X509_get_subject_name(cert); - subject = sname ? X509_NAME_oneline(sname, NULL, 0) : "(none)"; +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + if (http || http_v2) { + unsigned int protos_len; + const char *protos; + if (http_v2) { + protos = HTTP2_H2_ALPN HTTP1_NPN; + protos_len = sizeof(HTTP2_H2_ALPN HTTP1_NPN) - 1; + } else { + 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 uchar *) protos, protos_len) < 0) { + return false; + } - iname = X509_get_issuer_name(cert); - issuer = iname ? X509_NAME_oneline(iname, NULL, 0) : "(none)"; - swWarn("verify:%d, error:%d, depth:%d, subject:\"%s\", issuer:\"%s\"", ok, err, depth, subject, issuer); +#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation + SSL_CTX_set_alpn_select_cb(context, ssl_alpn_advertised, (void *) this); +#endif - if (sname) - { - OPENSSL_free(subject); - } - if (iname) - { - OPENSSL_free(issuer); + SSL_CTX_set_session_id_context(context, (const unsigned char *) "HTTP", sizeof("HTTP") - 1); + SSL_CTX_set_session_cache_mode(context, SSL_SESS_CACHE_SERVER); + SSL_CTX_sess_set_cache_size(context, 1); } #endif - return 1; -} - -int swSSL_set_client_certificate(SSL_CTX *ctx, char *cert_file, int depth) -{ - STACK_OF(X509_NAME) *list; - - SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, swSSL_verify_callback); - SSL_CTX_set_verify_depth(ctx, depth); +#ifdef OPENSSL_IS_BORINGSSL + SSL_CTX_set_grease_enabled(context, grease); +#endif - if (SSL_CTX_load_verify_locations(ctx, cert_file, NULL) == 0) - { - swWarn("SSL_CTX_load_verify_locations(\"%s\") failed", cert_file); - return SW_ERR; + if (!client_cert_file.empty() && !set_client_certificate()) { + swoole_warning("set_client_certificate() error"); + return false; } - ERR_clear_error(); - list = SSL_load_client_CA_file(cert_file); - if (list == NULL) - { - swWarn("SSL_load_client_CA_file(\"%s\") failed", cert_file); - return SW_ERR; + if (!set_ciphers()) { + swoole_warning("set_cipher() error"); + return false; } - ERR_clear_error(); - SSL_CTX_set_client_CA_list(ctx, list); - - return SW_OK; + return true; } -int swSSL_set_capath(swSSL_option *cfg, SSL_CTX *ctx) -{ - if (cfg->cafile || cfg->capath) - { - if (!SSL_CTX_load_verify_locations(ctx, cfg->cafile, cfg->capath)) - { - return SW_ERR; +bool SSLContext::set_capath() { + if (!cafile.empty() || !capath.empty()) { + 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(ctx)) - { - swWarn("Unable to set default verify locations and no CA settings specified"); - return SW_ERR; + } else { + if (!SSL_CTX_set_default_verify_paths(context)) { + swoole_warning("Unable to set default verify locations and no CA settings specified"); + return false; } } - if (cfg->verify_depth > 0) - { - SSL_CTX_set_verify_depth(ctx, cfg->verify_depth); + if (verify_depth > 0) { + SSL_CTX_set_verify_depth(context, verify_depth); } - return SW_OK; + return true; } -#ifndef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT -static int swSSL_check_name(char *name, ASN1_STRING *pattern) -{ - char *s, *end; - size_t slen, plen; +bool SSLContext::set_ciphers() { +#ifndef TLS1_2_VERSION + return true; +#endif - s = name; - slen = strlen(name); + if (!ciphers.empty()) { + if (SSL_CTX_set_cipher_list(context, ciphers.c_str()) == 0) { + swoole_warning("SSL_CTX_set_cipher_list(\"%s\") failed", ciphers.c_str()); + return false; + } + if (prefer_server_ciphers) { + SSL_CTX_set_options(context, SSL_OP_CIPHER_SERVER_PREFERENCE); + } + } - uchar *p = ASN1_STRING_data(pattern); - plen = ASN1_STRING_length(pattern); +#ifndef OPENSSL_NO_RSA + SSL_CTX_set_tmp_rsa_callback(context, swoole_ssl_rsa_key_callback); +#endif - if (swoole_strcaseeq(s, slen, (char*) p, plen)) - { - return SW_OK; + if (!dhparam.empty() && !set_dhparam()) { + return false; + } +#if OPENSSL_VERSION_NUMBER < 0x10100000L + else { + swoole_ssl_set_default_dhparam(context); + } +#endif + if (!ecdh_curve.empty() && !set_ecdh_curve()) { + return false; } + return true; +} - if (plen > 2 && p[0] == '*' && p[1] == '.') - { - plen -= 1; - p += 1; +bool SSLContext::set_client_certificate() { + STACK_OF(X509_NAME) * list; - end = s + slen; - s = swoole_strlchr(s, end, '.'); + const char *cert_file = client_cert_file.c_str(); + int depth = verify_depth; - if (s == NULL) - { - return SW_ERR; - } + SSL_CTX_set_verify(context, SSL_VERIFY_PEER, swoole_ssl_verify_callback); + SSL_CTX_set_verify_depth(context, depth); - slen = end - s; + if (SSL_CTX_load_verify_locations(context, cert_file, nullptr) == 0) { + swoole_warning("SSL_CTX_load_verify_locations(\"%s\") failed", cert_file); + return false; + } - if (swoole_strcaseeq(s, slen, (char*) p, plen)) - { - return SW_OK; - } + ERR_clear_error(); + list = SSL_load_client_CA_file(cert_file); + if (list == nullptr) { + swoole_warning("SSL_load_client_CA_file(\"%s\") failed", cert_file); + return false; } - return SW_ERR; + + ERR_clear_error(); + SSL_CTX_set_client_CA_list(context, list); + + return true; } + +bool SSLContext::set_ecdh_curve() { +#ifndef OPENSSL_NO_ECDH + /* + * Elliptic-Curve Diffie-Hellman parameters are either "named curves" + * from RFC 4492 section 5.1.1, or explicitly described curves over + * binary fields. OpenSSL only supports the "named curves", which provide + * maximum interoperability. + */ +#if (defined SSL_CTX_set1_curves_list || defined SSL_CTRL_SET_CURVES_LIST) + /* + * OpenSSL 1.0.2+ allows configuring a curve list instead of a single + * curve previously supported. By default an internal list is used, + * with prime256v1 being preferred by server in OpenSSL 1.0.2b+ + * and X25519 in OpenSSL 1.1.0+. + * + * By default a curve preferred by the client will be used for + * key exchange. The SSL_OP_CIPHER_SERVER_PREFERENCE option can + * be used to prefer server curves instead, similar to what it + * does for ciphers. + */ + SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE); +#if SSL_CTRL_SET_ECDH_AUTO + /* not needed in OpenSSL 1.1.0+ */ + SSL_CTX_set_ecdh_auto(context, 1); #endif + if (strcmp(ecdh_curve.c_str(), "auto") == 0) { + return true; + } + if (SSL_CTX_set1_curves_list(context, ecdh_curve.c_str()) == 0) { + swoole_warning("SSL_CTX_set1_curves_list(\"%s\") failed", ecdh_curve.c_str()); + return false; + } +#else + EC_KEY *ecdh; + /* + * Elliptic-Curve Diffie-Hellman parameters are either "named curves" + * from RFC 4492 section 5.1.1, or explicitly described curves over + * binary fields. OpenSSL only supports the "named curves", which provide + * maximum interoperability. + */ + int nid = OBJ_sn2nid(ecdh_curve.c_str()); + if (nid == 0) { + swoole_warning("Unknown curve name \"%s\"", ecdh_curve.c_str()); + return false; + } -#ifdef SW_SUPPORT_DTLS + ecdh = EC_KEY_new_by_curve_name(nid); + if (ecdh == nullptr) { + swoole_warning("Unable to create curve \"%s\"", ecdh_curve.c_str()); + return false; + } -#define COOKIE_SECRET_LENGTH (32) + SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE); + SSL_CTX_set_tmp_ecdh(context, ecdh); -static void calculate_cookie(SSL* ssl, uchar *cookie_secret, uint cookie_length) -{ - long rv = (long) ssl; - long inum = (cookie_length - (((long) cookie_secret) % sizeof(long))) / sizeof(long); - long i = 0; - long *ip = (long*) cookie_secret; - for (i = 0; i < inum; ++i, ++ip) - { - *ip = rv; - } + EC_KEY_free(ecdh); +#endif +#endif + + return true; } -static int swSSL_generate_cookie(SSL *ssl, uchar *cookie, uint *cookie_len) -{ - uchar *buffer, result[EVP_MAX_MD_SIZE]; - uint length = 0, result_len; - swSocketAddress sa = {}; +bool SSLContext::set_dhparam() { + DH *dh; + BIO *bio; - uchar cookie_secret[COOKIE_SECRET_LENGTH]; - calculate_cookie(ssl, cookie_secret, sizeof(cookie_secret)); + const char *file = dhparam.c_str(); - /* Read peer information */ - (void) BIO_dgram_get_peer(SSL_get_wbio(ssl), &sa); + bio = BIO_new_file(file, "r"); + if (bio == nullptr) { + swoole_warning("BIO_new_file(%s) failed", file); + return false; + } - length = 0; - switch (sa.addr.ss.sa_family) - { - case AF_INET: - length += sizeof(struct in_addr); - break; - case AF_INET6: - length += sizeof(struct in6_addr); - break; - default: - OPENSSL_assert(0); - break; - } - - length += sizeof(in_port_t); - buffer = (uchar*) OPENSSL_malloc(length); - - if (buffer == NULL) - { - swSysWarn("out of memory"); - return 0; - } - - switch (sa.addr.ss.sa_family) - { - case AF_INET: - memcpy(buffer, &sa.addr.inet_v4.sin_port, sizeof(in_port_t)); - memcpy(buffer + sizeof(sa.addr.inet_v4.sin_port), &sa.addr.inet_v4.sin_addr, sizeof(struct in_addr)); - break; - case AF_INET6: - memcpy(buffer, &sa.addr.inet_v6.sin6_port, sizeof(in_port_t)); - memcpy(buffer + sizeof(in_port_t), &sa.addr.inet_v6.sin6_addr, sizeof(struct in6_addr)); - break; - default: - OPENSSL_assert(0); - break; + dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr); + if (dh == nullptr) { + swoole_warning("PEM_read_bio_DHparams(%s) failed", file); + BIO_free(bio); + return false; } - HMAC(EVP_sha1(), (const void*) cookie_secret, COOKIE_SECRET_LENGTH, buffer, length, result, &result_len); - OPENSSL_free(buffer); + SSL_CTX_set_tmp_dh(context, dh); - memcpy(cookie, result, result_len); - *cookie_len = result_len; + DH_free(dh); + BIO_free(bio); - return 1; + return true; } -static int swSSL_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); - - return cookie_len == result_len && memcmp(result, cookie, result_len) == 0; +SSLContext::~SSLContext() { + SSL_CTX_free(context); } -#endif - -int swSSL_check_host(swSocket *conn, char *tls_host_name) -{ - X509 *cert = SSL_get_peer_certificate(conn->ssl); - if (cert == NULL) - { - return SW_ERR; - } - -#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, NULL) != 1) - { - swWarn("X509_check_host(): no match"); - goto _failed; - } - goto _found; -#else - int n, i; - X509_NAME *sname; - ASN1_STRING *str; - X509_NAME_ENTRY *entry; - GENERAL_NAME *altname; - STACK_OF(GENERAL_NAME) *altnames; - - /* - * As per RFC6125 and RFC2818, we check subjectAltName extension, - * and if it's not present - commonName in Subject is checked. - */ - altnames = (STACK_OF(GENERAL_NAME) *) X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); - - if (altnames) - { - n = sk_GENERAL_NAME_num(altnames); - - for (i = 0; i < n; i++) - { - altname = sk_GENERAL_NAME_value(altnames, i); - if (altname->type != GEN_DNS) - { - continue; - } - - str = altname->d.dNSName; - swTrace("SSL subjectAltName: \"%.*s\"", ASN1_STRING_length(str), ASN1_STRING_data(str)); - - if (swSSL_check_name(tls_host_name, str) == SW_OK) - { - swTrace("SSL subjectAltName: match"); - GENERAL_NAMES_free(altnames); - goto _found; - } - } - - swTrace("SSL subjectAltName: no match"); - GENERAL_NAMES_free(altnames); - goto _failed; - } - - /* - * If there is no subjectAltName extension, check commonName - * in Subject. While RFC2818 requires to only check "most specific" - * CN, both Apache and OpenSSL check all CNs, and so do we. - */ - sname = X509_get_subject_name(cert); +} // namespace swoole - if (sname == NULL) - { - goto _failed; - } - - i = -1; - for (;;) - { - i = X509_NAME_get_index_by_NID(sname, NID_commonName, i); - - if (i < 0) - { - break; - } - - 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)); - - if (swSSL_check_name(tls_host_name, str) == SW_OK) - { - swTrace("SSL commonName: match"); - goto _found; - } - } - swTrace("SSL commonName: no match"); -#endif - - _failed: - X509_free(cert); - return SW_ERR; - - _found: - X509_free(cert); - return SW_OK; -} - -int swSSL_verify(swSocket *conn, int allow_self_signed) -{ - int err = SSL_get_verify_result(conn->ssl); - switch (err) - { - case X509_V_OK: - return SW_OK; - case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: - if (allow_self_signed) - { - return SW_OK; - } - else - { - swoole_error_log( - SW_LOG_NOTICE, SW_ERROR_SSL_VERIFY_FAILED, - "self signed certificate from fd#%d is not allowed", - conn->fd - ); - return SW_ERR; - } - default: - break; - } - swoole_error_log( - SW_LOG_NOTICE, SW_ERROR_SSL_VERIFY_FAILED, - "could not verify peer from fd#%d with error#%d: %s", - conn->fd, err, X509_verify_cert_error_string(err) - ); - return SW_ERR; -} - -int swSSL_get_peer_cert(SSL *ssl, char *buffer, size_t length) -{ - long len; - BIO *bio; +static int swoole_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store) { +#if 0 + char *subject, *issuer; + int err, depth; X509 *cert; - int n; + X509_NAME *sname, *iname; + X509_STORE_CTX_get_ex_data(x509_store, SSL_get_ex_data_X509_STORE_CTX_idx()); + cert = X509_STORE_CTX_get_current_cert(x509_store); + err = X509_STORE_CTX_get_error(x509_store); + depth = X509_STORE_CTX_get_error_depth(x509_store); - cert = SSL_get_peer_certificate(ssl); - if (cert == NULL) - { - return SW_ERR; - } + sname = X509_get_subject_name(cert); + subject = sname ? X509_NAME_oneline(sname, nullptr, 0) : "(none)"; - bio = BIO_new(BIO_s_mem()); - if (bio == NULL) - { - swWarn("BIO_new() failed"); - X509_free(cert); - return SW_ERR; - } + iname = X509_get_issuer_name(cert); + issuer = iname ? X509_NAME_oneline(iname, nullptr, 0) : "(none)"; + swoole_warning("verify:%d, error:%d, depth:%d, subject:\"%s\", issuer:\"%s\"", ok, err, depth, subject, issuer); - if (PEM_write_bio_X509(bio, cert) == 0) + if (sname) { - swWarn("PEM_write_bio_X509() failed"); - goto _failed; + OPENSSL_free(subject); } - - len = BIO_pending(bio); - if (len < 0 && len > (long) length) + if (iname) { - swWarn("certificate length[%ld] is too big", len); - goto _failed; + OPENSSL_free(issuer); } - - n = BIO_read(bio, buffer, len); - - BIO_free(bio); - X509_free(cert); - - return n; - - _failed: - - BIO_free(bio); - X509_free(cert); - - return SW_ERR; -} - -enum swReturn_code swSSL_accept(swSocket *conn) -{ - swSSL_clear_error(conn); - - int n = SSL_accept(conn->ssl); - /** - * The TLS/SSL handshake was successfully completed - */ - if (n == 1) - { - conn->ssl_state = SW_SSL_STATE_READY; -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#ifdef SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS - if (conn->ssl->s3) - { - conn->ssl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS; - } #endif -#endif - return SW_READY; - } - /** - * The TLS/SSL handshake was not successful but was shutdown. - */ - else if (n == 0) - { - return SW_ERROR; - } - long err = SSL_get_error(conn->ssl, n); - if (err == SSL_ERROR_WANT_READ) - { - conn->ssl_want_read = 1; - conn->ssl_want_write = 0; - return SW_WAIT; - } - else if (err == SSL_ERROR_WANT_WRITE) - { - conn->ssl_want_read = 0; - conn->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( - "bad SSL client[%s:%d], reason=%d, error_string=%s", - swSocket_get_ip(conn->socket_type, &conn->info), - swSocket_get_port(conn->socket_type, &conn->info), - reason, error_string - ); - return SW_ERROR; - } - else if (err == SSL_ERROR_SYSCALL) - { -#ifdef SW_SUPPORT_DTLS - if (conn->dtls && errno == 0) - { - conn->ssl_want_read = 1; - return SW_WAIT; - } -#endif - return SW_ERROR; - } - swWarn("SSL_do_handshake() failed. Error: %s[%ld|%d]", strerror(errno), err, errno); - return SW_ERROR; + return 1; } -int swSSL_connect(swSocket *conn) -{ - swSSL_clear_error(conn); - - int n = SSL_connect(conn->ssl); - if (n == 1) - { - conn->ssl_state = SW_SSL_STATE_READY; - -#ifdef SW_LOG_TRACE_OPEN - const char *ssl_version = SSL_get_version(conn->ssl); - const char *ssl_cipher = SSL_get_cipher_name(conn->ssl); - swTraceLog(SW_TRACE_SSL, "connected (%s %s)", ssl_version, ssl_cipher); -#endif +#ifdef SW_SUPPORT_DTLS - return SW_OK; - } +#define COOKIE_SECRET_LENGTH (32) - long err = SSL_get_error(conn->ssl, n); - if (err == SSL_ERROR_WANT_READ) - { - conn->ssl_want_read = 1; - conn->ssl_want_write = 0; - conn->ssl_state = SW_SSL_STATE_WAIT_STREAM; - return SW_OK; - } - else if (err == SSL_ERROR_WANT_WRITE) - { - conn->ssl_want_read = 0; - conn->ssl_want_write = 1; - conn->ssl_state = SW_SSL_STATE_WAIT_STREAM; - return SW_OK; - } - else if (err == SSL_ERROR_ZERO_RETURN) - { - swDebug("SSL_connect(fd=%d) closed", conn->fd); - return SW_ERR; - } - else if (err == SSL_ERROR_SYSCALL) - { - if (n) - { - SwooleG.error = errno; - return SW_ERR; - } +static void calculate_cookie(SSL *ssl, uchar *cookie_secret, uint cookie_length) { + long rv = (long) ssl; + long inum = (cookie_length - (((long) cookie_secret) % sizeof(long))) / sizeof(long); + long i = 0; + long *ip = (long *) cookie_secret; + for (i = 0; i < inum; ++i, ++ip) { + *ip = rv; } - - long err_code = ERR_get_error(); - char *msg = ERR_error_string(err_code, SwooleTG.buffer_stack->str); - swWarn("SSL_connect(fd=%d) failed. Error: %s[%ld|%d]", conn->fd, msg, err, ERR_GET_REASON(err_code)); - - return SW_ERR; } -int swSSL_sendfile(swSocket *conn, int fd, off_t *offset, size_t size) -{ - char buf[SW_BUFFER_SIZE_BIG]; - int readn = size > sizeof(buf) ? sizeof(buf) : size; - - int ret; - int n = pread(fd, buf, readn, *offset); - - if (n > 0) - { - ret = swSSL_send(conn, buf, n); - if (ret < 0) - { - if (swSocket_error(errno) == SW_ERROR) - { - swSysWarn("write() failed"); - } - } - else - { - *offset += ret; - } - swTraceLog(SW_TRACE_REACTOR, "fd=%d, readn=%d, n=%d, ret=%d", fd, readn, n, ret); - return ret; - } - else - { - swSysWarn("pread() failed"); - return SW_ERR; - } -} +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{}; -void swSSL_close(swSocket *conn) -{ - int n, sslerr, err; + uchar cookie_secret[COOKIE_SECRET_LENGTH]; + calculate_cookie(ssl, cookie_secret, sizeof(cookie_secret)); - if (SSL_in_init(conn->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(conn->ssl); - conn->ssl = NULL; - return; - } + /* Read peer information */ + (void) BIO_dgram_get_peer(SSL_get_wbio(ssl), &sa); - if (conn->ssl_quiet_shutdown) - { - SSL_set_quiet_shutdown(conn->ssl, 1); - SSL_set_shutdown(conn->ssl, SSL_RECEIVED_SHUTDOWN | SSL_SENT_SHUTDOWN); + length = 0; + switch (sa.addr.ss.sa_family) { + case AF_INET: + length += sizeof(struct in_addr); + break; + case AF_INET6: + length += sizeof(struct in6_addr); + break; + default: + OPENSSL_assert(0); + break; } - n = SSL_shutdown(conn->ssl); - - swTrace("SSL_shutdown: %d", n); - - 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(conn->ssl, n); - swTrace("SSL_get_error: %d", sslerr); - } + length += sizeof(in_port_t); + buffer = (uchar *) OPENSSL_malloc(length); - 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); + if (buffer == nullptr) { + swoole_sys_warning("out of memory"); + return 0; } - SSL_free(conn->ssl); - conn->ssl = NULL; -} - -static sw_inline void swSSL_connection_error(swSocket *conn) -{ - uint32_t level = SW_LOG_NOTICE; - int reason = ERR_GET_REASON(ERR_peek_error()); - -#if 0 - /* handshake failures */ - switch (reason) - { - case SSL_R_BAD_CHANGE_CIPHER_SPEC: /* 103 */ - case SSL_R_BLOCK_CIPHER_PAD_IS_WRONG: /* 129 */ - case SSL_R_DIGEST_CHECK_FAILED: /* 149 */ - case SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST: /* 151 */ - case SSL_R_EXCESSIVE_MESSAGE_SIZE: /* 152 */ - case SSL_R_LENGTH_MISMATCH:/* 159 */ - case SSL_R_NO_CIPHERS_PASSED:/* 182 */ - case SSL_R_NO_CIPHERS_SPECIFIED:/* 183 */ - case SSL_R_NO_COMPRESSION_SPECIFIED: /* 187 */ - case SSL_R_NO_SHARED_CIPHER:/* 193 */ - case SSL_R_RECORD_LENGTH_MISMATCH: /* 213 */ -#ifdef SSL_R_PARSE_TLSEXT - case SSL_R_PARSE_TLSEXT:/* 227 */ -#endif - case SSL_R_UNEXPECTED_MESSAGE:/* 244 */ - case SSL_R_UNEXPECTED_RECORD:/* 245 */ - case SSL_R_UNKNOWN_ALERT_TYPE: /* 246 */ - case SSL_R_UNKNOWN_PROTOCOL:/* 252 */ - case SSL_R_WRONG_VERSION_NUMBER:/* 267 */ - case SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC: /* 281 */ -#ifdef SSL_R_RENEGOTIATE_EXT_TOO_LONG - case SSL_R_RENEGOTIATE_EXT_TOO_LONG:/* 335 */ - case SSL_R_RENEGOTIATION_ENCODING_ERR:/* 336 */ - case SSL_R_RENEGOTIATION_MISMATCH:/* 337 */ -#endif -#ifdef SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED - case SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED: /* 338 */ -#endif -#ifdef SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING - case SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING:/* 345 */ -#endif -#ifdef SSL_R_INAPPROPRIATE_FALLBACK - case SSL_R_INAPPROPRIATE_FALLBACK: /* 373 */ -#endif - case 1000:/* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */ - case SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE:/* 1010 */ - case SSL_R_SSLV3_ALERT_BAD_RECORD_MAC:/* 1020 */ - case SSL_R_TLSV1_ALERT_DECRYPTION_FAILED:/* 1021 */ - case SSL_R_TLSV1_ALERT_RECORD_OVERFLOW:/* 1022 */ - case SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE:/* 1030 */ - case SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE:/* 1040 */ - case SSL_R_SSLV3_ALERT_NO_CERTIFICATE:/* 1041 */ - case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE:/* 1042 */ - case SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE: /* 1043 */ - case SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED:/* 1044 */ - case SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED:/* 1045 */ - case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN:/* 1046 */ - case SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER:/* 1047 */ - case SSL_R_TLSV1_ALERT_UNKNOWN_CA:/* 1048 */ - case SSL_R_TLSV1_ALERT_ACCESS_DENIED:/* 1049 */ - case SSL_R_TLSV1_ALERT_DECODE_ERROR:/* 1050 */ - case SSL_R_TLSV1_ALERT_DECRYPT_ERROR:/* 1051 */ - case SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION:/* 1060 */ - case SSL_R_TLSV1_ALERT_PROTOCOL_VERSION:/* 1070 */ - case SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY:/* 1071 */ - case SSL_R_TLSV1_ALERT_INTERNAL_ERROR:/* 1080 */ - case SSL_R_TLSV1_ALERT_USER_CANCELLED:/* 1090 */ - case SSL_R_TLSV1_ALERT_NO_RENEGOTIATION: /* 1100 */ - level = SW_LOG_WARNING; + switch (sa.addr.ss.sa_family) { + case AF_INET: + memcpy(buffer, &sa.addr.inet_v4.sin_port, sizeof(in_port_t)); + memcpy(buffer + sizeof(sa.addr.inet_v4.sin_port), &sa.addr.inet_v4.sin_addr, sizeof(struct in_addr)); + break; + case AF_INET6: + memcpy(buffer, &sa.addr.inet_v6.sin6_port, sizeof(in_port_t)); + memcpy(buffer + sizeof(in_port_t), &sa.addr.inet_v6.sin6_addr, sizeof(struct in6_addr)); + break; + default: + OPENSSL_assert(0); break; -#endif - - swoole_error_log(level, SW_ERROR_SSL_BAD_PROTOCOL, "SSL connection#%d[%s:%d] protocol error[%d]", conn->fd, - swSocket_get_ip(conn->socket_type, &conn->info), swSocket_get_port(conn->socket_type, &conn->info), - reason); -} - -ssize_t swSSL_recv(swSocket *conn, void *__buf, size_t __n) -{ - swSSL_clear_error(conn); - - int n = SSL_read(conn->ssl, __buf, __n); - if (n < 0) - { - int _errno = SSL_get_error(conn->ssl, n); - switch (_errno) - { - case SSL_ERROR_WANT_READ: - conn->ssl_want_read = 1; - errno = EAGAIN; - return SW_ERR; - - case SSL_ERROR_WANT_WRITE: - conn->ssl_want_write = 1; - errno = EAGAIN; - return SW_ERR; - - case SSL_ERROR_SYSCALL: - errno = SW_ERROR_SSL_RESET; - return SW_ERR; - - case SSL_ERROR_SSL: - swSSL_connection_error(conn); - errno = SW_ERROR_SSL_BAD_CLIENT; - return SW_ERR; - - default: - break; - } } - return n; -} -ssize_t swSSL_send(swSocket *conn, const void *__buf, size_t __n) -{ - swSSL_clear_error(conn); + HMAC(EVP_sha1(), (const void *) cookie_secret, COOKIE_SECRET_LENGTH, buffer, length, result, &result_len); + OPENSSL_free(buffer); -#ifdef SW_SUPPORT_DTLS - if (conn->dtls && conn->chunk_size && __n > conn->chunk_size) - { - __n = conn->chunk_size; - } -#endif + memcpy(cookie, result, result_len); + *cookie_len = result_len; - int n = SSL_write(conn->ssl, __buf, __n); - if (n < 0) - { - int _errno = SSL_get_error(conn->ssl, n); - switch (_errno) - { - case SSL_ERROR_WANT_READ: - conn->ssl_want_read = 1; - errno = EAGAIN; - return SW_ERR; - - case SSL_ERROR_WANT_WRITE: - conn->ssl_want_write = 1; - errno = EAGAIN; - return SW_ERR; - - case SSL_ERROR_SYSCALL: - errno = SW_ERROR_SSL_RESET; - return SW_ERR; - - case SSL_ERROR_SSL: - swSSL_connection_error(conn); - errno = SW_ERROR_SSL_BAD_CLIENT; - return SW_ERR; - - default: - break; - } - } - return n; + return 1; } -int swSSL_create(swSocket *conn, SSL_CTX* ssl_context, int flags) -{ - swSSL_clear_error(conn); +static int swoole_ssl_verify_cookie(SSL *ssl, const uchar *cookie, uint cookie_len) { + uint result_len = 0; + uchar result[COOKIE_SECRET_LENGTH]; - SSL *ssl = SSL_new(ssl_context); - if (ssl == NULL) - { - swWarn("SSL_new() failed"); - return SW_ERR; - } - if (!SSL_set_fd(ssl, conn->fd)) - { - long err = ERR_get_error(); - swWarn("SSL_set_fd() failed. Error: %s[%ld]", ERR_reason_error_string(err), err); - return SW_ERR; - } - if (flags & SW_SSL_CLIENT) - { - SSL_set_connect_state(ssl); - } - else if (flags & SW_SSL_SERVER) - { - SSL_set_accept_state(ssl); - } - if (SSL_set_ex_data(ssl, ssl_connection_index, conn) == 0) - { - swWarn("SSL_set_ex_data() failed"); - return SW_ERR; - } - conn->ssl = ssl; - conn->ssl_state = 0; - return SW_OK; -} + swoole_ssl_generate_cookie(ssl, result, &result_len); -void swSSL_free_context(SSL_CTX* ssl_context) -{ - SSL_CTX_free(ssl_context); + 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 *rsa_tmp = NULL; - if (rsa_tmp) - { +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; } BIGNUM *bn = BN_new(); - if (bn == NULL) - { - swWarn("allocation error generating RSA key"); - return NULL; + if (bn == nullptr) { + swoole_warning("allocation error generating RSA key"); + return nullptr; } - if (!BN_set_word(bn, RSA_F4) || ((rsa_tmp = RSA_new()) == NULL) - || !RSA_generate_key_ex(rsa_tmp, key_length, bn, NULL)) - { - if (rsa_tmp) - { + if (!BN_set_word(bn, RSA_F4) || ((rsa_tmp = RSA_new()) == nullptr) || + !RSA_generate_key_ex(rsa_tmp, key_length, bn, nullptr)) { + if (rsa_tmp) { RSA_free(rsa_tmp); } - rsa_tmp = NULL; + rsa_tmp = nullptr; } BN_free(bn); return rsa_tmp; @@ -1333,33 +838,29 @@ 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, 0xD9, 0x58, 0xA3, - 0xF5, 0x68, 0xB4, 0x2D, 0x4B, 0x08, 0xEE, 0xD4, 0xEB, 0x0F, 0xB3, 0x50, 0x4C, 0x6C, 0x03, 0x02, 0x76, 0xE7, - 0x10, 0x80, 0x0C, 0x5C, 0xCB, 0xBA, 0xA8, 0x92, 0x26, 0x14, 0xC5, 0xBE, 0xEC, 0xA5, 0x65, 0xA5, 0xFD, 0xF1, - 0xD2, 0x87, 0xA2, 0xBC, 0x04, 0x9B, 0xE6, 0x77, 0x80, 0x60, 0xE9, 0x1A, 0x92, 0xA7, 0x57, 0xE3, 0x04, 0x8F, - 0x68, 0xB0, 0x76, 0xF7, 0xD3, 0x6C, 0xC8, 0xF2, 0x9B, 0xA5, 0xDF, 0x81, 0xDC, 0x2C, 0xA7, 0x25, 0xEC, 0xE6, - 0x62, 0x70, 0xCC, 0x9A, 0x50, 0x35, 0xD8, 0xCE, 0xCE, 0xEF, 0x9E, 0xA0, 0x27, 0x4A, 0x63, 0xAB, 0x1E, 0x58, - 0xFA, 0xFD, 0x49, 0x88, 0xD0, 0xF6, 0x5D, 0x14, 0x67, 0x57, 0xDA, 0x07, 0x1D, 0xF0, 0x45, 0xCF, 0xE1, 0x6B, - 0x9B }; - - static unsigned char dh1024_g[] = - { 0x02 }; + static unsigned char dh1024_p[] = { + 0xBB, 0xBC, 0x2D, 0xCA, 0xD8, 0x46, 0x74, 0x90, 0x7C, 0x43, 0xFC, 0xF5, 0x80, 0xE9, 0xCF, 0xDB, + 0xD9, 0x58, 0xA3, 0xF5, 0x68, 0xB4, 0x2D, 0x4B, 0x08, 0xEE, 0xD4, 0xEB, 0x0F, 0xB3, 0x50, 0x4C, + 0x6C, 0x03, 0x02, 0x76, 0xE7, 0x10, 0x80, 0x0C, 0x5C, 0xCB, 0xBA, 0xA8, 0x92, 0x26, 0x14, 0xC5, + 0xBE, 0xEC, 0xA5, 0x65, 0xA5, 0xFD, 0xF1, 0xD2, 0x87, 0xA2, 0xBC, 0x04, 0x9B, 0xE6, 0x77, 0x80, + 0x60, 0xE9, 0x1A, 0x92, 0xA7, 0x57, 0xE3, 0x04, 0x8F, 0x68, 0xB0, 0x76, 0xF7, 0xD3, 0x6C, 0xC8, + 0xF2, 0x9B, 0xA5, 0xDF, 0x81, 0xDC, 0x2C, 0xA7, 0x25, 0xEC, 0xE6, 0x62, 0x70, 0xCC, 0x9A, 0x50, + 0x35, 0xD8, 0xCE, 0xCE, 0xEF, 0x9E, 0xA0, 0x27, 0x4A, 0x63, 0xAB, 0x1E, 0x58, 0xFA, 0xFD, 0x49, + 0x88, 0xD0, 0xF6, 0x5D, 0x14, 0x67, 0x57, 0xDA, 0x07, 0x1D, 0xF0, 0x45, 0xCF, 0xE1, 0x6B, 0x9B}; + + static unsigned char dh1024_g[] = {0x02}; dh = DH_new(); - if (dh == NULL) - { - swWarn("DH_new() failed"); + if (dh == nullptr) { + swoole_warning("DH_new() failed"); return SW_ERR; } - dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL); - dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL); + dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), nullptr); + dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), nullptr); - if (dh->p == NULL || dh->g == NULL) - { + if (dh->p == nullptr || dh->g == nullptr) { DH_free(dh); } SSL_CTX_set_tmp_dh(ssl_context, dh); @@ -1368,115 +869,4 @@ static int swSSL_set_default_dhparam(SSL_CTX* ssl_context) } #endif -static int swSSL_set_ecdh_curve(SSL_CTX* ssl_context, const char *ecdh_curve) -{ -#ifndef OPENSSL_NO_ECDH - - EC_KEY *ecdh; - /* - * Elliptic-Curve Diffie-Hellman parameters are either "named curves" - * from RFC 4492 section 5.1.1, or explicitly described curves over - * binary fields. OpenSSL only supports the "named curves", which provide - * maximum interoperability. - */ - int nid = OBJ_sn2nid(ecdh_curve); - if (nid == 0) - { - swWarn("Unknown curve name \"%s\"", ecdh_curve); - return SW_ERR; - } - - ecdh = EC_KEY_new_by_curve_name(nid); - if (ecdh == NULL) - { - swWarn("Unable to create curve \"%s\"", ecdh_curve); - return SW_ERR; - } - - SSL_CTX_set_options(ssl_context, SSL_OP_SINGLE_ECDH_USE); - SSL_CTX_set_tmp_ecdh(ssl_context, ecdh); - - EC_KEY_free(ecdh); -#endif - - return SW_OK; -} - -static int swSSL_set_dhparam(SSL_CTX* ssl_context, const char *file) -{ - DH *dh; - BIO *bio; - - bio = BIO_new_file((char *) file, "r"); - if (bio == NULL) - { - swWarn("BIO_new_file(%s) failed", file); - return SW_ERR; - } - - dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); - if (dh == NULL) - { - swWarn("PEM_read_bio_DHparams(%s) failed", file); - BIO_free(bio); - return SW_ERR; - } - - SSL_CTX_set_tmp_dh(ssl_context, dh); - - DH_free(dh); - BIO_free(bio); - - return SW_OK; -} - -#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation - -static int swSSL_alpn_advertised(SSL *ssl, const uchar **out, uchar *outlen, const uchar *in, uint32_t inlen, void *arg) -{ - unsigned int srvlen; - unsigned char *srv; - -#ifdef SW_USE_HTTP2 - swSSL_config *cfg = (swSSL_config *) arg; - if (cfg->http_v2) - { - srv = (unsigned char *) SW_SSL_HTTP2_NPN_ADVERTISE SW_SSL_NPN_ADVERTISE; - srvlen = sizeof (SW_SSL_HTTP2_NPN_ADVERTISE SW_SSL_NPN_ADVERTISE) - 1; - } - else -#endif - { - srv = (unsigned char *) SW_SSL_NPN_ADVERTISE; - srvlen = sizeof (SW_SSL_NPN_ADVERTISE) - 1; - } - if (SSL_select_next_proto((unsigned char **) out, outlen, srv, srvlen, in, inlen) != OPENSSL_NPN_NEGOTIATED) - { - return SSL_TLSEXT_ERR_NOACK; - } - return SSL_TLSEXT_ERR_OK; -} -#endif - -#ifdef TLSEXT_TYPE_next_proto_neg - -static int swSSL_npn_advertised(SSL *ssl, const uchar **out, uint32_t *outlen, void *arg) -{ -#ifdef SW_USE_HTTP2 - swSSL_config *cfg = (swSSL_config *) arg; - if (cfg->http_v2) - { - *out = (uchar *) SW_SSL_HTTP2_NPN_ADVERTISE SW_SSL_NPN_ADVERTISE; - *outlen = sizeof (SW_SSL_HTTP2_NPN_ADVERTISE SW_SSL_NPN_ADVERTISE) - 1; - } - else -#endif - { - *out = (uchar *) SW_SSL_NPN_ADVERTISE; - *outlen = sizeof(SW_SSL_NPN_ADVERTISE) - 1; - } - return SSL_TLSEXT_ERR_OK; -} -#endif - #endif diff --git a/src/protocol/websocket.cc b/src/protocol/websocket.cc index ef32835f295..0b2cc4f2d57 100644 --- a/src/protocol/websocket.cc +++ b/src/protocol/websocket.cc @@ -10,16 +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.h" -#include "server.h" -#include "websocket.h" - -static inline uint16_t swWebSocket_get_ext_flags(uchar opcode, uchar flags) -{ +#include "swoole_server.h" +#include "swoole_websocket.h" + +using swoole::Connection; +using swoole::Protocol; +using swoole::Server; +using swoole::String; +using swoole::network::Socket; + +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; @@ -47,324 +54,303 @@ static inline uint16_t swWebSocket_get_ext_flags(uchar opcode, uchar flags) | Payload Data continued ... | +---------------------------------------------------------------+ */ - -ssize_t swWebSocket_get_package_length(swProtocol *protocol, swSocket *conn, char *buf, uint32_t length) -{ - //need more data - if (length < SW_WEBSOCKET_HEADER_LEN) - { +static ssize_t get_package_length_impl(PacketLength *pl) { + // need more data + if (pl->buf_size < SW_WEBSOCKET_HEADER_LEN) { return 0; } - + const char *buf = pl->buf; char mask = (buf[1] >> 7) & 0x1; - //0-125 + // 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; + // uint16_t, 2byte + if (payload_length == SW_WEBSOCKET_EXT16_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; + // uint64_t, 8byte + else if (payload_length == SW_WEBSOCKET_EXT64_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; + if (mask) { + 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; +} + +ssize_t get_package_length(const Protocol *protocol, Socket *conn, PacketLength *pl) { + return get_package_length_impl(pl); } -static sw_inline void swWebSocket_mask(char *data, size_t len, const char *mask_key) -{ +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); + uint64_t mask_key64 = ((uint64_t)(*((uint32_t *) mask_key)) << 32) | *((uint32_t *) mask_key); size_t i; - for (i = 0; i < n; i++) - { + for (i = 0; i < n; i++) { ((uint64_t *) data)[i] ^= mask_key64; } - for (i = n * 8; i < len; i++) - { + for (i = n * 8; i < len; i++) { data[i] ^= mask_key[i % SW_WEBSOCKET_MASK_LEN]; } } -void swWebSocket_encode(swString *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); pos += sizeof(*length_ptr); - } - else - { + } else { header->LENGTH = SW_WEBSOCKET_EXT64_LENGTH; uint64_t *length_ptr = (uint64_t *) (frame_header + pos); *length_ptr = swoole_hton64(length); pos += sizeof(*length_ptr); } - swString_append_ptr(buffer, frame_header, pos); + buffer->append(frame_header, pos); /** * frame body */ - if (header->MASK) - { - swString_append_ptr(buffer, SW_WEBSOCKET_MASK_DATA, SW_WEBSOCKET_MASK_LEN); - if (length > 0) - { + if (header->MASK) { + buffer->append(SW_WEBSOCKET_MASK_DATA, SW_WEBSOCKET_MASK_LEN); + 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 - swString_append_ptr(buffer, data, length); - swWebSocket_mask(buffer->str + offset, length, SW_WEBSOCKET_MASK_DATA); + buffer->append(data, length); + mask(buffer->str + offset, length, SW_WEBSOCKET_MASK_DATA); } - } - else - { - if (length > 0) - { - swString_append_ptr(buffer, data, length); + } else { + if (length > 0 and !(_flags & FLAG_ENCODE_HEADER_ONLY)) { + buffer->append(data, length); } } -} -void swWebSocket_decode(swWebSocket_frame *frame, swString *data) -{ - memcpy(frame, data->str, SW_WEBSOCKET_HEADER_LEN); - - //0-125 - size_t payload_length = frame->header.LENGTH; - uint8_t header_length = SW_WEBSOCKET_HEADER_LEN; - char *buf = data->str + SW_WEBSOCKET_HEADER_LEN; + return true; +} - //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 (frame->header.MASK) - { - memcpy(frame->mask_key, data->str + header_length, SW_WEBSOCKET_MASK_LEN); - header_length += SW_WEBSOCKET_MASK_LEN; - if (payload_length > 0) - { - swWebSocket_mask(data->str + header_length, payload_length, frame->mask_key); + frame->payload_length = total_length - pl.header_len; + frame->header_length = pl.header_len; + + 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->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->header_length = header_length; - frame->payload = data->str + header_length; - frame->payload_length = payload_length; + return true; } -int swWebSocket_pack_close_frame(swString *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); +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)) { + swoole_warning("the max length of close reason is %d", SW_WEBSOCKET_CLOSE_REASON_MAX_LEN); return SW_ERR; } char payload[SW_WEBSOCKET_HEADER_LEN + SW_WEBSOCKET_CLOSE_CODE_LEN + SW_WEBSOCKET_CLOSE_REASON_MAX_LEN]; payload[0] = (char) ((code >> 8 & 0xFF)); payload[1] = (char) ((code & 0xFF)); - if (length > 0) - { + 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) -{ - printf("FIN: %x, RSV1: %d, RSV2: %d, RSV3: %d, opcode: %d, MASK: %d, length: %ld\n", frame->header.FIN, - frame->header.RSV1, frame->header.RSV2, frame->header.RSV3, frame->header.OPCODE, frame->header.MASK, - frame->payload_length); - - if (frame->payload_length) - { +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, + frame->header.RSV2, + frame->header.RSV3, + frame->header.OPCODE, + frame->header.MASK, + frame->payload_length); + + if (frame->payload_length) { printf("payload: %.*s\n", (int) frame->payload_length, frame->payload); } } -int swWebSocket_dispatch_frame(swProtocol *proto, swSocket *_socket, char *data, uint32_t length) -{ - swServer *serv = (swServer *) proto->private_data_2; - swConnection *conn = (swConnection *) _socket->object; - swString frame; - bzero(&frame, sizeof(frame)); - frame.str = data; - frame.length = length; - - swString send_frame = {}; +int dispatch_frame(const Protocol *proto, Socket *_socket, const RecvData *rdata) { + Server *serv = (Server *) proto->private_data_2; + Connection *conn = (Connection *) _socket->object; + 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, &frame); + Frame ws; + if (!decode(&ws, const_cast(data), length)) { + return SW_ERR; + } - swString *frame_buffer; + String *frame_buffer; int frame_length; - swListenPort *port; + ListenPort *port; size_t offset; - switch (ws.header.OPCODE) - { - case WEBSOCKET_OPCODE_CONTINUATION: + switch (ws.header.OPCODE) { + case OPCODE_CONTINUATION: frame_buffer = conn->websocket_buffer; - if (frame_buffer == NULL) - { - swWarn("bad frame[opcode=0]. remote_addr=%s:%d", swSocket_get_ip(conn->socket_type, &conn->info), - swSocket_get_port(conn->socket_type, &conn->info)); + if (frame_buffer == nullptr) { + 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; frame_length = length - offset; - port = swServer_get_port(serv, 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", swSocket_get_ip(conn->socket_type, &conn->info), - swSocket_get_port(conn->socket_type, &conn->info)); + port = serv->get_port_by_fd(conn->fd); + // frame data overflow + if (frame_buffer->length + frame_length > port->protocol.package_max_length) { + 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 - swString_append_ptr(frame_buffer, data + offset, frame_length); - //frame is finished, do dispatch - if (ws.header.FIN) - { - proto->ext_flags = conn->websocket_buffer->offset; - swReactorThread_dispatch(proto, _socket, frame_buffer->str, frame_buffer->length); - swString_free(frame_buffer); - conn->websocket_buffer = NULL; + // merge incomplete data + frame_buffer->append(data + offset, frame_length); + // frame is finished, do dispatch + if (ws.header.FIN) { + 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)); - - if (!ws.header.FIN) - { - if (conn->websocket_buffer) - { - swWarn("merging incomplete frame, bad request. remote_addr=%s:%d", - swSocket_get_ip(conn->socket_type, &conn->info), - swSocket_get_port(conn->socket_type, &conn->info)); + int ext_flags = get_ext_flags(ws.header.OPCODE, get_flags(&ws)); + if (!ws.header.FIN) { + if (conn->websocket_buffer) { + swoole_warning("merging incomplete frame, bad request. remote_addr=%s:%d", + conn->info.get_ip(), + conn->info.get_port()); return SW_ERR; } - conn->websocket_buffer = swString_dup(data + offset, length - offset); - conn->websocket_buffer->offset = proto->ext_flags; - } - else - { - swReactorThread_dispatch(proto, _socket, data + offset, length - offset); + conn->websocket_buffer = new swoole::String(data + offset, length - offset); + conn->websocket_buffer->offset = ext_flags; + } else { + 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: - if (length >= (sizeof(buf) - SW_WEBSOCKET_HEADER_LEN)) - { - swWarn("ping frame application data is too big. remote_addr=%s:%d", - swSocket_get_ip(conn->socket_type, &conn->info), - swSocket_get_port(conn->socket_type, &conn->info)); + case OPCODE_PING: + case OPCODE_PONG: + if (length >= (sizeof(buf) - SW_WEBSOCKET_HEADER_LEN)) { + swoole_warning("%s frame application data is too big. remote_addr=%s:%d", + ws.header.OPCODE == OPCODE_PING ? "ping" : "pong", + conn->info.get_ip(), + conn->info.get_port()); return SW_ERR; - } - else if (length == SW_WEBSOCKET_HEADER_LEN) - { - swWebSocket_encode(&send_frame, NULL, 0, WEBSOCKET_OPCODE_PONG, SW_WEBSOCKET_FLAG_FIN); - } - else - { + } else if (length == SW_WEBSOCKET_HEADER_LEN) { + 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; - swWebSocket_encode(&send_frame, data += offset, length - offset, WEBSOCKET_OPCODE_PONG, SW_WEBSOCKET_FLAG_FIN); + dispatch_data.info.len = length - offset; + dispatch_data.data = dispatch_data.info.len == 0 ? nullptr : data + offset; } - swSocket_send(_socket, send_frame.str, send_frame.length, 0); + 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_PONG: - break; - - case WEBSOCKET_OPCODE_CLOSE: - if ((length - SW_WEBSOCKET_HEADER_LEN) > SW_WEBSOCKET_CLOSE_REASON_MAX_LEN) - { + 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)); - - swReactorThread_dispatch(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 + send_frame.str[0] = 0x88; // FIN | OPCODE: WEBSOCKET_OPCODE_CLOSE send_frame.str[1] = ws.payload_length; // Get payload and return it as it is - memcpy(send_frame.str + SW_WEBSOCKET_HEADER_LEN, frame.str + frame.length - ws.payload_length, ws.payload_length); + memcpy(send_frame.str + SW_WEBSOCKET_HEADER_LEN, data + length - ws.payload_length, ws.payload_length); send_frame.length = SW_WEBSOCKET_HEADER_LEN + ws.payload_length; - swSocket_send(_socket, send_frame.str, send_frame.length, 0); - } - else - { + _socket->send(send_frame.str, send_frame.length, 0); + } else { // Server attempt to close, frame sent by swoole_websocket_server->disconnect() conn->websocket_status = 0; } @@ -372,8 +358,10 @@ int swWebSocket_dispatch_frame(swProtocol *proto, swSocket *_socket, char *data, 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 e47e56981e1..bc3c1bc658e 100644 --- a/src/reactor/base.cc +++ b/src/reactor/base.cc @@ -10,20 +10,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 | +----------------------------------------------------------------------+ */ -#include "swoole_cxx.h" -#include "async.h" +#include "swoole.h" +#include "swoole_socket.h" +#include "swoole_signal.h" +#include "swoole_reactor.h" +#include "swoole_api.h" +#include "swoole_c_api.h" -#include "coroutine_c_api.h" -#include "coroutine_socket.h" -#include "coroutine_system.h" - -using swoole::CallbackManager; -using swoole::coroutine::Socket; -using swoole::coroutine::System; +namespace swoole { +using network::Socket; #ifdef SW_USE_MALLOC_TRIM #ifdef __APPLE__ @@ -33,411 +32,385 @@ using swoole::coroutine::System; #endif #endif -static void reactor_timeout(swReactor *reactor); -static void reactor_finish(swReactor *reactor); -static void reactor_begin(swReactor *reactor); -static void defer_task_do(swReactor *reactor); -static void defer_task_add(swReactor *reactor, swCallback callback, void *data); +static void reactor_begin(Reactor *reactor); -int swReactor_create(swReactor *reactor, int max_event) -{ - int ret; - bzero(reactor, sizeof(swReactor)); +#ifdef HAVE_EPOLL +ReactorImpl *make_reactor_epoll(Reactor *_reactor, int max_events); +#endif +#ifdef HAVE_POLL +ReactorImpl *make_reactor_poll(Reactor *_reactor, int max_events); +#endif + +#ifdef HAVE_KQUEUE +ReactorImpl *make_reactor_kqueue(Reactor *_reactor, int max_events); +#endif + +ReactorImpl *make_reactor_select(Reactor *_reactor); + +void ReactorImpl::after_removal_failure(Socket *_socket) { + if (!_socket->silent_remove) { + swoole_sys_warning("failed to delete events[fd=%d#%d, type=%d, events=%d]", + _socket->fd, + reactor_->id, + _socket->fd_type, + _socket->events); + } +} + +Reactor::Reactor(int max_event, Type _type) { + if (_type == TYPE_AUTO) { #ifdef HAVE_EPOLL - ret = swReactorEpoll_create(reactor, max_event); + type_ = TYPE_EPOLL; #elif defined(HAVE_KQUEUE) - ret = swReactorKqueue_create(reactor, max_event); + type_ = TYPE_KQUEUE; #elif defined(HAVE_POLL) - ret = swReactorPoll_create(reactor, max_event); + type_ = TYPE_POLL; #else - ret = swReactorSelect_create(reactor); + type_ = TYPE_SELECT; #endif + } else { + type_ = _type; + } - reactor->running = 1; - - reactor->onFinish = reactor_finish; - reactor->onTimeout = reactor_timeout; - reactor->is_empty = swReactor_empty; - reactor->can_exit = SwooleG.reactor_can_exit; + switch (type_) { +#ifdef HAVE_EPOLL + case TYPE_EPOLL: + impl = make_reactor_epoll(this, max_event); + break; +#endif +#ifdef HAVE_KQUEUE + case TYPE_KQUEUE: + impl = make_reactor_kqueue(this, max_event); + break; +#endif +#ifdef HAVE_POLL + case TYPE_POLL: + impl = make_reactor_poll(this, max_event); + break; +#endif + case TYPE_SELECT: + default: + impl = make_reactor_select(this); + break; + } - reactor->write = swReactor_write; - reactor->close = swReactor_close; + if (!impl->ready()) { + running = false; + return; + } - reactor->defer = defer_task_add; - reactor->defer_tasks = nullptr; + running = true; + idle_task = {}; + future_task = {}; - reactor->default_write_handler = swReactor_onWrite; + write = _write; + writev = _writev; + close = _close; - Socket::init_reactor(reactor); - System::init_reactor(reactor); - swClient_init_reactor(reactor); + default_write_handler = _writable_callback; - if (SwooleG.hooks[SW_GLOBAL_HOOK_ON_REACTOR_CREATE]) - { - swoole_call_hook(SW_GLOBAL_HOOK_ON_REACTOR_CREATE, reactor); + if (swoole_isset_hook(SW_GLOBAL_HOOK_ON_REACTOR_CREATE)) { + swoole_call_hook(SW_GLOBAL_HOOK_ON_REACTOR_CREATE, this); } - return ret; -} + set_end_callback(PRIORITY_DEFER_TASK, [](Reactor *reactor) { + CallbackManager *cm = reactor->defer_tasks; + if (cm) { + reactor->defer_tasks = nullptr; + cm->execute(); + delete cm; + } + }); -int swReactor_set_handler(swReactor *reactor, int _fdtype, swReactor_handler handle) -{ - int fdtype = swReactor_fdtype(_fdtype); + set_exit_condition(EXIT_CONDITION_DEFER_TASK, + [](Reactor *reactor, size_t &event_num) -> bool { return reactor->defer_tasks == nullptr; }); - if (fdtype >= SW_MAX_FDTYPE) - { - swWarn("fdtype > SW_MAX_FDTYPE[%d]", SW_MAX_FDTYPE); - return SW_ERR; - } + set_end_callback(PRIORITY_IDLE_TASK, [](Reactor *reactor) { + if (reactor->idle_task.callback) { + reactor->idle_task.callback(reactor->idle_task.data); + } + }); - if (swReactor_event_read(_fdtype)) - { - reactor->read_handler[fdtype] = handle; - } - else if (swReactor_event_write(_fdtype)) - { - reactor->write_handler[fdtype] = handle; - } - else if (swReactor_event_error(_fdtype)) - { - reactor->error_handler[fdtype] = handle; - } - else - { - swWarn("unknow fdtype"); - return SW_ERR; - } + set_end_callback(PRIORITY_SIGNAL_CALLBACK, [](Reactor *reactor) { + if (sw_unlikely(reactor->singal_no)) { + swoole_signal_callback(reactor->singal_no); + reactor->singal_no = 0; + } + }); - return SW_OK; -} + set_end_callback(PRIORITY_TRY_EXIT, [](Reactor *reactor) { + if (reactor->wait_exit && reactor->if_exit()) { + reactor->running = false; + } + }); -int swReactor_empty(swReactor *reactor) -{ - if (reactor->timer && reactor->timer->num > 0) - { - return SW_FALSE; - } - if (reactor->defer_tasks) - { - return SW_FALSE; - } - if (swoole_coroutine_wait_count() > 0) - { - return SW_FALSE; - } - if (SwooleTG.reactor->co_signal_listener_num > 0) - { - return SW_FALSE; - } +#ifdef SW_USE_MALLOC_TRIM + 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); + reactor->last_malloc_trim_time = now; + } + }); +#endif - int event_num = reactor->event_num; - int empty = SW_FALSE; - //aio thread pool - if (SwooleTG.aio_init && SwooleTG.aio_task_num == 0) - { - event_num--; - } - //signalfd - if (swReactor_isset_handler(reactor, SW_FD_SIGNAL)) - { - event_num--; - } - //no event - if (event_num == 0) - { - empty = SW_TRUE; - } - //custom - if (reactor->can_exit && !reactor->can_exit(reactor)) - { - empty = SW_FALSE; - } - return empty; + set_exit_condition(EXIT_CONDITION_DEFAULT, + [](Reactor *reactor, size_t &event_num) -> bool { return event_num == 0; }); } -/** - * execute when reactor timeout and reactor finish - */ -static void reactor_finish(swReactor *reactor) -{ - //check timer - if (reactor->check_timer) - { - swTimer_select(reactor->timer); - } - //defer tasks - if (reactor->defer_tasks) - { - defer_task_do(reactor); - } - //callback at the end - if (reactor->idle_task.callback) - { - reactor->idle_task.callback(reactor->idle_task.data); - } - //check signal - if (sw_unlikely(reactor->singal_no)) - { - swSignal_callback(reactor->singal_no); - reactor->singal_no = 0; - } - //the event loop is empty - if (reactor->wait_exit && reactor->is_empty(reactor)) - { - reactor->running = 0; +bool Reactor::set_handler(int _fdtype, ReactorHandler handler) { + int fdtype = get_fd_type(_fdtype); + + if (fdtype >= SW_MAX_FDTYPE) { + swoole_warning("fdtype > SW_MAX_FDTYPE[%d]", SW_MAX_FDTYPE); + return false; } -#ifdef SW_USE_MALLOC_TRIM - time_t now = time(NULL); - if (reactor->last_malloc_trim_time < now - SW_MALLOC_TRIM_INTERVAL) - { - malloc_trim(SW_MALLOC_TRIM_PAD); - reactor->last_malloc_trim_time = now; + + if (isset_read_event(_fdtype)) { + read_handler[fdtype] = handler; + } else if (isset_write_event(_fdtype)) { + write_handler[fdtype] = handler; + } else if (isset_error_event(_fdtype)) { + error_handler[fdtype] = handler; + } else { + swoole_warning("unknown fdtype"); + return false; } -#endif + + return true; } -static void reactor_timeout(swReactor *reactor) -{ - reactor_finish(reactor); +bool Reactor::if_exit() { + size_t _event_num = get_event_num(); + for (auto &kv : exit_conditions) { + if (kv.second(this, _event_num) == false) { + return false; + } + } + return true; } -void swReactor_activate_future_task(swReactor *reactor) -{ - reactor->onBegin = reactor_begin; +void Reactor::activate_future_task() { + onBegin = reactor_begin; } -static void reactor_begin(swReactor *reactor) -{ - if (reactor->future_task.callback) - { +static void reactor_begin(Reactor *reactor) { + if (reactor->future_task.callback) { reactor->future_task.callback(reactor->future_task.data); } } -int swReactor_close(swReactor *reactor, swSocket *socket) -{ - if (socket->out_buffer) - { - swBuffer_free(socket->out_buffer); - socket->out_buffer = NULL; - } - if (socket->in_buffer) - { - swBuffer_free(socket->in_buffer); - socket->in_buffer = NULL; - } - - swTraceLog(SW_TRACE_CLOSE, "fd=%d", socket->fd); - - swSocket_free(socket); - +int Reactor::_close(Reactor *reactor, Socket *socket) { + swoole_trace_log(SW_TRACE_CLOSE, "fd=%d", socket->fd); + socket->free(); return SW_OK; } -int swReactor_write(swReactor *reactor, swSocket *socket, const void *buf, int n) -{ - int ret; - swBuffer *buffer = socket->out_buffer; - const char *ptr = (const char *) buf; +using SendFunc = std::function; +using AppendFunc = std::function; + +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 = SwooleG.socket_buffer_size; + if (socket->buffer_size == 0) { + socket->set_memory_buffer_size(Socket::default_buffer_size); } - if (socket->nonblock == 0) - { - swoole_fcntl_set_option(socket->fd, 1, -1); - socket->nonblock = 1; + if (socket->nonblock == 0) { + socket->set_fd_option(1, -1); } - if ((uint32_t) n > socket->buffer_size) - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, "data is too large, cannot exceed buffer size"); + if ((uint32_t) __len > socket->buffer_size) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, + "data packet is too large, cannot exceed the buffer size"); return SW_ERR; } - if (swBuffer_empty(buffer)) - { + if (Buffer::empty(buffer)) { #ifdef SW_USE_OPENSSL - if (socket->ssl_send) - { - goto _do_buffer; + if (socket->ssl_send_) { + goto _alloc_buffer; } #endif - _do_send: - ret = swSocket_send(socket, ptr, n, 0); - - if (ret > 0) - { - if (n == ret) - { - return ret; - } - else - { - ptr += ret; - n -= ret; - goto _do_buffer; + _do_send: + retval = send_fn(); + + if (retval > 0) { + if ((ssize_t) __len == retval) { + return retval; + } else { + goto _alloc_buffer; } - } - else if (swSocket_error(errno) == SW_WAIT) - { - _do_buffer: - if (!socket->out_buffer) - { - buffer = swBuffer_new(socket->chunk_size); - if (!buffer) - { - swWarn("create worker buffer failed"); + } else if (socket->catch_write_error(errno) == SW_WAIT) { + _alloc_buffer: + if (!socket->out_buffer) { + buffer = new Buffer(socket->chunk_size); + if (!buffer) { + swoole_warning("create worker buffer failed"); return SW_ERR; } socket->out_buffer = buffer; } - - swReactor_add_write_event(reactor, socket); + if (!socket->isset_writable_event()) { + reactor->add_write_event(socket); + } goto _append_buffer; - } - else if (errno == EINTR) - { + } else if (errno == EINTR) { goto _do_send; - } - else - { - SwooleG.error = errno; + } else { + swoole_set_last_error(errno); return SW_ERR; } - } - else - { - _append_buffer: - if (buffer->length > socket->buffer_size) - { - if (socket->dontwait) - { - SwooleG.error = SW_ERROR_OUTPUT_BUFFER_OVERFLOW; + } else { + _append_buffer: + if (buffer->length() > socket->buffer_size) { + if (socket->dontwait) { + swoole_set_last_error(SW_ERROR_OUTPUT_BUFFER_OVERFLOW); return SW_ERR; - } - else - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_OUTPUT_BUFFER_OVERFLOW, "socket#%d output buffer overflow", fd); - swYield(); - swSocket_wait(socket->fd, SW_SOCKET_OVERFLOW_WAIT, SW_EVENT_WRITE); + } else { + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_OUTPUT_BUFFER_OVERFLOW, "socket#%d output buffer overflow", fd); + sw_yield(); + socket->wait_event(SW_SOCKET_OVERFLOW_WAIT, SW_EVENT_WRITE); } } + append_fn(buffer); + } + return __len; +} - if (swBuffer_append(buffer, ptr, n) < 0) - { - return SW_ERR; - } +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, 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); +} + +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"); + return SW_ERR; } - return SW_OK; +#endif + + ssize_t send_bytes = 0; + size_t n = 0; + SW_LOOP_N(iovcnt) { + n += iov[i].iov_len; + } + auto send_fn = [&send_bytes, socket, iov, iovcnt]() -> ssize_t { + send_bytes = socket->writev(iov, iovcnt); + return send_bytes; + }; + auto append_fn = [&send_bytes, iov, iovcnt](Buffer *buffer) { + ssize_t offset = send_bytes > 0 ? send_bytes : 0; + buffer->append(iov, iovcnt, offset); + }; + return write_func(reactor, socket, n, send_fn, append_fn); } -int swReactor_onWrite(swReactor *reactor, swEvent *ev) -{ +int Reactor::_writable_callback(Reactor *reactor, Event *ev) { int ret; - swSocket *socket = ev->socket; - swBuffer_chunk *chunk = NULL; - swBuffer *buffer = socket->out_buffer; - - //send to socket - while (!swBuffer_empty(buffer)) - { - chunk = swBuffer_get_chunk(buffer); - if (chunk->type == SW_CHUNK_CLOSE) - { - _close_fd: - reactor->close(reactor, ev->socket); - return SW_OK; - } - else if (chunk->type == SW_CHUNK_SENDFILE) - { - ret = swSocket_onSendfile(socket, chunk); - } - else - { - ret = swSocket_buffer_send(socket); + Socket *socket = ev->socket; + Buffer *buffer = socket->out_buffer; + + while (!Buffer::empty(buffer)) { + BufferChunk *chunk = buffer->front(); + if (chunk->type == BufferChunk::TYPE_CLOSE) { + return reactor->close(reactor, ev->socket); + } else if (chunk->type == BufferChunk::TYPE_SENDFILE) { + ret = socket->handle_sendfile(); + } else { + ret = socket->handle_send(); } - if (ret < 0) - { - if (socket->close_wait) - { - goto _close_fd; - } - else if (socket->send_wait) - { + if (ret < 0) { + if (socket->close_wait) { + return reactor->trigger_close_event(ev); + } else if (socket->send_wait) { return SW_OK; } } } - //remove EPOLLOUT event - if (swBuffer_empty(buffer)) - { - swReactor_remove_write_event(reactor, ev->socket); + if (socket->send_timer) { + swoole_timer_del(socket->send_timer); + socket->send_timer = nullptr; + } + + // remove EPOLLOUT event + if (Buffer::empty(buffer)) { + reactor->remove_write_event(ev->socket); } return SW_OK; } -int swReactor_wait_write_buffer(swReactor *reactor, swSocket *socket) -{ - swEvent event = {}; +void Reactor::drain_write_buffer(Socket *socket) { + Event event = {}; + event.socket = socket; + event.fd = socket->fd; - if (!swBuffer_empty(socket->out_buffer)) - { - swSocket_set_block(socket); - event.socket = socket; - event.fd = socket->fd; - return swReactor_onWrite(reactor, &event); + while (!Buffer::empty(socket->out_buffer)) { + if (socket->wait_event(Socket::default_write_timeout, SW_EVENT_WRITE) == SW_ERR) { + break; + } + _writable_callback(this, &event); + if (socket->close_wait || socket->removed) { + break; + } } - - return SW_OK; } -void swReactor_add_destroy_callback(swReactor *reactor, swCallback cb, void *data) -{ - CallbackManager *cm = (CallbackManager *) reactor->destroy_callbacks; - if (cm == nullptr) - { - cm = new CallbackManager; - reactor->destroy_callbacks = cm; - } - cm->append(cb, data); +void Reactor::add_destroy_callback(Callback cb, void *data) { + destroy_callbacks.append(cb, data); +} + +void Reactor::set_end_callback(enum EndCallback id, const std::function &fn) { + end_callbacks[id] = fn; } -static void defer_task_do(swReactor *reactor) -{ - CallbackManager *cm = (CallbackManager *) reactor->defer_tasks; - reactor->defer_tasks = nullptr; - cm->execute(); - delete cm; +/** + * 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; +} + +void Reactor::defer(Callback cb, void *data) { + if (defer_tasks == nullptr) { + defer_tasks = new CallbackManager; + } + defer_tasks->append(cb, data); } -static void defer_task_add(swReactor *reactor, swCallback callback, void *data) -{ - CallbackManager *cm = (CallbackManager *) reactor->defer_tasks; - if (cm == nullptr) - { - cm = new CallbackManager; - reactor->defer_tasks = cm; +void Reactor::execute_end_callbacks(bool timedout) { + for (auto &kv : end_callbacks) { + kv.second(this); } - cm->append(callback, data); } -void swReactor_destroy(swReactor *reactor) -{ - if (reactor->destroy_callbacks) - { - CallbackManager *cm = (CallbackManager *) reactor->destroy_callbacks; - cm->execute(); - reactor->destroy_callbacks = nullptr; - delete cm; +Reactor::~Reactor() { + destroyed = true; + destroy_callbacks.execute(); + delete impl; + if (swoole_isset_hook(SW_GLOBAL_HOOK_ON_REACTOR_DESTROY)) { + swoole_call_hook(SW_GLOBAL_HOOK_ON_REACTOR_DESTROY, this); } - reactor->free(reactor); } + +} // namespace swoole diff --git a/src/reactor/epoll.cc b/src/reactor/epoll.cc index e48e387ec39..f0b9114523d 100644 --- a/src/reactor/epoll.cc +++ b/src/reactor/epoll.cc @@ -10,13 +10,13 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "swoole.h" - -#define EVENT_DEBUG 0 +#include "swoole_socket.h" +#include "swoole_reactor.h" #ifdef HAVE_EPOLL #include @@ -28,276 +28,209 @@ #error "require linux kernel version 2.6.32 or later" #endif -typedef struct swReactorEpoll_s swReactorEpoll; +namespace swoole { -#if EVENT_DEBUG -#include -static thread_local std::unordered_map event_map; +using network::Socket; -swSocket* swoole_event_map_get(int sockfd) -{ - return event_map[sockfd]; -} -#endif +class ReactorEpoll : public ReactorImpl { + private: + int epfd_; + struct epoll_event *events_ = nullptr; -static int swReactorEpoll_add(swReactor *reactor, swSocket *socket, int events); -static int swReactorEpoll_set(swReactor *reactor, swSocket *socket, int events); -static int swReactorEpoll_del(swReactor *reactor, swSocket *_socket); -static int swReactorEpoll_wait(swReactor *reactor, struct timeval *timeo); -static void swReactorEpoll_free(swReactor *reactor); - -static sw_inline int swReactorEpoll_event_set(int fdtype) -{ - uint32_t flag = 0; - if (swReactor_event_read(fdtype)) - { - flag |= EPOLLIN; - } - if (swReactor_event_write(fdtype)) - { - flag |= EPOLLOUT; - } - if (fdtype & SW_EVENT_ONCE) - { - flag |= EPOLLONESHOT; - } - if (swReactor_event_error(fdtype)) - { - //flag |= (EPOLLRDHUP); - flag |= (EPOLLRDHUP | EPOLLHUP | EPOLLERR); - } - return flag; -} + public: + ReactorEpoll(Reactor *_reactor, int max_events); + ~ReactorEpoll(); + bool ready() override; + int add(Socket *socket, int events) override; + int set(Socket *socket, int events) override; + int del(Socket *socket) override; + int wait(struct timeval *) override; -struct swReactorEpoll_s -{ - int epfd; - struct epoll_event *events; + static inline int get_events(int fdtype) { + int events = 0; + if (Reactor::isset_read_event(fdtype)) { + events |= EPOLLIN; + } + if (Reactor::isset_write_event(fdtype)) { + events |= EPOLLOUT; + } + if (fdtype & SW_EVENT_ONCE) { + events |= EPOLLONESHOT; + } + if (Reactor::isset_error_event(fdtype)) { + events |= (EPOLLRDHUP | EPOLLHUP | EPOLLERR); + } + return events; + } }; -int swReactorEpoll_create(swReactor *reactor, int max_event_num) -{ - swReactorEpoll *object = (swReactorEpoll *) sw_malloc(sizeof(swReactorEpoll)); - if (object == NULL) - { - swWarn("malloc[0] failed"); - return SW_ERR; - } - bzero(object, sizeof(swReactorEpoll)); - reactor->object = object; - reactor->max_event_num = max_event_num; - - object->events = (struct epoll_event *) sw_calloc(max_event_num, sizeof(struct epoll_event)); - if (object->events == NULL) - { - swWarn("malloc[1] failed"); - sw_free(object); - return SW_ERR; - } +ReactorImpl *make_reactor_epoll(Reactor *_reactor, int max_events) { + return new ReactorEpoll(_reactor, max_events); +} - object->epfd = epoll_create(512); - if (object->epfd < 0) - { - swSysWarn("epoll_create failed"); - sw_free(object); - return SW_ERR; +ReactorEpoll::ReactorEpoll(Reactor *_reactor, int max_events) : ReactorImpl(_reactor) { + epfd_ = epoll_create(512); + if (!ready()) { + swoole_sys_warning("epoll_create failed"); + return; } - //binding method - reactor->add = swReactorEpoll_add; - reactor->set = swReactorEpoll_set; - reactor->del = swReactorEpoll_del; - reactor->wait = swReactorEpoll_wait; - reactor->free = swReactorEpoll_free; - return SW_OK; + events_ = new struct epoll_event[max_events]; + reactor_->max_event_num = max_events; + reactor_->native_handle = epfd_; } -static void swReactorEpoll_free(swReactor *reactor) -{ - swReactorEpoll *object = (swReactorEpoll *) reactor->object; - close(object->epfd); - sw_free(object->events); - sw_free(object); +bool ReactorEpoll::ready() { + return epfd_ >= 0; } -static int swReactorEpoll_add(swReactor *reactor, swSocket *socket, int events) -{ - swReactorEpoll *object = (swReactorEpoll *) reactor->object; +ReactorEpoll::~ReactorEpoll() { + if (epfd_ >= 0) { + close(epfd_); + } + delete[] events_; +} + +int ReactorEpoll::add(Socket *socket, int events) { struct epoll_event e; - e.events = swReactorEpoll_event_set(events); + e.events = get_events(events); e.data.ptr = socket; - if (epoll_ctl(object->epfd, EPOLL_CTL_ADD, socket->fd, &e) < 0) - { - swSysWarn("add events[fd=%d#%d, type=%d, events=%d] failed", socket->fd, reactor->id, socket->fdtype, events); + if (epoll_ctl(epfd_, EPOLL_CTL_ADD, socket->fd, &e) < 0) { + 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 - - swReactor_add(reactor, socket, events); - swTraceLog(SW_TRACE_EVENT, "add events[fd=%d#%d, type=%d, events=%d]", socket->fd, reactor->id, socket->fdtype, events); + reactor_->_add(socket, 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; } -static int swReactorEpoll_del(swReactor *reactor, swSocket *_socket) -{ - swReactorEpoll *object = (swReactorEpoll *) reactor->object; - if (epoll_ctl(object->epfd, EPOLL_CTL_DEL, _socket->fd, NULL) < 0) - { - swSysWarn("epoll remove fd[%d#%d] failed", _socket->fd, reactor->id); +int ReactorEpoll::del(Socket *_socket) { + if (_socket->removed) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_EVENT_SOCKET_REMOVED, + "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) { + after_removal_failure(_socket); + if (errno != EBADF && errno != ENOENT) { + return SW_ERR; + } + } -#if EVENT_DEBUG - event_map.erase(_socket->fd); -#endif - - swTraceLog(SW_TRACE_REACTOR, "remove event[reactor_id=%d|fd=%d]", reactor->id, _socket->fd); - swReactor_del(reactor, _socket); + swoole_trace_log(SW_TRACE_REACTOR, "remove event[reactor_id=%d|fd=%d]", reactor_->id, _socket->fd); + reactor_->_del(_socket); return SW_OK; } -static int swReactorEpoll_set(swReactor *reactor, swSocket *socket, int events) -{ - swReactorEpoll *object = (swReactorEpoll *) reactor->object; +int ReactorEpoll::set(Socket *socket, int events) { struct epoll_event e; - int fd = socket->fd; - e.events = swReactorEpoll_event_set(events); + e.events = get_events(events); e.data.ptr = socket; - int ret = epoll_ctl(object->epfd, EPOLL_CTL_MOD, socket->fd, &e); - if (ret < 0) - { - swSysWarn("reactor#%d->set(fd=%d|type=%d|events=%d) failed", reactor->id, fd, socket->fdtype, e.events); + int ret = epoll_ctl(epfd_, EPOLL_CTL_MOD, socket->fd, &e); + if (ret < 0) { + 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, fd, events); - swReactor_set(reactor, socket, 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; } -static int swReactorEpoll_wait(swReactor *reactor, struct timeval *timeo) -{ - swEvent event; - swReactorEpoll *object = (swReactorEpoll *) reactor->object; - swReactor_handler handler; +int ReactorEpoll::wait(struct timeval *timeo) { + Event event; + ReactorHandler handler; int i, n, ret; - int reactor_id = reactor->id; - int epoll_fd = object->epfd; - int max_event_num = reactor->max_event_num; - struct epoll_event *events = object->events; + int reactor_id = reactor_->id; + int max_event_num = reactor_->max_event_num; - if (reactor->timeout_msec == 0) - { - if (timeo == NULL) - { - reactor->timeout_msec = -1; - } - else - { - reactor->timeout_msec = timeo->tv_sec * 1000 + timeo->tv_usec / 1000; + if (reactor_->timeout_msec == 0) { + if (timeo == nullptr) { + reactor_->timeout_msec = -1; + } else { + reactor_->timeout_msec = timeo->tv_sec * 1000 + timeo->tv_usec / 1000; } } - swReactor_before_wait(reactor); + reactor_->before_wait(); - while (reactor->running > 0) - { - if (reactor->onBegin != NULL) - { - reactor->onBegin(reactor); + while (reactor_->running) { + if (reactor_->onBegin != nullptr) { + reactor_->onBegin(reactor_); } - n = epoll_wait(epoll_fd, events, max_event_num, swReactor_get_timeout_msec(reactor)); - if (n < 0) - { - if (swReactor_error(reactor) < 0) - { - swSysWarn("[Reactor#%d] epoll_wait failed", reactor_id); + n = epoll_wait(epfd_, events_, max_event_num, reactor_->get_timeout_msec()); + if (n < 0) { + if (!reactor_->catch_error()) { + swoole_sys_warning("[Reactor#%d] epoll_wait failed", reactor_id); return SW_ERR; - } - else - { + } else { goto _continue; } - } - else if (n == 0) - { - if (reactor->onTimeout) - { - reactor->onTimeout(reactor); - } + } else if (n == 0) { + reactor_->execute_end_callbacks(true); SW_REACTOR_CONTINUE; } - for (i = 0; i < n; i++) - { + for (i = 0; i < n; i++) { event.reactor_id = reactor_id; - event.socket = (swSocket *) events[i].data.ptr; - event.type = event.socket->fdtype; + event.socket = (Socket *) events_[i].data.ptr; + event.type = event.socket->fd_type; event.fd = event.socket->fd; - //read - if ((events[i].events & EPOLLIN) && !event.socket->removed) - { - if (events[i].events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP)) - { - event.socket->event_hup = 1; - } - handler = swReactor_get_handler(reactor, SW_EVENT_READ, event.type); - ret = handler(reactor, &event); - if (ret < 0) - { - swSysWarn("EPOLLIN handle failed. fd=%d", event.fd); + if (events_[i].events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP)) { + event.socket->event_hup = 1; + } + // read + if ((events_[i].events & EPOLLIN) && !event.socket->removed) { + handler = reactor_->get_handler(SW_EVENT_READ, event.type); + ret = handler(reactor_, &event); + if (ret < 0) { + swoole_sys_warning("EPOLLIN handle failed. fd=%d", event.fd); } } - //write - if ((events[i].events & EPOLLOUT) && !event.socket->removed) - { - handler = swReactor_get_handler(reactor, SW_EVENT_WRITE, event.type); - ret = handler(reactor, &event); - if (ret < 0) - { - swSysWarn("EPOLLOUT handle failed. fd=%d", event.fd); + // write + if ((events_[i].events & EPOLLOUT) && !event.socket->removed) { + handler = reactor_->get_handler(SW_EVENT_WRITE, event.type); + ret = handler(reactor_, &event); + if (ret < 0) { + swoole_sys_warning("EPOLLOUT handle failed. fd=%d", event.fd); } } - //error - if ((events[i].events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP)) && !event.socket->removed) - { - //ignore ERR and HUP, because event is already processed at IN and OUT handler. - if ((events[i].events & EPOLLIN) || (events[i].events & EPOLLOUT)) - { + // error + if ((events_[i].events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP)) && !event.socket->removed) { + // ignore ERR and HUP, because event is already processed at IN and OUT handler. + if ((events_[i].events & EPOLLIN) || (events_[i].events & EPOLLOUT)) { continue; } - handler = swReactor_get_handler(reactor, SW_EVENT_ERROR, event.type); - ret = handler(reactor, &event); - if (ret < 0) - { - swSysWarn("EPOLLERR handle failed. fd=%d", event.fd); + handler = reactor_->get_error_handler(event.type); + ret = handler(reactor_, &event); + if (ret < 0) { + swoole_sys_warning("EPOLLERR handle failed. fd=%d", event.fd); } } - if (!event.socket->removed && (event.socket->events & SW_EVENT_ONCE)) - { - swReactor_del(reactor, event.socket); + if (!event.socket->removed && (event.socket->events & SW_EVENT_ONCE)) { + reactor_->_del(event.socket); } } - _continue: - if (reactor->onFinish) - { - reactor->onFinish(reactor); - } + _continue: + reactor_->execute_end_callbacks(false); SW_REACTOR_CONTINUE; } return 0; } - +} // namespace swoole #endif diff --git a/src/reactor/kqueue.cc b/src/reactor/kqueue.cc index 3c54bdf5e6b..98f90226f6d 100644 --- a/src/reactor/kqueue.cc +++ b/src/reactor/kqueue.cc @@ -10,388 +10,327 @@ | to obtain it through the world-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 +#include "swoole_socket.h" +#include "swoole_reactor.h" +#include "swoole_signal.h" -#ifdef IDE_HELPER #ifdef HAVE_KQUEUE -#include -#else -#define USE_KQUEUE_IDE_HELPER + +#ifdef USE_KQUEUE_IDE_HELPER #include "helper/kqueue.h" -#define HAVE_KQUEUE -#endif #else -#ifdef HAVE_KQUEUE #include #endif +#ifdef __NetBSD__ +#include #endif -#ifdef HAVE_KQUEUE +namespace swoole { -typedef struct -{ - int epfd; - int event_max; - struct kevent *events; -} swReactorKqueue; - -static int swReactorKqueue_add(swReactor *reactor, swSocket *socket, int events); -static int swReactorKqueue_set(swReactor *reactor, swSocket *socket, int events); -static int swReactorKqueue_del(swReactor *reactor, swSocket *socket); -static int swReactorKqueue_wait(swReactor *reactor, struct timeval *timeo); -static void swReactorKqueue_free(swReactor *reactor); - -static sw_inline enum swBool_type swReactorKqueue_fetch_event(swReactor *reactor, swEvent *event, void *udata) -{ - event->socket = (swSocket *) udata; - event->fd = event->socket->fd; - event->type = event->socket->fdtype; - event->reactor_id = reactor->id; - - if (event->socket->removed) - { - return SW_FALSE; - } - return SW_TRUE; -} +using network::Socket; + +class ReactorKqueue : public ReactorImpl { + int epfd_; + int event_max_; + struct kevent *events_; + + bool fetch_event(Event *event, void *udata) { + event->socket = (Socket *) udata; + event->fd = event->socket->fd; + event->type = event->socket->fd_type; + event->reactor_id = reactor_->id; -static sw_inline void swReactorKqueue_del_once_socket(swReactor *reactor, swSocket *socket) -{ - if ((socket->events & SW_EVENT_ONCE) && !socket->removed) - { - swReactorKqueue_del(reactor, socket); + if (event->socket->removed) { + return false; + } + return true; } -} -int swReactorKqueue_create(swReactor *reactor, int max_event_num) -{ - //create reactor object - swReactorKqueue *object = (swReactorKqueue *) sw_calloc(1, sizeof(swReactorKqueue)); - if (object == NULL) - { - swWarn("[swReactorKqueueCreate] calloc[0] fail"); - return SW_ERR; + void del_once_socket(Socket *socket) { + if ((socket->events & SW_EVENT_ONCE) && !socket->removed) { + del(socket); + } } - reactor->object = object; - reactor->max_event_num = max_event_num; - object->events = (struct kevent *) sw_calloc(max_event_num, sizeof(struct kevent)); + public: + ReactorKqueue(Reactor *reactor, int max_events); + ~ReactorKqueue(); + bool ready() override; + int add(Socket *socket, int events) override; + int set(Socket *socket, int events) override; + int del(Socket *socket) override; + int wait(struct timeval *) override; +}; + +ReactorImpl *make_reactor_kqueue(Reactor *_reactor, int max_events) { + return new ReactorKqueue(_reactor, max_events); +} - if (object->events == NULL) - { - swWarn("[swReactorKqueueCreate] calloc[1] fail"); - return SW_ERR; - } - //kqueue create - object->event_max = max_event_num; - object->epfd = kqueue(); - if (object->epfd < 0) - { - swWarn("[swReactorKqueueCreate] kqueue_create[0] fail"); - return SW_ERR; +ReactorKqueue::ReactorKqueue(Reactor *reactor, int max_events) : ReactorImpl(reactor) { + epfd_ = kqueue(); + if (epfd_ < 0) { + swoole_warning("[swReactorKqueueCreate] kqueue_create[0] fail"); + return; } - //binding method - reactor->add = swReactorKqueue_add; - reactor->set = swReactorKqueue_set; - reactor->del = swReactorKqueue_del; - reactor->wait = swReactorKqueue_wait; - reactor->free = swReactorKqueue_free; + reactor_->max_event_num = max_events; + reactor_->native_handle = epfd_; + event_max_ = max_events; + events_ = new struct kevent[max_events]; +} - return SW_OK; +bool ReactorKqueue::ready() { + return epfd_ >= 0; } -static void swReactorKqueue_free(swReactor *reactor) -{ - swReactorKqueue *object = (swReactorKqueue *) reactor->object; - close(object->epfd); - sw_free(object->events); - sw_free(object); +ReactorKqueue::~ReactorKqueue() { + if (epfd_ >= 0) { + close(epfd_); + } + delete[] events_; } -static int swReactorKqueue_add(swReactor *reactor, swSocket *socket, int events) -{ - swReactorKqueue *object = (swReactorKqueue *) reactor->object; +int ReactorKqueue::add(Socket *socket, int events) { struct kevent e; int ret; int fd = socket->fd; int fflags = 0; - if (swReactor_event_read(events)) - { +#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); - ret = kevent(object->epfd, &e, 1, NULL, 0, NULL); - if (ret < 0) - { - swSysWarn("add events[fd=%d#%d, type=%d, events=read] failed", fd, reactor->id, socket->fdtype); + EV_SET(&e, fd, EVFILT_READ, EV_ADD, fflags, 0, sobj); + ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr); + if (ret < 0) { + 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 (swReactor_event_write(events)) - { - EV_SET(&e, fd, EVFILT_WRITE, EV_ADD, 0, 0, socket); - ret = kevent(object->epfd, &e, 1, NULL, 0, NULL); - if (ret < 0) - { - swSysWarn("add events[fd=%d#%d, type=%d, events=write] failed", fd, reactor->id, socket->fdtype); + if (Reactor::isset_write_event(events)) { + EV_SET(&e, fd, EVFILT_WRITE, EV_ADD, 0, 0, sobj); + ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr); + if (ret < 0) { + swoole_sys_warning( + "add events_[fd=%d, reactor_id=%d, type=%d, events=write] failed", fd, reactor_->id, socket->fd_type); return SW_ERR; } } - swReactor_add(reactor, socket, events); - swTraceLog(SW_TRACE_EVENT, "[THREAD #%d]EP=%d|FD=%d, events=%d", SwooleTG.id, object->epfd, fd, socket->events); + reactor_->_add(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; } -static int swReactorKqueue_set(swReactor *reactor, swSocket *socket, int events) -{ - swReactorKqueue *object = (swReactorKqueue *) reactor->object; +int ReactorKqueue::set(Socket *socket, int events) { struct kevent e; int ret; int fd = socket->fd; int fflags = 0; - if (swReactor_event_read(events)) - { +#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); - ret = kevent(object->epfd, &e, 1, NULL, 0, NULL); - if (ret < 0) - { - swSysWarn("kqueue->set(%d, SW_EVENT_READ) failed", fd); + EV_SET(&e, fd, EVFILT_READ, EV_ADD, fflags, 0, sobj); + ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr); + if (ret < 0) { + 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); - ret = kevent(object->epfd, &e, 1, NULL, 0, NULL); - if (ret < 0) - { - swSysWarn("kqueue->del(%d, SW_EVENT_READ) failed", fd); + } else { + EV_SET(&e, fd, EVFILT_READ, EV_DELETE, 0, 0, sobj); + ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr); + if (ret < 0) { + swoole_sys_warning("kqueue->del(%d, SW_EVENT_READ) failed", fd); return SW_ERR; } } - if (swReactor_event_write(events)) - { - EV_SET(&e, fd, EVFILT_WRITE, EV_ADD, 0, 0, socket); - ret = kevent(object->epfd, &e, 1, NULL, 0, NULL); - if (ret < 0) - { - swSysWarn("kqueue->set(%d, SW_EVENT_WRITE) failed", fd); + if (Reactor::isset_write_event(events)) { + EV_SET(&e, fd, EVFILT_WRITE, EV_ADD, 0, 0, sobj); + ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr); + if (ret < 0) { + 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); - ret = kevent(object->epfd, &e, 1, NULL, 0, NULL); - if (ret < 0) - { - swSysWarn("kqueue->del(%d, SW_EVENT_WRITE) failed", fd); + } else { + EV_SET(&e, fd, EVFILT_WRITE, EV_DELETE, 0, 0, sobj); + ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr); + if (ret < 0) { + swoole_sys_warning("kqueue->del(%d, SW_EVENT_WRITE) failed", fd); return SW_ERR; } } - swReactor_set(reactor, socket, events); - swTraceLog(SW_TRACE_EVENT, "[THREAD #%d]EP=%d|FD=%d, events=%d", SwooleTG.id, object->epfd, fd, socket->events); + reactor_->_set(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; } -static int swReactorKqueue_del(swReactor *reactor, swSocket *socket) -{ - swReactorKqueue *object = (swReactorKqueue *) reactor->object; +int ReactorKqueue::del(Socket *socket) { struct kevent e; int ret; int fd = socket->fd; - if (socket->events & SW_EVENT_READ) - { - EV_SET(&e, fd, EVFILT_READ, EV_DELETE, 0, 0, socket); - ret = kevent(object->epfd, &e, 1, NULL, 0, NULL); - if (ret < 0) - { - swSysWarn("kqueue->del(%d, SW_EVENT_READ) failed", fd); - return SW_ERR; +#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); + return SW_ERR; + } + + if (socket->events & SW_EVENT_READ) { + EV_SET(&e, fd, EVFILT_READ, EV_DELETE, 0, 0, sobj); + ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr); + if (ret < 0) { + swoole_sys_warning("kqueue->del(%d, SW_EVENT_READ) failed", fd); + if (errno != EBADF && errno != ENOENT) { + return SW_ERR; + } } } - if (socket->events & SW_EVENT_WRITE) - { - EV_SET(&e, fd, EVFILT_WRITE, EV_DELETE, 0, 0, socket); - ret = kevent(object->epfd, &e, 1, NULL, 0, NULL); - if (ret < 0) - { - swSysWarn("kqueue->del(%d, SW_EVENT_WRITE) failed", fd); - return SW_ERR; + if (socket->events & SW_EVENT_WRITE) { + 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); + if (errno != EBADF && errno != ENOENT) { + return SW_ERR; + } } } - swReactor_del(reactor, socket); - swTraceLog(SW_TRACE_EVENT, "[THREAD #%d]EP=%d|FD=%d", SwooleTG.id, object->epfd, fd); + reactor_->_del(socket); + swoole_trace_log(SW_TRACE_EVENT, "[THREAD #%d]epfd=%d, fd=%d", SwooleTG.id, epfd_, fd); return SW_OK; } -static int swReactorKqueue_wait(swReactor *reactor, struct timeval *timeo) -{ - swEvent event; - swReactorKqueue *object = (swReactorKqueue *) reactor->object; - swReactor_handler handler; +int ReactorKqueue::wait(struct timeval *timeo) { + Event event; + ReactorHandler handler; int i, n; struct timespec t = {}; struct timespec *t_ptr; - if (reactor->timeout_msec == 0) - { - if (timeo == NULL) - { - reactor->timeout_msec = -1; - } - else - { - reactor->timeout_msec = timeo->tv_sec * 1000 + timeo->tv_usec / 1000; + if (reactor_->timeout_msec == 0) { + if (timeo == nullptr) { + reactor_->timeout_msec = -1; + } else { + reactor_->timeout_msec = timeo->tv_sec * 1000 + timeo->tv_usec / 1000; } } - swReactor_before_wait(reactor); + reactor_->before_wait(); - while (reactor->running > 0) - { - if (reactor->onBegin != NULL) - { - reactor->onBegin(reactor); + while (reactor_->running) { + if (reactor_->onBegin != nullptr) { + reactor_->onBegin(reactor_); } - if (reactor->timeout_msec > 0) - { - t.tv_sec = reactor->timeout_msec / 1000; - t.tv_nsec = (reactor->timeout_msec - t.tv_sec * 1000) * 1000 * 1000; + if (reactor_->timeout_msec > 0) { + t.tv_sec = reactor_->timeout_msec / 1000; + t.tv_nsec = (reactor_->timeout_msec - t.tv_sec * 1000) * 1000 * 1000; t_ptr = &t; - } - else if (reactor->defer_tasks) - { + } else if (reactor_->defer_tasks) { t.tv_sec = 0; t.tv_nsec = 0; t_ptr = &t; - } - else - { - t_ptr = NULL; + } else { + t_ptr = nullptr; } - n = kevent(object->epfd, NULL, 0, object->events, object->event_max, t_ptr); - if (n < 0) - { - if (swReactor_error(reactor) < 0) - { - swWarn("kqueue[#%d], epfd=%d", reactor->id, object->epfd); + n = ::kevent(epfd_, nullptr, 0, events_, event_max_, t_ptr); + if (n < 0) { + if (!reactor_->catch_error()) { + swoole_warning("kqueue[#%d], epfd=%d", reactor_->id, epfd_); return SW_ERR; - } - else - { + } else { goto _continue; } - } - else if (n == 0) - { - if (reactor->onTimeout) - { - reactor->onTimeout(reactor); - } + } else if (n == 0) { + reactor_->execute_end_callbacks(true); 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 = &object->events[i]; + for (i = 0; i < n; i++) { + struct kevent *kevent = &events_[i]; void *udata = (void *) kevent->udata; - if (!udata) - { + if (!udata) { continue; } - switch (kevent->filter) - { + switch (kevent->filter) { case EVFILT_READ: - case EVFILT_WRITE: - { - if (swReactorKqueue_fetch_event(reactor, &event, udata)) - { - handler = swReactor_get_handler( - reactor, - 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", - kevent->filter == EVFILT_READ ? "read" : "write", - event.fd - ); + case EVFILT_WRITE: { + if (fetch_event(&event, udata)) { + handler = reactor_->get_handler(kevent->filter == EVFILT_READ ? SW_EVENT_READ : SW_EVENT_WRITE, + event.type); + if (sw_unlikely(handler(reactor_, &event) < 0)) { + swoole_sys_warning("kqueue event %s socket#%d handler failed", + kevent->filter == EVFILT_READ ? "read" : "write", + event.fd); } - swReactorKqueue_del_once_socket(reactor, event.socket); + del_once_socket(event.socket); } 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); - } - else - { - swoole_error_log( - SW_LOG_WARNING, SW_ERROR_UNREGISTERED_SIGNAL, - SW_UNREGISTERED_SIGNAL_FMT, - swSignal_str(sw_signal->signo) - ); + case EVFILT_SIGNAL: { + 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, + 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; } } - _continue: - if (reactor->onFinish) - { - reactor->onFinish(reactor); - } + _continue: + reactor_->execute_end_callbacks(false); SW_REACTOR_CONTINUE; } return 0; } +} // namespace swoole #endif diff --git a/src/reactor/poll.cc b/src/reactor/poll.cc index 7f1c5e37f13..1fc15e98a97 100644 --- a/src/reactor/poll.cc +++ b/src/reactor/poll.cc @@ -10,135 +10,106 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ -#include "swoole.h" #include -static int swReactorPoll_add(swReactor *reactor, swSocket *socket, int events); -static int swReactorPoll_set(swReactor *reactor, swSocket *socket, int events); -static int swReactorPoll_del(swReactor *reactor, swSocket *socket); -static int swReactorPoll_wait(swReactor *reactor, struct timeval *timeo); -static void swReactorPoll_free(swReactor *reactor); -static int swReactorPoll_exist(swReactor *reactor, int fd); +#include "swoole.h" +#include "swoole_socket.h" +#include "swoole_reactor.h" + +namespace swoole { + +using network::Socket; -typedef struct _swReactorPoll -{ +class ReactorPoll : public ReactorImpl { uint32_t max_fd_num; - swSocket **fds; - struct pollfd *events; -} swReactorPoll; - -int swReactorPoll_create(swReactor *reactor, int max_fd_num) -{ - //create reactor object - swReactorPoll *object = (swReactorPoll *) sw_malloc(sizeof(swReactorPoll)); - if (object == NULL) - { - swWarn("malloc[0] failed"); - return SW_ERR; - } - bzero(object, sizeof(swReactorPoll)); + Socket **fds_; + struct pollfd *events_; + bool exists(int fd); + + public: + ReactorPoll(Reactor *_reactor, int max_events); + ~ReactorPoll(); + bool ready() override { + return true; + }; + int add(Socket *socket, int events) override; + int set(Socket *socket, int events) override; + int del(Socket *socket) override; + int wait(struct timeval *) override; +}; + +ReactorImpl *make_reactor_poll(Reactor *_reactor, int max_events) { + return new ReactorPoll(_reactor, max_events); +} - object->fds = (swSocket **) sw_calloc(max_fd_num, sizeof(swSocket*)); - if (object->fds == NULL) - { - swWarn("malloc[1] failed"); - sw_free(object); - return SW_ERR; - } - object->events = (struct pollfd *) sw_calloc(max_fd_num, sizeof(struct pollfd)); - if (object->events == NULL) - { - swWarn("malloc[2] failed"); - sw_free(object); - return SW_ERR; - } - object->max_fd_num = max_fd_num; - reactor->max_event_num = max_fd_num; - reactor->object = object; - reactor->add = swReactorPoll_add; - reactor->del = swReactorPoll_del; - reactor->set = swReactorPoll_set; - reactor->wait = swReactorPoll_wait; - reactor->free = swReactorPoll_free; +ReactorPoll::ReactorPoll(Reactor *_reactor, int max_events) : ReactorImpl(_reactor) { + fds_ = new Socket *[max_events]; + events_ = new struct pollfd[max_events]; - return SW_OK; + max_fd_num = max_events; + reactor_->max_event_num = max_events; } -static void swReactorPoll_free(swReactor *reactor) -{ - swReactorPoll *object = (swReactorPoll *) reactor->object; - sw_free(object->fds); - sw_free(reactor->object); +ReactorPoll::~ReactorPoll() { + delete[] fds_; + delete[] events_; } -static int swReactorPoll_add(swReactor *reactor, swSocket *socket, int events) -{ +int ReactorPoll::add(Socket *socket, int events) { int fd = socket->fd; - if (swReactorPoll_exist(reactor, fd)) - { - swWarn("fd#%d is already exists", fd); + if (exists(fd)) { + swoole_warning("fd#%d is already exists", fd); return SW_ERR; } - swReactorPoll *object = (swReactorPoll *) reactor->object; - int cur = reactor->event_num; - if (reactor->event_num == object->max_fd_num) - { - swWarn("too many connection, more than %d", object->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; } - swReactor_add(reactor, socket, events); + reactor_->_add(socket, events); - swTrace("fd=%d, events=%d", fd, events); + swoole_trace("fd=%d, events=%d", fd, events); - object->fds[cur] = socket; - object->events[cur].fd = fd; - object->events[cur].events = 0; + fds_[cur] = socket; + events_[cur].fd = fd; + events_[cur].events = 0; - if (swReactor_event_read(events)) - { - object->events[cur].events |= POLLIN; + if (Reactor::isset_read_event(events)) { + events_[cur].events |= POLLIN; } - if (swReactor_event_write(events)) - { - object->events[cur].events |= POLLOUT; + if (Reactor::isset_write_event(events)) { + events_[cur].events |= POLLOUT; } - if (swReactor_event_error(events)) - { - object->events[cur].events |= POLLHUP; + if (Reactor::isset_error_event(events)) { + events_[cur].events |= POLLHUP; } return SW_OK; } -static int swReactorPoll_set(swReactor *reactor, swSocket *socket, int events) -{ +int ReactorPoll::set(Socket *socket, int events) { uint32_t i; - swReactorPoll *object = (swReactorPoll *) reactor->object; - - swTrace("fd=%d, events=%d", socket->fd, events); - - for (i = 0; i < reactor->event_num; i++) - { - //found - if (object->events[i].fd == socket->fd) - { - object->events[i].events = 0; - if (swReactor_event_read(events)) - { - object->events[i].events |= POLLIN; + + swoole_trace("fd=%d, events=%d", socket->fd, events); + + for (i = 0; i < reactor_->get_event_num(); i++) { + // found + if (events_[i].fd == socket->fd) { + events_[i].events = 0; + if (Reactor::isset_read_event(events)) { + events_[i].events |= POLLIN; } - if (swReactor_event_write(events)) - { - object->events[i].events |= POLLOUT; + if (Reactor::isset_write_event(events)) { + events_[i].events |= POLLOUT; } - //execute parent method - swReactor_set(reactor, socket, events); + // execute parent method + reactor_->_set(socket, events); return SW_OK; } } @@ -146,159 +117,125 @@ static int swReactorPoll_set(swReactor *reactor, swSocket *socket, int events) return SW_ERR; } -static int swReactorPoll_del(swReactor *reactor, swSocket *socket) -{ - uint32_t i; - swReactorPoll *object = (swReactorPoll *) reactor->object; - - for (i = 0; i < reactor->event_num; i++) - { - if (object->events[i].fd == socket->fd) - { - for (; i < reactor->event_num; i++) - { - if (i == reactor->event_num) - { - object->fds[i] = NULL; - object->events[i].fd = 0; - object->events[i].events = 0; - } - else - { - object->fds[i] = object->fds[i + 1]; - object->events[i] = object->events[i + 1]; +int ReactorPoll::del(Socket *socket) { + if (socket->removed) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_EVENT_SOCKET_REMOVED, + "failed to delete event[%d], it has already been removed", + socket->fd); + return SW_ERR; + } + + for (uint32_t i = 0; i < reactor_->get_event_num(); i++) { + if (events_[i].fd == socket->fd) { + for (; i < reactor_->get_event_num(); i++) { + if (i == reactor_->get_event_num()) { + fds_[i] = nullptr; + events_[i].fd = 0; + events_[i].events = 0; + } else { + fds_[i] = fds_[i + 1]; + events_[i] = events_[i + 1]; } } - swReactor_del(reactor, socket); + reactor_->_del(socket); return SW_OK; } } + return SW_ERR; } -static int swReactorPoll_wait(swReactor *reactor, struct timeval *timeo) -{ - swReactorPoll *object = (swReactorPoll *) reactor->object; - swEvent event; - swReactor_handler handler; +int ReactorPoll::wait(struct timeval *timeo) { + Event event; + ReactorHandler handler; int ret; - if (reactor->timeout_msec == 0) - { - if (timeo == NULL) - { - reactor->timeout_msec = -1; - } - else - { - reactor->timeout_msec = timeo->tv_sec * 1000 + timeo->tv_usec / 1000; + if (reactor_->timeout_msec == 0) { + if (timeo == nullptr) { + reactor_->timeout_msec = -1; + } else { + reactor_->timeout_msec = timeo->tv_sec * 1000 + timeo->tv_usec / 1000; } } - swReactor_before_wait(reactor); + reactor_->before_wait(); - while (reactor->running > 0) - { - if (reactor->onBegin != NULL) - { - reactor->onBegin(reactor); + while (reactor_->running) { + if (reactor_->onBegin != nullptr) { + reactor_->onBegin(reactor_); } - ret = poll(object->events, reactor->event_num, swReactor_get_timeout_msec(reactor)); - if (ret < 0) - { - if (swReactor_error(reactor) < 0) - { - swSysWarn("poll error"); + ret = poll(events_, reactor_->get_event_num(), reactor_->get_timeout_msec()); + if (ret < 0) { + if (!reactor_->catch_error()) { + swoole_sys_warning("poll error"); break; - } - else - { + } else { goto _continue; } - } - else if (ret == 0) - { - if (reactor->onTimeout) - { - reactor->onTimeout(reactor); - } + } else if (ret == 0) { + reactor_->execute_end_callbacks(true); SW_REACTOR_CONTINUE; - } - else - { - for (uint32_t i = 0; i < reactor->event_num; i++) - { - event.socket = object->fds[i]; - event.fd = object->events[i].fd; - event.reactor_id = reactor->id; - event.type = event.socket->fdtype; - - swTrace("Event: fd=%d|reactor_id=%d|type=%d", event.fd, reactor->id, event.type); - //in - if ((object->events[i].revents & POLLIN) && !event.socket->removed) - { - if (object->events[i].revents & (POLLHUP | POLLERR)) - { - event.socket->event_hup = 1; - } - handler = swReactor_get_handler(reactor, SW_EVENT_READ, event.type); - ret = handler(reactor, &event); - if (ret < 0) - { - swSysWarn("poll[POLLIN] handler failed. fd=%d", event.fd); + } else { + 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; + event.type = event.socket->fd_type; + + if (events_[i].revents & (POLLHUP | POLLERR)) { + event.socket->event_hup = 1; + } + + 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) { + swoole_sys_warning("poll[POLLIN] handler failed. fd=%d", event.fd); } } - //out - if ((object->events[i].revents & POLLOUT) && !event.socket->removed) - { - handler = swReactor_get_handler(reactor, SW_EVENT_WRITE, event.type); - ret = handler(reactor, &event); - if (ret < 0) - { - swSysWarn("poll[POLLOUT] handler failed. fd=%d", event.fd); + // out + if ((events_[i].revents & POLLOUT) && !event.socket->removed) { + handler = reactor_->get_handler(SW_EVENT_WRITE, event.type); + ret = handler(reactor_, &event); + if (ret < 0) { + swoole_sys_warning("poll[POLLOUT] handler failed. fd=%d", event.fd); } } - //error - if ((object->events[i].revents & (POLLHUP | POLLERR)) && !event.socket->removed) - { - //ignore ERR and HUP, because event is already processed at IN and OUT handler. - if ((object->events[i].revents & POLLIN) || (object->events[i].revents & POLLOUT)) - { + // error + if ((events_[i].revents & (POLLHUP | POLLERR)) && !event.socket->removed) { + // ignore ERR and HUP, because event is already processed at IN and OUT handler. + if ((events_[i].revents & POLLIN) || (events_[i].revents & POLLOUT)) { continue; } - handler = swReactor_get_handler(reactor, SW_EVENT_ERROR, event.type); - ret = handler(reactor, &event); - if (ret < 0) - { - swSysWarn("poll[POLLERR] handler failed. fd=%d", event.fd); + handler = reactor_->get_error_handler(event.type); + ret = handler(reactor_, &event); + if (ret < 0) { + swoole_sys_warning("poll[POLLERR] handler failed. fd=%d", event.fd); } } - if (!event.socket->removed && (event.socket->events & SW_EVENT_ONCE)) - { - swReactorPoll_del(reactor, event.socket); + if (!event.socket->removed && (event.socket->events & SW_EVENT_ONCE)) { + del(event.socket); } } } - _continue: - if (reactor->onFinish) - { - reactor->onFinish(reactor); - } + _continue: + reactor_->execute_end_callbacks(false); SW_REACTOR_CONTINUE; } return SW_OK; } -static int swReactorPoll_exist(swReactor *reactor, int fd) -{ - swReactorPoll *object = (swReactorPoll *) reactor->object; - for (uint32_t i = 0; i < reactor->event_num; i++) - { - if (object->events[i].fd == fd) - { - return SW_TRUE; +bool ReactorPoll::exists(int fd) { + for (uint32_t i = 0; i < reactor_->get_event_num(); i++) { + if (events_[i].fd == fd) { + return true; } } - return SW_FALSE; + return false; } + +} // namespace swoole diff --git a/src/reactor/select.cc b/src/reactor/select.cc index 308db404bed..71ec4d146c5 100644 --- a/src/reactor/select.cc +++ b/src/reactor/select.cc @@ -10,253 +10,212 @@ | to obtain it through the world-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_reactor.h" #include + #include -struct swReactorSelect -{ +namespace swoole { + +using network::Socket; + +class ReactorSelect : public ReactorImpl { fd_set rfds; fd_set wfds; fd_set efds; - std::unordered_map fds; + std::unordered_map fds; int maxfd; - swReactorSelect() - { - maxfd = 0; + public: + ReactorSelect(Reactor *reactor); + ~ReactorSelect() {} + bool ready() override { + return true; } + int add(Socket *socket, int events) override; + int set(Socket *socket, int events) override; + int del(Socket *socket) override; + int wait(struct timeval *) override; }; -#define SW_FD_SET(fd, set) do{ if (fdobject = object; - //binding method - reactor->add = swReactorSelect_add; - reactor->set = swReactorSelect_set; - reactor->del = swReactorSelect_del; - reactor->wait = swReactorSelect_wait; - reactor->free = swReactorSelect_free; +#define SW_FD_ISSET(fd, set) ((fd < FD_SETSIZE) && FD_ISSET(fd, set)) - return SW_OK; +ReactorImpl *make_reactor_select(Reactor *_reactor) { + return new ReactorSelect(_reactor); } -void swReactorSelect_free(swReactor *reactor) -{ - swReactorSelect *object = (swReactorSelect *) reactor->object; - delete object; - sw_free(reactor->object); +ReactorSelect::ReactorSelect(Reactor *reactor) : ReactorImpl(reactor) { + maxfd = 0; } -int swReactorSelect_add(swReactor *reactor, swSocket *socket, int events) -{ +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); + if (fd > FD_SETSIZE) { + swoole_warning("max fd value is FD_SETSIZE(%d).\n", FD_SETSIZE); return SW_ERR; } - swReactorSelect *object = (swReactorSelect *) reactor->object; - swReactor_add(reactor, socket, events); - object->fds.emplace(fd, socket); - if (fd > object->maxfd) - { - object->maxfd = fd; + reactor_->_add(socket, events); + fds.emplace(fd, socket); + if (fd > maxfd) { + maxfd = fd; } return SW_OK; } -int swReactorSelect_del(swReactor *reactor, swSocket *socket) -{ - swReactorSelect *object = (swReactorSelect *) reactor->object; +int ReactorSelect::del(Socket *socket) { + if (socket->removed) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_EVENT_SOCKET_REMOVED, + "failed to delete event[%d], it has already been removed", + socket->fd); + return SW_ERR; + } int fd = socket->fd; - if (object->fds.erase(fd) == 0) - { - swWarn("swReactorSelect: fd[%d] not found", fd); + if (fds.erase(fd) == 0) { + swoole_warning("swReactorSelect: fd[%d] not found", fd); return SW_ERR; } - SW_FD_CLR(fd, &object->rfds); - SW_FD_CLR(fd, &object->wfds); - SW_FD_CLR(fd, &object->efds); - swReactor_del(reactor, socket); + SW_FD_CLR(fd, &rfds); + SW_FD_CLR(fd, &wfds); + SW_FD_CLR(fd, &efds); + reactor_->_del(socket); return SW_OK; } -int swReactorSelect_set(swReactor *reactor, swSocket *socket, int events) -{ - swReactorSelect *object = (swReactorSelect *) reactor->object; - auto i = object->fds.find(socket->fd); - if (i == object->fds.end()) - { - swWarn("swReactorSelect: sock[%d] not found", socket->fd); +int ReactorSelect::set(Socket *socket, int events) { + auto i = fds.find(socket->fd); + if (i == fds.end()) { + swoole_warning("swReactorSelect: sock[%d] not found", socket->fd); return SW_ERR; } - swReactor_set(reactor, socket, events); + reactor_->_set(socket, events); return SW_OK; } -int swReactorSelect_wait(swReactor *reactor, struct timeval *timeo) -{ - swReactorSelect *object = (swReactorSelect *) reactor->object; - swEvent event; - swReactor_handler handler; +int ReactorSelect::wait(struct timeval *timeo) { + Event event; + ReactorHandler handler; struct timeval timeout; int ret; - if (reactor->timeout_msec == 0) - { - if (timeo == NULL) - { - reactor->timeout_msec = -1; - } - else - { - reactor->timeout_msec = timeo->tv_sec * 1000 + timeo->tv_usec / 1000; + if (reactor_->timeout_msec == 0) { + if (timeo == nullptr) { + reactor_->timeout_msec = -1; + } else { + reactor_->timeout_msec = timeo->tv_sec * 1000 + timeo->tv_usec / 1000; } } - swReactor_before_wait(reactor); + reactor_->before_wait(); - while (reactor->running > 0) - { - FD_ZERO(&(object->rfds)); - FD_ZERO(&(object->wfds)); - FD_ZERO(&(object->efds)); + while (reactor_->running) { + FD_ZERO(&(rfds)); + FD_ZERO(&(wfds)); + FD_ZERO(&(efds)); - if (reactor->onBegin != NULL) - { - reactor->onBegin(reactor); + if (reactor_->onBegin != nullptr) { + reactor_->onBegin(reactor_); } - for (auto i = object->fds.begin(); i != object->fds.end(); i++) - { + for (auto i = fds.begin(); i != fds.end(); i++) { int fd = i->first; int events = i->second->events; - if (swReactor_event_read(events)) - { - SW_FD_SET(fd, &(object->rfds)); + if (Reactor::isset_read_event(events)) { + SW_FD_SET(fd, &(rfds)); } - if (swReactor_event_write(events)) - { - SW_FD_SET(fd, &(object->wfds)); + if (Reactor::isset_write_event(events)) { + SW_FD_SET(fd, &(wfds)); } - if (swReactor_event_error(events)) - { - SW_FD_SET(fd, &(object->efds)); + if (Reactor::isset_error_event(events)) { + SW_FD_SET(fd, &(efds)); } } - if (reactor->timeout_msec < 0) - { + if (reactor_->timeout_msec < 0) { timeout.tv_sec = UINT_MAX; timeout.tv_usec = 0; - } - else if (reactor->defer_tasks) - { + } else if (reactor_->defer_tasks) { timeout.tv_sec = 0; timeout.tv_usec = 0; - } - else - { - timeout.tv_sec = reactor->timeout_msec / 1000; - timeout.tv_usec = reactor->timeout_msec - timeout.tv_sec * 1000; + } else { + timeout.tv_sec = reactor_->timeout_msec / 1000; + timeout.tv_usec = reactor_->timeout_msec - timeout.tv_sec * 1000; } - ret = select(object->maxfd + 1, &(object->rfds), &(object->wfds), &(object->efds), &timeout); - if (ret < 0) - { - if (swReactor_error(reactor) < 0) - { - swSysWarn("select error"); + ret = select(maxfd + 1, &(rfds), &(wfds), &(efds), &timeout); + if (ret < 0) { + if (!reactor_->catch_error()) { + swoole_sys_warning("select error"); break; - } - else - { + } else { goto _continue; } - } - else if (ret == 0) - { - if (reactor->onTimeout) - { - reactor->onTimeout(reactor); - } + } else if (ret == 0) { + reactor_->execute_end_callbacks(true); SW_REACTOR_CONTINUE; - } - else - { - for (int fd = 0; fd <= object->maxfd; fd++) - { - auto i = object->fds.find(fd); - if (i == object->fds.end()) - { + } else { + for (int fd = 0; fd <= maxfd; fd++) { + auto i = fds.find(fd); + if (i == fds.end()) { continue; } event.socket = i->second; event.fd = event.socket->fd; - event.reactor_id = reactor->id; - event.type = event.socket->fdtype; - - //read - if (SW_FD_ISSET(event.fd, &(object->rfds)) && !event.socket->removed) - { - handler = swReactor_get_handler(reactor, 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); + event.reactor_id = reactor_->id; + event.type = event.socket->fd_type; + + // read + if (SW_FD_ISSET(event.fd, &(rfds)) && !event.socket->removed) { + handler = reactor_->get_handler(SW_EVENT_READ, event.type); + ret = handler(reactor_, &event); + if (ret < 0) { + swoole_sys_warning("[Reactor#%d] select event[type=READ, fd=%d] handler fail", reactor_->id, event.fd); } } - //write - if (SW_FD_ISSET(event.fd, &(object->wfds)) && !event.socket->removed) - { - handler = swReactor_get_handler(reactor, 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); + // write + if (SW_FD_ISSET(event.fd, &(wfds)) && !event.socket->removed) { + handler = reactor_->get_handler(SW_EVENT_WRITE, event.type); + ret = handler(reactor_, &event); + if (ret < 0) { + swoole_sys_warning("[Reactor#%d] select event[type=WRITE, fd=%d] handler fail", reactor_->id, event.fd); } } - //error - if (SW_FD_ISSET(event.fd, &(object->efds)) && !event.socket->removed) - { - handler = swReactor_get_handler(reactor, 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); + // error + if (SW_FD_ISSET(event.fd, &(efds)) && !event.socket->removed) { + handler = reactor_->get_handler(SW_EVENT_ERROR, event.type); + ret = handler(reactor_, &event); + if (ret < 0) { + 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)) - { - swReactorSelect_del(reactor, event.socket); + if (!event.socket->removed && (event.socket->events & SW_EVENT_ONCE)) { + del(event.socket); } } } - _continue: - if (reactor->onFinish) - { - reactor->onFinish(reactor); - } + _continue: + reactor_->execute_end_callbacks(false); SW_REACTOR_CONTINUE; } return SW_OK; } + +} // namespace swoole diff --git a/src/server/base.cc b/src/server/base.cc index 9dd51ca4e1c..5655186ddaa 100644 --- a/src/server/base.cc +++ b/src/server/base.cc @@ -10,186 +10,234 @@ | to obtain it through the world-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 "server.h" - -static int swFactory_start(swFactory *factory); -static int swFactory_shutdown(swFactory *factory); -static int swFactory_dispatch(swFactory *factory, swSendData *req); -static int swFactory_notify(swFactory *factory, swDataHead *event); -static int swFactory_end(swFactory *factory, int fd); -static void swFactory_free(swFactory *factory); - -int swFactory_create(swFactory *factory) -{ - factory->dispatch = swFactory_dispatch; - factory->finish = swFactory_finish; - factory->start = swFactory_start; - factory->shutdown = swFactory_shutdown; - factory->end = swFactory_end; - factory->notify = swFactory_notify; - factory->free = swFactory_free; - - return SW_OK; +#include "swoole_server.h" + +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; } -static int swFactory_start(swFactory *factory) -{ - SwooleWG.run_always = 1; - return SW_OK; +BaseFactory::BaseFactory(Server *server) : Factory(server) {} + +BaseFactory::~BaseFactory() {} + +bool BaseFactory::start() { + return true; } -static int swFactory_shutdown(swFactory *factory) -{ - return SW_OK; +bool BaseFactory::shutdown() { + return true; } -static int swFactory_dispatch(swFactory *factory, swSendData *task) -{ - swServer *serv = (swServer *) factory->ptr; - swPacket_ptr pkg; - - if (swEventData_is_stream(task->info.type)) - { - swConnection *conn = swServer_connection_get(serv, task->info.fd); - if (conn == NULL || conn->active == 0) - { - swWarn("dispatch[type=%d] failed, connection#%d is not active", task->info.type, task->info.fd); - return SW_ERR; +bool BaseFactory::dispatch(SendData *task) { + Connection *conn = nullptr; + + if (Server::is_stream_event(task->info.type)) { + conn = server_->get_connection(task->info.fd); + if (conn == nullptr || conn->active == 0) { + 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, connection#%d is closed by server", task->info.type, task->info.fd); - return SW_OK; + // server active close, discard data. + if (conn->closed) { + 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 + // 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; - swString_clear(&pkg.data); - pkg.data.length = task->info.len; - pkg.data.str = task->data; - return swWorker_onTask(factory, (swEventData *) &pkg); - } - //no data - else - { - return swWorker_onTask(factory, (swEventData *) &task->info); + 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) { + task->info.flags |= SW_EVENT_DATA_POP_PTR; + } } + + auto bus = server_->get_worker_message_bus(); + bus->pass(task); + server_->worker_accept_event(&bus->get_buffer()->info); + + return true; } /** * only stream fd */ -static int swFactory_notify(swFactory *factory, swDataHead *info) -{ - swServer *serv = (swServer *) factory->ptr; - swConnection *conn = swServer_connection_get(serv, info->fd); - if (conn == NULL || conn->active == 0) - { - swWarn("dispatch[type=%d] failed, connection#%d is not active", info->type, info->fd); - return SW_ERR; - } - //server active close, discard data. - if (conn->closed) - { - swWarn("dispatch[type=%d] failed, connection#%d is closed by server", info->type, info->fd); - return SW_OK; - } - //converted fd to session_id +bool BaseFactory::notify(DataHead *info) { + Connection *conn = server_->get_connection(info->fd); + if (conn == nullptr || conn->active == 0) { + 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) { + swoole_warning("dispatch[type=%d] failed, session#%ld is closed by server", info->type, conn->session_id); + return false; + } + // converted fd to session_id info->fd = conn->session_id; info->server_fd = conn->server_fd; info->flags = SW_EVENT_DATA_NORMAL; - return swWorker_onTask(factory, (swEventData *) info); -} + server_->worker_accept_event(info); -static int swFactory_end(swFactory *factory, int fd) -{ - swServer *serv = (swServer *) factory->ptr; - swSendData _send; - swDataHead info; + return true; +} - bzero(&_send, sizeof(_send)); - _send.info.fd = fd; +bool BaseFactory::end(SessionId session_id, int flags) { + SendData _send{}; + _send.info.fd = session_id; _send.info.len = 0; _send.info.type = SW_SERVER_EVENT_CLOSE; + _send.info.reactor_id = swoole_get_process_id(); + + Session *session = server_->get_session(session_id); + if (!session->fd) { + swoole_error_log(SW_LOG_TRACE, + SW_ERROR_SESSION_NOT_EXIST, + "failed to close connection, session#%ld does not exist", + session_id); + return false; + } - swConnection *conn = swWorker_get_connection(serv, fd); - if (conn == NULL || conn->active == 0) - { - //swWarn("can not close. Connection[%d] not found", _send.info.fd); - return SW_ERR; - } - else if (conn->close_force) - { - goto _do_close; - } - else if (conn->closing) - { - swWarn("The connection[%d] is closing", fd); - return SW_ERR; - } - else if (conn->closed) - { - return SW_ERR; - } - else - { - _do_close: - conn->closing = 1; - if (serv->onClose != NULL) - { - info.fd = fd; - if (conn->close_actively) - { - info.reactor_id = -1; - } - else - { - info.reactor_id = conn->reactor_id; - } - info.server_fd = conn->server_fd; - serv->onClose(serv, &info); - } - conn->closing = 0; - conn->closed = 1; - conn->close_errno = 0; - - if (swBuffer_empty(conn->socket->out_buffer) || conn->peer_closed) - { - swReactor *reactor = SwooleTG.reactor; - return swReactorThread_close(reactor, conn->socket); - } - else - { - swBuffer_chunk *chunk = swBuffer_new_chunk(conn->socket->out_buffer, SW_CHUNK_CLOSE, 0); - chunk->store.data.val1 = _send.info.type; - conn->close_queued = 1; - return SW_OK; + if (server_->if_forward_message(session)) { + swoole_trace_log(SW_TRACE_SERVER, + "session_id=%ld, fd=%d, session->reactor_id=%d", + session_id, + session->fd, + session->reactor_id); + return forward_message(session, &_send); + } + + Connection *conn = server_->get_connection_verify_no_ssl(session_id); + if (conn == nullptr) { + swoole_set_last_error(SW_ERROR_SESSION_NOT_EXIST); + return false; + } + // Reset send buffer, Immediately close the connection. + if (flags & Server::CLOSE_RESET) { + conn->close_reset = 1; + } + // Server is initiative to close the connection + if (flags & Server::CLOSE_ACTIVELY) { + conn->close_actively = 1; + } + + if (conn->closing) { + swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSING, "session#%ld is closing", session_id); + return false; + } else if (!(conn->close_force || conn->close_reset) && conn->closed) { + swoole_error_log(SW_LOG_TRACE, SW_ERROR_SESSION_CLOSED, "session#%ld is closed", session_id); + return false; + } + + conn->closing = 1; + if (server_->onClose != nullptr && !conn->closed) { + 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; + network::Socket *_socket = conn->socket; + + if (_socket == nullptr) { + swoole_warning("session#%ld->socket is nullptr", session_id); + return false; } -} -/** - * @return: success returns SW_OK, failure returns SW_ERR. - */ -int swFactory_finish(swFactory *factory, swSendData *resp) -{ - return swServer_master_send((swServer *) factory->ptr, resp); + 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; + } } -static void swFactory_free(swFactory *factory) -{ +bool BaseFactory::finish(SendData *data) { + SessionId session_id = data->info.fd; + + Session *session = server_->get_session(session_id); + if (server_->if_forward_message(session)) { + swoole_trace_log(SW_TRACE_SERVER, + "session_id=%ld, fd=%d, session->reactor_id=%d", + session_id, + session->fd, + session->reactor_id); + + if (data->info.type == SW_SERVER_EVENT_SEND_DATA || data->info.type == SW_SERVER_EVENT_SEND_FILE) { + return forward_message(session, data); + } else { + swoole_warning("unknown event type[%d]", data->info.type); + return false; + } + } else { + return server_->send_to_connection(data) == SW_OK; + } +} +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 5640c6ad001..5bdea0ec9fc 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -10,629 +10,438 @@ | to obtain it through the world-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 "server.h" +#include "swoole_server.h" +#include "swoole_util.h" #include #include -#include -#ifdef __linux__ +#if defined(__linux__) #include +#elif defined(__FreeBSD__) +#include #endif -struct swManagerProcess -{ - bool reloading; +namespace swoole { + +using ReloadWorkerList = std::unordered_map; + +struct Manager { 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; - swWorker *reload_workers; + pid_t reload_worker_pid; + Server *server_; std::vector kill_workers; -}; -typedef std::unordered_map reload_list_t; + void wait(Server *_server); + void add_timeout_killer(Worker *workers, int n); + void terminate_all_worker(); -static int swManager_loop(swServer *serv); -static void swManager_signal_handler(int sig); -static void swManager_check_exit_status(swServer *serv, int worker_id, pid_t pid, int status); - -static swManagerProcess ManagerProcess; + static void signal_handler(int sig); + static void timer_callback(Timer *timer, TimerNode *tnode); + static void kill_timeout_process(Timer *timer, TimerNode *tnode); +}; -static void swManager_onTimer(swTimer *timer, swTimer_node *tnode) -{ - swServer *serv = (swServer *) tnode->data; - if (serv->hooks[SW_SERVER_HOOK_MANAGER_TIMER]) - { - swServer_call_hook(serv, SW_SERVER_HOOK_MANAGER_TIMER, serv); +void Manager::timer_callback(Timer *timer, TimerNode *tnode) { + Server *serv = (Server *) tnode->data; + if (serv->isset_hook(Server::HOOK_MANAGER_TIMER)) { + serv->call_hook(Server::HOOK_MANAGER_TIMER, serv); } } -static void swManager_kill_timeout_process(swTimer *timer, swTimer_node *tnode) -{ - reload_list_t *_list = (reload_list_t *) tnode->data; +void Manager::kill_timeout_process(Timer *timer, TimerNode *tnode) { + ReloadWorkerList *_list = (ReloadWorkerList *) tnode->data; - for (auto i = _list->begin(); i!= _list->end(); i++) - { + for (auto i = _list->begin(); i != _list->end(); i++) { pid_t pid = i->second; uint32_t worker_id = i->first; - if (swoole_kill(pid, 0) == -1) - { + if (swoole_kill(pid, 0) == -1) { continue; } - if (swoole_kill(pid, SIGKILL) < 0) - { - swSysWarn("swKill(%d, SIGKILL) [%d] failed", pid, i); - } - else - { - swoole_error_log( - SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT, - "[Manager] Worker#%d[pid=%d] exit timeout, forced kill", worker_id, pid - ); + if (swoole_kill(pid, SIGKILL) < 0) { + swoole_sys_warning("kill(%d, SIGKILL) [%u] failed", pid, worker_id); + } else { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT, + "worker(pid=%d, id=%d) exit timeout, force kill the process", + pid, + worker_id); } } errno = 0; - delete(_list); + delete (_list); } -static void swManager_add_timeout_killer(swServer *serv, swWorker *workers, int n) -{ - if (!serv->max_wait_time) - { +void Manager::add_timeout_killer(Worker *workers, int n) { + if (!server_->max_wait_time) { return; } /** * separate old workers, free memory in the timer */ - reload_list_t *_list = new reload_list_t; - for (int i = 0; i < n; i++) - { + ReloadWorkerList *_list = new ReloadWorkerList(); + SW_LOOP_N(n) { _list->emplace(workers[i].id, workers[i].pid); } /** * Multiply max_wait_time by 2 to prevent conflict with worker */ - swoole_timer_after((long) (serv->max_wait_time * 2 * 1000), swManager_kill_timeout_process, _list); + swoole_timer_after((long) (server_->max_wait_time * 2 * 1000), kill_timeout_process, _list); } -/** - * fork task worker processes - */ -static sw_inline int swManager_spawn_task_workers(swServer *serv) -{ - if (serv->task_worker_num > 0) - { - return swProcessPool_start(&serv->gs->task_workers); +int Server::start_manager_process() { + SW_LOOP_N(worker_num) { + create_worker(get_worker(i)); } - return SW_OK; -} - -/** - * fork worker processes - */ -static sw_inline int swManager_spawn_workers(swServer *serv) -{ - pid_t pid; - - for (uint32_t i = 0; i < serv->worker_num; i++) - { - pid = swManager_spawn_worker(serv, &serv->workers[i]); - if (pid < 0) - { - swError("fork() failed"); - return SW_ERR; - } - else - { - serv->workers[i].pid = pid; - } + if (gs->event_workers.create_message_box(SW_MESSAGE_BOX_SIZE) == SW_ERR) { + return SW_ERR; } - return SW_OK; -} - -/** - * fork user worker processes - */ -static sw_inline int swManager_spawn_user_workers(swServer *serv) -{ - pid_t pid; - - if (serv->user_worker_list) - { - for (auto worker : *serv->user_worker_list) - { - if (worker->pipe_object) - { - swServer_store_pipe_fd(serv, worker->pipe_object); - } - pid = swManager_spawn_user_worker(serv, worker); - if (pid < 0) - { - return SW_ERR; - } - } + if (task_worker_num > 0 && create_task_workers() < 0) { + return SW_ERR; } - return SW_OK; -} + if (get_user_worker_num() > 0 && create_user_workers() < 0) { + return SW_ERR; + } -//create worker child proccess -int swManager_start(swServer *serv) -{ - uint32_t i; - pid_t pid; + auto fn = [this](void) { + swoole_set_process_type(SW_PROCESS_MANAGER); + gs->manager_pid = SwooleG.pid = getpid(); - if (serv->task_worker_num > 0) - { - if (swServer_create_task_workers(serv) < 0) - { - return SW_ERR; - } - swTaskWorker_init(serv); - - swWorker *worker; - for (i = 0; i < serv->task_worker_num; i++) - { - worker = &serv->gs->task_workers.workers[i]; - if (swServer_worker_create(serv, worker) < 0) - { - return SW_ERR; - } - if (serv->task_ipc_mode == SW_TASK_IPC_UNIXSOCK) - { - swServer_store_pipe_fd(serv, worker->pipe_object); + if (task_worker_num > 0) { + if (gs->task_workers.start() == SW_ERR) { + swoole_sys_error("failed to start task worker"); + return; } } - } - //User Worker Process - if (serv->user_worker_num > 0) - { - if (swServer_create_user_workers(serv) < 0) - { - return SW_ERR; + SW_LOOP_N(worker_num) { + Worker *worker = get_worker(i); + if (factory->spawn_event_worker(worker) < 0) { + swoole_sys_error("failed to fork event worker"); + return; + } } - i = 0; - for (auto worker : *serv->user_worker_list) - { - memcpy(&serv->user_workers[i], worker, sizeof(swWorker)); - if (swServer_worker_create(serv, &serv->user_workers[i]) < 0) - { - 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; + } } - i++; } - } - serv->message_box = swChannel_new(65536, sizeof(swWorkerStopMessage), SW_CHAN_LOCK | SW_CHAN_SHM); - if (serv->message_box == NULL) - { - return SW_ERR; - } + Manager manager{}; + manager.wait(this); + }; - pid = swoole_fork(0); - switch (pid) - { - //fork manager process - case 0: - //wait master process - SW_START_SLEEP; - if (!serv->gs->start) - { - return SW_OK; - } - swServer_close_port(serv, SW_TRUE); - - if (swManager_spawn_task_workers(serv) < 0) - { - return SW_ERR; - } - if (swManager_spawn_workers(serv) < 0) - { + if (is_base_mode()) { + fn(); + } else { + if (swoole_fork_exec(fn) < 0) { + swoole_sys_warning("failed fork manager process"); return SW_ERR; } - if (swManager_spawn_user_workers(serv) < 0) - { - return SW_ERR; - } - - SwooleG.process_type = SW_PROCESS_MANAGER; - SwooleG.pid = getpid(); - exit(swManager_loop(serv)); - break; - - //master process - default: - serv->gs->manager_pid = pid; - break; - case -1: - swError("fork() failed"); - return SW_ERR; } return SW_OK; } -static void swManager_check_exit_status(swServer *serv, 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 (serv->onWorkerError != NULL) - { - serv->onWorkerError(serv, worker_id, pid, WEXITSTATUS(status), WTERMSIG(status)); - } - } -} - -static int swManager_loop(swServer *serv) -{ - uint32_t i; - pid_t pid, new_pid; - pid_t reload_worker_pid = 0; - - int status; - - SwooleG.use_signalfd = 0; - SwooleTG.reactor = NULL; - SwooleG.enable_coroutine = 0; - - ManagerProcess.reload_workers = (swWorker *) sw_calloc(serv->worker_num + serv->task_worker_num, sizeof(swWorker)); - if (ManagerProcess.reload_workers == NULL) - { - swError("malloc[reload_workers] failed"); - return SW_ERR; - } - - //for reload - swSignal_add(SIGHUP, NULL); - swSignal_add(SIGCHLD, swManager_signal_handler); - swSignal_add(SIGTERM, swManager_signal_handler); - swSignal_add(SIGUSR1, swManager_signal_handler); - swSignal_add(SIGUSR2, swManager_signal_handler); - swSignal_add(SIGIO, swManager_signal_handler); - swSignal_add(SIGALRM, swManager_signal_handler); +void Manager::wait(Server *_server) { + server_ = _server; + server_->manager = this; + + ProcessPool *pool = &server_->gs->event_workers; + pool->onWorkerMessage = Server::read_worker_message; + _server->gs->manager_pid = _server->gs->event_workers.master_pid = getpid(); + + SwooleTG.reactor = nullptr; + + 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 + 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_add(SIGRTMIN, swManager_signal_handler); + swoole_signal_set(SIGRTMIN, signal_handler); #endif - //swSignal_add(SIGINT, swManager_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 (serv->hooks[SW_SERVER_HOOK_MANAGER_START]) - { - swServer_call_hook(serv, SW_SERVER_HOOK_MANAGER_START, serv); + if (_server->isset_hook(Server::HOOK_MANAGER_START)) { + _server->call_hook(Server::HOOK_MANAGER_START, _server); } - if (serv->onManagerStart) - { - serv->onManagerStart(serv); + if (_server->onManagerStart) { + _server->onManagerStart(_server); } - if (serv->manager_alarm > 0) - { - swoole_timer_add((long) (serv->manager_alarm * 1000), SW_TRUE, swManager_onTimer, serv); + if (_server->manager_alarm > 0) { + swoole_timer_add((long) (_server->manager_alarm * 1000), true, timer_callback, _server); } - while (SwooleG.running > 0) - { - pid = wait(&status); - - if (ManagerProcess.read_message) - { - swWorkerStopMessage msg; - while (swChannel_pop(serv->message_box, &msg, sizeof(msg)) > 0) - { - if (SwooleG.running == 0) - { + while (_server->running) { + ExitStatus exit_status = wait_process(); + const auto errnoAfterWait = errno; + if (pool->read_message) { + EventData msg; + while (pool->pop_message(&msg, sizeof(msg)) > 0) { + if (!_server->running) { continue; } - if (msg.worker_id >= serv->worker_num) - { - swManager_spawn_task_worker(serv, swServer_get_worker(serv, msg.worker_id)); + if (msg.info.type != SW_WORKER_MESSAGE_STOP && pool->onWorkerMessage) { + pool->onWorkerMessage(pool, &msg); + continue; } - else - { - pid_t new_pid = swManager_spawn_worker(serv, &serv->workers[msg.worker_id]); - if (new_pid > 0) - { - serv->workers[msg.worker_id].pid = new_pid; - } + 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(worker_stop_msg.worker_id); + _server->factory->spawn_event_worker(worker); } } - ManagerProcess.read_message = false; + pool->read_message = false; } - if (SwooleWG.signal_alarm && SwooleTG.timer) - { - SwooleWG.signal_alarm = 0; - swTimer_select(SwooleTG.timer); + if (SwooleTG.timer) { + SwooleTG.timer->select(); } - if (pid < 0) - { - if (!ManagerProcess.reloading) - { - _error: - if (errno > 0 && errno != EINTR) - { - swSysWarn("wait() failed"); + if (exit_status.get_pid() < 0) { + if (!pool->reloading) { + _error: + if (errnoAfterWait > 0 && errnoAfterWait != EINTR) { + swoole_sys_warning("wait() failed"); } continue; } - //reload task & event workers - else if (ManagerProcess.reload_all_worker) - { - swInfo("Server is reloading all workers now"); - if (serv->onBeforeReload != NULL) - { - serv->onBeforeReload(serv); + // reload task & event workers + else if (reload_all_worker) { + swoole_info("Server is reloading all workers now"); + if (_server->onBeforeReload != nullptr) { + _server->onBeforeReload(_server); } - if (!ManagerProcess.reload_init) - { - ManagerProcess.reload_init = true; - memcpy(ManagerProcess.reload_workers, serv->workers, sizeof(swWorker) * serv->worker_num); + if (!pool->reload_init) { + pool->reload_init = true; + memcpy(pool->reload_workers, _server->workers, sizeof(Worker) * _server->worker_num); - swManager_add_timeout_killer(serv, serv->workers, serv->worker_num); + add_timeout_killer(_server->workers, _server->worker_num); - ManagerProcess.reload_worker_num = serv->worker_num; - if (serv->task_worker_num > 0) - { - memcpy(ManagerProcess.reload_workers + serv->worker_num, serv->gs->task_workers.workers, - sizeof(swWorker) * serv->task_worker_num); - ManagerProcess.reload_worker_num += serv->task_worker_num; + reload_worker_num = _server->worker_num; + if (_server->task_worker_num > 0) { + 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; - swManager_add_timeout_killer(serv, serv->gs->task_workers.workers, serv->task_worker_num); + add_timeout_killer(_server->gs->task_workers.workers, _server->task_worker_num); } - ManagerProcess.reload_all_worker = false; - if (serv->reload_async) - { - for (i = 0; i < serv->worker_num; i++) - { - if (swoole_kill(ManagerProcess.reload_workers[i].pid, SIGTERM) < 0) - { - swSysWarn("swKill(%d, SIGTERM) [%d] failed", ManagerProcess.reload_workers[i].pid, i); + reload_all_worker = false; + if (_server->reload_async) { + SW_LOOP_N(_server->worker_num) { + 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); } } - ManagerProcess.reload_worker_i = serv->worker_num; - } - else - { - ManagerProcess.reload_worker_i = 0; + pool->reload_worker_i = _server->worker_num; + } else { + pool->reload_worker_i = 0; } } goto _kill_worker; } - //only reload task workers - else if (ManagerProcess.reload_task_worker) - { - if (serv->task_worker_num == 0) - { - swWarn("cannot reload task workers, task workers is not started"); - ManagerProcess.reloading = false; + // only reload task workers + else if (reload_task_worker) { + if (_server->task_worker_num == 0) { + swoole_warning("cannot reload task workers, task workers is not started"); + pool->reloading = false; continue; } - swInfo("Server is reloading task workers now"); - if (serv->onBeforeReload != NULL) - { - serv->onBeforeReload(serv); + swoole_info("Server is reloading task workers now"); + if (_server->onBeforeReload != nullptr) { + _server->onBeforeReload(_server); } - if (!ManagerProcess.reload_init) - { - memcpy(ManagerProcess.reload_workers, serv->gs->task_workers.workers, sizeof(swWorker) * serv->task_worker_num); - swManager_add_timeout_killer(serv, serv->gs->task_workers.workers, serv->task_worker_num); - ManagerProcess.reload_worker_num = serv->task_worker_num; - ManagerProcess.reload_worker_i = 0; - ManagerProcess.reload_init = true; - ManagerProcess.reload_task_worker = false; + 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; + pool->reload_worker_i = 0; + pool->reload_init = true; + reload_task_worker = false; } goto _kill_worker; - } - else - { + } else { goto _error; } } - if (SwooleG.running == 1) - { - //event workers - for (i = 0; i < serv->worker_num; i++) - { - //compare PID - if (pid != serv->workers[i].pid) - { + if (_server->running) { + // event workers + SW_LOOP_N(_server->worker_num) { + Worker *worker = _server->get_worker(i); + // find worker + if (exit_status.get_pid() != worker->pid) { continue; } - //check the process return code and signal - swManager_check_exit_status(serv, i, pid, status); + // check the process return code and signal + _server->factory->check_worker_exit_status(worker, exit_status); - while (1) - { - new_pid = swManager_spawn_worker(serv, &serv->workers[i]); - if (new_pid < 0) - { + do { + if (_server->factory->spawn_event_worker(worker) < 0) { SW_START_SLEEP; continue; } - else - { - serv->workers[i].pid = new_pid; - break; - } - } + } while (0); } - swWorker *exit_worker; - //task worker - if (serv->gs->task_workers.map) - { - exit_worker = (swWorker *) swHashMap_find_int(serv->gs->task_workers.map, pid); - if (exit_worker != NULL) - { - swManager_check_exit_status(serv, exit_worker->id, pid, status); - swManager_spawn_task_worker(serv, exit_worker); + // task worker + if (_server->gs->task_workers.map_) { + auto iter = _server->gs->task_workers.map_->find(exit_status.get_pid()); + if (iter != _server->gs->task_workers.map_->end()) { + _server->factory->check_worker_exit_status(iter->second, exit_status); + _server->factory->spawn_task_worker(iter->second); } } - //user process - if (serv->user_worker_map != NULL) - { - swManager_wait_other_worker(&serv->gs->event_workers, pid, status); + // user process + if (!_server->user_worker_map.empty()) { + Server::wait_other_worker(&_server->gs->event_workers, exit_status); } - if (pid == reload_worker_pid && ManagerProcess.reloading) - { - ManagerProcess.reload_worker_i++; + if (exit_status.get_pid() == reload_worker_pid && pool->reloading) { + pool->reload_worker_i++; } } - //reload worker - _kill_worker: - if (ManagerProcess.reloading) - { - //reload finish - if (ManagerProcess.reload_worker_i >= ManagerProcess.reload_worker_num) - { - reload_worker_pid = ManagerProcess.reload_worker_i = 0; - ManagerProcess.reload_init = ManagerProcess.reloading = false; - if (serv->onAfterReload != NULL) - { - serv->onAfterReload(serv); + // reload worker + _kill_worker: + if (pool->reloading) { + // reload finish + 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 = ManagerProcess.reload_workers[ManagerProcess.reload_worker_i].pid; - if (swoole_kill(reload_worker_pid, SIGTERM) < 0) - { - if (errno == ECHILD || errno == ESRCH) - { - ManagerProcess.reload_worker_i++; + reload_worker_pid = pool->reload_workers[pool->reload_worker_i].pid; + if (swoole_kill(reload_worker_pid, SIGTERM) < 0) { + if (errno == ECHILD || errno == ESRCH) { + pool->reload_worker_i++; goto _kill_worker; } - swSysWarn("swKill(%d, SIGTERM) [%d] failed", ManagerProcess.reload_workers[ManagerProcess.reload_worker_i].pid, ManagerProcess.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(ManagerProcess.reload_workers); - - if (SwooleTG.timer) - { + if (SwooleTG.timer) { swoole_timer_free(); } - //wait child process - if (serv->max_wait_time) - { - ManagerProcess.force_kill = true; - for (i = 0; i < serv->worker_num; i++) - { - ManagerProcess.kill_workers.push_back(serv->workers[i].pid); + // wait child process + if (_server->max_wait_time) { + force_kill = true; + SW_LOOP_N(_server->worker_num) { + kill_workers.push_back(_server->workers[i].pid); } - if (serv->task_worker_num > 0) - { - for (i = 0; i < serv->gs->task_workers.worker_num; i++) - { - ManagerProcess.kill_workers.push_back(serv->gs->task_workers.workers[i].pid); + if (_server->task_worker_num > 0) { + SW_LOOP_N(_server->gs->task_workers.worker_num) { + kill_workers.push_back(_server->gs->task_workers.workers[i].pid); } } - if (serv->user_worker_map) - { - swHashMap_rewind(serv->user_worker_map); - uint64_t key; - while (1) - { - swWorker *user_worker = (swWorker *) swHashMap_each_int(serv->user_worker_map, &key); - // no other user_worker - if (user_worker == NULL) - { - break; - } - ManagerProcess.kill_workers.push_back(user_worker->pid); + if (!_server->user_worker_map.empty()) { + for (auto &kv : _server->user_worker_map) { + kill_workers.push_back(kv.second->pid); } } /** * Multiply max_wait_time by 2 to prevent conflict with worker */ - alarm(serv->max_wait_time * 2); + alarm(_server->max_wait_time * 2); } - swManager_kill_workers(serv); - swManager_kill_task_workers(serv); - swManager_kill_user_workers(serv); - //force kill - if (serv->max_wait_time) - { + _server->factory->kill_event_workers(); + _server->factory->kill_task_workers(); + _server->factory->kill_user_workers(); + // force kill + if (_server->max_wait_time) { alarm(0); } - if (serv->onManagerStop) - { - serv->onManagerStop(serv); + 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); + } } -static void swManager_signal_handler(int sig) -{ - switch (sig) - { +void Manager::signal_handler(int signo) { + Server *_server = sw_server(); + if (!_server || !_server->manager) { + return; + } + Manager *manager = _server->manager; + ProcessPool *pool = &_server->gs->event_workers; + + switch (signo) { case SIGTERM: - SwooleG.running = 0; + _server->running = false; break; - /** - * reload all workers - */ case SIGUSR1: - if (!ManagerProcess.reloading) - { - ManagerProcess.reloading = true; - ManagerProcess.reload_all_worker = true; - } - break; - /** - * only reload task workers - */ case SIGUSR2: - if (!ManagerProcess.reloading) - { - ManagerProcess.reloading = true; - ManagerProcess.reload_task_worker = true; - } + _server->reload(signo == SIGUSR1); + sw_logger()->reopen(); break; case SIGIO: - ManagerProcess.read_message = true; + pool->read_message = true; break; case SIGALRM: - SwooleWG.signal_alarm = 1; - if (ManagerProcess.force_kill) - { - alarm(0); - for (auto i = ManagerProcess.kill_workers.begin(); i != ManagerProcess.kill_workers.end(); i++) - { - kill(*i, SIGKILL); - } + if (manager->force_kill) { + manager->terminate_all_worker(); } break; default: #ifdef SIGRTMIN - if (sig == SIGRTMIN) - { - swLog_reopen(sw_server()->daemonize ? SW_TRUE : SW_FALSE); + if (signo == SIGRTMIN) { + sw_logger()->reopen(); } #endif break; @@ -642,209 +451,106 @@ static void swManager_signal_handler(int sig) /** * @return: success returns pid, failure returns SW_ERR. */ -int swManager_wait_other_worker(swProcessPool *pool, pid_t pid, int status) -{ - swServer *serv = (swServer *) pool->ptr; - swWorker *exit_worker = nullptr; +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) - { - exit_worker = (swWorker *) swHashMap_find_int(serv->gs->task_workers.map, pid); - if (exit_worker != nullptr) - { + if (serv->gs->task_workers.map_) { + 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) - { - exit_worker = (swWorker *) swHashMap_find_int(serv->user_worker_map, pid); - if (exit_worker != nullptr) - { + 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; } } return SW_ERR; } while (0); - swManager_check_exit_status(serv, exit_worker->id, pid, status); - return swManager_spawn_worker_by_type(serv, exit_worker, worker_type); -} + serv->factory->check_worker_exit_status(exit_worker, exit_status); -/** - * kill and wait all user process - */ -void swManager_kill_user_workers(swServer *serv) -{ - if (!serv->user_worker_map) - { - return; - } - swWorker *user_worker; - uint64_t key; - int __stat_loc; - - //kill user process - swHashMap_rewind(serv->user_worker_map); - while (1) - { - user_worker = (swWorker *) swHashMap_each_int(serv->user_worker_map, &key); - //hashmap empty - if (user_worker == NULL) - { - break; - } - swoole_kill(user_worker->pid, SIGTERM); - } + pid_t new_process_pid = -1; - //wait user process - swHashMap_rewind(serv->user_worker_map); - while (1) - { - user_worker = (swWorker *) swHashMap_each_int(serv->user_worker_map, &key); - //hashmap empty - if (user_worker == NULL) - { - break; - } - if (swoole_waitpid(user_worker->pid, &__stat_loc, 0) < 0) - { - swSysWarn("waitpid(%d) failed", user_worker->pid); - } + switch (worker_type) { + case SW_PROCESS_TASKWORKER: + new_process_pid = serv->factory->spawn_task_worker(exit_worker); + break; + case SW_PROCESS_USERWORKER: + new_process_pid = serv->factory->spawn_user_worker(exit_worker); + break; + default: + /* never here */ + abort(); } + + return new_process_pid; } /** - * kill and wait all child process + * [manager] */ -void swManager_kill_workers(swServer *serv) -{ - int status; - - if (serv->worker_num == 0) - { +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 (uint32_t i = 0; i < serv->worker_num; i++) - { - swTrace("[Manager]kill worker processor"); - swoole_kill(serv->workers[i].pid, SIGTERM); - } - for (uint32_t i = 0; i < serv->worker_num; i++) - { - if (swoole_waitpid(serv->workers[i].pid, &status, 0) < 0) - { - swSysWarn("waitpid(%d) failed", serv->workers[i].pid); - } - } -} - -/** - * kill and wait task process - */ -void swManager_kill_task_workers(swServer *serv) -{ - if (serv->task_worker_num == 0) - { + 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; } - swProcessPool_shutdown(&serv->gs->task_workers); -} -pid_t swManager_spawn_worker(swServer *serv, swWorker *worker) -{ - pid_t pid; + Server::Command::Handler handler = iter->second; + auto result = handler(serv, std::string(msg->data, msg->info.len)); - pid = swoole_fork(0); + 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(); - //fork() failed - if (pid < 0) - { - swSysWarn("Fork Worker failed"); - return SW_ERR; - } - //worker child processor - else if (pid == 0) - { - - exit(swWorker_loop(serv, worker)); - } - //parent,add to writer - else - { - return pid; - } + serv->message_bus.write(serv->get_command_reply_socket(), &task); } -pid_t swManager_spawn_user_worker(swServer *serv, swWorker* worker) -{ - pid_t pid = swoole_fork(0); - - if (pid < 0) - { - swSysWarn("Fork Worker failed"); - return SW_ERR; - } - //child - else if (pid == 0) - { - SwooleG.process_type = SW_PROCESS_USERWORKER; - SwooleWG.worker = worker; - SwooleWG.id = worker->id; - worker->pid = getpid(); - //close tcp listen socket - if (serv->factory_mode == SW_MODE_BASE) - { - swServer_close_port(serv, SW_TRUE); - } - serv->onUserWorkerStart(serv, worker); - exit(0); - } - //parent - else - { - if (worker->pid) - { - swHashMap_del_int(serv->user_worker_map, worker->pid); - } - /** - * worker: local memory - * serv->user_workers: shared memory - */ - swServer_get_worker(serv, worker->id)->pid = worker->pid = pid; - swHashMap_add_int(serv->user_worker_map, pid, worker); - return pid; +bool Server::reload(bool reload_all_workers) { + if (is_thread_mode()) { + return reload_worker_threads(reload_all_workers); } -} -pid_t swManager_spawn_task_worker(swServer *serv, swWorker* worker) -{ - return swProcessPool_spawn(&serv->gs->task_workers, 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; + } -pid_t swManager_spawn_worker_by_type(swServer *serv, swWorker *worker, int worker_type) -{ - pid_t pid = -1; + if (getpid() != gs->manager_pid) { + return swoole_kill(get_manager_pid(), reload_all_workers ? SIGUSR1 : SIGUSR2) == 0; + } - switch (worker_type) - { - case SW_PROCESS_WORKER: - pid = swManager_spawn_worker(serv, worker); - break; - case SW_PROCESS_TASKWORKER: - pid = swManager_spawn_task_worker(serv, worker); - break; - case SW_PROCESS_USERWORKER: - pid = swManager_spawn_user_worker(serv, worker); - break; - default: - /* never here */ - abort(); + ProcessPool *pool = &gs->event_workers; + if (!pool->reload()) { + return false; } - return pid; + 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 722e7237708..b7cfa038d6e 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -10,172 +10,207 @@ | to obtain it through the world-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 "server.h" -#include "swoole_cxx.h" -#include "http.h" -#include -#include +#include "swoole_server.h" +#include "swoole_memory.h" +#include "swoole_lock.h" +#include "swoole_util.h" -using namespace swoole; +#include -static int swServer_destory(swServer *serv); -static int swServer_start_check(swServer *serv); -static void swServer_signal_handler(int sig); -static void swServer_enable_accept(swTimer *timer, swTimer_node *tnode); -static void swServer_disable_accept(swServer *serv); +using swoole::network::Address; +using swoole::network::SendfileTask; +using swoole::network::Socket; -static int swServer_tcp_send(swServer *serv, int session_id, void *data, uint32_t length); -static int swServer_tcp_sendwait(swServer *serv, int session_id, void *data, uint32_t length); -static int swServer_tcp_close(swServer *serv, int session_id, int reset); -static int swServer_tcp_sendfile(swServer *serv, int session_id, const char *file, uint32_t l_file, off_t offset, size_t length); -static int swServer_tcp_notify(swServer *serv, swConnection *conn, int event); -static int swServer_tcp_feedback(swServer *serv, int session_id, int event); +swoole::Server *g_server_instance = nullptr; -static void** swServer_worker_create_buffers(swServer *serv, uint buffer_num); -static void* swServer_worker_get_buffer(swServer *serv, swDataHead *info); -static size_t swServer_worker_get_buffer_len(swServer *serv, swDataHead *info); -static void swServer_worker_add_buffer_len(swServer *serv, swDataHead *info, size_t len); -static void swServer_worker_move_buffer(swServer *serv, swPipeBuffer *buffer); +namespace swoole { -static size_t swServer_worker_get_packet(swServer *serv, swEventData *req, char **data_ptr); - -static swConnection* swServer_connection_new(swServer *serv, swListenPort *ls, swSocket *_socket, int server_fd); +TimerCallback Server::get_timeout_callback(ListenPort *port, Reactor *reactor, Connection *conn) { + return [this, port, conn, reactor](Timer *, TimerNode *) { + if (conn->protect) { + return; + } + long ms = time(true); + if (ms - conn->socket->last_received_time < port->max_idle_time && + ms - conn->socket->last_sent_time < port->max_idle_time) { + return; + } + if (disable_notify || conn->closed || conn->close_force) { + close_connection(reactor, conn->socket); + return; + } + conn->close_force = 1; + Event _ev{}; + _ev.fd = conn->fd; + _ev.socket = conn->socket; + reactor->trigger_close_event(&_ev); + }; +} -static void swServer_check_port_type(swServer *serv, swListenPort *ls); +void Server::disable_accept() { + enable_accept_timer = swoole_timer_add( + SW_ACCEPT_RETRY_TIME, + false, + [](Timer *timer, TimerNode *tnode) { + Server *serv = (Server *) tnode->data; + for (auto port : serv->ports) { + if (port->is_dgram()) { + continue; + } + swoole_event_add(port->socket, SW_EVENT_READ); + } + serv->enable_accept_timer = nullptr; + }, + this); -static void swServer_disable_accept(swServer *serv) -{ - serv->enable_accept_timer = swoole_timer_add(SW_ACCEPT_RETRY_TIME * 1000, 0, swServer_enable_accept, serv); - if (serv->enable_accept_timer == nullptr) - { + if (enable_accept_timer == nullptr) { return; } - for (auto ls : *serv->listen_list) - { - //UDP - if (swSocket_is_dgram(ls->type)) - { + for (auto port : ports) { + if (port->is_dgram()) { continue; } - swoole_event_del(ls->socket); + swoole_event_del(port->socket); } } -static void swServer_enable_accept(swTimer *timer, swTimer_node *tnode) -{ - swServer *serv = (swServer *) tnode->data; +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); +} - for (auto ls : *serv->listen_list) - { - if (swSocket_is_dgram(ls->type)) - { - continue; - } - swoole_event_add(ls->socket, SW_EVENT_READ); +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 ""; } - serv->enable_accept_timer = nullptr; + Server::Command::Handler handler = iter->second; + return handler(this, msg); } -void swServer_close_port(swServer *serv, enum swBool_type only_stream_port) -{ - for (auto ls : *serv->listen_list) - { - if (only_stream_port && swSocket_is_dgram(ls->type)) - { - continue; - } - swSocket_free(ls->socket); +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 swServer_master_onAccept(swReactor *reactor, swEvent *event) -{ - swServer *serv = (swServer *) reactor->ptr; - swListenPort *listen_host = (swListenPort *) serv->connection_list[event->fd].object; - swSocketAddress client_addr; +int Server::accept_connection(Reactor *reactor, Event *event) { + Server *serv = (Server *) reactor->ptr; + ListenPort *listen_host = (ListenPort *) event->socket->object; - for (int i = 0; i < SW_ACCEPT_MAX_COUNT; i++) - { - swSocket *sock = swSocket_accept(event->socket, &client_addr); - if (sock == nullptr) - { - switch (errno) - { + for (int i = 0; i < SW_ACCEPT_MAX_COUNT; i++) { + Socket *sock = event->socket->accept(); + if (sock == nullptr) { + switch (errno) { case EAGAIN: return SW_OK; case EINTR: continue; default: - if (errno == EMFILE || errno == ENFILE) - { - swServer_disable_accept(serv); + 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", swServer_get_maxfd(serv), swServer_get_minfd(serv), 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); - swSocket_free(sock); - swServer_disable_accept(serv); + // 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); + serv->abort_connection(reactor, listen_host, sock); + serv->disable_accept(); return SW_OK; } - //add to connection_list - swConnection *conn = swServer_connection_new(serv, listen_host, sock, event->fd); - if (conn == nullptr) - { - swSocket_free(sock); + // add to connection_list + Connection *conn = serv->add_connection(listen_host, sock, event->fd); + if (conn == nullptr) { + serv->abort_connection(reactor, listen_host, sock); return SW_OK; } sock->chunk_size = SW_SEND_BUFFER_SIZE; #ifdef SW_USE_OPENSSL - if (listen_host->ssl) - { - if (swSSL_create(sock, listen_host->ssl_context, SW_SSL_SERVER) < 0) - { - reactor->close(reactor, sock); + if (listen_host->ssl) { + if (!listen_host->ssl_create(conn, sock)) { + serv->abort_connection(reactor, listen_host, sock); return SW_OK; } - else - { - conn->ssl = 1; - } - } - else - { - sock->ssl = NULL; + } else { + sock->ssl = nullptr; } #endif - if (serv->single_thread) - { - if (swServer_connection_incoming(serv, reactor, conn) < 0) - { - reactor->close(reactor, sock); + if (serv->single_thread) { + if (serv->connection_incoming(reactor, conn) < 0) { + serv->abort_connection(reactor, listen_host, sock); return SW_OK; } - } - else - { - swDataHead ev = {}; + } else { + DataHead ev{}; ev.type = SW_SERVER_EVENT_INCOMING; - ev.fd = sock->fd; - swSocket *_pipe_sock = swServer_get_send_pipe(serv, conn->session_id, conn->reactor_id); - if (reactor->write(reactor, _pipe_sock, &ev, sizeof(ev)) < 0) - { - reactor->close(reactor, sock); + ev.fd = conn->session_id; + ev.reactor_id = conn->reactor_id; + ev.server_fd = event->fd; + if (serv->send_to_reactor_thread((EventData *) &ev, sizeof(ev), conn->session_id) < 0) { + serv->abort_connection(reactor, listen_host, sock); return SW_OK; } } @@ -184,682 +219,476 @@ int swServer_master_onAccept(swReactor *reactor, swEvent *event) return SW_OK; } +int Server::connection_incoming(Reactor *reactor, Connection *conn) { + ListenPort *port = get_port_by_server_fd(conn->server_fd); + 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((long) (port->max_idle_time * 1000), true, timeout_callback); + } +#ifdef SW_USE_OPENSSL + if (conn->socket->ssl) { + return reactor->add(conn->socket, SW_EVENT_READ); + } +#endif + // delay receive, wait resume command + if (!enable_delay_receive) { + if (reactor->add(conn->socket, SW_EVENT_READ) < 0) { + return SW_ERR; + } + } + // notify worker process + if (onConnect) { + if (!notify(conn, SW_SERVER_EVENT_CONNECT)) { + return SW_ERR; + } + } + return SW_OK; +} + #ifdef SW_SUPPORT_DTLS -dtls::Session* swServer_dtls_accept(swServer *serv, swListenPort *port, swSocketAddress *sa) -{ - swSocket *sock = nullptr; +dtls::Session *Server::accept_dtls_connection(ListenPort *port, Address *sa) { dtls::Session *session = nullptr; - swConnection *conn = nullptr; + Connection *conn = nullptr; - int fd = swSocket_create(port->type, 1, 1); - if (fd < 0) - { + Socket *sock = make_socket(port->type, SW_FD_SESSION, SW_SOCK_CLOEXEC | SW_SOCK_NONBLOCK); + if (!sock) { return nullptr; } - int on = 1, off = 0; - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void*) &on, (socklen_t) sizeof(on)); + int fd = sock->fd; + sock->set_reuse_addr(); #ifdef HAVE_KQUEUE - setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (const void*) &on, (socklen_t) sizeof(on)); + sock->set_reuse_port(); #endif - switch (port->type) - { + switch (port->type) { case SW_SOCK_UDP: - { - if (inet_pton(AF_INET, port->host, &port->socket->info.addr.inet_v4.sin_addr) < 0) - { - swSysWarn("inet_pton(AF_INET, %s) failed", port->host); - goto _cleanup; - } - port->socket->info.addr.inet_v4.sin_port = htons(port->port); - port->socket->info.addr.inet_v4.sin_family = AF_INET; - - if (bind(fd, (const struct sockaddr *) &port->socket->info.addr, sizeof(struct sockaddr_in))) - { - swSysWarn("bind() failed"); - goto _cleanup; - } - if (connect(fd, (struct sockaddr *) &sa->addr, sizeof(struct sockaddr_in))) - { - swSysWarn("connect() failed"); - goto _cleanup; - } - break; - } case SW_SOCK_UDP6: - { - if (inet_pton(AF_INET6, port->host, &port->socket->info.addr.inet_v6.sin6_addr) < 0) - { - swSysWarn("inet_pton(AF_INET6, %s) failed", port->host); - goto _cleanup; - } - port->socket->info.addr.inet_v6.sin6_port = htons(port->port); - port->socket->info.addr.inet_v6.sin6_family = AF_INET6; - - setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &off, sizeof(off)); - if (bind(fd, (const struct sockaddr *) &port->socket->info.addr, sizeof(struct sockaddr_in6))) - { - swSysWarn("bind() failed"); - goto _cleanup; - } - if (connect(fd, (struct sockaddr *) &sa->addr, sizeof(struct sockaddr_in6))) - { - swSysWarn("connect() failed"); - goto _cleanup; - } break; - } default: OPENSSL_assert(0); break; } - sock = swSocket_new(fd, SW_FD_SESSION); - if (!sock) - { + if (sock->bind(port->socket->info) < 0) { + swoole_sys_warning("bind() failed"); + goto _cleanup; + } + if (sock->is_inet6()) { + sock->set_option(IPPROTO_IPV6, IPV6_V6ONLY, 0); + } + if (sock->connect(sa) < 0) { + swoole_sys_warning("connect(%s:%d) failed", sa->get_addr(), sa->get_port()); goto _cleanup; } memcpy(&sock->info, sa, sizeof(*sa)); - sock->socket_type = port->type; - sock->nonblock = 1; - sock->cloexec = 1; - sock->chunk_size = SW_BUFFER_SIZE_STD; - - conn = swServer_connection_new(serv, port, sock, port->socket->fd); - if (conn == nullptr) - { + sock->chunk_size = SW_SSL_BUFFER_SIZE; + + conn = add_connection(port, sock, port->socket->fd); + if (conn == nullptr) { goto _cleanup; } session = new dtls::Session(sock, port->ssl_context); port->dtls_sessions->emplace(fd, session); - if (!session->init()) - { + if (!session->init()) { goto _cleanup; } return session; - _cleanup: - if (sock) - { - sw_free(sock); - } - if (conn) - { - bzero(conn, sizeof(*conn)); +_cleanup: + if (conn) { + *conn = {}; } - if (session) - { + if (session) { delete session; } - close(fd); - + sock->free(); return nullptr; } #endif -static int swServer_start_check(swServer *serv) -{ - //disable notice when use SW_DISPATCH_ROUND and SW_DISPATCH_QUEUE - if (serv->factory_mode == SW_MODE_PROCESS) - { - if (!swServer_support_unsafe_events(serv)) - { - if (serv->onConnect) - { - swWarn("cannot set 'onConnect' event when using dispatch_mode=1/3/7"); - serv->onConnect = nullptr; +void Server::set_max_connection(uint32_t _max_connection) { + if (connection_list != nullptr) { + swoole_warning("max_connection must be set before server create"); + return; + } + max_connection = _max_connection; + if (max_connection == 0) { + max_connection = SW_MIN(SW_MAX_CONNECTION, SwooleG.max_sockets); + } else if (max_connection > SW_SESSION_LIST_SIZE) { + max_connection = 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; + 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() { + // disable notice when use SW_DISPATCH_ROUND and SW_DISPATCH_QUEUE + if (is_process_mode()) { + if (!is_support_unsafe_events()) { + if (onConnect) { + 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 (serv->onClose) - { - swWarn("cannot set 'onClose' event when using dispatch_mode=1/3/7"); - serv->onClose = nullptr; + if (onClose) { + 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 (serv->onBufferFull) - { - swWarn("cannot set 'onBufferFull' event when using dispatch_mode=1/3/7"); - serv->onBufferFull = nullptr; + if (onBufferFull) { + 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 (serv->onBufferEmpty) - { - swWarn("cannot set 'onBufferEmpty' event when using dispatch_mode=1/3/7"); - serv->onBufferEmpty = nullptr; + if (onBufferEmpty) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SERVER_INVALID_CALLBACK, + "cannot set 'onBufferEmpty' event when using dispatch_mode=%d", + dispatch_mode); + onBufferEmpty = nullptr; } - serv->disable_notify = 1; + disable_notify = 1; } - if (!swServer_support_send_yield(serv)) - { - serv->send_yield = 0; + if (!is_support_send_yield()) { + send_yield = 0; } + } else { + max_queued_bytes = 0; } - else - { - serv->max_queued_bytes = 0; - } - //AsyncTask - if (serv->task_worker_num > 0) - { - if (serv->onTask == NULL) - { - swWarn("onTask event callback must be set"); + if (task_worker_num > 0) { + if (onTask == nullptr) { + swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_INVALID_CALLBACK, "require 'onTask' callback"); return SW_ERR; } - if (serv->task_worker_num > SW_CPU_NUM * SW_MAX_WORKER_NCPU) - { - swWarn("serv->task_worker_num == %d, Too many processes, reset to max value %d", serv->task_worker_num, SW_CPU_NUM * SW_MAX_WORKER_NCPU); - serv->task_worker_num = SW_CPU_NUM * SW_MAX_WORKER_NCPU; - } - } - //check thread num - if (serv->reactor_num > SW_CPU_NUM * SW_MAX_THREAD_NCPU) - { - swWarn("serv->reactor_num == %d, Too many threads, reset to max value %d", serv->reactor_num, SW_CPU_NUM * SW_MAX_THREAD_NCPU); - serv->reactor_num = SW_CPU_NUM * SW_MAX_THREAD_NCPU; - } - else if (serv->reactor_num == 0) - { - serv->reactor_num = SW_CPU_NUM; - } - if (serv->single_thread) - { - serv->reactor_num = 1; - } - //check worker num - if (serv->worker_num > SW_CPU_NUM * SW_MAX_WORKER_NCPU) - { - swWarn("serv->worker_num == %d, Too many processes, reset to max value %d", serv->worker_num, SW_CPU_NUM * SW_MAX_WORKER_NCPU); - serv->worker_num = SW_CPU_NUM * SW_MAX_WORKER_NCPU; - } - if (serv->worker_num < serv->reactor_num) - { - serv->reactor_num = serv->worker_num; - } - // max connections - uint32_t minimum_connection = (serv->worker_num + serv->task_worker_num) * 2 + 32; - if (serv->max_connection < minimum_connection) - { - serv->max_connection = SwooleG.max_sockets; - swWarn("serv->max_connection must be bigger than %u, it's reset to %u", minimum_connection, SwooleG.max_sockets); - } - else if (SwooleG.max_sockets > 0 && serv->max_connection > SwooleG.max_sockets) - { - serv->max_connection = SwooleG.max_sockets; - swWarn("serv->max_connection is exceed the maximum value, it's reset to %u", SwooleG.max_sockets); - } - else if (serv->max_connection > SW_SESSION_LIST_SIZE) - { - serv->max_connection = SW_SESSION_LIST_SIZE; - swWarn("serv->max_connection is exceed the SW_SESSION_LIST_SIZE, it's reset to %u", SW_SESSION_LIST_SIZE); - } - // package max length - for (auto ls : *serv->listen_list) - { - if (ls->protocol.package_max_length < SW_BUFFER_MIN_SIZE) - { + } + 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 (swServer_if_require_receive_callback(serv, ls, serv->onReceive)) - { - swWarn("require onReceive callback"); + if (if_require_receive_callback(ls, onReceive != nullptr)) { + swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_INVALID_CALLBACK, "require 'onReceive' callback"); return SW_ERR; } - if (swServer_if_require_packet_callback(serv, ls, serv->onPacket)) - { - swWarn("require onPacket callback"); + if (if_require_packet_callback(ls, onPacket != nullptr)) { + 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 (serv->factory_mode != SW_MODE_BASE) - { - swSSL_init_thread_safety(); + if ((is_process_mode() && !single_thread) || is_thread_mode()) { + swoole_ssl_init_thread_safety(); } #endif return SW_OK; } -void swServer_store_listen_socket(swServer *serv) -{ - int sockfd; - - for (auto ls : *serv->listen_list) - { - sockfd = ls->socket->fd; - //save server socket to connection_list - serv->connection_list[sockfd].fd = sockfd; - //socket type - serv->connection_list[sockfd].socket_type = ls->type; - //save listen_host object - serv->connection_list[sockfd].object = ls; - - if (swSocket_is_dgram(ls->type)) - { - if (ls->type == SW_SOCK_UDP) - { - serv->connection_list[sockfd].info.addr.inet_v4.sin_port = htons(ls->port); - } - else if (ls->type == SW_SOCK_UDP6) - { - serv->udp_socket_ipv6 = sockfd; - serv->connection_list[sockfd].info.addr.inet_v6.sin6_port = htons(ls->port); - } - } - else - { - //IPv4 - if (ls->type == SW_SOCK_TCP) - { - serv->connection_list[sockfd].info.addr.inet_v4.sin_port = htons(ls->port); - } - //IPv6 - else if (ls->type == SW_SOCK_TCP6) - { - serv->connection_list[sockfd].info.addr.inet_v6.sin6_port = htons(ls->port); - } - } - if (sockfd >= 0) - { - swServer_set_minfd(serv, sockfd); - swServer_set_maxfd(serv, sockfd); +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); } -} -uint sw_inline swServer_worker_buffer_num(swServer *serv) -{ - uint buffer_num; + if ((master_timer = swoole_timer_add(1000L, true, Server::timer_callback, this)) == nullptr) { + swoole_event_free(); + return SW_ERR; + } - if (serv->factory_mode == SW_MODE_BASE) - { - buffer_num = 1; + if (!single_thread && !is_thread_mode()) { + reactor_thread_barrier.wait(); } - else - { - buffer_num = serv->reactor_num + serv->dgram_port_num; + if (is_process_mode()) { + gs->manager_barrier.wait(); } - return buffer_num; -} + gs->master_pid = getpid(); -void** swServer_worker_create_buffers(swServer *serv, uint buffer_num) -{ - swString **buffers = (swString **) sw_malloc(sizeof(swString *) * buffer_num); - if (buffers == NULL) - { - swError("malloc for worker input_buffers failed"); + if (isset_hook(HOOK_MASTER_START)) { + call_hook(HOOK_MASTER_START, this); } - for (uint i = 0; i < buffer_num; i++) - { - buffers[i] = swString_new(SW_BUFFER_SIZE_BIG); - if (buffers[i] == NULL) - { - swError("worker input_buffers init failed"); - } + if (onStart) { + onStart(this); } - return (void **) buffers; + return swoole_event_wait(); +} + +void Server::store_listen_socket() { + for (auto ls : ports) { + int sockfd = ls->socket->fd; + // save server socket to connection_list + connection_list[sockfd].fd = sockfd; + connection_list[sockfd].socket = ls->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); + } + } } /** - * only the memory of the swWorker structure is allocated, no process is fork + * only the memory of the Worker structure is allocated, no process is fork */ -int swServer_create_task_workers(swServer *serv) -{ +int Server::create_task_workers() { key_t key = 0; - int ipc_mode; + swIPCMode ipc_mode; - if (serv->task_ipc_mode == SW_TASK_IPC_MSGQUEUE || serv->task_ipc_mode == SW_TASK_IPC_PREEMPTIVE) - { - key = serv->message_queue_key; + if (task_ipc_mode == TASK_IPC_MSGQUEUE || task_ipc_mode == TASK_IPC_PREEMPTIVE) { + key = message_queue_key; ipc_mode = SW_IPC_MSGQUEUE; - } - else if (serv->task_ipc_mode == SW_TASK_IPC_STREAM) - { + } else if (task_ipc_mode == TASK_IPC_STREAM) { ipc_mode = SW_IPC_SOCKET; - } - else - { + } else { ipc_mode = SW_IPC_UNIXSOCK; } - swProcessPool *pool = &serv->gs->task_workers; - if (swProcessPool_create(pool, serv->task_worker_num, key, ipc_mode) < 0) - { - swWarn("[Master] create task_workers failed"); + ProcessPool *pool = &gs->task_workers; + *pool = {}; + if (pool->create(task_worker_num, key, ipc_mode) < 0) { + swoole_warning("[Master] create task_workers failed"); return SW_ERR; } - swProcessPool_set_max_request(pool, serv->task_max_request, serv->task_max_request_grace); - swProcessPool_set_start_id(pool, serv->worker_num); - swProcessPool_set_type(pool, SW_PROCESS_TASKWORKER); + pool->set_max_request(task_max_request, task_max_request_grace); + pool->set_start_id(worker_num); + pool->set_type(SW_PROCESS_TASKWORKER); - if (ipc_mode == SW_IPC_SOCKET) - { + if (ipc_mode == SW_IPC_SOCKET) { char sockfile[sizeof(struct sockaddr_un)]; - snprintf(sockfile, sizeof(sockfile), "/tmp/swoole.task.%d.sock", serv->gs->master_pid); - if (swProcessPool_create_unix_socket(&serv->gs->task_workers, sockfile, 2048) < 0) - { + snprintf(sockfile, sizeof(sockfile), "/tmp/swoole.task.%d.sock", gs->master_pid); + 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; } /** - * @description: - * only the memory of the swWorker structure is allocated, no process is fork. + * @description: + * only the memory of the Worker structure is allocated, no process is fork. * called when the manager process start. - * @param swServer * @return: SW_OK|SW_ERR */ -int swServer_create_user_workers(swServer *serv) -{ - /** - * if Swoole\Server::addProcess is called first, - * swServer::user_worker_list is initialized in the swServer_add_worker function - */ - if (serv->user_worker_list == nullptr) - { - serv->user_worker_list = new std::vector; +int Server::create_user_workers() { + user_workers = (Worker *) sw_shm_calloc(get_user_worker_num(), sizeof(Worker)); + if (user_workers == nullptr) { + swoole_sys_warning("gmalloc[server->user_workers] failed"); + return SW_ERR; } - serv->user_workers = (swWorker *) SwooleG.memory_pool->alloc(SwooleG.memory_pool, serv->user_worker_num * sizeof(swWorker)); - if (serv->user_workers == NULL) - { - swSysWarn("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; } /** * [Master] */ -int swServer_worker_create(swServer *serv, swWorker *worker) -{ - return swMutex_create(&worker->lock, 1); -} - -/** - * [Worker] - */ -int swServer_worker_init(swServer *serv, swWorker *worker) -{ -#ifdef HAVE_CPU_AFFINITY - 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[SwooleWG.id % serv->cpu_affinity_available_num], &cpu_set); - } - else - { - CPU_SET(SwooleWG.id % SW_CPU_NUM, &cpu_set); - } -#ifdef __FreeBSD__ - if (cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, - sizeof(cpu_set), &cpu_set) < 0) -#else - if (sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set) < 0) -#endif - { - swSysWarn("sched_setaffinity() failed"); - } - } -#endif - - //signal init - swWorker_signal_init(); - - SwooleWG.input_buffers = serv->create_buffers(serv, swServer_worker_buffer_num(serv)); - if (!SwooleWG.input_buffers) - { - return SW_ERR; - } - - if (serv->max_request < 1) - { - SwooleWG.run_always = 1; - } - else - { - SwooleWG.max_request = serv->max_request; - if (serv->max_request_grace > 0) - { - SwooleWG.max_request += swoole_system_random(1, serv->max_request_grace); - } +void Server::create_worker(Worker *worker) { + worker->lock = new Mutex(Mutex::PROCESS_SHARED); + if (worker->pipe_object) { + store_pipe_fd(worker->pipe_object); } - - worker->start_time = time(NULL); - worker->request_count = 0; - - return SW_OK; } -void swServer_worker_start(swServer *serv, swWorker *worker) -{ - void *hook_args[2]; - hook_args[0] = serv; - 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 (serv->hooks[SW_SERVER_HOOK_WORKER_START]) - { - swServer_call_hook(serv, SW_SERVER_HOOK_WORKER_START, hook_args); - } - if (serv->onWorkerStart) - { - serv->onWorkerStart(serv, worker->id); - } +void Server::destroy_worker(Worker *worker) { + delete worker->lock; + worker->lock = nullptr; } -void swServer_reopen_log_file(swServer *serv) -{ - if (!SwooleG.log_file) - { - return; - } - /** - * reopen log file - */ - close(SwooleG.log_fd); - swLog_init(SwooleG.log_file); - /** - * redirect STDOUT & STDERR to log file - */ - if (serv->daemonize) - { - swoole_redirect_stdout(SwooleG.log_fd); - } +/** + * [Worker] + */ +void Server::init_event_worker(Worker *worker) { + worker->init(); + worker->set_max_request(max_request, max_request_grace); } -int swServer_start(swServer *serv) -{ - swFactory *factory = &serv->factory; - int ret; - - ret = swServer_start_check(serv); - if (ret < 0) - { +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]) - { - swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_START, serv); + 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. - if (!sw_atomic_cmp_set(&serv->gs->start, 0, 1)) - { - swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_ONLY_START_ONE, "must only start one server"); + // cannot start 2 servers at the same time, please use process->exec. + if (!sw_atomic_cmp_set(&gs->start, 0, 1)) { + swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_ONLY_START_ONE, "can only start one server"); return SW_ERR; } - //init logger - if (SwooleG.log_file) - { - swLog_init(SwooleG.log_file); - } - //run as daemon - if (serv->daemonize > 0) - { + // run as daemon + if (daemonize > 0) { /** * redirect STDOUT to log file */ - if (SwooleG.log_fd > STDOUT_FILENO) - { - swoole_redirect_stdout(SwooleG.log_fd); + if (sw_logger()->is_opened()) { + sw_logger()->redirect_stdout_and_stderr(1); } /** * redirect STDOUT_FILENO/STDERR_FILENO to /dev/null */ - else - { - SwooleG.null_fd = open("/dev/null", O_WRONLY); - if (SwooleG.null_fd > 0) - { - swoole_redirect_stdout(SwooleG.null_fd); - } - else - { - swSysWarn("open(/dev/null) failed"); + else { + null_fd = open("/dev/null", O_WRONLY); + if (null_fd > 0) { + swoole_redirect_stdout(null_fd); + } else { + swoole_sys_warning("open(/dev/null) failed"); } } - if (swoole_daemon(0, 1) < 0) - { + if (swoole_daemon(0, 1) < 0) { return SW_ERR; } } - //master pid - serv->gs->master_pid = getpid(); - serv->stats->start_time = time(NULL); + gs->start_time = ::time(nullptr); /** - * init method + * store to ProcessPool object */ - serv->send = swServer_tcp_send; - serv->sendwait = swServer_tcp_sendwait; - serv->sendfile = swServer_tcp_sendfile; - serv->close = swServer_tcp_close; - serv->notify = swServer_tcp_notify; - serv->feedback = swServer_tcp_feedback; - - serv->workers = (swWorker *) SwooleG.memory_pool->alloc(SwooleG.memory_pool, serv->worker_num * sizeof(swWorker)); - if (serv->workers == NULL) - { - swSysWarn("gmalloc[server->workers] failed"); - return SW_ERR; - } - - if (swMutex_create(&serv->lock, 0) < 0) - { - return SW_ERR; - } - - /** - * store to swProcessPool object - */ - serv->gs->event_workers.ptr = serv; - serv->gs->event_workers.workers = serv->workers; - serv->gs->event_workers.worker_num = serv->worker_num; - serv->gs->event_workers.use_msgqueue = 0; + 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 < serv->worker_num; i++) - { - serv->gs->event_workers.workers[i].pool = &serv->gs->event_workers; - serv->gs->event_workers.workers[i].id = i; - serv->gs->event_workers.workers[i].type = SW_PROCESS_WORKER; - } - - /* - * For swoole_server->taskwait, create notify pipe and result shared memory. - */ - if (serv->task_worker_num > 0 && serv->worker_num > 0) - { - serv->task_result = (swEventData *) sw_shm_calloc(serv->worker_num, sizeof(swEventData)); - if (!serv->task_result) - { - swWarn("malloc[serv->task_result] failed"); - return SW_ERR; - } - serv->task_notify = (swPipe *) sw_calloc(serv->worker_num, sizeof(swPipe)); - if (!serv->task_notify) - { - swWarn("malloc[serv->task_notify] failed"); - sw_shm_free(serv->task_result); - return SW_ERR; - } - for (i = 0; i < serv->worker_num; i++) - { - if (swPipeNotify_auto(&serv->task_notify[i], 1, 0)) - { - sw_shm_free(serv->task_result); - sw_free(serv->task_notify); - return SW_ERR; - } - } + 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; } - /** - * user worker process - */ - if (serv->user_worker_list) - { - i = 0; - for (auto worker : *serv->user_worker_list) - { - worker->id = serv->worker_num + serv->task_worker_num + i; + if (!user_worker_list.empty()) { + uint32_t i = 0; + for (auto worker : user_worker_list) { + worker->id = worker_num + task_worker_num + i; i++; } } - serv->running = 1; - //factory start - if (factory->start(factory) < 0) - { - return SW_ERR; - } - //signal Init - swServer_signal_init(serv); - //write PID file - if (serv->pid_file) - { - ret = sw_snprintf(SwooleTG.buffer_stack->str, SwooleTG.buffer_stack->size, "%d", getpid()); - swoole_file_put_contents(serv->pid_file, SwooleTG.buffer_stack->str, ret); + running = true; + // factory start + if (!factory->start()) { + return SW_ERR; } - if (serv->factory_mode == SW_MODE_BASE) - { - ret = swReactorProcess_start(serv); + // write PID file + if (!pid_file.empty()) { + size_t n = sw_snprintf(sw_tg_buffer()->str, sw_tg_buffer()->size, "%d", getpid()); + file_put_contents(pid_file, sw_tg_buffer()->str, n); } - else - { - ret = swReactorThread_start(serv); + int ret; + if (is_base_mode()) { + ret = start_reactor_processes(); + } 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) - { + // failed to start + if (ret < 0) { return SW_ERR; } - swServer_destory(serv); - //remove PID file - if (serv->pid_file) - { - unlink(serv->pid_file); + destroy(); + // remove PID file + if (!pid_file.empty()) { + unlink(pid_file.c_str()); } return SW_OK; } @@ -867,526 +696,800 @@ int swServer_start(swServer *serv) /** * initializing server config, set default */ -void swServer_init(swServer *serv) -{ - swoole_init(); - bzero(serv, sizeof(swServer)); - - serv->factory_mode = SW_MODE_BASE; +Server::Server(enum Mode _mode) { + 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; - serv->reactor_num = SW_REACTOR_NUM > SW_REACTOR_MAX_THREAD ? SW_REACTOR_MAX_THREAD : SW_REACTOR_NUM; + // http server + http_compression = 1; + http_compression_level = SW_Z_BEST_SPEED; + compression_min_length = SW_COMPRESSION_MIN_LENGTH_DEFAULT; - serv->dispatch_mode = SW_DISPATCH_FDMOD; + timezone_ = get_timezone(); - serv->worker_num = SW_CPU_NUM; - serv->max_connection = SW_MIN(SW_MAX_CONNECTION, SwooleG.max_sockets); + gs = (ServerGS *) sw_shm_malloc(sizeof(ServerGS)); + if (gs == nullptr) { + swoole_error("[Master] Fatal Error: failed to allocate memory for Server->gs"); + } + gs->pipe_packet_msg_id = 1; + gs->max_concurrency = UINT_MAX; - serv->max_wait_time = SW_WORKER_MAX_WAIT_TIME; + 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(); }; - //http server - serv->http_parse_cookie = 1; - serv->http_parse_post = 1; -#ifdef SW_HAVE_COMPRESSION - serv->http_compression = 1; - serv->http_compression_level = SW_Z_BEST_SPEED; -#endif - serv->upload_tmp_dir = sw_strdup("/tmp"); + g_server_instance = this; +} - serv->input_buffer_size = SW_INPUT_BUFFER_SIZE; - serv->output_buffer_size = SW_OUTPUT_BUFFER_SIZE; +Server::~Server() { + if (!is_shutdown() && getpid() == gs->master_pid) { + destroy(); + } + for (auto port : ports) { + delete port; + } + sw_shm_free(gs); +} - serv->task_ipc_mode = SW_TASK_IPC_UNIXSOCK; +int Server::create() { + if (is_created()) { + return SW_ERR; + } - serv->enable_coroutine = 1; - serv->reload_async = 1; - serv->send_yield = 1; + if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_CREATE)) { + swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_CREATE, this); + } -#ifdef __linux__ - serv->timezone = timezone; -#else - struct timezone tz; - struct timeval tv; - gettimeofday(&tv, &tz); - serv->timezone = tz.tz_minuteswest * 60; -#endif + session_list = (Session *) sw_shm_calloc(SW_SESSION_LIST_SIZE, sizeof(Session)); + if (session_list == nullptr) { + swoole_error("sw_shm_calloc(%ld) for session_list failed", SW_SESSION_LIST_SIZE * sizeof(Session)); + return SW_ERR; + } - /** - * alloc shared memory - */ - serv->stats = (swServerStats *) SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(swServerStats)); - if (serv->stats == NULL) - { - swError("[Master] Fatal Error: failed to allocate memory for swServer->stats"); + 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; } - serv->gs = (swServerGS *) SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(swServerGS)); - if (serv->gs == NULL) - { - swError("[Master] Fatal Error: failed to allocate memory for swServer->gs"); + + int index = 0; + for (auto port : ports) { + port->gs = &port_gs_list[index++]; } - /** - * init method - */ - serv->create_buffers = swServer_worker_create_buffers; - serv->get_buffer = swServer_worker_get_buffer; - serv->get_buffer_len = swServer_worker_get_buffer_len; - serv->add_buffer_len = swServer_worker_add_buffer_len; - serv->move_buffer = swServer_worker_move_buffer; - serv->get_packet = swServer_worker_get_packet; + if (enable_static_handler and locations == nullptr) { + locations = std::make_shared>(); + } - SwooleG.serv = serv; -} + if (http_compression_types && http_compression_types->size() > 0) { + http_compression = 1; + } -int swServer_create(swServer *serv) -{ - serv->factory.ptr = serv; + // Max Connections + uint32_t minimum_connection = (worker_num + task_worker_num) * 2 + 32; + if (ports.size() > 0) { + minimum_connection += ports.back()->get_fd(); + } + if (max_connection < minimum_connection) { + max_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) { + 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; + } + if (single_thread) { + reactor_num = 1; + } + // Worker Process Num + if (worker_num > SW_CPU_NUM * SW_MAX_WORKER_NCPU) { + 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; + } + if (worker_num < reactor_num) { + reactor_num = worker_num; + } + // TaskWorker Process Num + if (task_worker_num > 0) { + if (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) { + swoole_sys_warning("gmalloc[server->workers] failed"); + return SW_ERR; + } - serv->session_list = (swSession *) sw_shm_calloc(SW_SESSION_LIST_SIZE, sizeof(swSession)); - if (serv->session_list == NULL) - { - swError("sw_shm_calloc(%ld) for session_list failed", SW_SESSION_LIST_SIZE * sizeof(swSession)); + if (is_base_mode()) { + factory = create_base_factory(); + } else if (is_thread_mode()) { + factory = create_thread_factory(); + } else { + factory = create_process_factory(); + } + if (!factory) { return SW_ERR; } - if (serv->enable_static_handler && serv->locations == nullptr) - { - serv->locations = new std::unordered_set; + if (is_process_mode()) { + reactor_thread_barrier.init(false, reactor_num + 1); + gs->manager_barrier.init(true, 2); } - if (serv->factory_mode == SW_MODE_BASE) - { - return swReactorProcess_create(serv); + if (swoole_isset_hook(SW_GLOBAL_HOOK_AFTER_SERVER_CREATE)) { + swoole_call_hook(SW_GLOBAL_HOOK_AFTER_SERVER_CREATE, this); } - else - { - return swReactorThread_create(serv); + + return SW_OK; +} + +void Server::clear_timer() { + if (master_timer) { + swoole_timer_del(master_timer); + master_timer = nullptr; + } + if (heartbeat_timer) { + swoole_timer_del(heartbeat_timer); + heartbeat_timer = nullptr; + } + if (enable_accept_timer) { + swoole_timer_del(enable_accept_timer); + enable_accept_timer = nullptr; } } -void swServer_clear_timer(swServer *serv) -{ - if (serv->master_timer) - { - swoole_timer_del(serv->master_timer); - serv->master_timer = nullptr; +bool Server::shutdown() { + if (sw_unlikely(!is_started())) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + return false; } - if (serv->heartbeat_timer) - { - swoole_timer_del(serv->heartbeat_timer); - serv->heartbeat_timer = nullptr; + + 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 (serv->enable_accept_timer) - { - swoole_timer_del(serv->enable_accept_timer); - serv->enable_accept_timer = nullptr; + + 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; } -int swServer_shutdown(swServer *serv) -{ - serv->running = 0; - //stop all thread - if (SwooleTG.reactor) - { - swReactor *reactor = SwooleTG.reactor; - reactor->wait_exit = 1; - for (auto ls : *serv->listen_list) - { - if (swSocket_is_stream(ls->type)) - { - reactor->del(reactor, ls->socket); +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); + } + } + 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()) { + if (gs->manager_pid > 0) { + running = false; + } else { + // single process worker, exit directly + gs->event_workers.running = 0; + stop_async_worker(sw_worker()); } - swServer_clear_timer(serv); + return true; } - else - { - SwooleG.running = 0; + if (swoole_isset_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN)) { + swoole_call_hook(SW_GLOBAL_HOOK_BEFORE_SERVER_SHUTDOWN, this); } - swInfo("Server is shutdown now"); - return SW_OK; + if (onBeforeShutdown) { + onBeforeShutdown(this); + } + running = false; + stop_master_thread(); + swoole_trace_log(SW_TRACE_SERVER, "shutdown end"); + return true; } -static int swServer_destory(swServer *serv) -{ - swTraceLog(SW_TRACE_SERVER, "release service"); - /** - * shutdown workers - */ - if (serv->factory.shutdown) - { - serv->factory.shutdown(&(serv->factory)); +bool Server::signal_handler_child_exit() { + if (!running) { + return false; } - if (serv->factory_mode == SW_MODE_BASE) - { - swTraceLog(SW_TRACE_SERVER, "terminate task workers"); - if (serv->task_worker_num > 0) - { - swProcessPool_shutdown(&serv->gs->task_workers); - } + 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() { + 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); + } + + factory->shutdown(); + + SW_LOOP_N(worker_num) { + Worker *worker = &workers[i]; + destroy_worker(worker); } - else - { - swTraceLog(SW_TRACE_SERVER, "terminate reactor threads"); + + if (is_base_mode()) { + swoole_trace_log(SW_TRACE_SERVER, "terminate task workers"); + if (task_worker_num > 0) { + gs->task_workers.destroy(); + } + } else if (is_process_mode()) { + swoole_trace_log(SW_TRACE_SERVER, "terminate reactor threads"); /** * Wait until all the end of the thread */ - swReactorThread_join(serv); + join_reactor_thread(); } - for (auto ls : *serv->listen_list) - { - swPort_free(ls); - } - delete serv->listen_list; - serv->listen_list = nullptr; + release_pipe_buffers(); - /** - * because the swWorker 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. - */ - delete serv->user_worker_list; - serv->user_worker_list = nullptr; + for (auto port : ports) { + port->close(); + } - //close log file - if (SwooleG.log_file != 0) - { - swLog_free(); + if (user_workers) { + sw_shm_free(user_workers); + user_workers = nullptr; } - if (SwooleG.null_fd > 0) - { - close(SwooleG.null_fd); + if (null_fd > 0) { + ::close(null_fd); + null_fd = -1; } - swSignal_clear(); + swoole_signal_clear(); /** * shutdown status */ - serv->gs->start = 0; - serv->gs->shutdown = 1; + gs->start = 0; + gs->shutdown = 1; /** * callback */ - if (serv->onShutdown) - { - serv->onShutdown(serv); + if (onShutdown) { + onShutdown(this); } - if (serv->factory_mode == SW_MODE_BASE) - { - swReactorProcess_free(serv); - } - else - { - swReactorThread_free(serv); + SW_LOOP_N(SW_MAX_HOOK_TYPE) { + if (hooks[i]) { + std::list *l = reinterpret_cast *>(hooks[i]); + hooks[i] = nullptr; + delete l; + } } - if (serv->locations) - { - delete serv->locations; + if (is_process_mode()) { + reactor_thread_barrier.destroy(); + gs->manager_barrier.destroy(); } - if (serv->http_index_files) - { - delete serv->http_index_files; + if (is_base_mode()) { + destroy_base_factory(); + } else if (is_thread_mode()) { + destroy_thread_factory(); + } else { + destroy_process_factory(); } - serv->lock.free(&serv->lock); - SwooleG.serv = nullptr; - return SW_OK; + + sw_shm_free(session_list); + sw_shm_free(port_gs_list); + sw_shm_free(workers); + + session_list = nullptr; + port_gs_list = nullptr; + workers = nullptr; + + delete factory; + factory = nullptr; + + g_server_instance = nullptr; } /** * worker to master process */ -static int swServer_tcp_feedback(swServer *serv, int session_id, int event) -{ - swConnection *conn = swServer_connection_verify(serv, session_id); - if (!conn) - { - return SW_ERR; - } - - swSendData _send; - bzero(&_send, sizeof(_send)); +bool Server::feedback(Connection *conn, enum ServerEventType event) { + SendData _send{}; _send.info.type = event; - _send.info.fd = session_id; + _send.info.fd = conn->session_id; _send.info.reactor_id = conn->reactor_id; - if (serv->factory_mode == SW_MODE_PROCESS) - { - return swWorker_send2reactor(serv, (swEventData *) &_send.info, sizeof(_send.info), session_id); - } - else - { - return swServer_master_send(serv, &_send); + if (is_process_mode()) { + return send_to_reactor_thread((EventData *) &_send.info, sizeof(_send.info), conn->session_id) > 0; + } else { + return send_to_connection(&_send) == SW_OK; } } -void swServer_store_pipe_fd(swServer *serv, swPipe *p) -{ - swSocket* master_socket = p->getSocket(p, SW_PIPE_MASTER); - swSocket* worker_socket = p->getSocket(p, SW_PIPE_WORKER); +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); - serv->connection_list[master_socket->fd].object = p; - serv->connection_list[worker_socket->fd].object = p; + connection_list[master_socket->fd].object = p; + connection_list[worker_socket->fd].object = p; - if (master_socket->fd > swServer_get_maxfd(serv)) - { - swServer_set_maxfd(serv, master_socket->fd); + if (master_socket->fd > get_maxfd()) { + set_maxfd(master_socket->fd); } - if (worker_socket->fd > swServer_get_maxfd(serv)) - { - swServer_set_maxfd(serv, worker_socket->fd); + if (worker_socket->fd > get_maxfd()) { + set_maxfd(worker_socket->fd); } } -swPipe * swServer_get_pipe_object(swServer *serv, int pipe_fd) -{ - return (swPipe *) serv->connection_list[pipe_fd].object; -} - /** * @process Worker - * @return SW_OK or SW_ERR */ -static int swServer_tcp_send(swServer *serv, int session_id, void *data, uint32_t length) -{ - swSendData _send; - bzero(&_send.info, sizeof(_send.info)); - swFactory *factory = &(serv->factory); - - if (sw_unlikely(swIsMaster())) - { - swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_SEND_IN_MASTER, "can't send data to the connections in master process"); - return SW_ERR; - } - +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_SEND_DATA; - _send.data = (char*) data; + _send.data = (char *) data; _send.info.len = length; - return factory->finish(factory, &_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) { + uint32_t key = 0; + + if (dispatch_func) { + int id = dispatch_func(this, get_connection(fd), data); + if (id != DISPATCH_RESULT_USERFUNC_FALLBACK) { + return id; + } + } + + // polling mode + 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 == DISPATCH_FDMOD) { + key = fd; + } + // Using the IP touch access to hash + else if (dispatch_mode == DISPATCH_IPMOD) { + Connection *conn = get_connection(fd); + // UDP + if (conn == nullptr) { + key = fd; + } + // IPv4 + else if (conn->socket_type == SW_SOCK_TCP) { + key = conn->info.addr.inet_v4.sin_addr.s_addr; + } + // IPv6 + else { +#ifdef HAVE_KQUEUE + 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]; +#else + key = conn->info.addr.inet_v6.sin6_addr.s6_addr32[3]; +#endif + } + } 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(); + } + // deliver tasks to idle worker processes + else { + return get_idle_worker_id(); + } + + return key % worker_num; } /** * [Master] send to client or append to out_buffer + * @return SW_OK or SW_ERR */ -int swServer_master_send(swServer *serv, swSendData *_send) -{ - uint32_t session_id = _send->info.fd; - char *_send_data = _send->data; +int Server::send_to_connection(SendData *_send) { + SessionId session_id = _send->info.fd; + const char *_send_data = _send->data; uint32_t _send_length = _send->info.len; - swConnection *conn; - if (_send->info.type != SW_SERVER_EVENT_CLOSE) - { - conn = swServer_connection_verify(serv, session_id); - } - else - { - conn = swServer_connection_verify_no_ssl(serv, session_id); - } - if (!conn) - { - if (_send->info.type == SW_SERVER_EVENT_SEND_DATA) - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_NOT_EXIST, "send %d byte failed, session#%d does not exist", _send_length, session_id); - } - else - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_NOT_EXIST, "send event$[%d] failed, session#%d does not exist", _send->info.type, session_id); + Connection *conn; + if (_send->info.type != SW_SERVER_EVENT_CLOSE) { + conn = get_connection_verify(session_id); + } else { + conn = get_connection_verify_no_ssl(session_id); + } + if (!conn) { + 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_TRACE, + SW_ERROR_SESSION_NOT_EXIST, + "send event[%d] failed, session#%ld does not exist", + _send->info.type, + session_id); } return SW_ERR; } int fd = conn->fd; - swReactor *reactor = SwooleTG.reactor; + Reactor *reactor = SwooleTG.reactor; + ListenPort *port = get_port_by_server_fd(conn->server_fd); - if (!serv->single_thread) - { - assert(fd % serv->reactor_num == reactor->id); - assert(fd % serv->reactor_num == SwooleTG.id); + if (!single_thread) { + assert(fd % reactor_num == reactor->id); + assert(fd % reactor_num == SwooleTG.id); } - if (serv->factory_mode == SW_MODE_BASE && conn->overflow) - { - if (serv->send_yield) - { - SwooleG.error = SW_ERROR_OUTPUT_SEND_YIELD; - } - else - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_OUTPUT_BUFFER_OVERFLOW, "connection#%d output buffer overflow", fd); + if (!is_process_mode() && conn->overflow) { + if (send_yield) { + swoole_set_last_error(SW_ERROR_OUTPUT_SEND_YIELD); + } else { + swoole_error_log(SW_LOG_WARNING, SW_ERROR_OUTPUT_BUFFER_OVERFLOW, "socket#%d output buffer overflow", fd); } return SW_ERR; } - swSocket *_socket = conn->socket; + Socket *_socket = conn->socket; /** * Reset send buffer, Immediately close the connection. */ - if (_send->info.type == SW_SERVER_EVENT_CLOSE && (conn->close_reset || conn->peer_closed)) - { + if (_send->info.type == SW_SERVER_EVENT_CLOSE && (conn->close_reset || conn->close_force || conn->peer_closed)) { goto _close_fd; } /** * pause recv data */ - else if (_send->info.type == SW_SERVER_EVENT_PAUSE_RECV) - { - if (_socket->removed || !(_socket->events & SW_EVENT_READ)) - { + else if (_send->info.type == SW_SERVER_EVENT_PAUSE_RECV) { + if (_socket->removed || !(_socket->events & SW_EVENT_READ)) { return SW_OK; } - if (_socket->events & SW_EVENT_WRITE) - { - return reactor->set(reactor, conn->socket, SW_EVENT_WRITE); - } - else - { - return reactor->del(reactor, conn->socket); + if (_socket->events & SW_EVENT_WRITE) { + return reactor->set(conn->socket, SW_EVENT_WRITE); + } else { + return reactor->del(conn->socket); } } /** * resume recv data */ - else if (_send->info.type == SW_SERVER_EVENT_RESUME_RECV) - { - if (!_socket->removed || (_socket->events & SW_EVENT_READ)) - { + else if (_send->info.type == SW_SERVER_EVENT_RESUME_RECV) { + if (!_socket->removed || (_socket->events & SW_EVENT_READ)) { return SW_OK; } - if (_socket->events & SW_EVENT_WRITE) - { - return reactor->set(reactor, _socket, SW_EVENT_READ | SW_EVENT_WRITE); - } - else - { - return reactor->add(reactor, _socket, SW_EVENT_READ); + if (_socket->events & SW_EVENT_WRITE) { + return reactor->set(_socket, SW_EVENT_READ | SW_EVENT_WRITE); + } else { + return reactor->add(_socket, SW_EVENT_READ); } } - if (swBuffer_empty(_socket->out_buffer)) - { + if (Buffer::empty(_socket->out_buffer)) { /** * close connection. */ - if (_send->info.type == SW_SERVER_EVENT_CLOSE) - { - _close_fd: + if (_send->info.type == SW_SERVER_EVENT_CLOSE) { + _close_fd: reactor->close(reactor, _socket); return SW_OK; } - //Direct send - if (_send->info.type != SW_SERVER_EVENT_SEND_FILE) - { - if (!_socket->direct_send) - { + // Direct send + if (_send->info.type != SW_SERVER_EVENT_SEND_FILE) { + if (!_socket->direct_send) { goto _buffer_send; } ssize_t n; - _direct_send: - n = swSocket_send(_socket, _send_data, _send_length, 0); - if (n == _send_length) - { + _direct_send: + n = _socket->send(_send_data, _send_length, 0); + if (n == _send_length) { + conn->last_send_time = microtime(); return SW_OK; - } - else if (n > 0) - { + } else if (n > 0) { _send_data += n; _send_length -= n; goto _buffer_send; - } - else if (errno == EINTR) - { + } else if (errno == EINTR) { goto _direct_send; - } - else - { + } else { goto _buffer_send; } } - //buffer send - else - { - _buffer_send: - if (!_socket->out_buffer) - { - _socket->out_buffer = swBuffer_new(SW_SEND_BUFFER_SIZE); - if (_socket->out_buffer == NULL) - { - return SW_ERR; - } + // buffer send + else { + _buffer_send: + if (!_socket->out_buffer) { + _socket->out_buffer = new Buffer(SW_SEND_BUFFER_SIZE); } } } - swBuffer_chunk *chunk; - //close connection - if (_send->info.type == SW_SERVER_EVENT_CLOSE) - { - chunk = swBuffer_new_chunk(_socket->out_buffer, SW_CHUNK_CLOSE, 0); - if (chunk == nullptr) - { - return SW_ERR; - } - chunk->store.data.val1 = _send->info.type; + // close connection + if (_send->info.type == SW_SERVER_EVENT_CLOSE) { + _socket->out_buffer->alloc(BufferChunk::TYPE_CLOSE, 0); conn->close_queued = 1; } - //sendfile to client - else if (_send->info.type == SW_SERVER_EVENT_SEND_FILE) - { - swSendFile_request *req = (swSendFile_request *) _send_data; - if (swSocket_sendfile(conn->socket, req->filename, req->offset, req->length) < 0) - { - return SW_ERR; - } - } - //send data - else - { - //connection is closed - if (conn->peer_closed) - { - swWarn("connection#%d is closed by client", fd); - return SW_ERR; - } - //connection output buffer overflow - if (_socket->out_buffer->length >= _socket->buffer_size) - { - if (serv->send_yield) - { - SwooleG.error = SW_ERROR_OUTPUT_SEND_YIELD; - } - else - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_OUTPUT_BUFFER_OVERFLOW, "connection#%d output buffer overflow", fd); + // sendfile to client + else if (_send->info.type == SW_SERVER_EVENT_SEND_FILE) { + SendfileTask *task = (SendfileTask *) _send_data; + if (conn->socket->sendfile(task->filename, task->offset, task->length) < 0) { + return false; + } + } + // send data + else { + // connection is closed + if (conn->peer_closed) { + 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 + if (_socket->out_buffer->length() >= _socket->buffer_size) { + if (send_yield) { + swoole_set_last_error(SW_ERROR_OUTPUT_SEND_YIELD); + } else { + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_OUTPUT_BUFFER_OVERFLOW, "connection#%d output buffer overflow", fd); } conn->overflow = 1; - if (serv->onBufferEmpty && serv->onBufferFull == NULL) - { + if (onBufferEmpty && onBufferFull == nullptr) { conn->high_watermark = 1; } } - if (swBuffer_append(_socket->out_buffer, _send_data, _send_length) < 0) - { - swWarn("append to pipe_buffer failed"); - return SW_ERR; - } + _socket->out_buffer->append(_send_data, _send_length); + conn->send_queued_bytes = _socket->out_buffer->length(); - swListenPort *port = swServer_get_port(serv, fd); - if (serv->onBufferFull && conn->high_watermark == 0 && _socket->out_buffer->length >= port->buffer_high_watermark) - { - serv->notify(serv, conn, SW_SERVER_EVENT_BUFFER_FULL); + ListenPort *port = get_port_by_fd(fd); + if (onBufferFull && conn->high_watermark == 0 && _socket->out_buffer->length() >= port->buffer_high_watermark) { + notify(conn, SW_SERVER_EVENT_BUFFER_FULL); conn->high_watermark = 1; } } - //listen EPOLLOUT event - if (reactor->set(reactor, _socket, SW_EVENT_WRITE | SW_EVENT_READ) < 0 && (errno == EBADF || errno == ENOENT)) - { - goto _close_fd; + if (port->max_idle_time > 0 && _socket->send_timer == nullptr) { + 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((long) (port->max_idle_time * 1000), true, timeout_callback); + } + + if (!_socket->isset_writable_event()) { + reactor->add_write_event(_socket); } return SW_OK; @@ -1395,571 +1498,420 @@ int swServer_master_send(swServer *serv, swSendData *_send) /** * use in master process */ -static int swServer_tcp_notify(swServer *serv, swConnection *conn, int event) -{ - swDataHead notify_event = {}; +bool Server::notify(Connection *conn, enum ServerEventType event) { + DataHead notify_event{}; notify_event.type = event; notify_event.reactor_id = conn->reactor_id; notify_event.fd = conn->fd; notify_event.server_fd = conn->server_fd; - return serv->factory.notify(&serv->factory, ¬ify_event); + return factory->notify(¬ify_event); } /** * @process Worker - * @return SW_OK or SW_ERR */ -static int swServer_tcp_sendfile(swServer *serv, int session_id, const char *file, uint32_t l_file, off_t offset, size_t length) -{ - if (sw_unlikely(session_id <= 0 || session_id > SW_MAX_SESSION_ID)) - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_SESSION_INVALID_ID, "invalid fd[%d]", session_id); - return SW_ERR; +bool Server::sendfile(SessionId session_id, const char *file, uint32_t l_file, off_t offset, size_t length) { + if (sw_unlikely(session_id <= 0)) { + swoole_error_log(SW_LOG_WARNING, SW_ERROR_SESSION_INVALID_ID, "invalid fd[%ld]", session_id); + return false; } - if (sw_unlikely(swIsMaster())) - { - swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_SEND_IN_MASTER, "can't send data to the connections in master process"); - return SW_ERR; + if (sw_unlikely(is_master())) { + swoole_error_log( + SW_LOG_ERROR, SW_ERROR_SERVER_SEND_IN_MASTER, "can't send data to the connections in master process"); + return false; } char _buffer[SW_IPC_BUFFER_SIZE]; - swSendFile_request *req = (swSendFile_request*) _buffer; + SendfileTask *req = reinterpret_cast(_buffer); // file name size - if (sw_unlikely(l_file > SW_IPC_BUFFER_SIZE - sizeof(swSendFile_request) - 1)) - { - swoole_error_log( - SW_LOG_WARNING, SW_ERROR_NAME_TOO_LONG, "sendfile name[%.8s...] length %u is exceed the max name len %u", - file, l_file, (uint32_t) (SW_IPC_BUFFER_SIZE - sizeof(swSendFile_request) - 1) - ); - return SW_ERR; + if (sw_unlikely(l_file > sizeof(_buffer) - sizeof(*req) - 1)) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_NAME_TOO_LONG, + "sendfile name[%.8s...] length %u is exceed the max name len %u", + file, + l_file, + (uint32_t) (SW_IPC_BUFFER_SIZE - sizeof(SendfileTask) - 1)); + return false; } // string must be zero termination (for `state` system call) - char *_file = strncpy((char *) req->filename, file, l_file); - _file[l_file] = '\0'; + swoole_strlcpy((char *) req->filename, file, sizeof(_buffer) - sizeof(*req)); // check state struct stat file_stat; - if (stat(_file, &file_stat) < 0) - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_SYSTEM_CALL_FAIL, "stat(%s) failed", _file); - return SW_ERR; - } - if (file_stat.st_size <= offset) - { + if (stat(req->filename, &file_stat) < 0) { + 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 SW_ERR; + return false; } req->offset = offset; req->length = length; // construct send data - swSendData send_data = {}; + SendData send_data{}; send_data.info.fd = session_id; send_data.info.type = SW_SERVER_EVENT_SEND_FILE; - send_data.info.len = sizeof(swSendFile_request) + l_file + 1; + send_data.info.len = sizeof(SendfileTask) + l_file + 1; send_data.data = _buffer; - return serv->factory.finish(&serv->factory, &send_data) < 0 ? SW_ERR : SW_OK; + return factory->finish(&send_data); } /** * [Worker] Returns the number of bytes sent */ -static int swServer_tcp_sendwait(swServer *serv, int session_id, void *data, uint32_t length) -{ - swConnection *conn = swServer_connection_verify(serv, session_id); - if (!conn) - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED, "send %d byte failed, because session#%d is closed", length, session_id); - return SW_ERR; - } - return swSocket_write_blocking(conn->socket, data, length); -} - -static sw_inline swString *swServer_worker_get_input_buffer(swServer *serv, int reactor_id) -{ - swString **buffers = (swString **) SwooleWG.input_buffers; - if (serv->factory_mode == SW_MODE_BASE) - { - return buffers[0]; - } - else - { - return buffers[reactor_id]; - } +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_TRACE, + SW_ERROR_SESSION_NOT_EXIST, + "send %d byte failed, because session#%ld is not exists", + length, + session_id); + return false; + } + return conn->socket->send_blocking(data, length) == length; } -static sw_inline void swServer_server_worker_set_buffer(swServer *serv, swDataHead *info, swString *addr) -{ - swString **buffers = (swString **) SwooleWG.input_buffers; - buffers[info->reactor_id] = addr; +void Server::call_hook(HookType type, void *arg) { + assert(type <= HOOK_END); + swoole::hook_call(hooks, type, arg); } -static void* swServer_worker_get_buffer(swServer *serv, swDataHead *info) -{ - swString *worker_buffer = swServer_worker_get_input_buffer(serv, info->reactor_id); - - if (worker_buffer == NULL) - { - worker_buffer = swString_new(info->len); - swServer_server_worker_set_buffer(serv, info, worker_buffer); - } - - return worker_buffer->str + worker_buffer->length; +/** + * [Worker] + */ +bool Server::close(SessionId session_id, bool reset) { + return factory->end(session_id, reset ? (CLOSE_ACTIVELY | CLOSE_RESET) : CLOSE_ACTIVELY); } -static size_t swServer_worker_get_buffer_len(swServer *serv, swDataHead *info) -{ - swString *worker_buffer = swServer_worker_get_input_buffer(serv, info->reactor_id); - - return worker_buffer == NULL ? 0 : worker_buffer->length; -} +bool Server::send_pipe_message(WorkerId worker_id, EventData *msg) { + msg->info.type = SW_SERVER_EVENT_PIPE_MESSAGE; -static void swServer_worker_add_buffer_len(swServer *serv, swDataHead *info, size_t len) -{ - swString *worker_buffer = swServer_worker_get_input_buffer(serv, info->reactor_id); - worker_buffer->length += len; + return send_to_worker_from_worker(get_worker(worker_id), msg, msg->size(), SW_PIPE_MASTER | SW_PIPE_NONBLOCK) > 0; } -static void swServer_worker_move_buffer(swServer *serv, swPipeBuffer *buffer) -{ - swString *worker_buffer = swServer_worker_get_input_buffer(serv, buffer->info.reactor_id); - memcpy(buffer->data, &worker_buffer, sizeof(worker_buffer)); - swServer_server_worker_set_buffer(serv, &buffer->info, NULL); -} +void Server::init_signal_handler() { + swoole_signal_set(SIGPIPE, nullptr); + swoole_signal_set(SIGHUP, nullptr); + if (is_process_mode()) { + swoole_signal_set(SIGCHLD, master_signal_handler); + } else { + swoole_signal_set(SIGIO, master_signal_handler); + } + swoole_signal_set(SIGUSR1, master_signal_handler); + swoole_signal_set(SIGUSR2, master_signal_handler); + swoole_signal_set(SIGTERM, master_signal_handler); +#ifdef SIGRTMIN + swoole_signal_set(SIGRTMIN, master_signal_handler); +#endif + // for test + swoole_signal_set(SIGVTALRM, master_signal_handler); -static size_t swServer_worker_get_packet(swServer *serv, swEventData *req, char **data_ptr) -{ - size_t length; - if (req->info.flags & SW_EVENT_DATA_PTR) - { - swPacket_ptr *task = (swPacket_ptr *) req; - *data_ptr = task->data.str; - length = task->data.length; - } - else if (req->info.flags & SW_EVENT_DATA_OBJ_PTR) - { - swString *worker_buffer; - memcpy(&worker_buffer, req->data, sizeof(worker_buffer)); - *data_ptr = worker_buffer->str; - length = worker_buffer->length; + if (SwooleG.signal_fd > 0) { + set_minfd(SwooleG.signal_fd); } - else - { - *data_ptr = req->data; - length = req->info.len; - } - - return length; -} - -SW_API void swServer_call_hook(swServer *serv, enum swServer_hook_type type, void *arg) -{ - swoole::hook_call(serv->hooks, type, arg); } -/** - * [Worker] - */ -static int swServer_tcp_close(swServer *serv, int session_id, int reset) -{ - if (sw_unlikely(swIsMaster())) - { - swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_SEND_IN_MASTER, "can't close the connections in master process"); - return SW_ERR; - } - swConnection *conn = swServer_connection_verify_no_ssl(serv, session_id); - if (!conn) - { - return SW_ERR; - } - //Reset send buffer, Immediately close the connection. - if (reset) - { - conn->close_reset = 1; - } - //server is initiative to close the connection - conn->close_actively = 1; - swTraceLog(SW_TRACE_CLOSE, "session_id=%d, fd=%d", session_id, conn->session_id); - - int retval; - swWorker *worker; - swDataHead ev = {}; - - if (swServer_dispatch_mode_is_mod(serv)) - { - int worker_id = swServer_worker_schedule(serv, conn->fd, nullptr); - if (worker_id != (int) SwooleWG.id) - { - worker = swServer_get_worker(serv, worker_id); - goto _notify; - } - else - { - goto _close; - } - } - else if (!swIsWorker()) - { - worker = swServer_get_worker(serv, conn->fd % serv->worker_num); - _notify: - ev.type = SW_SERVER_EVENT_CLOSE; - ev.fd = session_id; - ev.reactor_id = conn->reactor_id; - retval = swWorker_send2worker(worker, &ev, sizeof(ev), SW_PIPE_MASTER); - } - else - { - _close: - retval = serv->factory.end(&serv->factory, session_id); - } - return retval; -} - -void swServer_signal_init(swServer *serv) -{ - swSignal_add(SIGPIPE, NULL); - swSignal_add(SIGHUP, NULL); - if (serv->factory_mode == SW_MODE_PROCESS) - { - swSignal_add(SIGCHLD, swServer_signal_handler); - } - swSignal_add(SIGUSR1, swServer_signal_handler); - swSignal_add(SIGUSR2, swServer_signal_handler); - swSignal_add(SIGTERM, swServer_signal_handler); -#ifdef SIGRTMIN - swSignal_add(SIGRTMIN, swServer_signal_handler); -#endif - swSignal_add(SIGALRM, swSystemTimer_signal_handler); - //for test - swSignal_add(SIGVTALRM, swServer_signal_handler); - swServer_set_minfd(sw_server(), SwooleG.signal_fd); -} - -void swServer_master_onTimer(swTimer *timer, swTimer_node *tnode) -{ - swServer *serv = (swServer *) tnode->data; - time_t now = time(NULL); - if (serv->scheduler_warning && serv->warning_time < now) - { - serv->scheduler_warning = 0; +void Server::timer_callback(Timer *timer, TimerNode *tnode) { + Server *serv = (Server *) tnode->data; + time_t now = ::time(nullptr); + if (serv->scheduler_warning && serv->warning_time < now) { + serv->scheduler_warning = false; serv->warning_time = now; swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_NO_IDLE_WORKER, "No idle worker is available"); } - if (serv->hooks[SW_SERVER_HOOK_MASTER_TIMER]) - { - swServer_call_hook(serv, SW_SERVER_HOOK_MASTER_TIMER, serv); + if (serv->gs->task_workers.scheduler_warning && serv->gs->task_workers.warning_time < now) { + serv->gs->task_workers.scheduler_warning = 0; + serv->gs->task_workers.warning_time = now; + swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_NO_IDLE_WORKER, "No idle task worker is available"); } -} -int swServer_add_worker(swServer *serv, swWorker *worker) -{ - if (serv->user_worker_list == nullptr) - { - serv->user_worker_list = new std::vector; + if (serv->hooks[Server::HOOK_MASTER_TIMER]) { + serv->call_hook(Server::HOOK_MASTER_TIMER, serv); } - serv->user_worker_num++; - serv->user_worker_list->push_back(worker); - if (!serv->user_worker_map) - { - serv->user_worker_map = swHashMap_new(SW_HASHMAP_INIT_BUCKET_N, NULL); + 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; } -SW_API int swServer_add_hook(swServer *serv, enum swServer_hook_type type, swCallback func, int push_back) -{ - return swoole::hook_add(serv->hooks, (int) type, func, push_back); +int Server::add_hook(Server::HookType type, const Callback &func, int push_back) { + return swoole::hook_add(hooks, (int) type, func, push_back); } -static void swServer_check_port_type(swServer *serv, swListenPort *ls) -{ - if (swSocket_is_dgram(ls->type)) - { - //dgram socket, setting socket buffer size - swSocket_set_buffer_size(ls->socket, ls->socket_buffer_size); - serv->have_dgram_sock = 1; - serv->dgram_port_num++; - if (ls->type == SW_SOCK_UDP) - { - serv->udp_socket_ipv4 = ls->socket->fd; - } - else if (ls->type == SW_SOCK_UDP6) - { - serv->udp_socket_ipv6 = ls->socket->fd; - } +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 + ls->socket->set_buffer_size(ls->socket_buffer_size); + have_dgram_sock = 1; + dgram_port_num++; + if (ls->type == SW_SOCK_UDP) { + udp_socket_ipv4 = ls->socket; + } else if (ls->type == SW_SOCK_UDP6) { + udp_socket_ipv6 = ls->socket; + } else if (ls->type == SW_SOCK_UNIX_DGRAM) { + dgram_socket = ls->socket; + } + } else { + have_stream_sock = 1; + } +} + +bool Server::is_healthy_connection(double now, Connection *conn) { + if (conn->protect || conn->last_recv_time == 0) { + return true; } - else - { - serv->have_stream_sock = 1; + 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 swServer_add_systemd_socket(swServer *serv) -{ - char *e = getenv("LISTEN_PID"); - if (!e) - { - return 0; - } - - int pid = atoi(e); - if (getpid() != pid) - { - swWarn("invalid LISTEN_PID"); +int Server::add_systemd_socket() { + int pid; + if (!swoole_get_env("LISTEN_PID", &pid) && getpid() != pid) { + swoole_warning("invalid LISTEN_PID"); return 0; } int n = swoole_get_systemd_listen_fds(); - if (n == 0) - { + if (n <= 0) { return 0; } int count = 0; int sock; - for (sock = SW_SYSTEMD_FDS_START; sock < SW_SYSTEMD_FDS_START + n; sock++) - { - swListenPort *ls = (swListenPort *) SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(swListenPort)); - if (ls == NULL) - { - swWarn("alloc failed"); - return count; - } + int start_fd; + if (!swoole_get_env("LISTEN_FDS_START", &start_fd)) { + start_fd = SW_SYSTEMD_FDS_START; + } else if (start_fd < 0) { + swoole_warning("invalid LISTEN_FDS_START"); + return 0; + } - if (swPort_set_address(ls, sock) < 0) - { - return count; - } - ls->host[SW_HOST_MAXSIZE - 1] = 0; + for (sock = start_fd; sock < start_fd + n; sock++) { + std::unique_ptr ptr(new ListenPort()); + ListenPort *ls = ptr.get(); - //O_NONBLOCK & O_CLOEXEC - swoole_fcntl_set_option(sock, 1, 1); - ls->socket = swSocket_new(sock, swSocket_is_dgram(ls->type) ? SW_FD_DGRAM_SERVER : SW_FD_STREAM_SERVER); - if (ls->socket == nullptr) - { - close(sock); - return count; + if (!ls->import(sock)) { + continue; } - swServer_check_port_type(serv, ls); - serv->listen_list->push_back(ls); - serv->listen_port_num++; + // O_NONBLOCK & O_CLOEXEC + ls->socket->set_fd_option(1, 1); + + ptr.release(); + check_port_type(ls); + ports.push_back(ls); count++; } + return count; } -swListenPort* swServer_add_port(swServer *serv, enum swSocket_type type, const char *host, int port) -{ - if (serv->listen_port_num >= SW_MAX_LISTEN_PORT) - { - swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_TOO_MANY_LISTEN_PORT, "allows up to %d ports to listen", SW_MAX_LISTEN_PORT); - return NULL; +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; + } + if (ports.size() >= SW_MAX_LISTEN_PORT) { + swoole_error_log(SW_LOG_ERROR, + SW_ERROR_SERVER_TOO_MANY_LISTEN_PORT, + "up to %d listening ports are allowed", + SW_MAX_LISTEN_PORT); + return nullptr; } - if (!(type == SW_SOCK_UNIX_DGRAM || type == SW_SOCK_UNIX_STREAM) && (port < 0 || port > 65535)) - { + if (!(type == SW_SOCK_UNIX_DGRAM || type == SW_SOCK_UNIX_STREAM) && (port < 0 || port > 65535)) { swoole_error_log(SW_LOG_ERROR, SW_ERROR_SERVER_INVALID_LISTEN_PORT, "invalid port [%d]", port); - return NULL; + return nullptr; } - if (strlen(host) + 1 > SW_HOST_MAXSIZE) - { - swoole_error_log(SW_LOG_ERROR, SW_ERROR_NAME_TOO_LONG, "address '%s' exceeds %ld characters limit", host, SW_HOST_MAXSIZE - 1); - return NULL; + if (strlen(host) + 1 > SW_HOST_MAXSIZE) { + swoole_error_log(SW_LOG_ERROR, + SW_ERROR_NAME_TOO_LONG, + "address '%s' exceeds the limit of %ld characters", + host, + SW_HOST_MAXSIZE - 1); + return nullptr; } - swListenPort *ls = (swListenPort *) SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(swListenPort)); - if (ls == NULL) - { - swError("alloc failed"); - return NULL; - } + std::unique_ptr ptr(new ListenPort); + ListenPort *ls = ptr.get(); - swPort_init(ls); ls->type = type; ls->port = port; - strncpy(ls->host, host, SW_HOST_MAXSIZE - 1); - ls->host[SW_HOST_MAXSIZE - 1] = 0; + ls->host = host; #ifdef SW_USE_OPENSSL - if (type & SW_SOCK_SSL) - { - type = (enum swSocket_type) (type & (~SW_SOCK_SSL)); + if (type & SW_SOCK_SSL) { + type = (SocketType) (type & (~SW_SOCK_SSL)); ls->type = type; ls->ssl = 1; - ls->ssl_config.prefer_server_ciphers = 1; - ls->ssl_config.session_tickets = 0; - ls->ssl_config.stapling = 1; - ls->ssl_config.stapling_verify = 1; - ls->ssl_config.ciphers = sw_strdup(SW_SSL_CIPHER_LIST); - ls->ssl_config.ecdh_curve = sw_strdup(SW_SSL_ECDH_CURVE); - - if (swSocket_is_dgram(type)) - { + ls->ssl_context = new SSLContext(); + ls->ssl_context->prefer_server_ciphers = 1; + ls->ssl_context->session_tickets = 0; + ls->ssl_context->stapling = 1; + ls->ssl_context->stapling_verify = 1; + ls->ssl_context->ciphers = sw_strdup(SW_SSL_CIPHER_LIST); + ls->ssl_context->ecdh_curve = sw_strdup(SW_SSL_ECDH_CURVE); + + if (ls->is_dgram()) { #ifdef SW_SUPPORT_DTLS - ls->ssl_option.method = SW_DTLS_SERVER_METHOD; - ls->ssl_option.dtls = 1; - ls->dtls_sessions = new std::unordered_map; - + ls->ssl_context->protocols = SW_SSL_DTLS; + ls->dtls_sessions = new std::unordered_map; #else - swWarn("DTLS support require openssl-1.1 or later"); - return NULL; + swoole_warning("DTLS support require openssl-1.1 or later"); + return nullptr; #endif } } #endif - //create server socket - int sock = swSocket_create(ls->type, 1, 1); - if (sock < 0) - { - swSysWarn("create socket failed"); - return NULL; - } -#if defined(SW_SUPPORT_DTLS) && !defined(__linux__) - if (ls->ssl_option.dtls) - { - int on = 1; - setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &on, (socklen_t) sizeof(on)); - } -#endif - ls->socket = swSocket_new(sock, swSocket_is_dgram(ls->type) ? SW_FD_DGRAM_SERVER : SW_FD_STREAM_SERVER); - if (ls->socket == nullptr) - { - close(sock); + if (ls->create_socket(this) < 0) { + swoole_set_last_error(errno); return nullptr; } - ls->socket->nonblock = 1; - ls->socket->cloexec = 1; - ls->socket->socket_type = ls->type; - if (swSocket_bind(ls->socket, ls->host, &ls->port) < 0) - { - swSocket_free(ls->socket); - return NULL; - } - swServer_check_port_type(serv, ls); - ls->socket_fd = ls->socket->fd; - if (serv->listen_list == nullptr) - { - serv->listen_list = new std::vector; - } - serv->listen_list->push_back(ls); - serv->listen_port_num++; + + check_port_type(ls); + ptr.release(); + ports.push_back(ls); return ls; } -int swServer_get_socket(swServer *serv, int port) -{ - for (auto ls : *serv->listen_list) - { - if (ls->port == port || port == 0) - { - return ls->socket->fd; - } - } - return SW_ERR; -} +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()); -static void swServer_signal_handler(int sig) -{ - swTraceLog(SW_TRACE_SERVER, "signal[%d] %s triggered in %d", sig, swSignal_str(sig), getpid()); + Server *serv = sw_server(); + if (!SwooleG.running || !serv || !serv->is_running()) { + return; + } - swServer *serv = sw_server(); - int status; - pid_t pid; - switch (sig) - { + switch (signo) { case SIGTERM: - swServer_shutdown(serv); - break; - case SIGALRM: - swSystemTimer_signal_handler(SIGALRM); + serv->signal_handler_shutdown(); break; case SIGCHLD: - if (!serv->running) - { - break; - } - if (sw_server()->factory_mode == SW_MODE_BASE) - { - 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 (sw_server()->factory_mode == SW_MODE_BASE) - { - if (serv->gs->event_workers.reloading) - { - break; - } - serv->gs->event_workers.reloading = 1; - serv->gs->event_workers.reload_init = 0; - } - else - { - swoole_kill(serv->gs->manager_pid, sig); - } + serv->signal_handler_reload(signo == SIGUSR1); + break; + case SIGIO: + serv->signal_handler_read_message(); break; default: #ifdef SIGRTMIN - if (sig == SIGRTMIN) - { - uint32_t i; - swWorker *worker; - for (i = 0; i < sw_server()->worker_num + serv->task_worker_num + sw_server()->user_worker_num; i++) - { - worker = swServer_get_worker(sw_server(), i); - swoole_kill(worker->pid, SIGRTMIN); - } - if (sw_server()->factory_mode == SW_MODE_PROCESS) - { - swoole_kill(serv->gs->manager_pid, SIGRTMIN); - } - swLog_reopen(sw_server()->daemonize ? SW_TRUE : SW_FALSE); + if (signo == SIGRTMIN) { + serv->signal_handler_reopen_logger(); } #endif break; } } -void swServer_connection_each(swServer *serv, void (*callback)(swConnection *conn)) -{ - swConnection *conn; +void Server::foreach_connection(const std::function &callback) { + for (int fd = get_minfd(); fd <= get_maxfd(); fd++) { + Connection *conn = get_connection(fd); + if (is_valid_connection(conn)) { + callback(conn); + } + } +} - int fd; - int serv_max_fd = swServer_get_maxfd(serv); - int serv_min_fd = swServer_get_minfd(serv); +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(); + } +} - for (fd = serv_min_fd; fd <= serv_max_fd; fd++) - { - conn = swServer_connection_get(serv, fd); - if (conn && conn->socket && conn->active == 1 && conn->closed == 0 && conn->socket->fdtype == SW_FD_SESSION) - { - callback(conn); +// 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; + } } } } @@ -1967,146 +1919,175 @@ void swServer_connection_each(swServer *serv, void (*callback)(swConnection *con /** * new connection */ -static swConnection* swServer_connection_new(swServer *serv, swListenPort *ls, swSocket *_socket, int server_fd) -{ - serv->stats->accept_count++; - sw_atomic_fetch_add(&serv->stats->connection_num, 1); - sw_atomic_fetch_add(&ls->connection_num, 1); - time_t now; - +Connection *Server::add_connection(ListenPort *ls, Socket *_socket, int server_fd) { int fd = _socket->fd; - if (fd > swServer_get_maxfd(serv)) - { - swServer_set_maxfd(serv, fd); - } - else if (fd < swServer_get_minfd(serv)) - { - swServer_set_minfd(serv, fd); + + 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; - swConnection *connection = &(serv->connection_list[fd]); - bzero(connection, sizeof(*connection)); _socket->object = connection; _socket->removed = 1; _socket->buffer_size = ls->socket_buffer_size; + _socket->send_timeout_ = _socket->recv_timeout_ = 0; - //TCP Nodelay - if (ls->open_tcp_nodelay && (ls->type == SW_SOCK_TCP || ls->type == SW_SOCK_TCP6)) - { - int sockopt = 1; - if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &sockopt, sizeof(sockopt)) != 0) - { - swSysWarn("setsockopt(TCP_NODELAY) failed"); + // TCP Nodelay + if (ls->open_tcp_nodelay && (ls->type == SW_SOCK_TCP || ls->type == SW_SOCK_TCP6)) { + if (_socket->set_tcp_nodelay() != 0) { + swoole_sys_warning("setsockopt(TCP_NODELAY) failed"); } - _socket->tcp_nodelay = 1; + _socket->enable_tcp_nodelay = true; } - //socket recv buffer size - if (ls->kernel_socket_recv_buffer_size > 0) - { - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &ls->kernel_socket_recv_buffer_size, sizeof(int)) != 0) - { - swSysWarn("setsockopt(SO_RCVBUF, %d) failed", ls->kernel_socket_recv_buffer_size); + // 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) { + 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 (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &ls->kernel_socket_send_buffer_size, sizeof(int)) != 0) - { - swSysWarn("setsockopt(SO_SNDBUF, %d) failed", ls->kernel_socket_send_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) { + swoole_sys_warning("setsockopt(SO_SNDBUF, %d) failed", ls->kernel_socket_send_buffer_size); } } - now = time(NULL); - connection->fd = fd; - connection->reactor_id = serv->factory_mode == SW_MODE_BASE ? SwooleWG.id : fd % serv->reactor_num; + connection->reactor_id = reactor_id; connection->server_fd = (sw_atomic_t) server_fd; - connection->connect_time = now; - connection->last_time = now; + connection->last_recv_time = connection->connect_time = microtime(); connection->active = 1; + connection->worker_id = -1; connection->socket_type = ls->type; connection->socket = _socket; memcpy(&connection->info.addr, &_socket->info.addr, _socket->info.len); connection->info.len = _socket->info.len; + connection->info.type = connection->socket_type; - if (!ls->ssl) - { + if (!ls->ssl) { _socket->direct_send = 1; } - swSession *session; - sw_spinlock(&serv->gs->spinlock); - uint32_t i; - uint32_t session_id = serv->gs->session_round; - //get session id - for (i = 0; i < serv->max_connection; i++) - { - session_id++; - //SwooleGS->session_round just has 24 bits size; - if (sw_unlikely(session_id == 1 << 24)) - { - session_id = 1; - } - session = swServer_get_session(serv, session_id); - //vacancy - 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); } - serv->gs->session_round = session_id; - sw_spinlock_release(&serv->gs->spinlock); - connection->session_id = session_id; return connection; } -void swServer_set_ipc_max_size(swServer *serv) -{ -#ifdef HAVE_KQUEUE - serv->ipc_max_size = SW_IPC_MAX_SIZE; +void Server::init_ipc_max_size() { +#ifndef __linux__ + ipc_max_size = SW_IPC_MAX_SIZE; #else int bufsize; - socklen_t _len = sizeof(bufsize); /** * Get the maximum ipc[unix socket with dgram] transmission length */ - if (getsockopt(serv->workers[0].pipe_master->fd, SOL_SOCKET, SO_SNDBUF, &bufsize, &_len) != 0) - { + if (workers[0].pipe_master->get_option(SOL_SOCKET, SO_SNDBUF, &bufsize) != 0) { bufsize = SW_IPC_MAX_SIZE; } - serv->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 swServer::pipe_buffers + * allocate memory for Server::pipe_buffers */ -int swServer_create_pipe_buffers(swServer *serv) -{ - serv->pipe_buffers = (swPipeBuffer **) sw_calloc(serv->reactor_num, sizeof(swPipeBuffer *)); - if (serv->pipe_buffers == NULL) - { - swSysError("malloc[buffers] failed"); - return SW_ERR; +int Server::create_pipe_buffers() { + message_bus.set_buffer_size(ipc_max_size); + return message_bus.alloc_buffer() ? SW_OK : SW_ERR; +} + +void Server::release_pipe_buffers() { + message_bus.free_buffer(); +} + +int Server::get_idle_worker_num() { + uint32_t i; + uint32_t idle_worker_num = 0; + + for (i = 0; i < worker_num; i++) { + Worker *worker = get_worker(i); + if (worker->is_idle()) { + idle_worker_num++; + } } - for (uint32_t i = 0; i < serv->reactor_num; i++) - { - serv->pipe_buffers[i] = (swPipeBuffer *) sw_malloc(serv->ipc_max_size); - if (serv->pipe_buffers[i] == NULL) - { - swSysError("malloc[sndbuf][%d] failed", i); - return SW_ERR; + + return idle_worker_num; +} + +int Server::get_idle_task_worker_num() { + uint32_t idle_worker_num = 0; + + for (uint32_t i = worker_num; i < (worker_num + task_worker_num); i++) { + Worker *worker = get_worker(i); + if (worker->is_idle()) { + idle_worker_num++; } - bzero(serv->pipe_buffers[i], sizeof(swDataHead)); } - return SW_OK; + 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 125f20b2aca..7bc3699104a 100644 --- a/src/server/port.cc +++ b/src/server/port.cc @@ -10,317 +10,313 @@ | to obtain it through the world-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 "server.h" -#include "http.h" -#include "http2.h" -#include "websocket.h" -#include "mqtt.h" -#include "redis.h" - -static int swPort_onRead_raw(swReactor *reactor, swListenPort *lp, swEvent *event); -static int swPort_onRead_check_length(swReactor *reactor, swListenPort *lp, swEvent *event); -static int swPort_onRead_check_eof(swReactor *reactor, swListenPort *lp, swEvent *event); -static int swPort_onRead_http(swReactor *reactor, swListenPort *lp, swEvent *event); -static int swPort_onRead_redis(swReactor *reactor, swListenPort *lp, swEvent *event); - -void swPort_init(swListenPort *port) -{ - port->socket = nullptr; - port->ssl = 0; - - //listen backlog - port->backlog = SW_BACKLOG; - //tcp keepalive - port->tcp_keepcount = SW_TCP_KEEPCOUNT; - port->tcp_keepinterval = SW_TCP_KEEPINTERVAL; - port->tcp_keepidle = SW_TCP_KEEPIDLE; - port->open_tcp_nopush = 1; - - port->protocol.package_length_type = 'N'; - port->protocol.package_length_size = 4; - port->protocol.package_body_offset = 4; - port->protocol.package_max_length = SW_INPUT_BUFFER_SIZE; - - port->socket_buffer_size = SwooleG.socket_buffer_size; - - char eof[] = SW_DATA_EOF; - port->protocol.package_eof_len = sizeof(SW_DATA_EOF) - 1; - memcpy(port->protocol.package_eof, eof, port->protocol.package_eof_len); +#include "swoole_server.h" +#include "swoole_http.h" +#include "swoole_http2.h" +#include "swoole_websocket.h" +#include "swoole_mqtt.h" +#include "swoole_redis.h" + +using swoole::http_server::Request; +using swoole::network::Address; +using swoole::network::Socket; + +namespace swoole { + +static int Port_onRead_raw(Reactor *reactor, ListenPort *lp, Event *event); +static int Port_onRead_check_length(Reactor *reactor, ListenPort *lp, Event *event); +static int Port_onRead_check_eof(Reactor *reactor, ListenPort *lp, Event *event); +static int Port_onRead_http(Reactor *reactor, ListenPort *lp, Event *event); +static int Port_onRead_redis(Reactor *reactor, ListenPort *lp, Event *event); + +ListenPort::ListenPort() { + protocol.package_length_type = 'N'; + protocol.package_length_size = 4; + protocol.package_body_offset = 4; + protocol.package_max_length = SW_INPUT_BUFFER_SIZE; + + protocol.package_eof_len = sizeof(SW_DATA_EOF) - 1; + memcpy(protocol.package_eof, SW_DATA_EOF, protocol.package_eof_len); } #ifdef SW_USE_OPENSSL -int swPort_enable_ssl_encrypt(swListenPort *ls) -{ - if (ls->ssl_option.cert_file == NULL || ls->ssl_option.key_file == NULL) - { - swWarn("SSL error, require ssl_cert_file and ssl_key_file"); - return SW_ERR; + +bool ListenPort::ssl_add_sni_cert(const std::string &name, SSLContext *ctx) { + if (!ssl_create_context(ctx)) { + return false; } - ls->ssl_context = swSSL_get_context(&ls->ssl_option); - if (ls->ssl_context == NULL) - { - swWarn("swSSL_get_context() error"); - return SW_ERR; + sni_contexts.emplace(name, std::shared_ptr(ctx)); + return true; +} + +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; + + if (strcasecmp(subjectname, certname) == 0) { + return 1; } - if (ls->ssl_option.client_cert_file - && swSSL_set_client_certificate(ls->ssl_context, ls->ssl_option.client_cert_file, - ls->ssl_option.verify_depth) == SW_ERR) - { - swWarn("swSSL_set_client_certificate() error"); - return SW_ERR; + + /* wildcard, if present, must only be present in the left-most component */ + if (!(wildcard = strchr(certname, '*')) || memchr(certname, '.', wildcard - certname)) { + return 0; } - if (ls->open_http_protocol) - { - ls->ssl_config.http = 1; + + /* 1) prefix, if not empty, must match subject */ + prefix_len = wildcard - certname; + if (prefix_len && strncasecmp(subjectname, certname, prefix_len) != 0) { + return 0; } - if (ls->open_http2_protocol) - { - ls->ssl_config.http_v2 = 1; - swSSL_server_http_advise(ls->ssl_context, &ls->ssl_config); + + suffix_len = strlen(wildcard + 1); + subject_len = strlen(subjectname); + if (suffix_len <= subject_len) { + /* 2) suffix must match + * 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; } - if (swSSL_server_set_cipher(ls->ssl_context, &ls->ssl_config) < 0) - { - swWarn("swSSL_server_set_cipher() error"); - return SW_ERR; + + return 0; +} + +static int ssl_server_sni_callback(SSL *ssl, int *al, void *arg) { + const char *server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (!server_name) { + return SSL_TLSEXT_ERR_NOACK; } - return SW_OK; + + ListenPort *port = (ListenPort *) SSL_get_ex_data(ssl, swoole_ssl_get_ex_port_index()); + + if (port->sni_contexts.empty()) { + return SSL_TLSEXT_ERR_NOACK; + } + + for (auto i = port->sni_contexts.begin(); i != port->sni_contexts.end(); i++) { + if (ssl_matches_wildcard_name(server_name, i->first.c_str())) { + SSL_set_SSL_CTX(ssl, i->second->get_context()); + return SSL_TLSEXT_ERR_OK; + } + } + + return SSL_TLSEXT_ERR_NOACK; } -#endif -int swPort_listen(swListenPort *ls) -{ - int sock = ls->socket->fd; - int option = 1; +bool ListenPort::ssl_init() { + if (!ssl_create_context(ssl_context)) { + return false; + } + if (sni_contexts.size() > 0) { + SSL_CTX_set_tlsext_servername_callback(ssl_context->get_context(), ssl_server_sni_callback); + } + return true; +} + +bool ListenPort::ssl_create(Connection *conn, Socket *sock) { + if (sock->ssl_create(ssl_context, SW_SSL_SERVER) < 0) { + return false; + } + conn->ssl = 1; + 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; +} - //listen stream socket - if (listen(sock, ls->backlog) < 0) - { - swSysWarn("listen(%s:%d, %d) failed", ls->host, ls->port, ls->backlog); +bool ListenPort::ssl_create_context(SSLContext *context) { + if (context->cert_file.empty() || context->key_file.empty()) { + swoole_warning("SSL error, require ssl_cert_file and ssl_key_file"); + return false; + } + if (open_http_protocol) { + context->http = 1; + } + if (open_http2_protocol) { + context->http_v2 = 1; + } + if (!context->create()) { + swoole_warning("failed to create ssl content"); + return false; + } + return true; +} +#endif + +int ListenPort::listen() { + // listen stream socket + if (!listening && socket->listen(backlog) < 0) { + swoole_sys_warning("listen(%s:%d, %d) failed", host.c_str(), port, backlog); return SW_ERR; } + listening = true; #ifdef TCP_DEFER_ACCEPT - if (ls->tcp_defer_accept) - { - if (setsockopt(sock, IPPROTO_TCP, TCP_DEFER_ACCEPT, (const void*) &ls->tcp_defer_accept, sizeof(int)) != 0) - { - swSysWarn("setsockopt(TCP_DEFER_ACCEPT) failed"); + if (tcp_defer_accept) { + if (socket->set_option(IPPROTO_TCP, TCP_DEFER_ACCEPT, tcp_defer_accept) != 0) { + 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 #ifdef TCP_FASTOPEN - if (ls->tcp_fastopen) - { - if (setsockopt(sock, IPPROTO_TCP, TCP_FASTOPEN, (const void*) &ls->tcp_fastopen, sizeof(int)) != 0) - { - swSysWarn("setsockopt(TCP_FASTOPEN) failed"); + if (tcp_fastopen) { + if (socket->set_option(IPPROTO_TCP, TCP_FASTOPEN, tcp_fastopen) != 0) { + swoole_sys_warning("setsockopt(TCP_FASTOPEN) failed"); } } #endif #ifdef SO_KEEPALIVE - if (ls->open_tcp_keepalive == 1) - { - if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *) &option, sizeof(option)) != 0) - { - swSysWarn("setsockopt(SO_KEEPALIVE) failed"); + if (open_tcp_keepalive == 1) { + if (socket->set_option(SOL_SOCKET, SO_KEEPALIVE, 1) != 0) { + swoole_sys_warning("setsockopt(SO_KEEPALIVE) failed"); } #ifdef TCP_KEEPIDLE - setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, (void*) &ls->tcp_keepidle, sizeof(int)); - setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, (void *) &ls->tcp_keepinterval, sizeof(int)); - setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, (void *) &ls->tcp_keepcount, sizeof(int)); + if (socket->set_option(IPPROTO_TCP, TCP_KEEPIDLE, tcp_keepidle) < 0) { + swoole_sys_warning("setsockopt(TCP_KEEPIDLE) failed"); + } + if (socket->set_option(IPPROTO_TCP, TCP_KEEPINTVL, tcp_keepinterval) < 0) { + swoole_sys_warning("setsockopt(TCP_KEEPINTVL) failed"); + } + if (socket->set_option(IPPROTO_TCP, TCP_KEEPCNT, tcp_keepcount) < 0) { + 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) { + swoole_sys_warning("setsockopt(TCP_USER_TIMEOUT) failed"); + } #endif } #endif - ls->buffer_high_watermark = ls->socket_buffer_size * 0.8; - ls->buffer_low_watermark = 0; + buffer_high_watermark = socket_buffer_size * 0.8; + buffer_low_watermark = 0; return SW_OK; } -void swPort_set_protocol(swServer *serv, swListenPort *ls) -{ - ls->protocol.private_data_2 = serv; - //Thread mode must copy the data. - //will free after onFinish - if (ls->open_eof_check) - { - if (ls->protocol.package_eof_len > SW_DATA_EOF_MAXLEN) - { +void Server::init_port_protocol(ListenPort *ls) { + ls->protocol.private_data_2 = this; + // Thread mode must copy the data. + // will free after onFinish + if (ls->open_eof_check) { + if (ls->protocol.package_eof_len > SW_DATA_EOF_MAXLEN) { ls->protocol.package_eof_len = SW_DATA_EOF_MAXLEN; } - ls->protocol.onPackage = swReactorThread_dispatch; - ls->onRead = swPort_onRead_check_eof; - } - else if (ls->open_length_check) - { - if (ls->protocol.package_length_type != '\0') - { - ls->protocol.get_package_length = swProtocol_get_package_length; - } - ls->protocol.onPackage = swReactorThread_dispatch; - ls->onRead = swPort_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; - } - else if (ls->open_http2_protocol) - { + ls->protocol.onPackage = Server::dispatch_task; + ls->onRead = Port_onRead_check_eof; + } else if (ls->open_length_check) { + if (ls->protocol.package_length_type != '\0') { + ls->protocol.get_package_length = Protocol::default_length_func; + } + ls->protocol.onPackage = Server::dispatch_task; + ls->onRead = Port_onRead_check_length; + } else if (ls->open_http_protocol) { + if (ls->open_http2_protocol && ls->open_websocket_protocol) { + ls->protocol.get_package_length = http_server::get_package_length; + ls->protocol.get_package_length_size = http_server::get_package_length_size; + 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.onPackage = swReactorThread_dispatch; + ls->protocol.get_package_length = http2::get_frame_length; + ls->protocol.onPackage = Server::dispatch_task; + } 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; } - 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; - } - ls->protocol.package_length_offset = 0; - ls->protocol.package_body_offset = 0; - ls->onRead = swPort_onRead_http; - } - else if (ls->open_mqtt_protocol) - { - ls->protocol.package_length_size = SW_MQTT_MIN_LENGTH; ls->protocol.package_length_offset = 0; ls->protocol.package_body_offset = 0; - ls->protocol.get_package_length = swMqtt_get_package_length; - ls->protocol.onPackage = swReactorThread_dispatch; - ls->onRead = swPort_onRead_check_length; - } - else if (ls->open_redis_protocol) - { - ls->protocol.onPackage = swReactorThread_dispatch; - ls->onRead = swPort_onRead_redis; - } - else - { - ls->onRead = swPort_onRead_raw; + ls->onRead = Port_onRead_http; + } else if (ls->open_mqtt_protocol) { + mqtt::set_protocol(&ls->protocol); + ls->protocol.onPackage = Server::dispatch_task; + ls->onRead = Port_onRead_check_length; + } else if (ls->open_redis_protocol) { + ls->protocol.onPackage = Server::dispatch_task; + ls->onRead = Port_onRead_redis; + } else { + ls->onRead = Port_onRead_raw; } } /** - * @description: set the swListenPort.host and swListenPort.port in swListenPort from sock + * @description: import listen port from socket-fd */ -int swPort_set_address(swListenPort *ls, int sock) -{ - socklen_t optlen; - swSocketAddress address; - int sock_type, sock_family; - char tmp[INET6_ADDRSTRLEN]; - - //get socket type - optlen = sizeof(sock_type); - if (getsockopt(sock, SOL_SOCKET, SO_TYPE, &sock_type, &optlen) < 0) - { - swWarn("getsockopt(%d, SOL_SOCKET, SO_TYPE) failed", sock); - return -1; - } - //get socket family -#ifndef SO_DOMAIN - swWarn("no getsockopt(SO_DOMAIN) supports"); - return -1; -#else - optlen = sizeof(sock_family); - if (getsockopt(sock, SOL_SOCKET, SO_DOMAIN, &sock_family, &optlen) < 0) - { - swWarn("getsockopt(%d, SOL_SOCKET, SO_DOMAIN) failed", sock); - return -1; - } -#endif +bool ListenPort::import(int sock) { + int _type, _family; - //get address info - address.len = sizeof(address.addr); - if (getsockname(sock, (struct sockaddr*) &address.addr, &address.len) < 0) - { - swWarn("getsockname(%d) failed", sock); - return -1; + socket = new Socket(); + socket->fd = sock; + + // get socket type + if (socket->get_option(SOL_SOCKET, SO_TYPE, &_type) < 0) { + swoole_sys_warning("getsockopt(%d, SOL_SOCKET, SO_TYPE) failed", sock); + return false; + } + if (socket->get_name(&socket->info) < 0) { + swoole_sys_warning("getsockname(%d) failed", sock); + return false; } - swPort_init(ls); + _family = socket->info.addr.ss.sa_family; + socket->socket_type = socket->info.type = type = Socket::convert_to_type(_family, _type); + host = socket->info.get_addr(); + port = socket->info.get_port(); + listening = true; - switch (sock_family) - { - case AF_INET: - if (sock_type == SOCK_STREAM) - { - ls->type = SW_SOCK_TCP; - } - else - { - ls->type = SW_SOCK_UDP; - } - ls->port = ntohs(address.addr.inet_v4.sin_port); - strncpy(ls->host, inet_ntoa(address.addr.inet_v4.sin_addr), SW_HOST_MAXSIZE - 1); - break; - case AF_INET6: - if (sock_type == SOCK_STREAM) - { - ls->type = SW_SOCK_TCP6; - } - else - { - ls->type = SW_SOCK_UDP6; - } - ls->port = ntohs(address.addr.inet_v6.sin6_port); - inet_ntop(AF_INET6, &address.addr.inet_v6.sin6_addr, tmp, sizeof(tmp)); - strncpy(ls->host, tmp, SW_HOST_MAXSIZE - 1); - break; - case AF_UNIX: - ls->type = sock_type == SOCK_STREAM ? SW_SOCK_UNIX_STREAM : SW_SOCK_UNIX_DGRAM; - ls->port = 0; - strncpy(ls->host, address.addr.un.sun_path, SW_HOST_MAXSIZE); - break; - default: - swWarn("Unknown socket family[%d]", sock_family); - break; - } + socket->fd_type = socket->is_dgram() ? SW_FD_DGRAM_SERVER : SW_FD_STREAM_SERVER; + socket->removed = 1; - return 0; + return true; } -void swPort_clear_protocol(swListenPort *ls) -{ - ls->open_eof_check = 0; - ls->open_length_check = 0; - ls->open_http_protocol = 0; - ls->open_websocket_protocol = 0; -#ifdef SW_USE_HTTP2 - ls->open_http2_protocol = 0; -#endif - ls->open_mqtt_protocol = 0; - ls->open_redis_protocol = 0; +void ListenPort::clear_protocol() { + open_eof_check = 0; + open_length_check = 0; + open_http_protocol = 0; + open_websocket_protocol = 0; + open_http2_protocol = 0; + open_mqtt_protocol = 0; + open_redis_protocol = 0; } -static int swPort_onRead_raw(swReactor *reactor, swListenPort *port, swEvent *event) -{ +static int Port_onRead_raw(Reactor *reactor, ListenPort *port, Event *event) { ssize_t n; - swSocket *_socket = event->socket; - swConnection *conn = (swConnection *) _socket->object; - char *buffer = SwooleTG.buffer_stack->str; - - n = swSocket_recv(_socket, buffer, SwooleTG.buffer_stack->size, 0); - if (n < 0) - { - switch (swSocket_error(errno)) - { + 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) { + return SW_ERR; + } + + n = _socket->recv(buffer->str, buffer->size, 0); + if (n < 0) { + 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; @@ -328,109 +324,99 @@ static int swPort_onRead_raw(swReactor *reactor, swListenPort *port, swEvent *ev default: return SW_OK; } - } - else if (n == 0) - { - _close_fd: - swReactor_trigger_close_event(reactor, event); + } else if (n == 0) { + _close_fd: + reactor->trigger_close_event(event); return SW_OK; - } - else - { - return swReactorThread_dispatch(&port->protocol, _socket, buffer, n); + } else { + buffer->offset = buffer->length = n; + rdata.info.len = n; + rdata.data = buffer->str; + return Server::dispatch_task(&port->protocol, _socket, &rdata); } } -static int swPort_onRead_check_length(swReactor *reactor, swListenPort *port, swEvent *event) -{ - swSocket *_socket = event->socket; - swConnection *conn = (swConnection *) _socket->object; - swProtocol *protocol = &port->protocol; +static int Port_onRead_check_length(Reactor *reactor, ListenPort *port, Event *event) { + Socket *_socket = event->socket; + Connection *conn = (Connection *) _socket->object; + Protocol *protocol = &port->protocol; + Server *serv = (Server *) reactor->ptr; - swString *buffer = swSocket_get_buffer(_socket); - if (!buffer) - { + String *buffer = serv->get_recv_buffer(_socket); + if (!buffer) { + reactor->trigger_close_event(event); return SW_ERR; } - if (swProtocol_recv_check_length(protocol, _socket, buffer) < 0) - { - swTrace("Close Event.FD=%d|From=%d", event->fd, event->reactor_id); + if (protocol->recv_with_length_protocol(_socket, buffer) < 0) { + swoole_trace("Close Event.FD=%d|From=%d", event->fd, event->reactor_id); conn->close_errno = errno; - swReactor_trigger_close_event(reactor, event); + reactor->trigger_close_event(event); + } + + /** + * if the length is 0, which means the onPackage has been called, we can free the buffer. + */ + if (_socket->recv_buffer && _socket->recv_buffer->length == 0 && + _socket->recv_buffer->size > SW_BUFFER_SIZE_BIG * 2) { + delete _socket->recv_buffer; + _socket->recv_buffer = nullptr; } return SW_OK; } -#define CLIENT_INFO_FMT " from session#%u on %s:%d" -#define CLIENT_INFO_ARGS conn->session_id, port->host, port->port +#define CLIENT_INFO_FMT " from session#%ld on %s:%d" +#define CLIENT_INFO_ARGS conn->session_id, port->host.c_str(), port->port /** * For Http Protocol */ -static int swPort_onRead_http(swReactor *reactor, swListenPort *port, swEvent *event) -{ - swSocket *_socket = event->socket; - swConnection *conn = (swConnection *) _socket->object; - swServer *serv = (swServer *) reactor->ptr; - - if (conn->websocket_status >= WEBSOCKET_STATUS_HANDSHAKE) - { - if (conn->http_upgrade == 0) - { - swHttpRequest_free(conn); - conn->websocket_status = WEBSOCKET_STATUS_ACTIVE; +static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { + Socket *_socket = event->socket; + Connection *conn = (Connection *) _socket->object; + Server *serv = (Server *) reactor->ptr; + RecvData dispatch_data{}; + + if (conn->websocket_status >= websocket::STATUS_HANDSHAKE) { + if (conn->http_upgrade == 0) { + serv->destroy_http_request(conn); + conn->websocket_status = websocket::STATUS_ACTIVE; conn->http_upgrade = 1; } - return swPort_onRead_check_length(reactor, port, event); + return Port_onRead_check_length(reactor, port, event); } -#ifdef SW_USE_HTTP2 - if (conn->http2_stream) - { - return swPort_onRead_check_length(reactor, port, event); + if (conn->http2_stream) { + return Port_onRead_check_length(reactor, port, event); } -#endif - swHttpRequest *request = NULL; - swProtocol *protocol = &port->protocol; + Request *request = nullptr; + Protocol *protocol = &port->protocol; - if (conn->object == NULL) - { - request = (swHttpRequest *) sw_calloc(1, sizeof(swHttpRequest)); - if (!request) - { - swWarn("calloc(%ld) failed", sizeof(swHttpRequest)); - return SW_ERR; - } + if (conn->object == nullptr) { + request = new Request(); conn->object = request; - } - else - { - request = (swHttpRequest *) conn->object; + } else { + request = reinterpret_cast(conn->object); } - if (!request->buffer) - { - request->buffer = swString_new(SW_HTTP_HEADER_MAX_SIZE); - if (!request->buffer) - { - swReactor_trigger_close_event(reactor, event); + if (!request->buffer_) { + request->buffer_ = serv->get_recv_buffer(_socket); + if (!request->buffer_) { + reactor->trigger_close_event(event); return SW_ERR; } } - swString *buffer = request->buffer; + String *buffer = request->buffer_; - _recv_data: - ssize_t n = swSocket_recv(_socket, buffer->str + buffer->length, buffer->size - buffer->length, 0); - if (n < 0) - { - switch (swSocket_error(errno)) - { +_recv_data: + ssize_t n = _socket->recv(buffer->str + buffer->length, buffer->size - buffer->length, 0); + if (n < 0) { + 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; @@ -440,90 +426,74 @@ static int swPort_onRead_http(swReactor *reactor, swListenPort *port, swEvent *e } } - if (n == 0) - { - if (0) - { - _bad_request: -#ifdef SW_HTTP_BAD_REQUEST_PACKET - swSocket_send(_socket, SW_STRL(SW_HTTP_BAD_REQUEST_PACKET), 0); -#endif + if (n == 0) { + if (0) { + _bad_request: + _socket->send(SW_STRL(SW_HTTP_BAD_REQUEST_PACKET), 0); } - if (0) - { - _too_large: -#ifdef SW_HTTP_REQUEST_ENTITY_TOO_LARGE_PACKET - swSocket_send(_socket, SW_STRL(SW_HTTP_REQUEST_ENTITY_TOO_LARGE_PACKET), 0); -#endif + if (0) { + _too_large: + _socket->send(SW_STRL(SW_HTTP_REQUEST_ENTITY_TOO_LARGE_PACKET), 0); } - if (0) - { - _unavailable: -#ifdef SW_HTTP_SERVICE_UNAVAILABLE_PACKET - swSocket_send(_socket, SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET), 0); -#endif + if (0) { + _unavailable: + _socket->send(SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET), 0); } - _close_fd: - swHttpRequest_free(conn); - swReactor_trigger_close_event(reactor, event); + _close_fd: + serv->destroy_http_request(conn); + reactor->trigger_close_event(event); return SW_OK; } buffer->length += n; - _parse: - if (request->method == 0 && swHttpRequest_get_protocol(request) < 0) - { - if (!request->excepted && buffer->length < SW_HTTP_HEADER_MAX_SIZE) - { +_parse: + if (request->method == 0 && request->get_protocol() < 0) { + if (!request->excepted && buffer->length < SW_HTTP_HEADER_MAX_SIZE) { return SW_OK; } - swoole_error_log(SW_LOG_TRACE, SW_ERROR_HTTP_INVALID_PROTOCOL, "Bad Request: unknown protocol" CLIENT_INFO_FMT, CLIENT_INFO_ARGS); + swoole_error_log(SW_LOG_TRACE, + SW_ERROR_HTTP_INVALID_PROTOCOL, + "Bad Request: unknown protocol" CLIENT_INFO_FMT, + CLIENT_INFO_ARGS); goto _bad_request; } - if (request->method > SW_HTTP_PRI) - { - swoole_error_log(SW_LOG_TRACE, SW_ERROR_HTTP_INVALID_PROTOCOL, "Bad Request: got unsupported HTTP method" CLIENT_INFO_FMT, CLIENT_INFO_ARGS); + if (request->method > SW_HTTP_PRI) { + swoole_error_log(SW_LOG_TRACE, + SW_ERROR_HTTP_INVALID_PROTOCOL, + "Bad Request: unknown HTTP method" CLIENT_INFO_FMT, + 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); + } else if (request->method == SW_HTTP_PRI) { + if (sw_unlikely(!port->open_http2_protocol)) { + 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); - if (buffer->length == sizeof(SW_HTTP2_PRI_STRING) - 1) - { - swHttpRequest_free(conn); + http2::send_setting_frame(protocol, _socket); + if (buffer->length == sizeof(SW_HTTP2_PRI_STRING) - 1) { + serv->destroy_http_request(conn); + buffer->clear(); return SW_OK; } - swString *h2_buffer = swSocket_get_buffer(_socket); - if (!h2_buffer) - { - goto _close_fd; - } - swString_append_ptr(h2_buffer, buffer->str + buffer->offset, buffer->length - buffer->offset); - swHttpRequest_free(conn); + buffer->reduce(buffer->offset); + serv->destroy_http_request(conn); conn->socket->skip_recv = 1; - return swPort_onRead_check_length(reactor, port, event); -#endif + return Port_onRead_check_length(reactor, port, event); } // http header is not the end - if (request->header_length == 0) - { - if (swHttpRequest_get_header_length(request) < 0) - { - if (buffer->size == buffer->length) - { - swoole_error_log(SW_LOG_TRACE, SW_ERROR_HTTP_INVALID_PROTOCOL, "Bad Request: request header is too long" CLIENT_INFO_FMT, CLIENT_INFO_ARGS); + if (request->header_length_ == 0) { + if (request->get_header_length() < 0) { + if (buffer->size == buffer->length) { + swoole_error_log(SW_LOG_TRACE, + SW_ERROR_HTTP_INVALID_PROTOCOL, + "Bad Request: request header size is too large" CLIENT_INFO_FMT, + CLIENT_INFO_ARGS); goto _bad_request; } goto _recv_data; @@ -531,213 +501,347 @@ static int swPort_onRead_http(swReactor *reactor, swListenPort *port, swEvent *e } // parse http header and got http body length - if (!request->header_parsed) - { - swHttpRequest_parse_header_info(request); - swTraceLog(SW_TRACE_SERVER, "content-length=%u, keep-alive=%u, chunked=%u", request->content_length, request->keep_alive, request->chunked); + if (!request->header_parsed) { + request->parse_header_info(); + 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) - { + 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 (request->nobody_chunked) { + 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); + request->header_length_ += (sizeof("0\r\n\r\n") - 1); } request->tried_to_dispatch = 1; // (know content-length is equal to 0) or (no content-length field and no chunked) - if (request->content_length == 0 && (request->known_length || !request->chunked)) - { + if (request->content_length_ == 0 && (request->known_length || !request->chunked)) { + buffer->offset = request->header_length_; // send static file content directly in the reactor thread - if (!serv->enable_static_handler || !swServer_http_static_handler_hit(serv, request, conn)) - { + if (!serv->enable_static_handler || !serv->select_static_handler(request, conn)) { // dynamic request, dispatch to worker - swReactorThread_dispatch(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; } - if (conn->active && buffer->length > request->header_length) - { + if (buffer->length > request->header_length_) { // http pipeline, multi requests, parse the next one - swString_pop_front(buffer, request->header_length); - swHttpRequest_clean(request); + buffer->reduce(request->header_length_); + request->clean(); goto _parse; - } - else - { - swHttpRequest_free(conn); + } else { + serv->destroy_http_request(conn); + buffer->clear(); return SW_OK; } } } size_t request_length; - if (request->chunked) - { + if (request->chunked) { /* unknown length, should find chunked eof */ - if (swHttpRequest_get_chunked_body_length(request) < 0) - { - if (request->excepted) - { - swoole_error_log(SW_LOG_TRACE, SW_ERROR_HTTP_INVALID_PROTOCOL, "Bad Request: protocol error when parse chunked length" CLIENT_INFO_FMT, CLIENT_INFO_ARGS); + if (request->get_chunked_body_length() < 0) { + if (request->excepted) { + swoole_error_log(SW_LOG_TRACE, + SW_ERROR_HTTP_INVALID_PROTOCOL, + "Bad Request: protocol error when parse chunked length" CLIENT_INFO_FMT, + CLIENT_INFO_ARGS); goto _bad_request; } - request_length = request->header_length + request->content_length; - if (request_length > protocol->package_max_length) - { - swoole_error_log( - SW_LOG_TRACE, 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, - protocol->package_max_length, CLIENT_INFO_ARGS - ); + request_length = request->header_length_ + request->content_length_; + if (request_length > protocol->package_max_length) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_HTTP_INVALID_PROTOCOL, + "Request Entity Too Large: request length (chunked) has already been greater than the " + "package_max_length(%u)" CLIENT_INFO_FMT, + protocol->package_max_length, + CLIENT_INFO_ARGS); goto _too_large; } - if (buffer->length == buffer->size && swString_extend(buffer, buffer->size * 2) < 0) - { + if (buffer->length == buffer->size && !buffer->extend()) { goto _unavailable; } - if (request_length > buffer->size && swString_extend_align(buffer, request_length) < 0) - { + if (request_length > buffer->size && !buffer->extend_align(request_length)) { goto _unavailable; } goto _recv_data; - } - else - { - request_length = request->header_length + request->content_length; - } - swTraceLog(SW_TRACE_SERVER, "received chunked eof, real content-length=%u", 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, SW_ERROR_HTTP_INVALID_PROTOCOL, - "Request Entity Too Large: header-length (%u) + content-length (%u) is greater than the package_max_length(%u)" CLIENT_INFO_FMT, - request->header_length, request->content_length, protocol->package_max_length, CLIENT_INFO_ARGS - ); + } else { + request_length = request->header_length_ + request->content_length_; + } + swoole_trace_log( + SW_TRACE_SERVER, "received chunked eof, real content-length=%" PRIu64, request->content_length_); + } else { + request_length = request->header_length_ + request->content_length_; + if (request_length > protocol->package_max_length) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_HTTP_INVALID_PROTOCOL, + "Request Entity Too Large: header-length (%u) + content-length (%" PRIu64 + ") is greater than the " + "package_max_length(%u)" CLIENT_INFO_FMT, + request->header_length_, + request->content_length_, + protocol->package_max_length, + CLIENT_INFO_ARGS); goto _too_large; } - if (request_length > buffer->size && swString_extend(buffer, request_length) < 0) - { + if (request_length > buffer->size && !buffer->extend(request_length)) { goto _unavailable; } - if (buffer->length < request_length) - { - #ifdef SW_HTTP_100_CONTINUE + if (buffer->length < request_length) { +#ifdef SW_HTTP_100_CONTINUE // Expect: 100-continue - if (swHttpRequest_has_expect_header(request)) - { - swSocket_send(_socket, 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 - ); + if (request->has_expect_header()) { + _socket->send(SW_STRL(SW_HTTP_100_CONTINUE_PACKET), 0); + } else { + 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 +#endif goto _recv_data; } } // discard the redundant data - 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, - buffer->length - request_length, CLIENT_INFO_ARGS - ); + if (buffer->length > request_length) { + swoole_error_log(SW_LOG_TRACE, + SW_ERROR_HTTP_INVALID_PROTOCOL, + "Invalid Request: %zu bytes has been discard" CLIENT_INFO_FMT, + buffer->length - request_length, + CLIENT_INFO_ARGS); buffer->length = request_length; } - swReactorThread_dispatch(protocol, _socket, buffer->str, buffer->length); - swHttpRequest_free(conn); + buffer->offset = request_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); + if (_socket->recv_buffer && _socket->recv_buffer->size > SW_BUFFER_SIZE_BIG * 2) { + delete _socket->recv_buffer; + _socket->recv_buffer = nullptr; + } else { + buffer->clear(); + } + } return SW_OK; } -static int swPort_onRead_redis(swReactor *reactor, swListenPort *port, swEvent *event) -{ - swSocket *_socket = event->socket; - swConnection *conn = (swConnection *) _socket->object; - swProtocol *protocol = &port->protocol; +static int Port_onRead_redis(Reactor *reactor, ListenPort *port, Event *event) { + Socket *_socket = event->socket; + Connection *conn = (Connection *) _socket->object; + Protocol *protocol = &port->protocol; + Server *serv = (Server *) reactor->ptr; - swString *buffer = swSocket_get_buffer(_socket); - if (!buffer) - { + String *buffer = serv->get_recv_buffer(_socket); + if (!buffer) { + reactor->trigger_close_event(event); return SW_ERR; } - if (swRedis_recv(protocol, conn, buffer) < 0) - { + if (redis::recv_packet(protocol, conn, buffer) < 0) { conn->close_errno = errno; - swReactor_trigger_close_event(reactor, event); + reactor->trigger_close_event(event); } return SW_OK; } -static int swPort_onRead_check_eof(swReactor *reactor, swListenPort *port, swEvent *event) -{ - swSocket *_socket = event->socket; - swConnection *conn = (swConnection *) _socket->object; - swProtocol *protocol = &port->protocol; +static int Port_onRead_check_eof(Reactor *reactor, ListenPort *port, Event *event) { + Socket *_socket = event->socket; + Connection *conn = (Connection *) _socket->object; + Protocol *protocol = &port->protocol; + Server *serv = (Server *) reactor->ptr; - swString *buffer = swSocket_get_buffer(_socket); - if (!buffer) - { + String *buffer = serv->get_recv_buffer(_socket); + if (!buffer) { + reactor->trigger_close_event(event); return SW_ERR; } - if (swProtocol_recv_check_eof(protocol, _socket, buffer) < 0) - { + if (protocol->recv_with_eof_protocol(_socket, buffer) < 0) { conn->close_errno = errno; - swReactor_trigger_close_event(reactor, event); + reactor->trigger_close_event(event); + } + + // If the length is 0, which means the onPackage has been called, we can free the buffer. + if (_socket->recv_buffer && _socket->recv_buffer->length == 0 && + _socket->recv_buffer->size > SW_BUFFER_SIZE_BIG * 2) { + delete _socket->recv_buffer; + _socket->recv_buffer = nullptr; } return SW_OK; } -void swPort_free(swListenPort *port) -{ +void ListenPort::close() { #ifdef SW_USE_OPENSSL - if (port->ssl) - { - if (port->ssl_context) - { - swSSL_free_context(port->ssl_context); - } - sw_free(port->ssl_option.cert_file); - sw_free(port->ssl_option.key_file); - if (port->ssl_option.client_cert_file) - { - sw_free(port->ssl_option.client_cert_file); + if (ssl) { + if (ssl_context) { + delete ssl_context; } #ifdef SW_SUPPORT_DTLS - if (port->dtls_sessions) + if (dtls_sessions) { + delete dtls_sessions; + } +#endif + } +#endif + + if (socket) { + socket->free(); + socket = nullptr; + } + + // remove unix socket file + if (type == SW_SOCK_UNIX_STREAM || type == SW_SOCK_UNIX_DGRAM) { + unlink(host.c_str()); + } +} + +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 { - delete port->dtls_sessions; + 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 - swSocket_free(port->socket); + 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; +} - //remove unix socket file - if (port->type == SW_SOCK_UNIX_STREAM || port->type == SW_SOCK_UNIX_DGRAM) - { - unlink(port->host); +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 53ea246e3a8..3e677260c0b 100644 --- a/src/server/process.cc +++ b/src/server/process.cc @@ -10,516 +10,411 @@ | to obtain it through the world-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 "server.h" - -#include - -typedef struct _swFactoryProcess -{ - swPipe *pipes; - swPipeBuffer *send_buffer; -} swFactoryProcess; - -typedef int (*send_func_t)(swServer *, swPipeBuffer *, size_t, void *); - -static int swFactoryProcess_start(swFactory *factory); -static int swFactoryProcess_notify(swFactory *factory, swDataHead *event); -static int swFactoryProcess_dispatch(swFactory *factory, swSendData *data); -static int swFactoryProcess_finish(swFactory *factory, swSendData *data); -static int swFactoryProcess_shutdown(swFactory *factory); -static int swFactoryProcess_end(swFactory *factory, int fd); -static void swFactoryProcess_free(swFactory *factory); -static int swFactoryProcess_create_pipes(swFactory *factory); - -static int process_send_packet(swServer *serv, swPipeBuffer *buf, swSendData *resp, send_func_t _send, void* private_data); -static int process_sendto_worker(swServer *serv, swPipeBuffer *buf, size_t n, void *private_data); -static int process_sendto_reactor(swServer *serv, swPipeBuffer *buf, size_t n, void *private_data); - -int swFactoryProcess_create(swFactory *factory, uint32_t worker_num) -{ - swFactoryProcess *object = (swFactoryProcess *) SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(swFactoryProcess)); - if (object == NULL) - { - swWarn("[Master] malloc[object] failed"); - return SW_ERR; +#include "swoole_server.h" + +namespace swoole { + +using network::Socket; + +Factory *Server::create_process_factory() { + /** + * init reactor thread pool + */ + reactor_threads = new ReactorThread[reactor_num](); + /** + * alloc the memory for connection_list + */ + connection_list = (Connection *) sw_shm_calloc(max_connection, sizeof(Connection)); + if (connection_list == nullptr) { + swoole_error("calloc[1] failed"); + return nullptr; } + reactor_pipe_num = worker_num / reactor_num; + return new ProcessFactory(this); +} - factory->object = object; - factory->dispatch = swFactoryProcess_dispatch; - factory->finish = swFactoryProcess_finish; - factory->start = swFactoryProcess_start; - factory->notify = swFactoryProcess_notify; - factory->shutdown = swFactoryProcess_shutdown; - factory->end = swFactoryProcess_end; - factory->free = swFactoryProcess_free; +void Server::destroy_process_factory() { + sw_shm_free(connection_list); + delete[] reactor_threads; - return SW_OK; + if (gs->event_workers.message_box) { + gs->event_workers.message_box->destroy(); + } } -static int swFactoryProcess_shutdown(swFactory *factory) -{ - int status; - swServer *serv = (swServer *) factory->ptr; +ProcessFactory::ProcessFactory(Server *server) : Factory(server) {} - if (swoole_kill(serv->gs->manager_pid, SIGTERM) < 0) - { - swSysWarn("swKill(%d) failed", serv->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(serv->gs->manager_pid, &status, 0) < 0) - { - swSysWarn("waitpid(%d) failed", serv->gs->manager_pid); + for (auto &kv : server_->user_worker_map) { + swoole_kill(kv.second->pid, SIGTERM); } - return SW_OK; + 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); + } + } } -static void swFactoryProcess_free(swFactory *factory) -{ - swServer *serv = (swServer *) factory->ptr; - swFactoryProcess *object = (swFactoryProcess *) serv->factory.object; - - uint32_t i; +/** + * [Manager] kill and wait all event worker process + */ +void Factory::kill_event_workers() { + int status; - for (i = 0; i < serv->reactor_num; i++) - { - sw_free(serv->pipe_buffers[i]); + if (server_->worker_num == 0) { + return; } - sw_free(serv->pipe_buffers); - if (serv->stream_socket_file) - { - unlink(serv->stream_socket_file); - sw_free(serv->stream_socket_file); - swSocket_free(serv->stream_socket); + 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) { + 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); + } } +} - for (i = 0; i < serv->worker_num; i++) - { - object->pipes[i].close(&object->pipes[i]); +/** + * [Manager] kill and wait task worker process + */ +void Factory::kill_task_workers() { + if (server_->task_worker_num == 0) { + return; } + server_->gs->task_workers.shutdown(); } -static int swFactoryProcess_create_pipes(swFactory *factory) -{ - swServer *serv = (swServer *) factory->ptr; - swFactoryProcess *object = (swFactoryProcess *) serv->factory.object; +pid_t Factory::spawn_event_worker(Worker *worker) { + pid_t pid = swoole_fork(0); - object->pipes = (swPipe *) sw_calloc(serv->worker_num, sizeof(swPipe)); - if (object->pipes == NULL) - { - swSysError("malloc[pipes] failed"); + 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; } - for (uint32_t i = 0; i < serv->worker_num; i++) - { - int kernel_buffer_size = SW_UNIXSOCK_MAX_BUF_SIZE; - - if (swPipeUnsock_create(&object->pipes[i], 1, SOCK_DGRAM) < 0) - { - sw_free(object->pipes); - object->pipes = NULL; - return SW_ERR; - } + if (server_->is_base_mode()) { + server_->gs->event_workers.main_loop(&server_->gs->event_workers, worker); + } else { + server_->start_event_worker(worker); + } - serv->workers[i].pipe_master = object->pipes[i].getSocket(&object->pipes[i], SW_PIPE_MASTER); - serv->workers[i].pipe_worker = object->pipes[i].getSocket(&object->pipes[i], SW_PIPE_WORKER); - - setsockopt(serv->workers[i].pipe_master->fd, SOL_SOCKET, SO_SNDBUF, &kernel_buffer_size, sizeof(kernel_buffer_size)); - setsockopt(serv->workers[i].pipe_worker->fd, SOL_SOCKET, SO_SNDBUF, &kernel_buffer_size, sizeof(kernel_buffer_size)); + exit(0); + return 0; +} - serv->workers[i].pipe_object = &object->pipes[i]; - swServer_store_pipe_fd(serv, serv->workers[i].pipe_object); +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; + } +} - return SW_OK; +pid_t Factory::spawn_task_worker(Worker *worker) { + return server_->gs->task_workers.spawn(worker); } -static int swFactoryProcess_start(swFactory *factory) -{ - swServer *serv = (swServer *) factory->ptr; - swFactoryProcess *object = (swFactoryProcess *) serv->factory.object; - - if (serv->dispatch_mode == SW_DISPATCH_STREAM) - { - serv->stream_socket_file = swoole_string_format(64, "/tmp/swoole.%d.sock", serv->gs->master_pid); - if (serv->stream_socket_file == NULL) - { - return SW_ERR; - } - swSocket *sock = swSocket_create_server(SW_SOCK_UNIX_STREAM, serv->stream_socket_file, 0, 2048); - if (sock == nullptr) - { - return SW_ERR; - } - serv->stream_socket = sock; - swoole_fcntl_set_option(sock->fd, 1, 1); - serv->stream_socket->nonblock = 1; - serv->stream_socket->cloexec = 1; +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); } +} - for (uint32_t i = 0; i < serv->worker_num; i++) - { - if (swServer_worker_create(serv, swServer_get_worker(serv, i)) < 0) - { - return SW_ERR; - } - } +bool ProcessFactory::shutdown() { + int status; - serv->reactor_pipe_num = serv->worker_num / serv->reactor_num; + if (swoole_kill(server_->gs->manager_pid, SIGTERM) < 0) { + swoole_sys_warning("kill(%d) failed", server_->gs->manager_pid); + } - if (swFactoryProcess_create_pipes(factory) < 0) - { - return SW_ERR; + if (swoole_waitpid(server_->gs->manager_pid, &status, 0) < 0) { + swoole_sys_warning("waitpid(%d) failed", server_->gs->manager_pid); } - swServer_set_ipc_max_size(serv); - if (swServer_create_pipe_buffers(serv) < 0) - { - return SW_ERR; + 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; + } + + 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; } - - object->send_buffer = (swPipeBuffer *) sw_malloc(serv->ipc_max_size); - if (object->send_buffer == NULL) - { - swSysError("malloc[send_buffer] failed"); - return SW_ERR; + + init_ipc_max_size(); + if (create_pipe_buffers() < 0) { + return false; } - bzero(object->send_buffer, sizeof(swDataHead)); + return true; +} - /** - * The manager process must be started first, otherwise it will have a thread fork - */ - if (swManager_start(serv) < 0) - { - swWarn("swFactoryProcess_manager_start failed"); - return SW_ERR; +bool ProcessFactory::start() { + if (!server_->create_worker_pipes()) { + return false; } - factory->finish = swFactory_finish; - return SW_OK; + return server_->start_manager_process() == SW_OK; } /** * [ReactorThread] notify info to worker process */ -static int swFactoryProcess_notify(swFactory *factory, swDataHead *ev) -{ - swSendData task; +bool ProcessFactory::notify(DataHead *ev) { + SendData task; task.info = *ev; - task.data = NULL; - return swFactoryProcess_dispatch(factory, &task); -} - -static int process_sendto_worker(swServer *serv, swPipeBuffer *buf, size_t n, void *private_data) -{ - return swReactorThread_send2worker(serv, (swWorker *) private_data, buf, n); -} - -static int process_sendto_reactor(swServer *serv, swPipeBuffer *buf, size_t n, void *private_data) -{ - return swWorker_send2reactor(serv, (swEventData *) buf, n, - ((swConnection *) private_data)->session_id); + task.data = nullptr; + return dispatch(&task); } /** * [ReactorThread] dispatch request to worker */ -static int swFactoryProcess_dispatch(swFactory *factory, swSendData *task) -{ - swServer *serv = (swServer *) factory->ptr; +bool ProcessFactory::dispatch(SendData *task) { int fd = task->info.fd; - int target_worker_id = swServer_worker_schedule(serv, fd, task); - if (target_worker_id < 0) - { - switch (target_worker_id) - { - case SW_DISPATCH_RESULT_DISCARD_PACKET: - return SW_ERR; - case SW_DISPATCH_RESULT_CLOSE_CONNECTION: + int target_worker_id = server_->schedule_worker(fd, task); + if (target_worker_id < 0) { + switch (target_worker_id) { + case Server::DISPATCH_RESULT_DISCARD_PACKET: + return false; + case Server::DISPATCH_RESULT_CLOSE_CONNECTION: // TODO: close connection - return SW_ERR; + return false; default: - swWarn("invalid target worker id[%d]", target_worker_id); - return SW_ERR; + swoole_warning("invalid target worker id[%d]", target_worker_id); + return false; } } - if (swEventData_is_stream(task->info.type)) - { - swConnection *conn = swServer_connection_get(serv, fd); - if (conn == NULL || conn->active == 0) - { - swWarn("dispatch[type=%d] failed, connection#%d is not active", task->info.type, fd); - return SW_ERR; + if (Server::is_stream_event(task->info.type)) { + Connection *conn = server_->get_connection(fd); + if (conn == nullptr || conn->active == 0) { + swoole_warning("dispatch[type=%d] failed, connection#%d is not active", task->info.type, fd); + return false; } - //server active close, discard data. - if (conn->closed) - { - //Connection has been clsoed by server - if (!(task->info.type == SW_SERVER_EVENT_CLOSE && conn->close_force)) - { - return SW_OK; + // server active close, discard data. + if (conn->closed) { + // Connection has been clsoed by server + if (!(task->info.type == SW_SERVER_EVENT_CLOSE && conn->close_force)) { + return true; } } - //converted fd to session_id + // converted fd to session_id task->info.fd = conn->session_id; task->info.server_fd = conn->server_fd; } - swWorker *worker = swServer_get_worker(serv, target_worker_id); - - //without data - if (task->data == NULL) - { - task->info.flags = 0; - return swReactorThread_send2worker(serv, worker, &task->info, sizeof(task->info)); - } + Worker *worker = server_->get_worker(target_worker_id); - if (task->info.type == SW_SERVER_EVENT_SEND_DATA) - { - worker->dispatch_count++; + if (task->info.type == SW_SERVER_EVENT_RECV_DATA) { + sw_atomic_fetch_add(&worker->dispatch_count, 1); } - /** - * Multi-Threads - */ - swPipeBuffer *buf = serv->pipe_buffers[SwooleTG.id]; - buf->info = task->info; - - return process_send_packet(serv, buf, task, process_sendto_worker, worker); -} + SendData _task; + memcpy(&_task, task, sizeof(SendData)); + network::Socket *sock; + MessageBus *mb; -/** - * @description: master process send data to worker process. - * If the data sent is larger than swServer::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 int process_send_packet(swServer *serv, swPipeBuffer *buf, swSendData *resp, send_func_t _send, void* private_data) -{ - const char* data = resp->data; - uint32_t send_n = resp->info.len; - off_t offset = 0; - uint32_t copy_n; - - uint32_t max_length = serv->ipc_max_size - sizeof(buf->info); - - if (send_n <= max_length) - { - buf->info.flags = 0; - buf->info.len = send_n; - memcpy(buf->data, data, send_n); - - int retval = _send(serv, buf, sizeof(buf->info) + send_n, private_data); -#ifdef __linux__ - if (retval < 0 && errno == ENOBUFS) - { - max_length = SW_IPC_BUFFER_SIZE; - goto _ipc_use_chunk; - } -#endif - return retval < 0 ? SW_ERR : SW_OK; + 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; } -#ifdef __linux__ - _ipc_use_chunk: -#endif - buf->info.flags = SW_EVENT_DATA_CHUNK; - buf->info.len = send_n; - - while (send_n > 0) - { - if (send_n > max_length) - { - copy_n = max_length; - } - else - { - buf->info.flags |= SW_EVENT_DATA_END; - copy_n = send_n; - } - - memcpy(buf->data, data + offset, copy_n); - - swTrace("finish, type=%d|len=%d", buf->info.type, copy_n); - - if (_send(serv, buf, sizeof(buf->info) + copy_n, private_data) < 0) - { -#ifdef __linux__ - if (errno == ENOBUFS && max_length > SW_BUFFER_SIZE_STD) - { - max_length = SW_IPC_BUFFER_SIZE; - continue; - } -#endif - return SW_ERR; - } - - send_n -= copy_n; - offset += copy_n; - } - - return SW_OK; + return mb->write(sock, &_task); } -static bool inline process_is_supported_send_yield(swServer *serv, swConnection *conn) -{ - if (!swServer_dispatch_mode_is_mod(serv)) - { +static bool inline process_is_supported_send_yield(Server *serv, Connection *conn) { + if (!serv->is_hash_dispatch_mode()) { return false; - } - else - { - return swServer_worker_schedule(serv, conn->fd, nullptr) == (int) SwooleWG.id; + } else { + return serv->schedule_worker(conn->fd, nullptr) == (int) swoole_get_process_id(); } } /** * [Worker] send to client, proxy by reactor */ -static int swFactoryProcess_finish(swFactory *factory, swSendData *resp) -{ - swServer *serv = (swServer *) factory->ptr; - swFactoryProcess *object = (swFactoryProcess *) serv->factory.object; - +bool ProcessFactory::finish(SendData *resp) { /** * More than the output buffer */ - if (resp->info.len > serv->output_buffer_size) - { - swoole_error_log( - SW_LOG_WARNING, SW_ERROR_DATA_LENGTH_TOO_LARGE, - "The length of data [%u] exceeds the output buffer size[%u], " - "please use the sendfile, chunked transfer mode or adjust the output_buffer_size", - resp->info.len, serv->output_buffer_size - ); - return SW_ERR; + if (resp->info.len > server_->output_buffer_size) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_DATA_LENGTH_TOO_LARGE, + "The length of data [%u] exceeds the output buffer size[%u], " + "please use the sendfile, chunked transfer mode or adjust the output_buffer_size", + resp->info.len, + server_->output_buffer_size); + return false; } - int session_id = resp->info.fd; - swConnection *conn; - if (resp->info.type != SW_SERVER_EVENT_CLOSE) - { - conn = swServer_connection_verify(serv, session_id); - } - else - { - conn = swServer_connection_verify_no_ssl(serv, session_id); - } - if (!conn) - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_NOT_EXIST, "connection[fd=%d] does not exists", session_id); - return SW_ERR; - } - else if ((conn->closed || conn->peer_closed) && resp->info.type != SW_SERVER_EVENT_CLOSE) - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED, - "send %d byte failed, because connection[fd=%d] is closed", resp->info.len, session_id); - return SW_ERR; + SessionId session_id = resp->info.fd; + Connection *conn; + if (resp->info.type != SW_SERVER_EVENT_CLOSE) { + conn = server_->get_connection_verify(session_id); + } else { + conn = server_->get_connection_verify_no_ssl(session_id); } - else if (conn->overflow) - { - if (serv->send_yield && process_is_supported_send_yield(serv, conn)) - { - SwooleG.error = SW_ERROR_OUTPUT_SEND_YIELD; + if (!conn) { + 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); } - else - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_OUTPUT_BUFFER_OVERFLOW, - "send failed, connection[fd=%d] output buffer has been overflowed", session_id); - } - return SW_ERR; - } - - /** - * stream - */ - if (serv->last_stream_socket) - { - int _len = resp->info.len; - int _header = htonl(_len + sizeof(resp->info)); - if (SwooleTG.reactor->write(SwooleTG.reactor, serv->last_stream_socket, (char*) &_header, sizeof(_header)) < 0) - { - return SW_ERR; - } - if (SwooleTG.reactor->write(SwooleTG.reactor, serv->last_stream_socket, &resp->info, sizeof(resp->info)) < 0) - { - return SW_ERR; - } - if (SwooleTG.reactor->write(SwooleTG.reactor, serv->last_stream_socket, resp->data, _len) < 0) - { - return SW_ERR; + return false; + } else if ((conn->closed || conn->peer_closed) && resp->info.type != SW_SERVER_EVENT_CLOSE) { + 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 && + (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 { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_OUTPUT_BUFFER_OVERFLOW, + "send failed, session=%ld output buffer overflow", + session_id); } - return SW_OK; + return false; } - swPipeBuffer *buf = object->send_buffer; - - buf->info.fd = session_id; - buf->info.type = resp->info.type; - buf->info.reactor_id = conn->reactor_id; - buf->info.server_fd = SwooleWG.id; + SendData task; + memcpy(&task, resp, sizeof(SendData)); + task.info.fd = session_id; + task.info.reactor_id = conn->reactor_id; + task.info.server_fd = swoole_get_process_id(); - swTrace("worker_id=%d, type=%d",SwooleWG.id, buf->info.type); + swoole_trace("worker_id=%d, type=%d", SwooleG.process_id, task.info.type); - return process_send_packet(serv, buf, resp, process_sendto_reactor, conn); + return server_->message_bus.write(server_->get_reactor_pipe_socket(session_id, task.info.reactor_id), &task); } -static int swFactoryProcess_end(swFactory *factory, int fd) -{ - swServer *serv = (swServer *) factory->ptr; - swSendData _send = {}; - swDataHead info = {}; +bool ProcessFactory::end(SessionId session_id, int flags) { + SendData _send{}; + DataHead info{}; - _send.info.fd = fd; + _send.info.fd = session_id; _send.info.len = 0; _send.info.type = SW_SERVER_EVENT_CLOSE; - swConnection *conn = swWorker_get_connection(serv, fd); - if (conn == NULL || conn->active == 0) - { - SwooleG.error = SW_ERROR_SESSION_NOT_EXIST; - return SW_ERR; + 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 does not exists", session_id); + return false; } - else if (conn->close_force) - { - goto _do_close; + // Reset send buffer, Immediately close the connection. + if (flags & Server::CLOSE_RESET) { + conn->close_reset = 1; } - else if (conn->closing) - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSING, "The connection[%d] is closing", fd); - return SW_ERR; + // Server is initiative to close the connection + if (flags & Server::CLOSE_ACTIVELY) { + conn->close_actively = 1; } - else if (conn->closed) - { - return SW_ERR; + + swoole_trace_log(SW_TRACE_CLOSE, "session_id=%ld, fd=%d", session_id, conn->fd); + + Worker *worker; + DataHead ev = {}; + + /** + * 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) { + 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; } - else - { - _do_close: - conn->closing = 1; - if (serv->onClose != NULL) - { - info.fd = fd; - if (conn->close_actively) - { - info.reactor_id = -1; - } - else - { - info.reactor_id = conn->reactor_id; - } - info.server_fd = conn->server_fd; - serv->onClose(serv, &info); + +_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->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; + } + + 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 factory->finish(factory, &_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 41ee3ef0644..1f5a3862d14 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -10,650 +10,332 @@ | to obtain it through the world-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 "server.h" +#include "swoole_server.h" +#include "swoole_memory.h" -static int swReactorProcess_loop(swProcessPool *pool, swWorker *worker); -static int swReactorProcess_onPipeRead(swReactor *reactor, swEvent *event); -static int swReactorProcess_onClose(swReactor *reactor, swEvent *event); -static int swReactorProcess_send2client(swFactory *, swSendData *data); -static int swReactorProcess_send2worker(swSocket *socket, const void *data, size_t length); -static void swReactorProcess_onTimeout(swTimer *timer, swTimer_node *tnode); +namespace swoole { +using network::Socket; -#ifdef HAVE_REUSEPORT -static int swReactorProcess_reuse_port(swListenPort *ls); -#endif - -static uint32_t heartbeat_check_lasttime = 0; - -static bool swServer_is_single(swServer *serv) -{ - return serv->worker_num == 1 && serv->task_worker_num == 0 && serv->max_request == 0 && serv->user_worker_list == nullptr; -} - -int swReactorProcess_create(swServer *serv) -{ - serv->reactor_num = serv->worker_num; - serv->connection_list = (swConnection *) sw_calloc(serv->max_connection, sizeof(swConnection)); - if (serv->connection_list == NULL) - { - swSysWarn("calloc[2](%d) failed", (int )(serv->max_connection * sizeof(swConnection))); - return SW_ERR; - } - //create factry object - if (swFactory_create(&(serv->factory)) < 0) - { - swError("create factory failed"); - return SW_ERR; - } - serv->factory.finish = swReactorProcess_send2client; - return SW_OK; -} +static int ReactorProcess_onPipeRead(Reactor *reactor, Event *event); +static int ReactorProcess_onClose(Reactor *reactor, Event *event); +static void ReactorProcess_onTimeout(Timer *timer, TimerNode *tnode); -void swReactorProcess_free(swServer *serv) -{ - serv->factory.free(&serv->factory); - sw_free(serv->connection_list); -} +int Server::start_reactor_processes() { + single_thread = true; -int swReactorProcess_start(swServer *serv) -{ - serv->single_thread = 1; - - //listen TCP - if (serv->have_stream_sock == 1) - { - for (auto ls : *serv->listen_list) - { - if (swSocket_is_dgram(ls->type)) - { - continue; - } -#ifdef HAVE_REUSEPORT - if (serv->enable_reuse_port) - { - if (close(ls->socket->fd) < 0) - { - swSysWarn("close(%d) failed", ls->socket->fd); - } - continue; - } - else + // listen TCP + if (have_stream_sock == 1) { + for (auto ls : ports) { + if (ls->is_stream()) { +#if defined(__linux__) && defined(HAVE_REUSEPORT) + if (!enable_reuse_port) { #endif - { - //listen server socket - if (swPort_listen(ls) < 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 } } } - swProcessPool *pool = &serv->gs->event_workers; - if (swProcessPool_create(pool, serv->worker_num, 0, SW_IPC_UNIXSOCK) < 0) - { + ProcessPool *pool = &gs->event_workers; + *pool = {}; + if (pool->create(worker_num, 0, SW_IPC_UNIXSOCK) < 0) { return SW_ERR; } - swProcessPool_set_max_request(pool, serv->max_request, serv->max_request_grace); + pool->set_max_request(max_request, max_request_grace); /** - * store to swProcessPool object + * store to ProcessPool object */ - serv->gs->event_workers.ptr = serv; - serv->gs->event_workers.max_wait_time = serv->max_wait_time; - serv->gs->event_workers.use_msgqueue = 0; - serv->gs->event_workers.main_loop = swReactorProcess_loop; - serv->gs->event_workers.onWorkerNotFound = swManager_wait_other_worker; - - uint32_t i; - for (i = 0; i < serv->worker_num; i++) - { - serv->gs->event_workers.workers[i].pool = &serv->gs->event_workers; - serv->gs->event_workers.workers[i].id = i; - serv->gs->event_workers.workers[i].type = SW_PROCESS_WORKER; - } - - //single worker - if (swServer_is_single(serv)) - { - return swReactorProcess_loop(&serv->gs->event_workers, &serv->gs->event_workers.workers[0]); + 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 = 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; + gs->event_workers.workers[i].id = i; + gs->event_workers.workers[i].type = SW_PROCESS_WORKER; + } + + init_ipc_max_size(); + if (create_pipe_buffers() < 0) { + return SW_ERR; } - for (i = 0; i < serv->worker_num; i++) - { - if (swServer_worker_create(serv, &serv->gs->event_workers.workers[i]) < 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; } - //task workers - if (serv->task_worker_num > 0) - { - if (swServer_create_task_workers(serv) < 0) - { - return SW_ERR; - } - swTaskWorker_init(serv); - if (swProcessPool_start(&serv->gs->task_workers) < 0) - { - return SW_ERR; - } - } - - /** - * create user worker process - */ - if (serv->user_worker_list) - { - serv->user_workers = (swWorker *) sw_malloc(serv->user_worker_num * sizeof(swWorker)); - if (serv->user_workers == NULL) - { - swSysWarn("gmalloc[server->user_workers] failed"); - return SW_ERR; - } - for (auto worker : *serv->user_worker_list) - { - /** - * store the pipe object - */ - if (worker->pipe_object) - { - swServer_store_pipe_fd(serv, worker->pipe_object); - } - swManager_spawn_user_worker(serv, worker); - } - } - - /** - * manager process is the same as the master process - */ - SwooleG.pid = serv->gs->manager_pid = getpid(); - SwooleG.process_type = SW_PROCESS_MANAGER; - - /** - * manager process can not use signalfd - */ - SwooleG.use_signalfd = 0; + return start_manager_process(); +} - swProcessPool_start(&serv->gs->event_workers); - swServer_signal_init(serv); +static int ReactorProcess_onPipeRead(Reactor *reactor, Event *event) { + SendData _send; + Server *serv = (Server *) reactor->ptr; + Factory *factory = serv->factory; + PipeBuffer *pipe_buffer = serv->message_bus.get_buffer(); - if (serv->onStart) - { - swWarn("The onStart event with SWOOLE_BASE is deprecated"); - serv->onStart(serv); + ssize_t retval = serv->message_bus.read(event->socket); + if (retval <= 0) { + return SW_OK; } - if (serv->onManagerStart) - { - serv->onManagerStart(serv); + switch (pipe_buffer->info.type) { + case SW_SERVER_EVENT_PIPE_MESSAGE: { + serv->onPipeMessage(serv, (EventData *) pipe_buffer); + break; } - - swProcessPool_wait(&serv->gs->event_workers); - swProcessPool_shutdown(&serv->gs->event_workers); - - swManager_kill_user_workers(serv); - - if (serv->onManagerStop) - { - serv->onManagerStop(serv); + case SW_SERVER_EVENT_FINISH: { + serv->onFinish(serv, (EventData *) pipe_buffer); + break; } - - return SW_OK; -} - -static int swReactorProcess_onPipeRead(swReactor *reactor, swEvent *event) -{ - swEventData task; - swSendData _send; - swServer *serv = (swServer *) reactor->ptr; - swFactory *factory = &serv->factory; - swString *output_buffer; - - if (read(event->fd, &task, sizeof(task)) <= 0) - { - return SW_ERR; + case SW_SERVER_EVENT_SEND_FILE: { + _send.info = pipe_buffer->info; + _send.data = pipe_buffer->data; + factory->finish(&_send); + break; } - - switch (task.info.type) - { - case SW_SERVER_EVENT_PIPE_MESSAGE: - serv->onPipeMessage(serv, &task); + 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; + } + 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_FINISH: - serv->onFinish(serv, &task); + } + case SW_SERVER_EVENT_CLOSE: { + factory->end(pipe_buffer->info.fd, Server::CLOSE_ACTIVELY); break; - case SW_SERVER_EVENT_SEND_FILE: - memcpy(&_send.info, &task.info, sizeof(_send.info)); - _send.data = task.data; - factory->finish(factory, &_send); + } + 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_PROXY_START: - case SW_SERVER_EVENT_PROXY_END: - output_buffer = SwooleWG.output_buffer[task.info.reactor_id]; - swString_append_ptr(output_buffer, 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_SEND_DATA; - _send.data = output_buffer->str; - _send.info.len = output_buffer->length; - factory->finish(factory, &_send); - swString_clear(output_buffer); - } + } + 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 swReactorProcess_alloc_output_buffer(int n_buffer) -{ - SwooleWG.output_buffer = (swString **) sw_malloc(sizeof(swString*) * n_buffer); - if (SwooleWG.output_buffer == NULL) - { - swError("malloc for SwooleWG.output_buffer failed"); - return SW_ERR; - } + serv->message_bus.pop(); - int i; - for (i = 0; i < n_buffer; i++) - { - SwooleWG.output_buffer[i] = swString_new(SW_BUFFER_SIZE_BIG); - if (SwooleWG.output_buffer[i] == NULL) - { - swError("output_buffer init failed"); - return SW_ERR; - } - } return SW_OK; } -static void swReactor_free_output_buffer(int n_buffer) -{ - int i; - for (i = 0; i < n_buffer; i++) - { - swString_free(SwooleWG.output_buffer[i]); - } - sw_free(SwooleWG.output_buffer); -} - -static int swReactorProcess_loop(swProcessPool *pool, swWorker *worker) -{ - swServer *serv = (swServer *) pool->ptr; - - SwooleG.process_type = SW_PROCESS_WORKER; +int Server::reactor_process_main_loop(ProcessPool *pool, Worker *worker) { + Server *serv = (Server *) pool->ptr; SwooleG.pid = getpid(); - - SwooleWG.id = worker->id; - if (serv->max_request > 0) - { - SwooleWG.run_always = 0; - } - 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); - swServer_worker_init(serv, worker); + serv->init_event_worker(worker); - //create reactor - if (!SwooleTG.reactor) - { - if (swoole_event_init() < 0) - { + if (!SwooleTG.reactor) { + if (swoole_event_init(0) < 0) { return SW_ERR; } } - swReactor *reactor = SwooleTG.reactor; - if (SwooleTG.timer && SwooleTG.timer->reactor == nullptr) - { - swTimer_reinit(SwooleTG.timer, reactor); - } + Reactor *reactor = SwooleTG.reactor; - int n_buffer = serv->worker_num + serv->task_worker_num + serv->user_worker_num; - if (swReactorProcess_alloc_output_buffer(n_buffer)) - { - return SW_ERR; + if (SwooleTG.timer && SwooleTG.timer->get_reactor() == nullptr) { + SwooleTG.timer->reinit(reactor); } - int fdtype; - - for (auto ls : *serv->listen_list) - { - fdtype = swSocket_is_dgram(ls->type) ? SW_FD_DGRAM_SERVER : SW_FD_STREAM_SERVER; -#ifdef HAVE_REUSEPORT - if (fdtype == SW_FD_STREAM_SERVER && serv->enable_reuse_port) - { - if (swReactorProcess_reuse_port(ls) < 0) - { - swReactor_free_output_buffer(n_buffer); + serv->worker_signal_init(); + + serv->gs->connection_nums[worker->id] = 0; + + for (auto ls : serv->ports) { +#if defined(__linux__) and defined(HAVE_REUSEPORT) + if (ls->is_stream() && serv->enable_reuse_port) { + if (ls->create_socket(serv) < 0) { swoole_event_free(); return SW_ERR; } + + if (ls->listen() < 0) { + return SW_ERR; + } } #endif - if (reactor->add(reactor, ls->socket, SW_EVENT_READ) < 0) - { + ls->gs->connection_nums[worker->id] = 0; + if (reactor->add(ls->socket, SW_EVENT_READ) < 0) { return SW_ERR; } } reactor->id = worker->id; reactor->ptr = serv; + reactor->max_socket = serv->get_max_connection(); -#ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd) - { - swSignalfd_setup(SwooleTG.reactor); - } -#endif - - reactor->max_socket = serv->max_connection; - - reactor->close = swReactorThread_close; + reactor->close = close_connection; - //set event handler - //connect - swReactor_set_handler(reactor, SW_FD_STREAM_SERVER, swServer_master_onAccept); - //close - reactor->default_error_handler = swReactorProcess_onClose; - //pipe - swReactor_set_handler(reactor, SW_FD_PIPE | SW_EVENT_READ, swReactorProcess_onPipeRead); + // set event handler + // connect + reactor->set_handler(SW_FD_STREAM_SERVER, accept_connection); + // close + reactor->default_error_handler = ReactorProcess_onClose; + // pipe + reactor->set_handler(SW_FD_PIPE | SW_EVENT_READ, ReactorProcess_onPipeRead); - swServer_store_listen_socket(serv); + serv->store_listen_socket(); - if (worker->pipe_worker) - { - swSocket_set_nonblock(worker->pipe_worker); - swSocket_set_nonblock(worker->pipe_master); - if (reactor->add(reactor, worker->pipe_worker, SW_EVENT_READ) < 0) - { + if (worker->pipe_worker) { + worker->pipe_worker->set_nonblock(); + worker->pipe_master->set_nonblock(); + if (reactor->add(worker->pipe_worker, SW_EVENT_READ) < 0) { return SW_ERR; } - if (reactor->add(reactor, worker->pipe_master, SW_EVENT_READ) < 0) - { + if (reactor->add(worker->pipe_master, SW_EVENT_READ) < 0) { return SW_ERR; } } - //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++) - { - swSocket_set_nonblock(serv->gs->task_workers.workers[i].pipe_master); + // task workers + if (serv->task_worker_num > 0) { + 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(); } } } - //set protocol function point - swReactorThread_set_protocol(serv, reactor); + serv->init_reactor(reactor); - //single server trigger onStart event - if (swServer_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, SW_TRUE, swServer_master_onTimer, serv)) == NULL) - { - _fail: - swReactor_free_output_buffer(n_buffer); + if ((serv->master_timer = swoole_timer_add(1000L, true, timer_callback, serv)) == nullptr) { + _fail: swoole_event_free(); return SW_ERR; } - swWorker_onStart(serv); + serv->worker_start_callback(worker); /** * for heartbeat check */ - if (serv->heartbeat_check_interval > 0) - { - serv->heartbeat_timer = swoole_timer_add((long) (serv->heartbeat_check_interval * 1000), SW_TRUE, swReactorProcess_onTimeout, reactor); - if (serv->heartbeat_timer == NULL) - { + if (serv->heartbeat_check_interval > 0) { + serv->heartbeat_timer = + swoole_timer_add((long) (serv->heartbeat_check_interval * 1000), true, ReactorProcess_onTimeout, reactor); + if (serv->heartbeat_timer == nullptr) { goto _fail; } } - int retval = reactor->wait(reactor, NULL); + int retval = reactor->wait(nullptr); /** * Close all connections */ - int fd; - int serv_max_fd = swServer_get_maxfd(serv); - int serv_min_fd = swServer_get_minfd(serv); - - for (fd = serv_min_fd; fd <= serv_max_fd; fd++) - { - swConnection *conn = swServer_connection_get(serv, fd); - if (conn != NULL && conn->active && conn->socket->fdtype == SW_FD_SESSION) - { - serv->close(serv, conn->session_id, 1); - } - } + serv->foreach_connection([serv](Connection *conn) { serv->close(conn->session_id, true); }); /** * call internal serv hooks */ - if (serv->hooks[SW_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)SwooleWG.id; - swServer_call_hook(serv, SW_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(); - - if (serv->onWorkerStop) - { - serv->onWorkerStop(serv, worker->id); - } + serv->worker_stop_callback(worker); return retval; } -static int swReactorProcess_onClose(swReactor *reactor, swEvent *event) -{ +static int ReactorProcess_onClose(Reactor *reactor, Event *event) { int fd = event->fd; - swServer *serv = (swServer *) reactor->ptr; - swConnection *conn = swServer_connection_get(serv, fd); - if (conn == NULL || conn->active == 0) - { + Server *serv = (Server *) reactor->ptr; + Connection *conn = serv->get_connection(fd); + if (conn == nullptr || conn->active == 0) { return SW_ERR; } - if (reactor->del(reactor, event->socket) == 0) - { - if (conn->close_queued) - { - swReactorThread_close(reactor, event->socket); - return SW_OK; - } - else - { - return serv->notify(serv, conn, SW_SERVER_EVENT_CLOSE); - } - } - else - { - return SW_ERR; - } -} - -static int swReactorProcess_send2worker(swSocket *socket, const void *data, size_t length) -{ - if (!SwooleTG.reactor) - { - return swSocket_write_blocking(socket, data, length); - } - else - { - return SwooleTG.reactor->write(SwooleTG.reactor, socket, data, length); - } -} - -static int swReactorProcess_send2client(swFactory *factory, swSendData *data) -{ - swServer *serv = (swServer *) factory->ptr; - int session_id = data->info.fd; - - swSession *session = swServer_get_session(serv, session_id); - if (session->fd == 0) - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_NOT_EXIST, "send %d byte failed, session#%d does not exist", - data->info.len, session_id); - return SW_ERR; + if (event->socket->removed) { + return Server::close_connection(reactor, event->socket); } - //proxy - if (session->reactor_id != SwooleWG.id) - { - swTrace("session->reactor_id=%d, SwooleWG.id=%d", session->reactor_id, SwooleWG.id); - swWorker *worker = swProcessPool_get_worker(&serv->gs->event_workers, session->reactor_id); - swEventData proxy_msg; - bzero(&proxy_msg.info, sizeof(proxy_msg.info)); - - if (data->info.type == SW_SERVER_EVENT_SEND_DATA) - { - proxy_msg.info.fd = session_id; - proxy_msg.info.reactor_id = SwooleWG.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; - swReactorProcess_send2worker(worker->pipe_master, (const char *) &proxy_msg, sizeof(proxy_msg.info) + proxy_msg.info.len); - } - - 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); - return swReactorProcess_send2worker(worker->pipe_master, (const char *) &proxy_msg, sizeof(proxy_msg.info) + proxy_msg.info.len); - } - else - { - swWarn("unkown event type[%d]", data->info.type); - return SW_ERR; + if (reactor->del(event->socket) == 0) { + 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; } - return SW_OK; - } - else - { - return swFactory_finish(factory, data); + } else { + return SW_ERR; } } -static void swReactorProcess_onTimeout(swTimer *timer, swTimer_node *tnode) -{ - swReactor *reactor = (swReactor *) tnode->data; - swServer *serv = (swServer *) reactor->ptr; - swEvent notify_ev; - swConnection *conn; - time_t now = time(NULL); - - if (now < heartbeat_check_lasttime + 10) - { - return; - } - - int fd; - int checktime; +static void ReactorProcess_onTimeout(Timer *timer, TimerNode *tnode) { + Reactor *reactor = (Reactor *) tnode->data; + Server *serv = (Server *) reactor->ptr; + Event notify_ev{}; + double now = microtime(); - bzero(¬ify_ev, sizeof(notify_ev)); notify_ev.type = SW_FD_SESSION; - int serv_max_fd = swServer_get_maxfd(serv); - int serv_min_fd = swServer_get_minfd(serv); - - checktime = now - serv->heartbeat_idle_time; - - for (fd = serv_min_fd; fd <= serv_max_fd; fd++) - { - conn = swServer_connection_get(serv, fd); - if (swServer_connection_valid(serv, conn)) - { - if (conn->protect || conn->last_time > checktime) - { - continue; - } + serv->foreach_connection([serv, reactor, now, ¬ify_ev](Connection *conn) { + if (serv->is_healthy_connection(now, conn)) { + return; + } #ifdef SW_USE_OPENSSL - if (conn->socket->ssl && conn->socket->ssl_state != SW_SSL_STATE_READY) - { - swReactorThread_close(reactor, conn->socket); - continue; - } + if (conn->socket->ssl && conn->socket->ssl_state != SW_SSL_STATE_READY) { + Server::close_connection(reactor, conn->socket); + return; + } #endif - notify_ev.fd = fd; - notify_ev.reactor_id = conn->reactor_id; - swReactorProcess_onClose(reactor, ¬ify_ev); + if (serv->disable_notify || conn->close_force) { + Server::close_connection(reactor, conn->socket); + return; } - } + conn->close_force = 1; + notify_ev.fd = conn->fd; + notify_ev.socket = conn->socket; + notify_ev.reactor_id = conn->reactor_id; + ReactorProcess_onClose(reactor, ¬ify_ev); + }); } - -#ifdef HAVE_REUSEPORT -static int swReactorProcess_reuse_port(swListenPort *ls) -{ - int sock = swSocket_create(ls->type, 1, 1); - if (sock < 0) - { - swSysWarn("create socket failed"); - return SW_ERR; - } - int option = 1; - if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &option, sizeof(int)) != 0) - { - close(sock); - return SW_ERR; - } - ls->socket->fd = sock; - //bind address and port - if (swSocket_bind(ls->socket, ls->host, &ls->port) < 0) - { - close(ls->socket->fd); - return SW_ERR; - } - ls->socket->nonblock = 1; - ls->socket->cloexec = 1; - return swPort_listen(ls); -} -#endif +} // namespace swoole diff --git a/src/server/reactor_thread.cc b/src/server/reactor_thread.cc index 99f72ced05a..2c1a5e9ae66 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -10,94 +10,71 @@ | to obtain it through the world-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_cxx.h" -#include "server.h" -#include "hash.h" -#include "client.h" -#include "websocket.h" +#include "swoole_server.h" +#include "swoole_memory.h" +#include "swoole_hash.h" +#include "swoole_util.h" -#include +#include using std::unordered_map; -using namespace swoole; - -static int swReactorThread_loop(swThreadParam *param); -static int swReactorThread_init(swServer *serv, swReactor *reactor, uint16_t reactor_id); -static int swReactorThread_onPipeWrite(swReactor *reactor, swEvent *ev); -static int swReactorThread_onPipeRead(swReactor *reactor, swEvent *ev); -static int swReactorThread_onRead(swReactor *reactor, swEvent *ev); -static int swReactorThread_onWrite(swReactor *reactor, swEvent *ev); -static int swReactorThread_onPacketReceived(swReactor *reactor, swEvent *event); -static int swReactorThread_onClose(swReactor *reactor, swEvent *event); -static void swReactorThread_onStreamResponse(swStream *stream, char *data, uint32_t length); -static int swReactorThread_is_empty(swReactor *reactor); -static void swReactorThread_shutdown(swReactor *reactor); -static void swReactorThread_resume_data_receiving(swTimer *timer, swTimer_node *tnode); - -static void swHeartbeatThread_start(swServer *serv); -static void swHeartbeatThread_loop(swThreadParam *param); + +namespace swoole { +using namespace network; + +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_resume_data_receiving(Timer *timer, TimerNode *tnode); #ifdef SW_USE_OPENSSL -static inline enum swReturn_code swReactorThread_verify_ssl_state(swReactor *reactor, swListenPort *port, swSocket *_socket) -{ - swServer *serv = (swServer *) reactor->ptr; - if (!_socket->ssl || _socket->ssl_state == SW_SSL_STATE_READY) - { +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 = swSSL_accept(_socket); - if (code != SW_READY) - { + ReturnCode code = _socket->ssl_accept(); + if (code != SW_READY) { return code; } - swConnection *conn = (swConnection *) _socket->object; + Connection *conn = (Connection *) _socket->object; conn->ssl_ready = 1; - if (port->ssl_option.client_cert_file) - { - int retval = swSSL_get_peer_cert(_socket->ssl, SwooleTG.buffer_stack->str, SwooleTG.buffer_stack->size); - if (retval < 0) - { - if (port->ssl_option.verify_peer) - { + if (!port->ssl_context->client_cert_file.empty()) { + if (!_socket->ssl_get_peer_certificate(sw_tg_buffer())) { + if (port->ssl_context->verify_peer) { return SW_ERROR; } - } - else - { - if (!port->ssl_option.verify_peer || swSSL_verify(_socket, port->ssl_option.allow_self_signed) == SW_OK) - { - swFactory *factory = &serv->factory; - swSendData task; + } else { + if (!port->ssl_context->verify_peer || _socket->ssl_verify(port->ssl_context->allow_self_signed)) { + SendData task; task.info.fd = _socket->fd; task.info.type = SW_SERVER_EVENT_CONNECT; task.info.reactor_id = reactor->id; - task.info.len = retval; - task.data = SwooleTG.buffer_stack->str; - factory->dispatch(factory, &task); + task.info.len = sw_tg_buffer()->length; + task.data = sw_tg_buffer()->str; + serv->factory->dispatch(&task); goto _delay_receive; - } - else - { + } else { return SW_ERROR; } } } - if (serv->onConnect) - { - serv->notify(serv, (swConnection *) _socket->object, SW_SERVER_EVENT_CONNECT); + if (serv->onConnect) { + serv->notify((Connection *) _socket->object, SW_SERVER_EVENT_CONNECT); } - _delay_receive: - if (serv->enable_delay_receive) - { - if (reactor->del(reactor, _socket) < 0) - { +_delay_receive: + if (serv->enable_delay_receive) { + if (reactor->del(_socket) < 0) { return SW_ERROR; } } @@ -106,108 +83,65 @@ static inline enum swReturn_code swReactorThread_verify_ssl_state(swReactor *rea } #endif -static void swReactorThread_onStreamResponse(swStream *stream, char *data, uint32_t length) -{ - swSendData response; - swDataHead *pkg_info = (swDataHead *) data; - swServer *serv = (swServer *) stream->private_data; - swConnection *conn = swServer_connection_verify(serv, pkg_info->fd); - if (!conn) - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_NOT_EXIST, "connection[fd=%d] does not exists", pkg_info->fd); - return; - } - response.info.fd = conn->session_id; - response.info.type = pkg_info->type; - response.info.len = length - sizeof(swDataHead); - response.data = data + sizeof(swDataHead); - swServer_master_send(serv, &response); -} - /** * for udp */ -static int swReactorThread_onPacketReceived(swReactor *reactor, swEvent *event) -{ +static int ReactorThread_onPacketReceived(Reactor *reactor, Event *event) { int fd = event->fd; - int ret; + ssize_t ret; - swServer *serv = (swServer *) reactor->ptr; - swConnection *server_sock = &serv->connection_list[fd]; - swSendData task; - swDgramPacket *pkt = (swDgramPacket *) SwooleTG.buffer_stack->str; - swFactory *factory = &serv->factory; + Server *serv = (Server *) reactor->ptr; + Connection *server_sock = serv->get_connection(fd); + network::Socket *sock = server_sock->socket; + SendData task = {}; + DgramPacket *pkt = (DgramPacket *) sw_tg_buffer()->str; - pkt->socket_addr.len = sizeof(pkt->socket_addr.addr); - - bzero(&task.info, sizeof(task.info)); task.info.server_fd = fd; task.info.reactor_id = SwooleTG.id; - task.info.type = SW_SERVER_EVENT_SNED_DGRAM; -#ifdef SW_BUFFER_RECV_TIME - task.info.time = swoole_microtime(); -#endif + task.info.type = SW_SERVER_EVENT_RECV_DGRAM; + task.info.time = microtime(); - int socket_type = server_sock->socket_type; + pkt->socket_addr.type = pkt->socket_type = server_sock->socket_type; - _do_recvfrom: +_do_recvfrom: - ret = recvfrom( - fd, pkt->data, SwooleTG.buffer_stack->size - sizeof(*pkt), 0, - (struct sockaddr *) &pkt->socket_addr.addr, &pkt->socket_addr.len - ); - - if (ret <= 0) - { - if (errno == EAGAIN) - { + ret = sock->recvfrom(pkt->data, sw_tg_buffer()->size - sizeof(*pkt), 0, &pkt->socket_addr); + if (ret <= 0) { + if (errno == EAGAIN) { return SW_OK; - } - else - { - swSysWarn("recvfrom(%d) failed", fd); + } else { + swoole_sys_warning("recvfrom(%d) failed", fd); return SW_ERR; } } #ifdef SW_SUPPORT_DTLS - swListenPort *port = (swListenPort *) server_sock->object; - - if (port->ssl_option.dtls) - { - swoole::dtls::Session *session = swServer_dtls_accept(serv, port, &pkt->socket_addr); + ListenPort *port = (ListenPort *) server_sock->object; - if (!session) - { + if (port->is_dtls()) { + dtls::Session *session = serv->accept_dtls_connection(port, &pkt->socket_addr); + if (!session) { return SW_ERR; } session->append(pkt->data, ret); - if (!session->listen()) - { - return swReactorThread_close(reactor, session->socket); + if (!session->listen()) { + return Server::close_connection(reactor, session->socket); } - swConnection *conn = (swConnection *) session->socket->object; - if (serv->single_thread) - { - if (swServer_connection_incoming(serv, reactor, conn) < 0) - { + Connection *conn = (Connection *) session->socket->object; + if (serv->single_thread) { + if (serv->connection_incoming(reactor, conn) < 0) { reactor->close(reactor, session->socket); return SW_OK; } - } - else - { - swDataHead ev = {}; + } else { + DataHead ev{}; ev.type = SW_SERVER_EVENT_INCOMING; - ev.fd = session->socket->fd; - swSocket *_pipe_sock = swServer_get_send_pipe(serv, conn->session_id, conn->reactor_id); - swReactorThread *thread = swServer_get_thread(serv, SwooleTG.id); - swSocket *socket = &thread->pipe_sockets[_pipe_sock->fd]; - if (reactor->write(reactor, socket, &ev, sizeof(ev)) < 0) - { + 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, session->socket); return SW_OK; } @@ -217,30 +151,19 @@ static int swReactorThread_onPacketReceived(swReactor *reactor, swEvent *event) } #endif - if (socket_type == SW_SOCK_UDP) - { - memcpy(&task.info.fd, &pkt->socket_addr.addr.inet_v4.sin_addr, sizeof(task.info.fd)); - } - else if (socket_type == SW_SOCK_UDP6) - { - memcpy(&task.info.fd, &pkt->socket_addr.addr.inet_v6.sin6_addr, sizeof(task.info.fd)); - } - else - { - task.info.fd = swoole_crc32(pkt->socket_addr.addr.un.sun_path, pkt->socket_addr.len); + if (pkt->socket_type == SW_SOCK_UDP) { + task.info.fd = *(int *) &pkt->socket_addr.addr.inet_v4.sin_addr; + } else { + task.info.fd = swoole_crc32(pkt->socket_addr.get_addr(), pkt->socket_addr.len); } - pkt->socket_type = socket_type; pkt->length = ret; task.info.len = sizeof(*pkt) + ret; - task.data = (char*) pkt; + task.data = (char *) pkt; - if (factory->dispatch(factory, &task) < 0) - { + if (!serv->factory->dispatch(&task)) { return SW_ERR; - } - else - { + } else { goto _do_recvfrom; } } @@ -248,35 +171,39 @@ static int swReactorThread_onPacketReceived(swReactor *reactor, swEvent *event) /** * close connection */ -int swReactorThread_close(swReactor *reactor, swSocket *socket) -{ - swServer *serv = (swServer *) reactor->ptr; - swConnection *conn = (swConnection *) socket->object; - swListenPort *port = swServer_get_port(serv, socket->fd); - - if (conn->timer) - { +int Server::close_connection(Reactor *reactor, Socket *socket) { + Server *serv = (Server *) reactor->ptr; + Connection *conn = (Connection *) socket->object; + ListenPort *port = serv->get_port_by_fd(socket->fd); + + if (conn->timer) { swoole_timer_del(conn->timer); } - if (!socket->removed && reactor->del(reactor, socket) < 0) - { + if (!socket->removed && reactor->del(socket) < 0) { return SW_ERR; } - sw_atomic_fetch_add(&serv->stats->close_count, 1); - sw_atomic_fetch_sub(&serv->stats->connection_num, 1); + sw_atomic_fetch_add(&serv->gs->close_count, 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) - { - swSSL_close(conn->socket); + if (socket->ssl) { + conn->socket->ssl_quiet_shutdown = conn->peer_closed; + conn->socket->ssl_close(); } #ifdef SW_SUPPORT_DTLS - if (socket->dtls) - { + if (socket->dtls) { dtls::Session *session = port->dtls_sessions->find(socket->fd)->second; port->dtls_sessions->erase(socket->fd); delete session; @@ -284,69 +211,63 @@ int swReactorThread_close(swReactor *reactor, swSocket *socket) #endif #endif - //free the receive memory buffer - swSocket_free_buffer(conn->socket); - - sw_atomic_fetch_sub(&port->connection_num, 1); + // free the receive memory buffer + if (socket->recv_buffer) { + delete socket->recv_buffer; + socket->recv_buffer = nullptr; + } - if (port->open_http_protocol && conn->object) - { - swHttpRequest_free(conn); + if (port->open_http_protocol && conn->object) { + serv->destroy_http_request(conn); } - if (port->open_redis_protocol && conn->object) - { + if (port->open_redis_protocol && conn->object) { sw_free(conn->object); conn->object = nullptr; } #ifdef SW_USE_SOCKET_LINGER - if (conn->close_force) - { + if (conn->close_force || conn->close_reset) { struct linger linger; linger.l_onoff = 1; linger.l_linger = 0; - if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger, sizeof(struct linger)) != 0) - { - swSysWarn("setsockopt(SO_LINGER) failed"); + if (conn->socket->set_option(SOL_SOCKET, SO_LINGER, &linger, sizeof(struct linger)) != 0) { + swoole_sys_warning("setsockopt(SO_LINGER) failed"); } } #endif - swSession *session = swServer_get_session(serv, conn->session_id); + Session *session = serv->get_session(conn->session_id); session->fd = 0; /** * reset maxfd, for connection_list */ int fd = socket->fd; - if (fd == swServer_get_maxfd(serv)) - { - swServer_lock(serv); + + 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); - /** - * Find the new max_fd - */ - for (; serv->connection_list[find_max_fd].active == 0 && find_max_fd > swServer_get_minfd(serv); find_max_fd--) - { - //pass + swoole_trace("set_maxfd=%d|close_fd=%d\n", find_max_fd, fd); + // find the new max_fd + for (; !serv->is_valid_connection(serv->get_connection(find_max_fd)) && find_max_fd > serv->get_minfd(); + find_max_fd--) { + // pass } - swServer_set_maxfd(serv, find_max_fd); - swServer_unlock(serv); + serv->set_maxfd(find_max_fd); } - bzero(conn, sizeof(swConnection)); - return swReactor_close(reactor, socket); + serv->unlock(); + + *conn = {}; + return Reactor::_close(reactor, socket); } /** * close the connection */ -static int swReactorThread_onClose(swReactor *reactor, swEvent *event) -{ - swServer *serv = (swServer *) reactor->ptr; +static int ReactorThread_onClose(Reactor *reactor, Event *event) { + Server *serv = (Server *) reactor->ptr; int fd = event->fd; - swDataHead notify_ev; - bzero(¬ify_ev, sizeof(notify_ev)); - swSocket *socket = event->socket; + DataHead notify_ev{}; + Socket *socket = event->socket; assert(fd % serv->reactor_num == reactor->id); assert(fd % serv->reactor_num == SwooleTG.id); @@ -355,321 +276,266 @@ static int swReactorThread_onClose(swReactor *reactor, swEvent *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); - swConnection *conn = swServer_connection_get(serv, fd); - if (conn == NULL || conn->active == 0) - { + Connection *conn = serv->get_connection(fd); + if (conn == nullptr || conn->active == 0) { return SW_ERR; - } - else if (serv->disable_notify) - { - swReactorThread_close(reactor, socket); + } else if (serv->disable_notify) { + Server::close_connection(reactor, socket); return SW_OK; - } - else if (reactor->del(reactor, socket) == 0) - { - if (conn->close_queued) - { - swReactorThread_close(reactor, socket); + } else if (reactor->del(socket) == 0) { + if (conn->close_queued) { + Server::close_connection(reactor, socket); return SW_OK; - } - else - { + } else { + /** + * peer_closed indicates that the client has closed the connection + * and the connection is no longer available. + */ conn->peer_closed = 1; - return serv->factory.notify(&serv->factory, ¬ify_ev); + return serv->factory->notify(¬ify_ev); } - } - else - { + } else { return SW_ERR; } } -static void swReactorThread_shutdown(swReactor *reactor) -{ - swServer *serv = (swServer *) reactor->ptr; - //stop listen UDP Port - if (serv->have_dgram_sock == 1) - { - for (auto ls : *serv->listen_list) - { - if (swSocket_is_dgram(ls->type)) - { - if (ls->socket->fd % serv->reactor_num != reactor->id) - { +void ReactorThread::shutdown(Reactor *reactor) { + Server *serv = (Server *) reactor->ptr; + // stop listen UDP Port + if (serv->have_dgram_sock == 1) { + for (auto ls : serv->ports) { + if (ls->is_dgram()) { + if (ls->socket->fd % serv->reactor_num != reactor->id) { continue; } - reactor->del(reactor, ls->socket); + if (!ls->socket->removed) { + reactor->del(ls->socket); + } } } } - int fd; - int serv_max_fd = swServer_get_maxfd(serv); - int serv_min_fd = swServer_get_minfd(serv); + if (serv->is_thread_mode()) { + Socket *socket = message_bus.get_pipe_socket(serv->get_worker_pipe_worker(reactor->id)); + reactor->del(socket); + } - for (fd = serv_min_fd; fd <= serv_max_fd; fd++) - { - if (fd % serv->reactor_num != reactor->id) - { + SW_LOOP_N(serv->worker_num) { + if (i % serv->reactor_num != reactor->id) { continue; } - swConnection *conn = swServer_connection_get(serv, fd); - if (swServer_connection_valid(serv, conn) && !conn->peer_closed && !conn->socket->removed) - { - swReactor_remove_read_event(reactor, conn->socket); + 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; + } + if (!conn->peer_closed && !conn->socket->removed) { + reactor->remove_read_event(conn->socket); } + }); + + if (serv->is_thread_mode()) { + serv->stop_async_worker(serv->get_worker(reactor->id)); } - reactor->wait_exit = 1; + reactor->set_wait_exit(true); } -/** - * receive data from worker process pipe - */ -static int swReactorThread_onPipeRead(swReactor *reactor, swEvent *ev) -{ - swSendData _send; +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; + } - swServer *serv = (swServer *) reactor->ptr; - swReactorThread *thread = swServer_get_thread(serv, reactor->id); - swString *package = nullptr; - swPipeBuffer *resp = serv->pipe_buffers[reactor->id]; + if (serv->disable_notify || conn->close_force) { + return Server::close_connection(reactor, conn->socket); + } -#ifdef SW_REACTOR_RECV_AGAIN - while (1) -#endif - { - ssize_t n = read(ev->fd, 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 = swString_new(SW_BUFFER_SIZE_BIG); - if (package == nullptr) - { - swSysWarn("get buffer(worker-%d) failed", worker_id); - return SW_OK; - } - else - { - thread->send_buffers->emplace(std::make_pair(key, package)); - } - } - else - { - package = it->second; - } - //merge data to package buffer - swString_append_ptr(package, 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; - swServer_master_send(serv, &_send); - swString_free(package); - thread->send_buffers->erase(key); - } - else - { - /** - * connection incoming - */ - if (resp->info.type == SW_SERVER_EVENT_INCOMING) - { - int fd = resp->info.fd; - swConnection *conn = swServer_connection_get(serv, fd); - if (swServer_connection_incoming(serv, reactor, conn) < 0) - { - return reactor->close(reactor, conn->socket); - } - } - /** - * server shutdown - */ - else if (resp->info.type == SW_SERVER_EVENT_SHUTDOWN) - { - swReactorThread_shutdown(reactor); - } - else - { - _send.info = resp->info; - _send.data = resp->data; - swServer_master_send(serv, &_send); - } - } - } - else if (errno == EAGAIN) - { - return SW_OK; - } - else - { - swSysWarn("read(worker_pipe) failed"); - return SW_ERR; - } +#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; } -int swReactorThread_send2worker(swServer *serv, swWorker *worker, void *data, size_t len) -{ - if (SwooleTG.reactor) - { - swReactorThread *thread = swServer_get_thread(serv, SwooleTG.id); - swSocket *socket = &thread->pipe_sockets[worker->pipe_master->fd]; - return swoole_event_write(socket, data, len); - } - else - { - return swSocket_write_blocking(worker->pipe_master, data, len); +/** + * 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); + + 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 (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 { + 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; } /** * [ReactorThread] worker pipe can write. */ -static int swReactorThread_onPipeWrite(swReactor *reactor, swEvent *ev) -{ +static int ReactorThread_onPipeWrite(Reactor *reactor, Event *ev) { int ret; - swBuffer_chunk *chunk = NULL; - swEventData *send_data; - swConnection *conn; - swServer *serv = (swServer *) reactor->ptr; - swBuffer *buffer = ev->socket->out_buffer; - - while (!swBuffer_empty(buffer)) - { - chunk = swBuffer_get_chunk(buffer); - send_data = (swEventData *) chunk->store.ptr; - - //server active close, discard data. - if (swEventData_is_stream(send_data->info.type)) - { - //send_data->info.fd is session_id - conn = swServer_connection_verify(serv, send_data->info.fd); - if (conn) - { - if (conn->closed) - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED_BY_SERVER, "Session#%d is closed by server", send_data->info.fd); - _discard: - swBuffer_pop_chunk(buffer, chunk); + Server *serv = (Server *) reactor->ptr; + Buffer *buffer = ev->socket->out_buffer; + + while (!Buffer::empty(buffer)) { + BufferChunk *chunk = buffer->front(); + EventData *send_data = (EventData *) chunk->value.str; + + // 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); + if (conn) { + conn->last_send_time = microtime(); + if (conn->closed) { + swoole_error_log(SW_LOG_NOTICE, + SW_ERROR_SESSION_CLOSED_BY_SERVER, + "Session#%ld is closed by server", + send_data->info.fd); + _discard: + buffer->pop(); continue; } - } - else if (serv->discard_timeout_request) - { - swoole_error_log( - SW_LOG_WARNING, SW_ERROR_SESSION_DISCARD_TIMEOUT_DATA, - "[1] received the wrong data[%d bytes] from socket#%d", - send_data->info.len, send_data->info.fd - ); + } else if (serv->discard_timeout_request) { + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SESSION_DISCARD_TIMEOUT_DATA, + "[1] ignore data[%u bytes] received from session#%ld", + send_data->info.len, + send_data->info.fd); goto _discard; } } - ret = swSocket_send(ev->socket, chunk->store.ptr, chunk->length, 0); - if (ret < 0) - { - return (swSocket_error(errno) == SW_WAIT) ? SW_OK : SW_ERR; - } - else - { - swBuffer_pop_chunk(buffer, chunk); + ret = ev->socket->send(chunk->value.str, chunk->length, 0); + if (ret < 0) { + return (ev->socket->catch_write_error(errno) == SW_WAIT) ? SW_OK : SW_ERR; + } else { + buffer->pop(); } } - if (swBuffer_empty(buffer)) - { - if (swReactor_remove_write_event(reactor, ev->socket) < 0) - { - swSysWarn("reactor->set(%d) failed", ev->fd); + if (Buffer::empty(buffer)) { + if (reactor->remove_write_event(ev->socket) < 0) { + swoole_sys_warning("reactor->set(%d) failed", ev->fd); } } return SW_OK; } -void swReactorThread_set_protocol(swServer *serv, swReactor *reactor) -{ - //64k packet - if (serv->have_dgram_sock) - { - swString_extend_align(SwooleTG.buffer_stack, SwooleTG.buffer_stack->size * 2); - } - //UDP Packet - swReactor_set_handler(reactor, SW_FD_DGRAM_SERVER, swReactorThread_onPacketReceived); - //Write - swReactor_set_handler(reactor, SW_FD_SESSION | SW_EVENT_WRITE, swReactorThread_onWrite); - //Read - swReactor_set_handler(reactor, SW_FD_SESSION | SW_EVENT_READ, swReactorThread_onRead); - - //listen the all tcp port - for (auto ls : *serv->listen_list) - { - if (swSocket_is_dgram(ls->type) +void Server::init_reactor(Reactor *reactor) { + // support 64K packet + if (have_dgram_sock) { + sw_tg_buffer()->extend(); + } + // UDP Packet + reactor->set_handler(SW_FD_DGRAM_SERVER, ReactorThread_onPacketReceived); + // Write + reactor->set_handler(SW_FD_SESSION | SW_EVENT_WRITE, ReactorThread_onWrite); + // Read + reactor->set_handler(SW_FD_SESSION | SW_EVENT_READ, ReactorThread_onRead); + + // listen the all tcp port + for (auto port : ports) { + if (port->is_dgram() #ifdef SW_SUPPORT_DTLS - && !ls->ssl_option.dtls + && !(port->is_dtls()) #endif - ) - { + ) { continue; } - swPort_set_protocol(serv, ls); + init_port_protocol(port); } } -static int swReactorThread_onRead(swReactor *reactor, swEvent *event) -{ - swServer *serv = (swServer *) reactor->ptr; - swConnection *conn = swServer_connection_get(serv, event->fd); +static int ReactorThread_onRead(Reactor *reactor, Event *event) { + Server *serv = (Server *) reactor->ptr; + Connection *conn = serv->get_connection(event->fd); /** * invalid event * The server has been actively closed the connection, the client also initiated off, fd has been reused. */ - if (!conn || conn->server_fd == 0) - { + if (!conn || conn->server_fd == 0) { return SW_OK; } - swListenPort *port = swServer_get_port(serv, event->fd); + ListenPort *port = serv->get_port_by_fd(event->fd); #ifdef SW_USE_OPENSSL #ifdef SW_SUPPORT_DTLS - if (port->ssl_option.dtls) - { + if (port->is_dtls()) { dtls::Buffer *buffer = (dtls::Buffer *) sw_malloc(sizeof(*buffer) + SW_BUFFER_SIZE_UDP); - buffer->length = read(event->fd, buffer->data, SW_BUFFER_SIZE_UDP); + buffer->length = event->socket->read(buffer->data, SW_BUFFER_SIZE_UDP); dtls::Session *session = port->dtls_sessions->find(event->fd)->second; session->append(buffer); - if (!session->listened && !session->listen()) - { - swReactorThread_close(reactor, event->socket); + if (!session->listened && !session->listen()) { + serv->abort_connection(reactor, port, event->socket); return SW_OK; } } #endif - enum swReturn_code code = swReactorThread_verify_ssl_state(reactor, port, event->socket); - switch (code) - { + ReturnCode code = ReactorThread_verify_ssl_state(reactor, port, event->socket); + switch (code) { case SW_ERROR: - return swReactorThread_close(reactor, event->socket); + serv->abort_connection(reactor, port, event->socket); + return SW_OK; case SW_READY: #ifdef SW_SUPPORT_DTLS - if (event->socket->dtls) - { + if (event->socket->dtls) { return SW_OK; } #endif @@ -683,150 +549,109 @@ static int swReactorThread_onRead(swReactor *reactor, swEvent *event) } #endif - conn->last_time = time(NULL); -#ifdef SW_BUFFER_RECV_TIME - conn->last_time_usec = swoole_microtime(); -#endif + conn->last_recv_time = microtime(); + long last_recv_bytes = event->socket->total_recv_bytes; int retval = port->onRead(reactor, port, event); - if (serv->factory_mode == SW_MODE_PROCESS && serv->max_queued_bytes && conn->queued_bytes > serv->max_queued_bytes) - { + + 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, swReactorThread_resume_data_receiving, event->socket); - if (conn->timer) - { - swReactor_remove_read_event(sw_reactor(), 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); } } return retval; } -static int swReactorThread_onWrite(swReactor *reactor, swEvent *ev) -{ +static int ReactorThread_onWrite(Reactor *reactor, Event *ev) { int ret; - swServer *serv = (swServer *) reactor->ptr; - swSocket *socket = ev->socket; - swBuffer_chunk *chunk; + Server *serv = (Server *) reactor->ptr; + Socket *socket = ev->socket; int fd = ev->fd; - if (serv->factory_mode == SW_MODE_PROCESS) - { + if (serv->is_process_mode()) { assert(fd % serv->reactor_num == reactor->id); assert(fd % serv->reactor_num == SwooleTG.id); } - swConnection *conn = swServer_connection_get(serv, fd); - if (conn == NULL || conn->active == 0) - { + Connection *conn = serv->get_connection(fd); + if (conn == nullptr || conn->active == 0) { 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) - { + if (conn->close_notify) { #ifdef SW_USE_OPENSSL - if (conn->socket->ssl && conn->socket->ssl_state != SW_SSL_STATE_READY) - { - return swReactorThread_close(reactor, socket); + if (socket->ssl && socket->ssl_state != SW_SSL_STATE_READY) { + return Server::close_connection(reactor, socket); } #endif - serv->notify(serv, conn, SW_SERVER_EVENT_CLOSE); + serv->notify(conn, SW_SERVER_EVENT_CLOSE); conn->close_notify = 0; return SW_OK; - } - else if (serv->disable_notify && conn->close_force) - { - return swReactorThread_close(reactor, socket); - } - - while (!swBuffer_empty(conn->socket->out_buffer)) - { - chunk = swBuffer_get_chunk(conn->socket->out_buffer); - if (chunk->type == SW_CHUNK_CLOSE) - { - _close_fd: - reactor->close(reactor, socket); - return SW_OK; - } - else if (chunk->type == SW_CHUNK_SENDFILE) - { - ret = swSocket_onSendfile(socket, chunk); - } - else - { - ret = swSocket_buffer_send(socket); + } else if (serv->disable_notify && conn->close_force) { + return Server::close_connection(reactor, socket); + } + + while (!Buffer::empty(socket->out_buffer)) { + BufferChunk *chunk = socket->out_buffer->front(); + if (chunk->type == BufferChunk::TYPE_CLOSE) { + return reactor->close(reactor, socket); + } else if (chunk->type == BufferChunk::TYPE_SENDFILE) { + ret = socket->handle_sendfile(); + } else { + ret = socket->handle_send(); + if (SW_OK == ret) { + conn->send_queued_bytes = socket->out_buffer->length(); + } } - if (ret < 0) - { - if (conn->socket->close_wait) - { + if (ret < 0) { + if (socket->close_wait) { conn->close_errno = errno; - goto _close_fd; - } - else if (conn->socket->send_wait) - { + return reactor->trigger_close_event(ev); + } else if (socket->send_wait) { break; } } } - if (conn->overflow && conn->socket->out_buffer->length < conn->socket->buffer_size) - { + if (conn->overflow && socket->out_buffer->length() < socket->buffer_size) { conn->overflow = 0; } - if (serv->onBufferEmpty && conn->high_watermark) - { - swListenPort *port = swServer_get_port(serv, fd); - if (conn->socket->out_buffer->length <= port->buffer_low_watermark) - { + if (serv->onBufferEmpty && conn->high_watermark) { + ListenPort *port = serv->get_port_by_fd(fd); + if (socket->out_buffer->length() <= port->buffer_low_watermark) { conn->high_watermark = 0; - serv->notify(serv, conn, SW_SERVER_EVENT_BUFFER_EMPTY); + serv->notify(conn, SW_SERVER_EVENT_BUFFER_EMPTY); } } - //remove EPOLLOUT event - if (!conn->peer_closed && swBuffer_empty(conn->socket->out_buffer)) - { - reactor->set(reactor, socket, SW_EVENT_READ); + if (socket->send_timer) { + swoole_timer_del(socket->send_timer); + socket->send_timer = nullptr; } - return SW_OK; -} -int swReactorThread_create(swServer *serv) -{ - int ret = 0; - /** - * init reactor thread pool - */ - serv->reactor_threads = (swReactorThread *) SwooleG.memory_pool->alloc(SwooleG.memory_pool, (serv->reactor_num * sizeof(swReactorThread))); - if (serv->reactor_threads == NULL) - { - swError("calloc[reactor_threads] fail.alloc_size=%d", (int )(serv->reactor_num * sizeof(swReactorThread))); - return SW_ERR; - } - /** - * alloc the memory for connection_list - */ - serv->connection_list = (swConnection *) sw_shm_calloc(serv->max_connection, sizeof(swConnection)); - if (serv->connection_list == NULL) - { - swError("calloc[1] failed"); - return SW_ERR; - } - if (serv->worker_num < 1) - { - swError("Fatal Error: serv->worker_num < 1"); - return SW_ERR; - } - ret = swFactoryProcess_create(&(serv->factory), serv->worker_num); - if (ret < 0) - { - swError("create factory failed"); - return SW_ERR; + // remove EPOLLOUT event + if (!conn->peer_closed && !socket->removed && Buffer::empty(socket->out_buffer)) { + reactor->set(socket, SW_EVENT_READ); } return SW_OK; } @@ -834,557 +659,313 @@ int swReactorThread_create(swServer *serv) /** * [master] */ -int swReactorThread_start(swServer *serv) -{ - if (swoole_event_init() < 0) - { +int Server::start_reactor_threads() { + if (swoole_event_init(0) < 0) { return SW_ERR; } - swReactor *reactor = SwooleTG.reactor; + Reactor *reactor = sw_reactor(); -#ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd) - { - swSignalfd_setup(reactor); - } -#endif - - //set listen socket options - std::vector::iterator ls; - for (ls = serv->listen_list->begin(); ls != serv->listen_list->end(); ls++) - { - if (swSocket_is_dgram((*ls)->type)) - { + for (auto iter = ports.begin(); iter != ports.end(); iter++) { + auto port = *iter; + if (port->is_dgram()) { continue; } - if (swPort_listen(*ls) < 0) - { - _failed: - reactor->free(reactor); - SwooleTG.reactor = nullptr; - sw_free(reactor); + if (port->listen() < 0) { + swoole_event_free(); return SW_ERR; } - reactor->add(reactor, (*ls)->socket, SW_EVENT_READ); + reactor->add(port->socket, SW_EVENT_READ); } - /** - * create reactor thread - */ - swThreadParam *param; - swReactorThread *thread; - pthread_t pidt; - int i; - - swServer_store_listen_socket(serv); + store_listen_socket(); - if (serv->single_thread) - { - swReactorThread_init(serv, reactor, 0); + if (single_thread) { + get_thread(0)->init(this, reactor, 0); goto _init_master_thread; } /** * multi-threads */ - else - { + else { /** * set a special id */ - reactor->id = serv->reactor_num; - SwooleTG.id = serv->reactor_num; + reactor->id = reactor_num; + SwooleTG.id = reactor_num; } -#ifdef HAVE_PTHREAD_BARRIER - //init thread barrier - pthread_barrier_init(&serv->barrier, NULL, serv->reactor_num + 1); -#endif - - //create reactor thread - for (i = 0; i < serv->reactor_num; i++) - { - thread = &(serv->reactor_threads[i]); - param = (swThreadParam *) SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(swThreadParam)); - if (param == NULL) - { - swError("malloc failed"); - goto _failed; - } - - param->object = serv; - param->pti = i; - - if (pthread_create(&pidt, NULL, (void * (*)(void *)) swReactorThread_loop, (void *) param) < 0) - { - swSysError("pthread_create[tcp_reactor] failed"); - } - thread->thread_id = pidt; + SW_LOOP_N(reactor_num) { + get_thread(i)->thread = std::thread([=]() { + swoole_thread_init(); + reactor_thread_main_loop(this, i); + swoole_thread_clean(); + }); } -#ifdef HAVE_PTHREAD_BARRIER - //wait reactor thread - pthread_barrier_wait(&serv->barrier); -#else - SW_START_SLEEP; -#endif - _init_master_thread: +_init_master_thread: /** * heartbeat thread */ - if (serv->heartbeat_check_interval >= 1 && serv->heartbeat_check_interval <= serv->heartbeat_idle_time) - { - swTrace("hb timer start, time: %d live time:%d", serv->heartbeat_check_interval, serv->heartbeat_idle_time); - swHeartbeatThread_start(serv); - } - - SwooleTG.type = SW_THREAD_MASTER; - SwooleTG.update_time = 1; - SwooleTG.reactor = reactor; - - if (SwooleTG.timer && SwooleTG.timer->reactor == nullptr) - { - swTimer_reinit(SwooleTG.timer, reactor); + if (heartbeat_check_interval >= 1) { + start_heartbeat_thread(); } - SwooleG.pid = getpid(); - SwooleG.process_type = SW_PROCESS_MASTER; - - reactor->ptr = serv; - swReactor_set_handler(reactor, SW_FD_STREAM_SERVER, swServer_master_onAccept); - - if (serv->hooks[SW_SERVER_HOOK_MASTER_START]) - { - swServer_call_hook(serv, SW_SERVER_HOOK_MASTER_START, serv); - } - - /** - * 1 second timer - */ - if ((serv->master_timer = swoole_timer_add(1000, SW_TRUE, swServer_master_onTimer, serv)) == NULL) - { - goto _failed; - } - - if (serv->onStart) - { - serv->onStart(serv); - } - - return swoole_event_wait(); + return start_master_thread(reactor); } -static int swReactorThread_init(swServer *serv, swReactor *reactor, uint16_t reactor_id) -{ - swReactorThread *thread = swServer_get_thread(serv, 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->max_connection; - reactor->close = swReactorThread_close; - reactor->is_empty = swReactorThread_is_empty; - - reactor->default_error_handler = swReactorThread_onClose; - - swReactor_set_handler(reactor, SW_FD_PIPE | SW_EVENT_READ, swReactorThread_onPipeRead); - swReactor_set_handler(reactor, SW_FD_PIPE | SW_EVENT_WRITE, swReactorThread_onPipeWrite); - - //listen UDP port - if (serv->have_dgram_sock == 1) - { - for (auto ls : *serv->listen_list) - { - if (swSocket_is_stream(ls->type)) - { + reactor->max_socket = serv->get_max_connection(); + reactor->close = Server::close_connection; + reactor->default_error_handler = ReactorThread_onClose; + + reactor->set_handler(SW_FD_PIPE | SW_EVENT_READ, ReactorThread_onPipeRead); + reactor->set_handler(SW_FD_PIPE | SW_EVENT_WRITE, ReactorThread_onPipeWrite); + + // listen UDP port + if (serv->have_dgram_sock == 1) { + for (auto ls : serv->ports) { + if (ls->is_stream()) { continue; } int server_fd = ls->socket->fd; - if (server_fd % serv->reactor_num != reactor_id) - { + if (server_fd % serv->reactor_num != reactor_id) { continue; } - if (ls->type == SW_SOCK_UDP) - { - serv->connection_list[server_fd].info.addr.inet_v4.sin_port = htons(ls->port); - } - else if (ls->type == SW_SOCK_UDP6) - { - serv->connection_list[server_fd].info.addr.inet_v6.sin6_port = htons(ls->port); + Connection *serv_sock = serv->get_connection(server_fd); + if (ls->type == SW_SOCK_UDP) { + serv_sock->info.addr.inet_v4.sin_port = htons(ls->port); + } else if (ls->type == SW_SOCK_UDP6) { + serv_sock->info.addr.inet_v6.sin6_port = htons(ls->port); } - serv->connection_list[server_fd].fd = server_fd; - serv->connection_list[server_fd].socket_type = ls->type; - serv->connection_list[server_fd].object = ls; + serv_sock->fd = server_fd; + serv_sock->socket_type = ls->type; + serv_sock->object = ls; ls->thread_id = pthread_self(); - if (reactor->add(reactor, ls->socket, SW_EVENT_READ) < 0) - { + if (reactor->add(ls->socket, SW_EVENT_READ) < 0) { return SW_ERR; } } } - //set protocol function point - swReactorThread_set_protocol(serv, reactor); - - thread->send_buffers = new unordered_map; + serv->init_reactor(reactor); + serv->init_pipe_sockets(&message_bus); - int max_pipe_fd = swServer_get_worker(serv, serv->worker_num - 1)->pipe_master->fd + 2; - thread->pipe_sockets = (swSocket *) sw_calloc(max_pipe_fd, sizeof(swSocket)); - if (!thread->pipe_sockets) - { - swSysError("calloc(%d, %ld) failed", max_pipe_fd, sizeof(swSocket)); - 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; - swSocket *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->fdtype = 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; + } - if (i % serv->reactor_num != reactor_id) - { + SW_LOOP_N(serv->worker_num) { + if (i % serv->reactor_num != reactor_id) { continue; } - - swSocket_set_nonblock(socket); - - if (reactor->add(reactor, socket, SW_EVENT_READ) < 0) - { + 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; } -static int swReactorThread_is_empty(swReactor *reactor) -{ - if (reactor->defer_tasks) - { - return SW_FALSE; - } - - swServer *serv = (swServer *) reactor->ptr; - swReactorThread *thread = swServer_get_thread(serv, reactor->id); - return reactor->event_num == thread->pipe_num; +void ReactorThread::clean() { + message_bus.free_buffer(); } -/** - * ReactorThread main Loop - */ -static int swReactorThread_loop(swThreadParam *param) -{ - swServer *serv = (swServer *) param->object; - int reactor_id = param->pti; - int ret; - +void Server::reactor_thread_main_loop(Server *serv, int reactor_id) { SwooleTG.id = reactor_id; - SwooleTG.type = SW_THREAD_REACTOR; - - SwooleTG.buffer_stack = swString_new(SW_STACK_BUFFER_SIZE); - if (SwooleTG.buffer_stack == NULL) - { - return SW_ERR; - } - - swReactorThread *thread = swServer_get_thread(serv, reactor_id); - swReactor *reactor = &thread->reactor; + SwooleTG.type = Server::THREAD_REACTOR; - SwooleTG.reactor = reactor; + ReactorThread *thread = serv->get_thread(reactor_id); + thread->id = reactor_id; + SwooleTG.message_bus = &thread->message_bus; -#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 (swoole_event_init(0) < 0) { + return; } -#endif - ret = swReactor_create(reactor, SW_REACTOR_MAXEVENTS); - if (ret < 0) - { - return SW_ERR; + if (serv->is_thread_mode()) { + serv->call_worker_start_callback(serv->get_worker(reactor_id)); } - swSignal_none(); - - if (swReactorThread_init(serv, reactor, reactor_id) < 0) - { - return SW_ERR; + 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 - //main loop - reactor->wait(reactor, NULL); - //shutdown - reactor->free(reactor); - - SwooleTG.reactor = nullptr; - - for (auto it = thread->send_buffers->begin(); it != thread->send_buffers->end(); it++) - { - swString_free(it->second); + // wait other thread + if (serv->is_process_mode()) { + serv->reactor_thread_barrier.wait(); } - delete thread->send_buffers; - - swString_free(SwooleTG.buffer_stack); - pthread_exit(0); - return SW_OK; + // main loop + swoole_event_wait(); + if (serv->is_thread_mode()) { + serv->call_worker_stop_callback(serv->get_worker(reactor_id)); + } + thread->clean(); } -static void swReactorThread_resume_data_receiving(swTimer *timer, swTimer_node *tnode) -{ - swSocket *_socket = (swSocket *) tnode->data; - swConnection *conn = (swConnection *) _socket->object; +static void ReactorThread_resume_data_receiving(Timer *timer, TimerNode *tnode) { + Socket *_socket = (Socket *) tnode->data; + Connection *conn = (Connection *) _socket->object; - if (conn->queued_bytes > sw_server()->max_queued_bytes) - { - if (conn->waiting_time != 1024) - { + if (conn->recv_queued_bytes > sw_server()->max_queued_bytes) { + if (conn->waiting_time != 1024) { conn->waiting_time *= 2; } - conn->timer = swoole_timer_add(conn->waiting_time, false, swReactorThread_resume_data_receiving, _socket); - if (conn->timer) - { + conn->timer = swoole_timer_add((long) conn->waiting_time, false, ReactorThread_resume_data_receiving, _socket); + if (conn->timer) { return; } } - swReactor_add_read_event(sw_reactor(), _socket); + timer->get_reactor()->add_read_event(_socket); conn->timer = nullptr; } /** * dispatch request data [only data frame] */ -int swReactorThread_dispatch(swProtocol *proto, swSocket *_socket, char *data, uint32_t length) -{ - swServer *serv = (swServer *) proto->private_data_2; - swSendData task; +int Server::dispatch_task(const Protocol *proto, Socket *_socket, const RecvData *rdata) { + Server *serv = (Server *) proto->private_data_2; + SendData task; - swConnection *conn = (swConnection *) _socket->object; + Connection *conn = (Connection *) _socket->object; + ListenPort *port = serv->get_port_by_fd(conn->fd); - bzero(&task.info, sizeof(task.info)); + 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.type = SW_SERVER_EVENT_SEND_DATA; -#ifdef SW_BUFFER_RECV_TIME - task.info.info.time = conn->last_time_usec; -#endif + 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) - { - swStream *stream = swStream_new(serv->stream_socket_file, 0, SW_SOCK_UNIX_STREAM); - if (stream == NULL) - { - return SW_ERR; - } - stream->response = swReactorThread_onStreamResponse; - stream->private_data = serv; - swListenPort *port = swServer_get_port(serv, conn->fd); - swStream_set_max_length(stream, port->protocol.package_max_length); - - task.info.fd = conn->session_id; + task.info.fd = conn->fd; + task.info.len = rdata->info.len; + task.data = rdata->data; - if (swStream_send(stream, (char*) &task.info, sizeof(task.info)) < 0) - { - _cancel: - stream->cancel = 1; - return SW_ERR; - } - if (swStream_send(stream, data, length) < 0) - { - goto _cancel; - } - return SW_OK; + 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); } - else - { - task.info.fd = conn->fd; - task.info.len = length; - task.data = data; - if (serv->factory.dispatch(&serv->factory, &task) < 0) - { - return SW_ERR; + + if (!serv->factory->dispatch(&task)) { + if (rdata->info.len > 0) { + sw_atomic_fetch_sub(&conn->recv_queued_bytes, rdata->info.len); } - if (serv->max_queued_bytes && length > 0) - { - sw_atomic_fetch_add(&conn->queued_bytes, length); - swTraceLog(SW_TRACE_SERVER, "[Master] len=%d, qb=%d\n", length, conn->queued_bytes); + return SW_ERR; + } else { + 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 swReactorThread_join(swServer *serv) -{ - if (serv->single_thread) - { - return; - } - swReactorThread *thread; +void Server::join_heartbeat_thread() { /** * Shutdown heartbeat thread */ - if (serv->heartbeat_pidt) - { - swTraceLog(SW_TRACE_SERVER, "terminate heartbeat thread"); - if (pthread_cancel(serv->heartbeat_pidt) < 0) - { - swSysWarn("pthread_cancel(%ld) failed", (ulong_t )serv->heartbeat_pidt); - } - //wait thread - if (pthread_join(serv->heartbeat_pidt, NULL) < 0) - { - swSysWarn("pthread_join(%ld) failed", (ulong_t )serv->heartbeat_pidt); - } - } - /** - * kill threads - */ - for (int i = 0; i < serv->reactor_num; i++) - { - thread = &(serv->reactor_threads[i]); - if (thread->notify_pipe) - { - swDataHead ev = {}; - ev.type = SW_SERVER_EVENT_SHUTDOWN; - if (swSocket_write_blocking(thread->notify_pipe, (void *) &ev, sizeof(ev)) < 0) - { - goto _cancel; - } - } - else - { - _cancel: if (pthread_cancel(thread->thread_id) < 0) - { - swSysWarn("pthread_cancel(%ld) failed", (long ) thread->thread_id); - } - } - //wait thread - if (pthread_join(thread->thread_id, NULL) != 0) - { - swSysWarn("pthread_join(%ld) failed", (long ) thread->thread_id); + if (heartbeat_thread.joinable()) { + swoole_trace_log(SW_TRACE_SERVER, "terminate heartbeat thread"); + if (pthread_cancel(heartbeat_thread.native_handle()) < 0) { + swoole_sys_warning("pthread_cancel(%ld) failed", (ulong_t) heartbeat_thread.native_handle()); } + // wait thread + heartbeat_thread.join(); } } -void swReactorThread_free(swServer *serv) -{ - serv->factory.free(&serv->factory); - sw_shm_free(serv->connection_list); -} - -static void swHeartbeatThread_start(swServer *serv) -{ - pthread_t thread_id; - swThreadParam *param = (swThreadParam *) SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(swThreadParam)); - if (param == NULL) - { - swError("heartbeat_param malloc failed"); +void Server::join_reactor_thread() { + if (single_thread) { return; } - param->object = serv; - param->pti = 0; + if (heartbeat_check_interval > 0) { + join_heartbeat_thread(); + } - if (pthread_create(&thread_id, NULL, (void * (*)(void *)) swHeartbeatThread_loop, (void *) param) < 0) - { - swWarn("pthread_create[hbcheck] failed"); + for (int i = 0; i < reactor_num; i++) { + ReactorThread *thread = get_thread(i); + if (thread->notify_pipe) { + DataHead ev = {}; + ev.type = SW_SERVER_EVENT_SHUTDOWN; + if (thread->notify_pipe->send_blocking((void *) &ev, sizeof(ev)) < 0) { + goto _cancel; + } + } else { + _cancel: + if (pthread_cancel(thread->thread.native_handle()) < 0) { + swoole_sys_warning("pthread_cancel(%ld) failed", (long) thread->thread.native_handle()); + } + } + thread->thread.join(); } - serv->heartbeat_pidt = thread_id; } -static void swHeartbeatThread_loop(swThreadParam *param) -{ - swSignal_none(); - - swServer *serv = (swServer *) param->object; - swReactor *reactor; +void Server::start_heartbeat_thread() { + heartbeat_thread = std::thread([this]() { + swoole_signal_block_all(); - int fd; - int serv_max_fd; - int serv_min_fd; - int checktime; + SwooleTG.type = THREAD_HEARTBEAT; + SwooleTG.id = reactor_num + 1; - SwooleTG.type = SW_THREAD_HEARTBEAT; - SwooleTG.id = serv->reactor_num; - - while (SwooleG.running) - { - serv_max_fd = swServer_get_maxfd(serv); - serv_min_fd = swServer_get_minfd(serv); - - checktime = (int) time(NULL) - serv->heartbeat_idle_time; - - for (fd = serv_min_fd; fd <= serv_max_fd; fd++) - { - swTrace("check fd=%d", fd); - swConnection *conn = swServer_connection_get(serv, fd); - if (swServer_connection_valid(serv, conn)) - { - if (conn->protect || conn->last_time > checktime) - { - continue; - } - - conn->close_force = 1; - conn->close_notify = 1; - - if (serv->single_thread) - { - reactor = SwooleTG.reactor; + while (running) { + double now = microtime(); + foreach_connection([this, now](Connection *conn) { + SessionId session_id = conn->session_id; + if (session_id <= 0) { + return; } - else - { - reactor = &serv->reactor_threads[conn->reactor_id].reactor; + if (is_healthy_connection(now, conn)) { + return; } - //notify to reactor thread - if (conn->peer_closed) - { - serv->notify(serv, conn, SW_SERVER_EVENT_CLOSE); - } - else - { - reactor->set(reactor, conn->socket, SW_EVENT_WRITE); - } - } - } - sleep(serv->heartbeat_check_interval); - } - pthread_exit(0); + 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 = session_id; + get_reactor_pipe_socket(session_id, conn->reactor_id)->send_blocking(&ev, sizeof(ev)); + }); + sleep(heartbeat_check_interval); + } + }); } + +} // namespace swoole diff --git a/src/server/static_handler.cc b/src/server/static_handler.cc index b634f07d633..e0015bfad23 100644 --- a/src/server/static_handler.cc +++ b/src/server/static_handler.cc @@ -10,63 +10,76 @@ | to obtain it through the world-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 "static_handler.h" +#include "swoole_static_handler.h" +#include "swoole_util.h" #include #include #include -using namespace std; -using swoole::http::StaticHandler; +namespace swoole { -bool StaticHandler::is_modified(const string &date_if_modified_since) -{ +namespace http_server { +bool StaticHandler::is_modified(const std::string &date_if_modified_since) { char date_tmp[64]; - if (date_if_modified_since.empty() || date_if_modified_since.length() > sizeof(date_tmp) - 1) - { + if (date_if_modified_since.empty() || date_if_modified_since.length() > sizeof(date_tmp) - 1) { 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; const char *date_format = nullptr; - if (strptime(date_tmp, SW_HTTP_RFC1123_DATE_GMT, &tm3) != NULL) - { + if (strptime(date_tmp, SW_HTTP_RFC1123_DATE_GMT, &tm3) != nullptr) { date_format = SW_HTTP_RFC1123_DATE_GMT; - } - else if (strptime(date_tmp, SW_HTTP_RFC1123_DATE_UTC, &tm3) != NULL) - { + } else if (strptime(date_tmp, SW_HTTP_RFC1123_DATE_UTC, &tm3) != nullptr) { date_format = SW_HTTP_RFC1123_DATE_UTC; - } - else if (strptime(date_tmp, SW_HTTP_RFC850_DATE, &tm3) != NULL) - { + } else if (strptime(date_tmp, SW_HTTP_RFC850_DATE, &tm3) != nullptr) { date_format = SW_HTTP_RFC850_DATE; + } else if (strptime(date_tmp, SW_HTTP_ASCTIME_DATE, &tm3) != nullptr) { + date_format = SW_HTTP_ASCTIME_DATE; } - else if (strptime(date_tmp, SW_HTTP_ASCTIME_DATE, &tm3) != NULL) - { + 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; } - return date_format && mktime(&tm3) - (int) serv->timezone >= get_file_mtime(); + 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() -{ +std::string StaticHandler::get_date() { char date_[64]; - time_t now = time(NULL); + time_t now = ::time(nullptr); struct tm *tm1 = gmtime(&now); strftime(date_, sizeof(date_), "%a, %d %b %Y %H:%M:%S %Z", tm1); return std::string(date_); } -std::string StaticHandler::get_date_last_modified() -{ +std::string StaticHandler::get_date_last_modified() { char date_last_modified[64]; time_t file_mtime = get_file_mtime(); struct tm *tm2 = gmtime(&file_mtime); @@ -74,9 +87,18 @@ std::string StaticHandler::get_date_last_modified() return std::string(date_last_modified); } -bool StaticHandler::hit() -{ - char *p = task.filename; +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 = filename; const char *url = request_url.c_str(); size_t url_length = request_url.length(); /** @@ -84,180 +106,152 @@ bool StaticHandler::hit() * [/test.jpg?version=1#position] -> [/test.jpg] */ char *params = (char *) memchr(url, '?', url_length); - if (params == NULL) - { - params = (char *) memchr(url, '#', url_length); + if (params == nullptr) { + params = (char *) memchr(url, '#', url_length); } size_t n = params ? params - url : url_length; - memcpy(p, serv->document_root, serv->document_root_len); - p += serv->document_root_len; + const std::string &document_root = serv->get_document_root(); + const size_t l_document_root = 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 (serv->locations->size() > 0) { + for (auto i = serv->locations->begin(); i != serv->locations->end(); i++) { + if (swoole_str_istarts_with(url, url_length, i->c_str(), i->size())) { last = true; } } - if (!last) - { + if (!last) { return false; } } - if (serv->document_root_len + n >= PATH_MAX) - { - return false; + if (l_document_root + n >= PATH_MAX) { + return catch_error(); } memcpy(p, url, n); p += n; *p = '\0'; - if (dir_path != "") - { + if (dir_path != "") { dir_path.clear(); } dir_path = std::string(url, n); - l_filename = swHttp_url_decode(task.filename, p - task.filename); - task.filename[l_filename] = '\0'; - - if (swoole_strnpos(url, n, SW_STRL("..")) == -1) - { - goto _detect_mime_type; - } + l_filename = http_server::url_decode(filename, p - filename); + filename[l_filename] = '\0'; - char real_path[PATH_MAX]; - if (!realpath(task.filename, real_path)) - { - if (last) - { - status_code = SW_HTTP_NOT_FOUND; - return true; - } - else - { - return false; - } + // The file does not exist + if (lstat(filename, &file_stat) < 0) { + return catch_error(); } - if (real_path[serv->document_root_len] != '/') - { - return false; - } - - if (swoole_streq(real_path, strlen(real_path), serv->document_root, serv->document_root_len) != 0) - { - return false; - } - - /** - * non-static file - */ - _detect_mime_type: - /** - * file does not exist - */ - if (lstat(task.filename, &file_stat) < 0) - { - if (last) - { - status_code = SW_HTTP_NOT_FOUND; - return true; + // 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(); + } } - else - { - return false; + } else { + if (!get_absolute_path() || !is_located_in_document_root()) { + return catch_error(); } } - if (serv->http_index_files && !serv->http_index_files->empty() && is_dir()) - { + if (serv->http_index_files && !serv->http_index_files->empty() && is_dir()) { return true; } - if(serv->http_autoindex && is_dir()) - { + if (serv->http_autoindex && is_dir()) { return true; } - if (!swoole::mime_type::exists(task.filename)) - { + if (!mime_type::exists(filename) && !last) { return false; } - if ((file_stat.st_mode & S_IFMT) != S_IFREG) - { + 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 (has_index_file() && !set_filename(index_file)) { + return false; + } + if (!has_index_file() && !is_enabled_auto_index()) { + return false; + } + } + return true; +} + +size_t StaticHandler::make_index_page(String *buffer) { + get_dir_files(); - if (dir_path.back() != '/') - { + if (dir_path.back() != '/') { dir_path.append("/"); } - 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() - ); - - p += ret; - - for (auto iter = files.begin(); iter != files.end(); iter++) - { - if (*iter == "." || (dir_path == "/" && *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()) - { + if (!is_dir()) { return false; } - DIR *dir = opendir(task.filename); - if (dir == NULL) - { + DIR *dir = opendir(filename); + if (dir == nullptr) { return false; } - while ((ptr = readdir(dir)) != NULL) - { - index_files.insert(ptr->d_name); + struct dirent *ptr; + while ((ptr = readdir(dir)) != nullptr) { + dir_files.insert(ptr->d_name); } closedir(dir); @@ -265,56 +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 != '/') - { + 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 ((file_stat.st_mode & S_IFMT) != S_IFREG) - { + if (!is_file()) { return false; } - - task.length = get_filesize(); return true; } -int swServer_http_static_handler_add_location(swServer *serv, const char *location, size_t length) -{ - if (serv->locations == nullptr) - { - serv->locations = new std::unordered_set; +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 = std::make_shared>(); } - serv->locations->insert(string(location, length)); - return SW_OK; + locations->emplace(location); } -int swServer_http_static_handler_add_http_index_files(swServer *serv, const char *filename, size_t length) -{ - if (serv->http_index_files == nullptr) - { - serv->http_index_files = new std::vector; +void Server::add_static_handler_index_files(const std::string &file) { + if (http_index_files == nullptr) { + http_index_files = std::make_shared>(); } - auto iter = std::find(serv->http_index_files->begin(), serv->http_index_files->end(), filename); - if (iter == serv->http_index_files->end()) - { - serv->http_index_files->push_back(filename); + auto iter = std::find(http_index_files->begin(), http_index_files->end(), file); + if (iter == http_index_files->end()) { + http_index_files->emplace_back(file); } - return SW_OK; } +} // namespace swoole diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index 7b47a63dbc6..424fbd992b9 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -10,212 +10,283 @@ | to obtain it through the world-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 "server.h" +#include "swoole_server.h" -static swEventData *g_current_task = NULL; +namespace swoole { +using network::Socket; -static void swTaskWorker_signal_init(swProcessPool *pool); -static int swTaskWorker_onPipeReceive(swReactor *reactor, swEvent *event); -static int swTaskWorker_loop_async(swProcessPool *pool, swWorker *worker); +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, 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 */ -void swTaskWorker_init(swServer *serv) -{ - swProcessPool *pool = &serv->gs->task_workers; - pool->ptr = serv; - pool->onTask = swTaskWorker_onTask; - pool->onWorkerStart = swTaskWorker_onStart; - pool->onWorkerStop = swTaskWorker_onStop; +void Server::init_task_workers() { + ProcessPool *pool = &gs->task_workers; + pool->ptr = this; + pool->onTask = TaskWorker_onTask; + pool->onWorkerStart = TaskWorker_onStart; + pool->onWorkerStop = TaskWorker_onStop; /** * Make the task worker support asynchronous */ - if (serv->task_enable_coroutine) - { - if (serv->task_ipc_mode == SW_TASK_IPC_MSGQUEUE || serv->task_ipc_mode == SW_TASK_IPC_PREEMPTIVE) - { - swError("cannot use msgqueue when task_enable_coroutine is enable"); + if (task_enable_coroutine) { + 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 = swTaskWorker_loop_async; + pool->main_loop = TaskWorker_loop_async; } - if (serv->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]); } } -/** - * in worker process - */ -int swTaskWorker_onFinish(swReactor *reactor, swEvent *event) -{ - swServer *serv = (swServer *) reactor->ptr; - swEventData task; - int n; - - do - { - n = read(event->fd, &task, sizeof(task)); - } while (n < 0 && errno == EINTR); - - return serv->onFinish(serv, &task); +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; } -int swTaskWorker_onTask(swProcessPool *pool, swEventData *task) -{ +static int TaskWorker_onTask(ProcessPool *pool, Worker *worker, EventData *task) { int ret = SW_OK; - swServer *serv = (swServer *) pool->ptr; - g_current_task = task; + Server *serv = (Server *) pool->ptr; + serv->last_task = task; - if (task->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) - { + worker->set_status_to_busy(); + if (task->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) { serv->onPipeMessage(serv, task); - } - else - { + } 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; } -int swTaskWorker_large_pack(swEventData *task, const void *data, size_t data_len) -{ - swPacket_task pkg; - bzero(&pkg, sizeof(pkg)); +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; + } - memcpy(pkg.tmpfile, SwooleG.task_tmpdir, SwooleG.task_tmpdir_len); + PacketTask pkg{}; + File file = make_tmpfile(); + if (!file.ready()) { + return false; + } - //create temp file - int tmp_fd = swoole_tmpfile(pkg.tmpfile); - if (tmp_fd < 0) - { - return SW_ERR; + if (file.write_all(_data, _length) != _length) { + swoole_warning("write to tmpfile failed"); + return false; } - //write to file - if (swoole_sync_writefile(tmp_fd, data, data_len) != data_len) - { - swWarn("write to tmpfile failed"); - return SW_ERR; + 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(task->data, &pkg, sizeof(pkg)); + + return true; +} + +bool Server::task(EventData *_task, int *dst_worker_id, bool blocking) { + sw_atomic_fetch_add(&gs->tasking_num, 1); + + swResultCode retval; + if (blocking) { + retval = gs->task_workers.dispatch_blocking(_task, dst_worker_id); + } else { + retval = gs->task_workers.dispatch(_task, dst_worker_id); } - task->info.len = sizeof(swPacket_task); - //use tmp file - swTask_type(task) |= SW_TASK_TMPFILE; + if (retval == SW_OK) { + sw_atomic_fetch_add(&gs->task_count, 1); + return true; + } - pkg.length = data_len; - memcpy(task->data, &pkg, sizeof(swPacket_task)); - close(tmp_fd); - return SW_OK; + sw_atomic_fetch_sub(&gs->tasking_num, 1); + return false; } -static void swTaskWorker_signal_init(swProcessPool *pool) -{ - /** - * use user settings - */ - SwooleG.use_signalfd = SwooleG.enable_signalfd; - - swSignal_add(SIGHUP, NULL); - swSignal_add(SIGPIPE, NULL); - swSignal_add(SIGUSR1, swWorker_signal_handler); - swSignal_add(SIGUSR2, NULL); - swSignal_add(SIGTERM, swWorker_signal_handler); - swSignal_add(SIGALRM, swSystemTimer_signal_handler); -#ifdef SIGRTMIN - swSignal_add(SIGRTMIN, swWorker_signal_handler); -#endif +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; } -void swTaskWorker_onStart(swProcessPool *pool, int worker_id) -{ - swServer *serv = (swServer *) pool->ptr; - SwooleWG.id = worker_id; +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; + } - if (serv->factory_mode == SW_MODE_BASE) - { - swServer_close_port(serv, SW_TRUE); + PacketTask _pkg{}; + memcpy(&_pkg, task->data, sizeof(_pkg) - 1); + + File fp(_pkg.tmpfile, O_RDONLY); + if (!fp.ready()) { + swoole_sys_warning("open(%s) failed", _pkg.tmpfile); + return false; + } + if (buffer->size < _pkg.length && !buffer->extend(_pkg.length)) { + return false; } + if (fp.read_all(buffer->str, _pkg.length) != _pkg.length) { + return false; + } + 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) { + 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 + swoole_signal_set(SIGRTMIN, Server::worker_signal_handler); +#endif +} + +static void TaskWorker_onStart(ProcessPool *pool, Worker *worker) { + Server *serv = (Server *) pool->ptr; + swoole_set_process_id(worker->id); /** * Make the task worker support asynchronous */ - if (serv->task_enable_coroutine) - { - if (swoole_event_init() < 0) - { - swError("[TaskWorker] create reactor failed"); + if (serv->task_enable_coroutine) { + if (swoole_event_init(0) < 0) { + swoole_error("[TaskWorker] create reactor failed"); return; } SwooleG.enable_signalfd = 1; - } - else - { + } else { SwooleG.enable_signalfd = 0; - SwooleTG.reactor = NULL; + SwooleTG.reactor = nullptr; } - swTaskWorker_signal_init(pool); - swWorker_onStart(serv); + TaskWorker_signal_init(pool); + serv->worker_start_callback(worker); - swWorker *worker = swProcessPool_get_worker(pool, worker_id); - worker->start_time = time(NULL); - worker->request_count = 0; - SwooleWG.worker = worker; - SwooleWG.worker->status = SW_WORKER_IDLE; - /** - * task_max_request - */ - if (pool->max_request > 0) - { - SwooleWG.run_always = 0; - SwooleWG.max_request = swProcessPool_get_max_request(pool); - } - else - { - SwooleWG.run_always = 1; - } + worker->init(); + worker->set_max_request(pool->max_request, pool->max_request_grace); } -void swTaskWorker_onStop(swProcessPool *pool, int worker_id) -{ +static void TaskWorker_onStop(ProcessPool *pool, Worker *worker) { swoole_event_free(); - swServer *serv = (swServer *) pool->ptr; - swWorker_onStop(serv); + Server *serv = (Server *) pool->ptr; + serv->worker_stop_callback(worker); } /** * receive data from worker process */ -static int swTaskWorker_onPipeReceive(swReactor *reactor, swEvent *event) -{ - swEventData task; - swProcessPool *pool = (swProcessPool *) reactor->ptr; - swWorker *worker = SwooleWG.worker; - - if (read(event->fd, &task, sizeof(task)) > 0) - { - worker->status = SW_WORKER_BUSY; - int retval = swTaskWorker_onTask(pool, &task); - worker->status = SW_WORKER_IDLE; - worker->request_count++; - //maximum number of requests, process will exit. - if (!SwooleWG.run_always && worker->request_count >= SwooleWG.max_request) - { - swWorker_stop(worker); +static int TaskWorker_onPipeReceive(Reactor *reactor, Event *event) { + EventData task; + ProcessPool *pool = (ProcessPool *) reactor->ptr; + Worker *worker = sw_worker(); + Server *serv = (Server *) pool->ptr; + + if (event->socket->read(&task, sizeof(task)) > 0) { + int retval = pool->onTask(pool, worker, &task); + // maximum number of requests, process will exit. + if (worker->has_exceeded_max_request()) { + serv->stop_async_worker(worker); } return retval; - } - else - { - swSysWarn("read(%d, %ld) failed", event->fd, sizeof(task)); + } else { + swoole_sys_warning("read(%d, %ld) failed", event->fd, sizeof(task)); return SW_ERR; } } @@ -223,198 +294,145 @@ static int swTaskWorker_onPipeReceive(swReactor *reactor, swEvent *event) /** * async task worker */ -static int swTaskWorker_loop_async(swProcessPool *pool, swWorker *worker) -{ - swServer *serv = (swServer *) pool->ptr; - swSocket *socket = worker->pipe_worker; - worker->status = SW_WORKER_IDLE; - - swSocket_set_nonblock(socket); - SwooleTG.reactor->ptr = pool; +static int TaskWorker_loop_async(ProcessPool *pool, Worker *worker) { + Server *serv = (Server *) pool->ptr; + Socket *socket = worker->pipe_worker; + worker->set_status_to_idle(); + + socket->set_nonblock(); + sw_reactor()->ptr = pool; swoole_event_add(socket, SW_EVENT_READ); - swReactor_set_handler(SwooleTG.reactor, SW_FD_PIPE, swTaskWorker_onPipeReceive); + swoole_event_set_handler(SW_FD_PIPE, TaskWorker_onPipeReceive); - for (uint i = 0; i < serv->worker_num + serv->task_worker_num; i++) - { - worker = swServer_get_worker(serv, i); + for (uint i = 0; i < serv->worker_num + serv->task_worker_num; i++) { + worker = serv->get_worker(i); worker->pipe_master->buffer_size = UINT_MAX; worker->pipe_worker->buffer_size = UINT_MAX; } - //main loop - return SwooleTG.reactor->wait(SwooleTG.reactor, NULL); + return swoole_event_wait(); } /** * Send the task result to worker */ -int swTaskWorker_finish(swServer *serv, const char *data, size_t data_len, int flags, swEventData *current_task) -{ - swEventData buf; - bzero(&buf.info, sizeof(buf.info)); - if (serv->task_worker_num < 1) - { - swWarn("cannot use task/finish, because no set serv->task_worker_num"); - return SW_ERR; +bool Server::finish(const char *data, size_t data_len, int flags, EventData *current_task) { + if (task_worker_num < 1) { + swoole_warning("cannot use Server::task()/Server::finish() method, because no set [task_worker_num]"); + return false; } - if (current_task == NULL) - { - current_task = g_current_task; + 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; + if (current_task->info.type == SW_SERVER_EVENT_PIPE_MESSAGE) { + 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; - swWorker *worker = swServer_get_worker(serv, source_worker_id); + Worker *worker = get_worker(source_worker_id); - if (worker == NULL) - { - swWarn("invalid worker_id[%d]", source_worker_id); - return SW_ERR; + if (worker == nullptr) { + swoole_warning("invalid worker_id[%d]", source_worker_id); + return false; } - int ret; - //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; - //callback function - if (swTask_type(current_task) & SW_TASK_CALLBACK) - { - flags |= SW_TASK_CALLBACK; + ssize_t retval; + // for swoole_server_task + if (current_task->info.ext_flags & SW_TASK_NONBLOCK) { + // write to file + EventData buf; + if (!task_pack(&buf, data, data_len)) { + swoole_warning("large task pack failed()"); + return false; } - else if (swTask_type(current_task) & SW_TASK_COROUTINE) - { + // callback function + if (current_task->info.ext_flags & SW_TASK_CALLBACK) { + flags |= SW_TASK_CALLBACK; + } else if (current_task->info.ext_flags & SW_TASK_COROUTINE) { flags |= SW_TASK_COROUTINE; } - swTask_type(&buf) = flags; - - //write to file - if (data_len >= SW_IPC_MAX_SIZE - sizeof(buf.info)) - { - if (swTaskWorker_large_pack(&buf, data, data_len) < 0) - { - swWarn("large task pack failed()"); - return SW_ERR; - } - } - else - { - memcpy(buf.data, data, data_len); - buf.info.len = data_len; - } + 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->last_connection) - { - int32_t _len = htonl(data_len); - ret = swSocket_write_blocking(worker->pool->stream->last_connection, (void *) &_len, sizeof(_len)); - if (ret > 0) - { - ret = swSocket_write_blocking(worker->pool->stream->last_connection, data, data_len); + if (worker->pool->use_socket && worker->pool->stream_info_->last_connection) { + uint32_t _len = htonl(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 { + retval = send_to_worker_from_worker(worker, &buf, buf.size(), SW_PIPE_MASTER); } - else - { - ret = swWorker_send2worker(worker, &buf, sizeof(buf.info) + buf.info.len, SW_PIPE_MASTER); - } - } - else - { + } else { uint64_t flag = 1; /** * Use worker shm store the result */ - swEventData *result = &(serv->task_result[source_worker_id]); - swPipe *task_notify_pipe = &(serv->task_notify[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(&worker->lock); + // lock worker + worker->lock->lock(); - if (swTask_type(current_task) & SW_TASK_WAITALL) - { - sw_atomic_t *finish_count = (sw_atomic_t*) result->data; + if (current_task->info.ext_flags & SW_TASK_WAITALL) { + sw_atomic_t *finish_count = (sw_atomic_t *) result->data; char *_tmpfile = result->data + 4; - int fd = open(_tmpfile, O_APPEND | O_WRONLY); - if (fd >= 0) - { - buf.info.type = SW_SERVER_EVENT_FINISH; - buf.info.fd = current_task->info.fd; - swTask_type(&buf) = flags; - //result pack - if (data_len >= SW_IPC_MAX_SIZE - sizeof(buf.info)) - { - if (swTaskWorker_large_pack(&buf, data, data_len) < 0) - { - swWarn("large task pack failed()"); - buf.info.len = 0; - } + File file(_tmpfile, O_APPEND | O_WRONLY); + if (file.ready()) { + EventData buf; + if (!task_pack(&buf, data, data_len)) { + swoole_warning("large task pack failed()"); + buf.info.len = 0; } - else - { - buf.info.len = data_len; - memcpy(buf.data, data, data_len); - } - //write to tmpfile - if (swoole_sync_writefile(fd, &buf, sizeof(buf.info) + buf.info.len) != sizeof(buf.info) + buf.info.len) - { - swSysWarn("write(%s, %ld) failed", _tmpfile, 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) { + swoole_sys_warning("write(%s, %ld) failed", _tmpfile, bytes); } sw_atomic_fetch_add(finish_count, 1); - close(fd); } - } - else - { - result->info.type = SW_SERVER_EVENT_FINISH; - result->info.fd = current_task->info.fd; - swTask_type(result) = flags; - - if (data_len >= SW_IPC_MAX_SIZE - sizeof(buf.info)) - { - if (swTaskWorker_large_pack(result, data, data_len) < 0) - { - //unlock worker - worker->lock.unlock(&worker->lock); - swWarn("large task pack failed()"); - return SW_ERR; - } - } - else - { - memcpy(result->data, data, data_len); - result->info.len = data_len; + } else { + if (!task_pack(result, data, data_len)) { + // unlock worker + worker->lock->unlock(); + 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(&worker->lock); + // unlock worker + worker->lock->unlock(); - while (1) - { - ret = task_notify_pipe->write(task_notify_pipe, &flag, sizeof(flag)); - if (ret < 0 && swSocket_error(errno) == SW_WAIT) - { - if (swSocket_wait(task_notify_pipe->getSocket(task_notify_pipe, SW_PIPE_MASTER)->fd, -1, SW_EVENT_WRITE) == 0) - { + while (1) { + retval = pipe->write(&flag, sizeof(flag)); + auto _sock = pipe->get_socket(true); + if (retval < 0 && _sock->catch_write_error(errno) == SW_WAIT) { + if (_sock->wait_event(-1, SW_EVENT_WRITE) == 0) { continue; } } break; } } - if (ret < 0) - { - swSysWarn("TaskWorker: send result to worker failed"); + if (retval < 0) { + if (swoole_get_last_error() == EAGAIN || swoole_get_last_error() == SW_ERROR_SOCKET_POLL_TIMEOUT) { + swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_SEND_TO_WOKER_TIMEOUT, "send result to worker timed out"); + } else { + swoole_sys_warning("send result to worker failed"); + } } - return ret; + return true; } +} // namespace swoole diff --git a/src/server/thread.cc b/src/server/thread.cc new file mode 100644 index 00000000000..798845a91bb --- /dev/null +++ b/src/server/thread.cc @@ -0,0 +1,373 @@ +/* + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Tianfeng Han | + +----------------------------------------------------------------------+ + */ + +#include "swoole_server.h" +#include "swoole_memory.h" +#include "swoole_thread.h" + +#define SW_RELOAD_SLEEP_FOR 100000 + +namespace swoole { +using network::Socket; + +Factory *Server::create_thread_factory() { +#ifndef SW_THREAD + swoole_error("Thread support is not enabled, cannot create server with MODE_THREAD"); + return nullptr; +#endif + reactor_num = worker_num; + connection_list = (Connection *) sw_calloc(max_connection, sizeof(Connection)); + if (connection_list == nullptr) { + swoole_sys_warning("calloc[2](%d) failed", (int) (max_connection * sizeof(Connection))); + return nullptr; + } + reactor_threads = new ReactorThread[reactor_num](); + reactor_pipe_num = 1; + return new ThreadFactory(this); +} + +void Server::destroy_thread_factory() { + sw_free(connection_list); + delete[] reactor_threads; +} + +ThreadFactory::ThreadFactory(Server *server) : BaseFactory(server) { + threads_.resize(server_->get_all_worker_num() + 1); + reloading = false; + reload_all_workers = false; + cv_timeout_ms_ = -1; +} + +bool ThreadFactory::start() { + if (!server_->create_worker_pipes()) { + return false; + } + if (server_->task_worker_num > 0 && + (server_->create_task_workers() < 0 || server_->gs->task_workers.start_check() < 0)) { + return false; + } + if (server_->get_user_worker_num() > 0 && server_->create_user_workers() < 0) { + return false; + } + return true; +} + +bool ThreadFactory::shutdown() { + for (auto &thread : threads_) { + if (thread.joinable()) { + join_thread(thread); + } + } + if (server_->heartbeat_check_interval > 0) { + server_->join_heartbeat_thread(); + } + return true; +} + +ThreadFactory::~ThreadFactory() {} + +void ThreadFactory::at_thread_exit(Worker *worker) { + std::unique_lock _lock(lock_); + queue_.push(worker); + cv_.notify_one(); +} + +void ThreadFactory::create_message_bus() { + auto mb = new MessageBus(); + mb->set_id_generator(server_->msg_id_generator); + mb->set_buffer_size(server_->ipc_max_size); + mb->set_always_chunked_transfer(); + if (!mb->alloc_buffer()) { + throw std::bad_alloc(); + } + server_->init_pipe_sockets(mb); + SwooleTG.message_bus = mb; +} + +void ThreadFactory::destroy_message_bus() { + SwooleTG.message_bus->clear(); + delete SwooleTG.message_bus; + SwooleTG.message_bus = nullptr; +} + +template +void ThreadFactory::create_thread(int i, _Callable fn) { + threads_[i] = std::thread(fn); +} + +void ThreadFactory::join_thread(std::thread &thread) { + thread.join(); + if (server_->worker_thread_join) { + server_->worker_thread_join(thread.native_handle()); + } +} + +void ThreadFactory::spawn_event_worker(WorkerId i) { + create_thread(i, [=]() { + swoole_set_process_type(SW_PROCESS_EVENTWORKER); + swoole_set_thread_type(Server::THREAD_WORKER); + swoole_set_process_id(i); + swoole_set_thread_id(i); + Worker *worker = server_->get_worker(i); + worker->type = SW_PROCESS_EVENTWORKER; + worker->pid = swoole_thread_get_native_id(); + SwooleWG.worker = worker; + server_->worker_thread_start([=]() { Server::reactor_thread_main_loop(server_, i); }); + at_thread_exit(worker); + }); +} + +void ThreadFactory::spawn_task_worker(WorkerId i) { + create_thread(i, [=]() { + swoole_set_process_type(SW_PROCESS_TASKWORKER); + swoole_set_thread_type(Server::THREAD_WORKER); + swoole_set_process_id(i); + swoole_set_thread_id(i); + create_message_bus(); + Worker *worker = server_->get_worker(i); + worker->type = SW_PROCESS_TASKWORKER; + worker->pid = swoole_thread_get_native_id(); + worker->set_status_to_idle(); + SwooleWG.worker = worker; + auto pool = &server_->gs->task_workers; + server_->worker_thread_start([=]() { + if (pool->onWorkerStart != nullptr) { + pool->onWorkerStart(pool, worker); + } + pool->main_loop(pool, worker); + if (pool->onWorkerStop != nullptr) { + pool->onWorkerStop(pool, worker); + } + }); + destroy_message_bus(); + at_thread_exit(worker); + }); +} + +void ThreadFactory::spawn_user_worker(WorkerId i) { + create_thread(i, [=]() { + Worker *worker = server_->get_worker(i); + swoole_set_process_type(SW_PROCESS_USERWORKER); + swoole_set_thread_type(Server::THREAD_WORKER); + swoole_set_process_id(i); + swoole_set_thread_id(i); + create_message_bus(); + worker->type = SW_PROCESS_USERWORKER; + worker->pid = swoole_thread_get_native_id(); + SwooleWG.worker = worker; + server_->worker_thread_start([=]() { server_->onUserWorkerStart(server_, worker); }); + destroy_message_bus(); + at_thread_exit(worker); + }); +} + +void ThreadFactory::spawn_manager_thread(WorkerId i) { + create_thread(i, [=]() { + swoole_set_process_type(SW_PROCESS_MANAGER); + swoole_set_thread_type(Server::THREAD_WORKER); + swoole_set_process_id(i); + swoole_set_thread_id(i); + manager.id = i; + manager.type = SW_PROCESS_MANAGER; + + SwooleTG.timer_scheduler = [this](Timer *timer, long exec_msec) -> int { + cv_timeout_ms_ = exec_msec; + return SW_OK; + }; + + server_->worker_thread_start([=]() { + if (server_->onManagerStart) { + server_->onManagerStart(server_); + } + wait(); + if (server_->onManagerStop) { + server_->onManagerStop(server_); + } + }); + + if (server_->running) { + swoole_warning("Fatal Error: manager thread exits abnormally"); + } + + SwooleTG.timer_scheduler = nullptr; + }); +} + +void ThreadFactory::wait() { + while (server_->running) { + std::unique_lock _lock(lock_); + if (!queue_.empty()) { + Worker *exited_worker = queue_.front(); + queue_.pop(); + + std::thread &thread = threads_[exited_worker->id]; + int status_code = 0; + if (server_->worker_thread_get_exit_status) { + status_code = server_->worker_thread_get_exit_status(thread.native_handle()); + } + if (status_code != 0) { + ExitStatus exit_status(exited_worker->pid, status_code << 8); + server_->call_worker_error_callback(exited_worker, exit_status); + swoole_warning("worker(tid=%d, id=%d) abnormal exit, status=%d", + exit_status.get_pid(), + exited_worker->id, + exit_status.get_code()); + } + + join_thread(threads_[exited_worker->id]); + + switch (exited_worker->type) { + case SW_PROCESS_EVENTWORKER: + spawn_event_worker(exited_worker->id); + break; + case SW_PROCESS_TASKWORKER: + spawn_task_worker(exited_worker->id); + break; + case SW_PROCESS_USERWORKER: + spawn_user_worker(exited_worker->id); + break; + default: + abort(); + break; + } + _lock.unlock(); + } else { + if (cv_timeout_ms_ > 0) { + cv_.wait_for(_lock, std::chrono::milliseconds(cv_timeout_ms_)); + } else { + cv_.wait(_lock); + } + } + if (sw_timer()) { + sw_timer()->select(); + } + if (server_->running && reloading) { + reload(reload_all_workers); + } + } +} + +bool ThreadFactory::reload(bool _reload_all_workers) { + if (!server_->is_manager()) { + // Prevent duplicate submission of reload requests. + if (reloading) { + swoole_set_last_error(SW_ERROR_OPERATION_NOT_SUPPORT); + return false; + } + reloading = true; + reload_all_workers = _reload_all_workers; + std::unique_lock _lock(lock_); + cv_.notify_one(); + } else { + swoole_info("Server is reloading %s workers now", _reload_all_workers ? "all" : "task"); + if (server_->onBeforeReload) { + server_->onBeforeReload(server_); + } + SW_LOOP_N(server_->get_core_worker_num()) { + if (i < server_->worker_num && !_reload_all_workers) { + continue; + } + if (!server_->kill_worker(i, true)) { + return false; + } + SW_LOOP { + usleep(SW_RELOAD_SLEEP_FOR); + // This worker thread has exited, proceeding to terminate the next one. + if (threads_[i].joinable()) { + break; + } + } + } + reload_all_workers = false; + reloading = false; + if (server_->onAfterReload) { + server_->onAfterReload(server_); + } + } + + return true; +} + +int Server::start_worker_threads() { + ThreadFactory *_factory = dynamic_cast(factory); + + if (heartbeat_check_interval > 0) { + start_heartbeat_thread(); + } + + if (task_worker_num > 0) { + SW_LOOP_N(task_worker_num) { + _factory->spawn_task_worker(worker_num + i); + } + } + + SW_LOOP_N(worker_num) { + _factory->spawn_event_worker(i); + } + + if (!user_worker_list.empty()) { + for (size_t i = 0; i < user_worker_list.size(); i++) { + _factory->spawn_user_worker(task_worker_num + worker_num + i); + } + } + + int manager_thread_id = get_all_worker_num(); + _factory->spawn_manager_thread(manager_thread_id); + + if (swoole_event_init(0) < 0) { + return SW_ERR; + } + + Reactor *reactor = sw_reactor(); + for (auto iter = ports.begin(); iter != ports.end(); iter++) { + auto port = *iter; + if (port->is_dgram()) { + continue; + } + if (port->listen() < 0) { + swoole_event_free(); + return SW_ERR; + } + reactor->add(port->socket, SW_EVENT_READ); + } + + SwooleTG.id = reactor->id = manager_thread_id + 1; + store_listen_socket(); + + return start_master_thread(reactor); +} + +void Server::stop_worker_threads() { + DataHead event = {}; + event.type = SW_SERVER_EVENT_SHUTDOWN; + + SW_LOOP_N(worker_num) { + send_to_worker_from_worker(get_worker(i), &event, sizeof(event), SW_PIPE_MASTER); + } + + if (task_worker_num > 0) { + SW_LOOP_N(task_worker_num) { + send_to_worker_from_worker(get_worker(worker_num + i), &event, sizeof(event), SW_PIPE_MASTER); + } + } +} + +bool Server::reload_worker_threads(bool reload_all_workers) { + ThreadFactory *_factory = dynamic_cast(factory); + return _factory->reload(reload_all_workers); +} + +} // namespace swoole diff --git a/src/server/worker.cc b/src/server/worker.cc index d6f7cb25408..0f6ca46074e 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -10,831 +10,605 @@ | to obtain it through the world-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 "server.h" -#include "client.h" -#include "async.h" - #include #include #include +#include -static int swWorker_onPipeReceive(swReactor *reactor, swEvent *event); -static int swWorker_onStreamAccept(swReactor *reactor, swEvent *event); -static int swWorker_onStreamRead(swReactor *reactor, swEvent *event); -static int swWorker_onStreamPackage(swProtocol *proto, swSocket *sock, char *data, uint32_t length); -static int swWorker_onStreamClose(swReactor *reactor, swEvent *event); -static int swWorker_reactor_is_empty(swReactor *reactor); +#include "swoole_server.h" +#include "swoole_memory.h" +#include "swoole_msg_queue.h" +#include "swoole_coroutine.h" -void swWorker_signal_init(void) -{ - /** - * use user settings - */ - SwooleG.use_signalfd = SwooleG.enable_signalfd; - - swSignal_add(SIGHUP, NULL); - swSignal_add(SIGPIPE, NULL); - swSignal_add(SIGUSR1, NULL); - swSignal_add(SIGUSR2, NULL); - //swSignal_add(SIGINT, swWorker_signal_handler); - swSignal_add(SIGTERM, swWorker_signal_handler); - swSignal_add(SIGALRM, swSystemTimer_signal_handler); - //for test - swSignal_add(SIGVTALRM, swWorker_signal_handler); +namespace swoole { +using namespace network; + +static int Worker_onPipeReceive(Reactor *reactor, Event *event); +static void Worker_reactor_try_to_exit(Reactor *reactor); + +void Server::worker_signal_init(void) { + 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); + swoole_signal_set(SIGTERM, Server::worker_signal_handler); + // for test + swoole_signal_set(SIGVTALRM, Server::worker_signal_handler); #ifdef SIGRTMIN - swSignal_add(SIGRTMIN, swWorker_signal_handler); + swoole_signal_set(SIGRTMIN, Server::worker_signal_handler); #endif } -void swWorker_signal_handler(int signo) -{ - switch (signo) - { +void Server::worker_signal_handler(int signo) { + if (!SwooleG.running || !sw_server() || !sw_worker() || !sw_server()->is_running()) { + return; + } + switch (signo) { case SIGTERM: - /** - * Event worker - */ - if (SwooleTG.reactor) - { - swWorker_stop(SwooleWG.worker); - } - /** - * Task worker - */ - else - { - SwooleG.running = 0; + if (swoole_event_is_available()) { + // Event Worker + sw_server()->stop_async_worker(sw_worker()); + } else { + // Task Worker + sw_worker()->shutdown(); } break; - case SIGALRM: - swSystemTimer_signal_handler(SIGALRM); - break; - /** - * for test - */ + // for test case SIGVTALRM: - swWarn("SIGVTALRM coming"); + swoole_warning("SIGVTALRM coming"); break; case SIGUSR1: - break; case SIGUSR2: + if (sw_logger()) { + sw_logger()->reopen(); + } break; default: #ifdef SIGRTMIN - if (signo == SIGRTMIN) - { - swLog_reopen(sw_server()->daemonize ? SW_TRUE : SW_FALSE); + if (signo == SIGRTMIN && sw_logger()) { + sw_logger()->reopen(); } #endif break; } } -static sw_inline int swWorker_discard_data(swServer *serv, swConnection *conn, swEventData *task) -{ - if (conn == nullptr) - { - if (serv->disable_notify && !serv->discard_timeout_request) - { - return SW_FALSE; +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; } goto _discard_data; - } - else - { - if (conn->closed) - { + } else { + if (conn->closed) { goto _discard_data; - } - else - { - return SW_FALSE; + } else { + return false; } } - _discard_data: - { - swoole_error_log( - SW_LOG_WARNING, SW_ERROR_SESSION_DISCARD_TIMEOUT_DATA, - "[2] received the wrong data[%d bytes] from socket#%d", - task->info.len, task->info.fd - ); - } - return SW_TRUE; +_discard_data: + swoole_error_log(SW_LOG_WARNING, + SW_ERROR_SESSION_DISCARD_TIMEOUT_DATA, + "[2] ignore data[%u bytes] received from session#%ld", + info->len, + info->fd); + return true; } -static int swWorker_onStreamAccept(swReactor *reactor, swEvent *event) -{ - swSocketAddress client_addr; - swSocket *sock = swSocket_accept(event->socket, &client_addr); - if (sock == nullptr) - { - switch (errno) - { - case EINTR: - case EAGAIN: - return SW_OK; - default: - swSysWarn("accept() failed"); - return SW_OK; - } - } - - sock->fdtype = SW_FD_STREAM; - sock->socket_type = SW_SOCK_UNIX_STREAM; +typedef std::function TaskCallback; - return reactor->add(reactor, sock, SW_EVENT_READ); -} - -static int swWorker_onStreamRead(swReactor *reactor, swEvent *event) -{ - swSocket *conn = event->socket; - swServer *serv = (swServer *) reactor->ptr; - swProtocol *protocol = &serv->stream_protocol; - swString *buffer; - - if (!event->socket->recv_buffer) - { - if (serv->buffer_pool->empty()) - { - buffer = swString_new(8192); - if (!buffer) - { - return SW_ERR; - } - } - else - { - buffer = serv->buffer_pool->front(); - serv->buffer_pool->pop(); - } - event->socket->recv_buffer = buffer; - } - else - { - buffer = event->socket->recv_buffer; - } +static sw_inline void Worker_do_task(Server *serv, Worker *worker, DataHead *info, const TaskCallback &callback) { + RecvData recv_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 (swProtocol_recv_check_length(protocol, conn, buffer) < 0) - { - swWorker_onStreamClose(reactor, event); + if (callback(serv, &recv_data) == SW_OK) { + worker->add_request_count(); + sw_atomic_fetch_add(&serv->gs->request_count, 1); } - - return SW_OK; -} - -static int swWorker_onStreamClose(swReactor *reactor, swEvent *event) -{ - swSocket *sock = event->socket; - swServer *serv = (swServer *) reactor->ptr; - - swString_clear(sock->recv_buffer); - serv->buffer_pool->push(sock->recv_buffer); - sock->recv_buffer = nullptr; - - reactor->del(reactor, sock); - reactor->close(reactor, sock); - - if (serv->last_stream_socket == sock) - { - serv->last_stream_socket = nullptr; - } - - return SW_OK; } -static int swWorker_onStreamPackage(swProtocol *proto, swSocket *sock, char *data, uint32_t length) -{ - swServer *serv = (swServer *) proto->private_data_2; - - /** - * passing memory pointer - */ - swPacket_ptr task; - memcpy(&task.info, data + 4, sizeof(task.info)); - task.info.flags = SW_EVENT_DATA_PTR; - - bzero(&task.data, sizeof(task.data)); - task.data.length = length - (uint32_t) sizeof(task.info) - 4; - task.data.str = data + 4 + sizeof(task.info); - - /** - * do task - */ - serv->last_stream_socket = sock; - swWorker_onTask(&serv->factory, (swEventData *) &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 int (*task_callback)(swServer *, swEventData *); - -static sw_inline void swWorker_do_task(swServer *serv, swWorker *worker, swEventData *task, task_callback callback) -{ -#ifdef SW_BUFFER_RECV_TIME - serv->last_receive_usec = task->info.time; -#endif - callback(serv, task); -#ifdef SW_BUFFER_RECV_TIME - serv->last_receive_usec = 0; -#endif - worker->request_count++; - sw_atomic_fetch_add(&serv->stats->request_count, 1); -} - -int swWorker_onTask(swFactory *factory, swEventData *task) -{ - swServer *serv = (swServer *) factory->ptr; - - swWorker *worker = SwooleWG.worker; - //worker busy - worker->status = SW_WORKER_BUSY; - - switch (task->info.type) - { - case SW_SERVER_EVENT_SEND_DATA: - { - swConnection *conn = swServer_connection_verify(serv, task->info.fd); - if (conn && serv->max_queued_bytes && task->info.len > 0) - { - sw_atomic_fetch_sub(&conn->queued_bytes, task->info.len); - swTraceLog(SW_TRACE_SERVER, "[Worker] len=%d, qb=%d\n", task->info.len, conn->queued_bytes); +void Server::worker_accept_event(DataHead *info) { + Worker *worker = sw_worker(); + worker->set_status_to_busy(); + + switch (info->type) { + case SW_SERVER_EVENT_RECV_DATA: { + Connection *conn = get_connection_verify(info->fd); + if (conn) { + if (info->len > 0) { + auto packet = 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 = info->time; } - //discard data - if (swWorker_discard_data(serv, conn, task) == SW_TRUE) - { - break; + if (!Worker_discard_data(this, conn, info)) { + Worker_do_task(this, worker, info, onReceive); } - swWorker_do_task(serv, worker, task, serv->onReceive); break; } - case SW_SERVER_EVENT_SNED_DGRAM: - { - swWorker_do_task(serv, worker, task, serv->onPacket); + case SW_SERVER_EVENT_RECV_DGRAM: { + Worker_do_task(this, worker, info, onPacket); break; } - case SW_SERVER_EVENT_CLOSE: - { + case SW_SERVER_EVENT_CLOSE: { #ifdef SW_USE_OPENSSL - swConnection *conn = swServer_connection_verify_no_ssl(serv, task->info.fd); - if (conn && conn->ssl_client_cert && conn->ssl_client_cert_pid == SwooleG.pid) - { - sw_free(conn->ssl_client_cert); + 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(factory, task->info.fd); + factory->end(info->fd, false); break; } - case SW_SERVER_EVENT_CONNECT: - { + case SW_SERVER_EVENT_CONNECT: { #ifdef SW_USE_OPENSSL - //SSL client certificate - if (task->info.len > 0) - { - swConnection *conn = swServer_connection_verify_no_ssl(serv, task->info.fd); - char *cert_data = NULL; - size_t length = serv->get_packet(serv, task, &cert_data); - conn->ssl_client_cert = swString_dup(cert_data, length); - conn->ssl_client_cert_pid = SwooleG.pid; + // SSL client certificate + if (info->len > 0) { + Connection *conn = get_connection_verify_no_ssl(info->fd); + if (conn) { + 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 (serv->onConnect) - { - serv->onConnect(serv, &task->info); + if (onConnect) { + onConnect(this, info); } break; } - case SW_SERVER_EVENT_BUFFER_FULL: - { - if (serv->onBufferFull) - { - serv->onBufferFull(serv, &task->info); + case SW_SERVER_EVENT_BUFFER_FULL: { + if (onBufferFull) { + onBufferFull(this, info); } break; } - case SW_SERVER_EVENT_BUFFER_EMPTY: - { - if (serv->onBufferEmpty) - { - serv->onBufferEmpty(serv, &task->info); + case SW_SERVER_EVENT_BUFFER_EMPTY: { + if (onBufferEmpty) { + onBufferEmpty(this, info); } break; } - case SW_SERVER_EVENT_FINISH: - { - serv->onFinish(serv, task); + case SW_SERVER_EVENT_FINISH: { + onFinish(this, (EventData *) get_worker_message_bus()->get_buffer()); + break; + } + case SW_SERVER_EVENT_PIPE_MESSAGE: { + 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_PIPE_MESSAGE: - { - serv->onPipeMessage(serv, task); + 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) - { - swWorker_stop(worker); + // maximum number of requests, process will exit. + if (worker->has_exceeded_max_request()) { + stop_async_worker(worker); } - return SW_OK; } -void swWorker_onStart(swServer *serv) -{ - if (SwooleWG.id >= serv->worker_num) - { - SwooleG.process_type = SW_PROCESS_TASKWORKER; - } - else - { - SwooleG.process_type = SW_PROCESS_WORKER; - } - - if (serv->enable_coroutine) - { - SwooleG.enable_coroutine = 1; +void Server::worker_start_callback(Worker *worker) { + if (swoole_get_process_id() >= worker_num) { + swoole_set_process_type(SW_PROCESS_TASKWORKER); + } else { + swoole_set_process_type(SW_PROCESS_WORKER); } int is_root = !geteuid(); - struct passwd *passwd = NULL; - struct group *group = NULL; - - if (is_root) - { - //get group info - if (SwooleG.group) - { - group = getgrnam(SwooleG.group); - if (!group) - { - swWarn("get group [%s] info failed", SwooleG.group); + struct passwd *_passwd = nullptr; + struct group *_group = nullptr; + + if (is_root) { + // get group info + if (!group_.empty()) { + _group = getgrnam(group_.c_str()); + if (!_group) { + swoole_warning("get group [%s] info failed", group_.c_str()); } } - //get user info - if (SwooleG.user) - { - passwd = getpwnam(SwooleG.user); - if (!passwd) - { - swWarn("get user [%s] info failed", SwooleG.user); + // get user info + if (!user_.empty()) { + _passwd = getpwnam(user_.c_str()); + if (!_passwd) { + swoole_warning("get user [%s] info failed", user_.c_str()); } } - //chroot - if (SwooleG.chroot) - { - if (0 > chroot(SwooleG.chroot)) - { - swSysWarn("chroot to [%s] failed", SwooleG.chroot); - } + // set process group + if (_group && setgid(_group->gr_gid) < 0) { + swoole_sys_warning("setgid to [%s] failed", group_.c_str()); } - //set process group - if (SwooleG.group && group) - { - if (setgid(group->gr_gid) < 0) - { - swSysWarn("setgid to [%s] failed", SwooleG.group); - } + // set process user + if (_passwd && setuid(_passwd->pw_uid) < 0) { + swoole_sys_warning("setuid to [%s] failed", user_.c_str()); } - //set process user - if (SwooleG.user && passwd) - { - if (setuid(passwd->pw_uid) < 0) - { - swSysWarn("setuid to [%s] failed", SwooleG.user); + // chroot + if (!chroot_.empty()) { + if (::chroot(chroot_.c_str()) == 0) { + if (chdir("/") < 0) { + swoole_sys_warning("chdir(\"/\") failed"); + } + } else { + swoole_sys_warning("chroot(\"%s\") failed", chroot_.c_str()); } } } - for (uint32_t i = 0; i < serv->worker_num + serv->task_worker_num; i++) - { - swWorker *worker = swServer_get_worker(serv, i); - if (SwooleWG.id == i) - { + SW_LOOP_N(worker_num + task_worker_num) { + if (worker->id == i) { continue; } - if (swIsWorker() && worker->pipe_master) - { - swSocket_set_nonblock(worker->pipe_master); + Worker *other_worker = get_worker(i); + if (is_worker() && other_worker->pipe_master) { + other_worker->pipe_master->set_nonblock(); } } - SwooleWG.worker = swServer_get_worker(serv, SwooleWG.id); - SwooleWG.worker->status = SW_WORKER_IDLE; - - if (serv->factory_mode == SW_MODE_PROCESS) - { - sw_shm_protect(serv->session_list, PROT_READ); - /** - * Use only the first block of pipe_buffer memory in worker process - */ - for (uint32_t i = 1; i < serv->reactor_num; i++) - { - sw_free(serv->pipe_buffers[i]); - } + if (sw_logger()->is_opened()) { + sw_logger()->reopen(); } -#ifdef HAVE_SIGNALFD - if (SwooleG.use_signalfd && SwooleTG.reactor && SwooleG.signal_fd == 0) - { - swSignalfd_setup(SwooleTG.reactor); + worker->set_status_to_idle(); + + if (is_process_mode()) { + sw_shm_protect(session_list, PROT_READ); } -#endif - swServer_worker_start(serv, SwooleWG.worker); + call_worker_start_callback(worker); +} + +void Server::worker_stop_callback(Worker *worker) { + call_worker_stop_callback(worker); } -void swWorker_onStop(swServer *serv) -{ - if (serv->onWorkerStop) - { - serv->onWorkerStop(serv, SwooleWG.id); +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); + } + + swoole_clear_last_error(); + swoole_clear_last_error_msg(); + + if (onWorkerStart) { + onWorkerStart(this, worker); } } -void swWorker_stop(swWorker *worker) -{ - swServer *serv = (swServer *) worker->pool->ptr; - worker->status = SW_WORKER_BUSY; +void Server::call_worker_stop_callback(Worker *worker) { + void *hook_args[2]; + hook_args[0] = this; + hook_args[1] = (void *) (uintptr_t) worker->id; - swReactor *reactor = SwooleTG.reactor; + 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, worker); + } - /** - * force to end - */ - if (serv->reload_async == 0) - { - SwooleG.running = 0; - reactor->running = 0; - return; + 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(); } - //The worker process is shutting down now. - if (reactor->wait_exit) - { - return; + SwooleWG.running = false; + if (SwooleWG.worker_copy) { + delete SwooleWG.worker_copy; + SwooleWG.worker_copy = nullptr; + SwooleWG.worker = nullptr; } +} - if (serv->stream_socket) - { - reactor->del(reactor, serv->stream_socket); - swSocket_free(serv->stream_socket); - serv->stream_socket = nullptr; +void Server::call_worker_error_callback(Worker *worker, const ExitStatus &status) { + if (onWorkerError != nullptr) { + onWorkerError(this, worker, status); + } + /** + * The work process has exited unexpectedly, requiring a cleanup of the shared memory state. + * This must be done between the termination of the old process and the initiation of the new one; + * otherwise, data contention may occur. + */ + if (worker->type == SW_PROCESS_EVENTWORKER) { + abort_worker(worker); } +} - if (worker->pipe_worker) - { - swReactor_remove_read_event(reactor, worker->pipe_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 (serv->factory_mode == SW_MODE_BASE && swIsWorker()) - { - for (auto ls : *serv->listen_list) - { - reactor->del(reactor, ls->socket); + if (worker_id == sw_worker()->id && !wait_reactor) { + if (swoole_event_is_available()) { + swoole_event_defer([](void *data) { sw_reactor()->running = false; }, nullptr); } - if (worker->pipe_master) - { - swReactor_remove_read_event(reactor, worker->pipe_master); + 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; } - int fd; - int serv_max_fd = swServer_get_maxfd(serv); - int serv_min_fd = swServer_get_minfd(serv); - - for (fd = serv_min_fd; fd <= serv_max_fd; fd++) - { - swConnection *conn = swServer_connection_get(serv, fd); - if (conn && conn->socket && conn->active && !conn->peer_closed && conn->socket->fdtype == SW_FD_SESSION) - { - swReactor_remove_read_event(reactor, conn->socket); - } + if (swoole_kill(worker->pid, SIGTERM) < 0) { + swoole_sys_warning("kill(%d, SIGTERM) failed", worker->pid); + return false; } - swServer_clear_timer(serv); - goto _try_to_exit; } + return true; +} - swWorkerStopMessage msg; - msg.pid = SwooleG.pid; - msg.worker_id = SwooleWG.id; +void Server::stop_async_worker(Worker *worker) { + Reactor *reactor = SwooleTG.reactor; - //send message to manager - if (serv->message_box && swChannel_push(serv->message_box, &msg, sizeof(msg)) < 0) - { - SwooleG.running = 0; + worker->shutdown(); + if (worker->type == SW_PROCESS_EVENTWORKER) { + reset_worker_counter(worker); } - else - { - swoole_kill(serv->gs->manager_pid, SIGIO); + + /** + * force to end. + */ + if (reload_async == 0) { + reactor->running = false; + return; + } + + // The worker process is shutting down now. + if (reactor->wait_exit) { + return; } - _try_to_exit: reactor->wait_exit = 1; - reactor->is_empty = swWorker_reactor_is_empty; - SwooleWG.exit_time = time(NULL); + // Separated from the event worker process pool + SwooleWG.worker_copy = new Worker{}; + *SwooleWG.worker_copy = *worker; + SwooleWG.worker = worker; - if (swWorker_reactor_is_empty(reactor)) - { - reactor->running = 0; - SwooleG.running = 0; + if (worker->pipe_worker && !worker->pipe_worker->removed) { + reactor->remove_read_event(worker->pipe_worker); } -} -static int swWorker_reactor_is_empty(swReactor *reactor) -{ - swServer *serv; - if (SwooleG.process_type == SW_PROCESS_TASKWORKER) - { - swProcessPool *pool = (swProcessPool *) reactor->ptr; - serv = (swServer *) pool->ptr; + 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); + } + if (worker->pipe_master && !worker->pipe_master->removed) { + reactor->remove_read_event(worker->pipe_master); + } + foreach_connection([reactor](Connection *conn) { + if (!conn->peer_closed && !conn->socket->removed) { + reactor->remove_read_event(conn->socket); + } + }); + clear_timer(); + } + } else if (is_process_mode()) { + WorkerStopMessage msg; + msg.pid = SwooleG.pid; + msg.worker_id = worker->id; + + 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); } - else - { - serv = (swServer *) reactor->ptr; + + reactor->set_wait_exit(true); + reactor->set_end_callback(Reactor::PRIORITY_TRY_EXIT, Worker_reactor_try_to_exit); + SwooleWG.exit_time = ::time(nullptr); + + Worker_reactor_try_to_exit(reactor); +} + +static void Worker_reactor_try_to_exit(Reactor *reactor) { + Server *serv; + if (swoole_get_process_type() == SW_PROCESS_TASKWORKER) { + ProcessPool *pool = (ProcessPool *) reactor->ptr; + serv = (Server *) pool->ptr; + } else { + serv = (Server *) reactor->ptr; } uint8_t call_worker_exit_func = 0; - while (1) - { - if (swReactor_empty(reactor)) - { - return SW_TRUE; - } - else - { - if (serv->onWorkerExit && call_worker_exit_func == 0) - { - serv->onWorkerExit(serv, SwooleWG.id); + while (1) { + if (reactor->if_exit()) { + reactor->running = false; + } else { + if (serv->onWorkerExit && call_worker_exit_func == 0) { + serv->onWorkerExit(serv, sw_worker()); call_worker_exit_func = 1; continue; } - int remaining_time = serv->max_wait_time - (time(NULL) - SwooleWG.exit_time); - if (remaining_time <= 0) - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT, "worker exit timeout, forced to terminate"); - return SW_TRUE; - } - else - { + 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; + } else { int timeout_msec = remaining_time * 1000; - if (reactor->timeout_msec < 0 || reactor->timeout_msec > timeout_msec) - { + if (reactor->timeout_msec < 0 || reactor->timeout_msec > timeout_msec) { reactor->timeout_msec = timeout_msec; } } } break; } - - return SW_FALSE; } -void swWorker_clean_pipe_buffer(swServer *serv) -{ - uint32_t i; - for (i = 0; i < serv->worker_num + serv->task_worker_num; i++) - { - swWorker *worker = swServer_get_worker(serv, i); - if (SwooleTG.reactor) - { - if (worker->pipe_worker) - { - swReactor_wait_write_buffer(SwooleTG.reactor, worker->pipe_worker); +void Server::drain_worker_pipe() { + for (uint32_t i = 0; i < worker_num + task_worker_num; i++) { + Worker *worker = get_worker(i); + if (sw_reactor()) { + if (worker->pipe_worker) { + sw_reactor()->drain_write_buffer(worker->pipe_worker); } - if (worker->pipe_master) - { - swReactor_wait_write_buffer(SwooleTG.reactor, worker->pipe_master); + if (worker->pipe_master) { + sw_reactor()->drain_write_buffer(worker->pipe_master); } } } } +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 swWorker_loop(swServer *serv, swWorker *worker) -{ - //worker_id - SwooleWG.id = worker->id; +int Server::start_event_worker(Worker *worker) { + swoole_set_process_id(worker->id); + swoole_set_process_type(SW_PROCESS_EVENTWORKER); - swServer_worker_init(serv, worker); + init_event_worker(worker); - if (swoole_event_init() < 0) - { + if (swoole_event_init(0) < 0) { return SW_ERR; } - swReactor *reactor = SwooleTG.reactor; + worker_signal_init(); + + Reactor *reactor = SwooleTG.reactor; /** * set pipe buffer size */ - for (uint32_t i = 0; i < serv->worker_num + serv->task_worker_num; i++) - { - swWorker *_worker = swServer_get_worker(serv, i); - if (_worker->pipe_master) - { + for (uint32_t i = 0; i < worker_num + task_worker_num; i++) { + Worker *_worker = get_worker(i); + if (_worker->pipe_master) { _worker->pipe_master->buffer_size = UINT_MAX; } - if (_worker->pipe_worker) - { + if (_worker->pipe_worker) { _worker->pipe_worker->buffer_size = UINT_MAX; } } - swSocket_set_nonblock(worker->pipe_worker); - reactor->ptr = serv; - reactor->add(reactor, worker->pipe_worker, SW_EVENT_READ); - swReactor_set_handler(reactor, SW_FD_PIPE, swWorker_onPipeReceive); + worker->pipe_worker->set_nonblock(); + reactor->ptr = this; + reactor->add(worker->pipe_worker, SW_EVENT_READ); + reactor->set_handler(SW_FD_PIPE, Worker_onPipeReceive); - if (serv->dispatch_mode == SW_DISPATCH_STREAM) - { - reactor->add(reactor, serv->stream_socket, SW_EVENT_READ); - swReactor_set_handler(reactor, SW_FD_STREAM_SERVER, swWorker_onStreamAccept); - swReactor_set_handler(reactor, SW_FD_STREAM, swWorker_onStreamRead); - swStream_set_protocol(&serv->stream_protocol); - serv->stream_protocol.private_data_2 = serv; - serv->stream_protocol.package_max_length = UINT_MAX; - serv->stream_protocol.onPackage = swWorker_onStreamPackage; - serv->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; - swWorker_onStart(serv); + worker_start_callback(worker); - //main loop - reactor->wait(reactor, NULL); - //clear pipe buffer - swWorker_clean_pipe_buffer(serv); - //reactor free + // main loop + reactor->wait(nullptr); + // drain pipe buffer + drain_worker_pipe(); + // reactor free swoole_event_free(); - //worker shutdown - swWorker_onStop(serv); + // worker shutdown + worker_stop_callback(worker); - if (serv->buffer_pool) - { - delete serv->buffer_pool; + if (buffer_pool) { + delete buffer_pool; } return SW_OK; } /** - * Send data to ReactorThread + * [Worker/TaskWorker/Master] Send data to ReactorThread */ -int swWorker_send2reactor(swServer *serv, swEventData *ev_data, size_t sendn, int session_id) -{ - swSocket *pipe_sock = swServer_get_send_pipe(serv, session_id, ev_data->info.reactor_id); - if (SwooleTG.reactor) - { - return SwooleTG.reactor->write(SwooleTG.reactor, pipe_sock, ev_data, sendn); - } - else - { - return swSocket_write_blocking(pipe_sock, ev_data, sendn); +ssize_t Server::send_to_reactor_thread(const EventData *ev_data, size_t sendn, SessionId session_id) { + Socket *pipe_sock = get_reactor_pipe_socket(session_id, ev_data->info.reactor_id); + if (swoole_event_is_available()) { + return swoole_event_write(pipe_sock, ev_data, sendn); + } else { + return pipe_sock->send_blocking(ev_data, sendn); } } /** - * receive data from reactor + * send message from worker to another worker */ -static int swWorker_onPipeReceive(swReactor *reactor, swEvent *event) -{ - ssize_t recv_n = 0; - swServer *serv = (swServer *) reactor->ptr; - swFactory *factory = &serv->factory; - swPipeBuffer *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; - } - - 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[%lu] receives the chunk data to the maximum[%d], return to event loop", - SwooleWG.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 swEventData parameters into swDataHead and data, - * we store the value of the worker_buffer pointer in swEventData.data. - * The value of this pointer will be fetched in the swServer_worker_get_packet function. - */ - serv->move_buffer(serv, pipe_buffer); - } - } - else - { - recv_n = read(event->fd, pipe_buffer, serv->ipc_max_size); - } - - if (recv_n > 0) - { - return swWorker_onTask(factory, (swEventData *) pipe_buffer); - } - - return SW_ERR; +ssize_t Server::send_to_worker_from_worker(Worker *dst_worker, const void *buf, size_t len, int flags) { + return dst_worker->send_pipe_message(buf, len, flags); } -int swWorker_send2worker(swWorker *dst_worker, const void *buf, int n, int flag) -{ - swSocket *pipe_sock; +/** + * receive data from reactor + */ +static int Worker_onPipeReceive(Reactor *reactor, Event *event) { + Server *serv = (Server *) reactor->ptr; + PipeBuffer *pipe_buffer = serv->get_worker_message_bus()->get_buffer(); - if (flag & SW_PIPE_MASTER) - { - pipe_sock = dst_worker->pipe_master; - } - else - { - pipe_sock = dst_worker->pipe_worker; + if (serv->get_worker_message_bus()->read(event->socket) <= 0) { + return SW_OK; } - //message-queue - if (dst_worker->pool->use_msgqueue) - { - struct - { - long mtype; - swEventData buf; - } msg; - - msg.mtype = dst_worker->id + 1; - memcpy(&msg.buf, buf, n); - - return swMsgQueue_push(dst_worker->pool->queue, (swQueue_data *) &msg, n); - } + serv->worker_accept_event(&pipe_buffer->info); + serv->get_worker_message_bus()->pop(); - if ((flag & SW_PIPE_NONBLOCK) && SwooleTG.reactor) - { - return SwooleTG.reactor->write(SwooleTG.reactor, pipe_sock, buf, n); - } - else - { - return swSocket_write_blocking(pipe_sock, buf, n); - } + return SW_OK; } + +} // namespace swoole diff --git a/src/wrapper/event.cc b/src/wrapper/event.cc index 95209854177..8598a4bcca6 100644 --- a/src/wrapper/event.cc +++ b/src/wrapper/event.cc @@ -10,102 +10,124 @@ | to obtain it through the world-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_client.h" +#include "swoole_coroutine_socket.h" +#include "swoole_coroutine_system.h" #include -#include -using namespace std; +using namespace swoole; -static mutex init_lock; +using swoole::network::Socket; + +static std::mutex init_lock; #ifdef __MACH__ -swReactor* sw_reactor() -{ +Reactor *sw_reactor() { return SwooleTG.reactor; } #endif -int swoole_event_init() -{ - if (!SwooleG.init) - { - unique_lock lock(init_lock); +int swoole_event_init(int flags) { + if (!SwooleG.init) { + std::unique_lock lock(init_lock); swoole_init(); } - SwooleTG.reactor = (swReactor *) sw_malloc(sizeof(swReactor)); - if (!SwooleTG.reactor) - { - swSysWarn("malloc failed"); + Reactor *reactor = new Reactor(SW_REACTOR_MAXEVENTS); + if (!reactor->ready()) { return SW_ERR; } - if (swReactor_create(SwooleTG.reactor, SW_REACTOR_MAXEVENTS) < 0) - { - sw_free(SwooleTG.reactor); - SwooleTG.reactor = nullptr; - return SW_ERR; + + if (flags & SW_EVENTLOOP_WAIT_EXIT) { + reactor->wait_exit = 1; } + + coroutine::Socket::init_reactor(reactor); + coroutine::System::init_reactor(reactor); + network::Client::init_reactor(reactor); + + SwooleTG.reactor = reactor; + return SW_OK; } -int swoole_event_add(swSocket *socket, int events) -{ - return SwooleTG.reactor->add(SwooleTG.reactor, socket, events); +int swoole_event_add(Socket *socket, int events) { + return SwooleTG.reactor->add(socket, events); } -int swoole_event_set(swSocket *socket, int events) -{ - return SwooleTG.reactor->set(SwooleTG.reactor, 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_del(swSocket *socket) -{ - return SwooleTG.reactor->del(SwooleTG.reactor, socket); +int swoole_event_set(Socket *socket, int events) { + return SwooleTG.reactor->set(socket, events); } -int swoole_event_wait() -{ - swReactor *reactor = SwooleTG.reactor; +int swoole_event_del(Socket *socket) { + return SwooleTG.reactor->del(socket); +} + +int swoole_event_wait() { + Reactor *reactor = SwooleTG.reactor; int retval = 0; - if (!reactor->is_empty(reactor)) - { - retval = SwooleTG.reactor->wait(SwooleTG.reactor, nullptr); + if (!reactor->wait_exit or !reactor->if_exit()) { + retval = reactor->wait(nullptr); } swoole_event_free(); return retval; } -int swoole_event_free() -{ - if (!SwooleTG.reactor) - { +int swoole_event_free() { + if (!SwooleTG.reactor) { return SW_ERR; } - swReactor_destroy(SwooleTG.reactor); - sw_free(SwooleTG.reactor); + delete SwooleTG.reactor; SwooleTG.reactor = nullptr; return SW_OK; } -void swoole_event_defer(swCallback cb, void *private_data) -{ - SwooleTG.reactor->defer(SwooleTG.reactor, cb, private_data); +void swoole_event_defer(Callback cb, void *private_data) { + SwooleTG.reactor->defer(cb, private_data); } -/** - * @return SW_OK or SW_ERR - */ -int swoole_event_write(swSocket *socket, const void *data, size_t len) -{ +ssize_t swoole_event_write(Socket *socket, const void *data, size_t len) { return SwooleTG.reactor->write(SwooleTG.reactor, socket, data, len); } -int swoole_event_set_handler(int fdtype, swReactor_handler handle) -{ - return swReactor_set_handler(SwooleTG.reactor, fdtype, handle); +ssize_t swoole_event_writev(swoole::network::Socket *socket, const iovec *iov, size_t iovcnt) { + return SwooleTG.reactor->writev(SwooleTG.reactor, socket, iov, iovcnt); +} + +bool swoole_event_set_handler(int fdtype, ReactorHandler handler) { + return SwooleTG.reactor->set_handler(fdtype, handler); +} + +bool swoole_event_isset_handler(int fdtype) { + return SwooleTG.reactor->isset_handler(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/server.cc b/src/wrapper/server.cc deleted file mode 100644 index 8a75a189ea1..00000000000 --- a/src/wrapper/server.cc +++ /dev/null @@ -1,713 +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 "wrapper/server.hpp" -#include - -namespace swoole -{ -swString *_callback_buffer; -Server::Server(string _host, int _port, int _mode, enum swSocket_type _type) -{ - host = _host; - port = _port; - mode = _mode; - - swServer_init(&serv); - - if (_mode == SW_MODE_BASE) - { - serv.reactor_num = 1; - serv.worker_num = 1; - } - - serv.factory_mode = (uint8_t) mode; - serv.dispatch_mode = 2; - - //create Server - int ret = swServer_create(&serv); - if (ret < 0) - { - swTrace("create server fail[error=%d].\n", ret); - exit(0); - } - this->listen(host, port, _type); -} - -void Server::setEvents(int _events) -{ - events = _events; -} - -bool Server::listen(string host, int port, enum swSocket_type type) -{ - auto ls = swServer_add_port(&serv, type, (char *) host.c_str(), port); - if (ls == NULL) - { - return false; - } - else - { - ports.push_back(ls); - return true; - } -} - -bool Server::send(int fd, const DataBuffer &data) -{ - if (serv.gs->start == 0) - { - return false; - } - if (data.length <= 0) - { - return false; - } - return serv.send(&serv, fd, (char *) data.buffer, data.length) == 0; -} - -bool Server::send(int fd, const char *data, int length) -{ - if (serv.gs->start == 0) - { - return false; - } - if (length <= 0) - { - return false; - } - return serv.send(&serv, fd, (char *) data, length) == SW_OK; -} - -bool Server::close(int fd, bool reset) -{ - if (serv.gs->start == 0) - { - return false; - } - if (swIsMaster()) - { - return false; - } - - swConnection *conn = swServer_connection_verify_no_ssl(&serv, fd); - if (!conn) - { - return false; - } - - //Reset send buffer, Immediately close the connection. - if (reset) - { - conn->close_reset = 1; - } - - int ret; - if (!swIsWorker()) - { - swWorker *worker = swServer_get_worker(&serv, conn->fd % serv.worker_num); - swDataHead ev; - ev.type = SW_SERVER_EVENT_CLOSE; - ev.fd = fd; - ev.reactor_id = conn->reactor_id; - ret = swWorker_send2worker(worker, &ev, sizeof(ev), SW_PIPE_MASTER); - } - else - { - ret = serv.factory.end(&serv.factory, fd); - } - return ret == SW_OK; -} - -static int task_id = 0; - -static int task_pack(swEventData *task, const DataBuffer &data) -{ - task->info.type = SW_SERVER_EVENT_TASK; - //field fd save task_id - task->info.fd = task_id++; - //field reactor_id save the worker_id - task->info.reactor_id = SwooleWG.id; - swTask_type(task) = 0; - - if (data.length >= SW_IPC_MAX_SIZE - sizeof(task->info)) - { - if (swTaskWorker_large_pack(task, (char *) data.buffer, (int) data.length) < 0) - { - swWarn("large task pack failed()"); - return SW_ERR; - } - } - else - { - memcpy(task->data, (char *) data.buffer, data.length); - task->info.len = (uint16_t) data.length; - } - return task->info.fd; -} - -static DataBuffer task_unpack(swEventData *task_result) -{ - DataBuffer retval; - swString *result = swTaskWorker_large_unpack(task_result); - if (result) - { - retval.copy(task_result->data, (size_t) task_result->info.len); - } - return retval; -} - -static DataBuffer get_recv_data(swServer *serv, swEventData *req, char *header, uint32_t header_length) -{ - char *data_ptr = NULL; - DataBuffer retval; - size_t data_len = serv->get_packet(serv, req, &data_ptr); - - if (header_length >= (uint32_t) data_len) - { - return retval; - } - else - { - retval.copy(data_ptr + header_length, data_len - header_length); - } - - if (header_length > 0) - { - memcpy(header, data_ptr, header_length); - } - - return retval; -} - -int Server::check_task_param(int dst_worker_id) -{ - if (serv.task_worker_num < 1) - { - swWarn("Task method cannot use, Please set task_worker_num"); - return SW_ERR; - } - if (dst_worker_id > 0 && (uint32_t) dst_worker_id >= serv.task_worker_num) - { - swWarn("worker_id must be less than serv->task_worker_num"); - return SW_ERR; - } - if (!swIsWorker()) - { - swWarn("The method can only be used in the worker process"); - return SW_ERR; - } - return SW_OK; -} - -int Server::task(DataBuffer &data, int dst_worker_id) -{ - if (serv.gs->start == 0) - { - swWarn("Server is not running"); - return false; - } - - swEventData buf; - bzero(&buf.info, sizeof(buf.info)); - if (check_task_param(dst_worker_id) < 0) - { - return false; - } - - if (task_pack(&buf, data) < 0) - { - return false; - } - - swTask_type(&buf) |= SW_TASK_NONBLOCK; - if (swProcessPool_dispatch(&serv.gs->task_workers, &buf, &dst_worker_id) >= 0) - { - sw_atomic_fetch_add(&serv.stats->tasking_num, 1); - return buf.info.fd; - } - else - { - return -1; - } -} - -bool Server::finish(DataBuffer &data) -{ - if (serv.gs->start == 0) - { - swWarn("Server is not running"); - return false; - } - return swTaskWorker_finish(&serv, (char *) data.buffer, (int) data.length, 0, nullptr) == 0; -} - -bool Server::sendto(const string &ip, int port, const DataBuffer &data, int server_socket) -{ - if (serv.gs->start == 0) - { - return false; - } - if (data.length <= 0) - { - return false; - } - bool ipv6 = false; - if (strchr(ip.c_str(), ':')) - { - ipv6 = true; - } - - if (ipv6 && serv.udp_socket_ipv6 <= 0) - { - return false; - } - else if (serv.udp_socket_ipv4 <= 0) - { - swWarn("You must add an UDP listener to server before using sendto"); - return false; - } - - if (server_socket < 0) - { - server_socket = ipv6 ? serv.udp_socket_ipv6 : serv.udp_socket_ipv4; - } - - int ret; - if (ipv6) - { - ret = swSocket_udp_sendto6(server_socket, (char *) ip.c_str(), port, (char *) data.buffer, data.length); - } - else - { - ret = swSocket_udp_sendto(server_socket, (char *) ip.c_str(), port, (char *) data.buffer, data.length); - } - return ret > 0; -} - -bool Server::sendfile(int fd, string &file, off_t offset, size_t length) -{ - if (serv.gs->start == 0) - { - swWarn("Server is not running"); - return false; - } - - struct stat file_stat; - if (stat(file.c_str(), &file_stat) < 0) - { - swWarn("stat(%s) failed", file.c_str()); - return false; - } - if (file_stat.st_size <= offset) - { - swWarn("file[offset=%jd] is empty", (intmax_t) offset); - return false; - } - return serv.sendfile(&serv, fd, (char *) file.c_str(), file.length(), offset, length) == SW_OK; -} - -bool Server::sendMessage(int worker_id, DataBuffer &data) -{ - swEventData buf; - - if (serv.gs->start == 0) - { - swWarn("Server is not running"); - return false; - } - - if (worker_id == (int) SwooleWG.id) - { - swWarn("cannot send message to self"); - return false; - } - - if (worker_id > 0 && (uint32_t) worker_id >= serv.worker_num + serv.task_worker_num) - { - swWarn("worker_id[%d] is invalid", worker_id); - return false; - } - - if (serv.onPipeMessage == NULL) - { - swWarn("onPipeMessage is null, cannot use sendMessage"); - return false; - } - - if (task_pack(&buf, data) < 0) - { - return false; - } - - buf.info.type = SW_SERVER_EVENT_PIPE_MESSAGE; - buf.info.reactor_id = SwooleWG.id; - - swWorker *to_worker = swServer_get_worker(&serv, (uint16_t) worker_id); - return swWorker_send2worker(to_worker, &buf, sizeof(buf.info) + buf.info.len, SW_PIPE_MASTER | SW_PIPE_NONBLOCK) - == SW_OK; -} - -bool Server::sendwait(int fd, const DataBuffer &data) -{ - if (serv.gs->start == 0) - { - swWarn("Server is not running"); - return false; - } - if (data.length <= 0) - { - return false; - } - if (serv.factory_mode != SW_MODE_BASE || swIsTaskWorker()) - { - swWarn("cannot sendwait"); - return false; - } - return serv.sendwait(&serv, fd, data.buffer, data.length) == 0; -} - -bool Server::start(void) -{ - serv.ptr2 = this; - if (this->events & EVENT_onStart) - { - serv.onStart = Server::_onStart; - } - if (this->events & EVENT_onShutdown) - { - serv.onShutdown = Server::_onShutdown; - } - if (this->events & EVENT_onConnect) - { - serv.onConnect = Server::_onConnect; - } - if (this->events & EVENT_onReceive) - { - serv.onReceive = Server::_onReceive; - } - if (this->events & EVENT_onPacket) - { - serv.onPacket = Server::_onPacket; - } - if (this->events & EVENT_onClose) - { - serv.onClose = Server::_onClose; - } - if (this->events & EVENT_onWorkerStart) - { - serv.onWorkerStart = Server::_onWorkerStart; - } - if (this->events & EVENT_onWorkerStop) - { - serv.onWorkerStop = Server::_onWorkerStop; - } - if (this->events & EVENT_onTask) - { - serv.onTask = Server::_onTask; - } - if (this->events & EVENT_onFinish) - { - serv.onFinish = Server::_onFinish; - } - if (this->events & EVENT_onPipeMessage) - { - serv.onPipeMessage = Server::_onPipeMessage; - } - _callback_buffer = swString_new(8192); - int ret = swServer_start(&serv); - if (ret < 0) - { - swTrace("start server fail[error=%d].\n", ret); - return false; - } - return true; -} - -int Server::_onReceive(swServer *serv, swEventData *req) -{ - DataBuffer data = get_recv_data(serv, req, NULL, 0); - Server *_this = (Server *) serv->ptr2; - _this->onReceive(req->info.fd, data); - return SW_OK; -} - -void Server::_onWorkerStart(swServer *serv, int worker_id) -{ - Server *_this = (Server *) serv->ptr2; - _this->onWorkerStart(worker_id); -} - -void Server::_onWorkerStop(swServer *serv, int worker_id) -{ - Server *_this = (Server *) serv->ptr2; - _this->onWorkerStop(worker_id); -} - -int Server::_onPacket(swServer *serv, swEventData *req) -{ - swDgramPacket *packet; - - char *buffer; - serv->get_packet(serv, req, &buffer); - packet = (swDgramPacket *) buffer; - - char *data = NULL; - int length = 0; - ClientInfo clientInfo; - clientInfo.server_socket = req->info.server_fd; - data = packet->data; - length = packet->length; - - if (packet->socket_type == SW_SOCK_UDP) - { - inet_ntop(AF_INET, &packet->socket_addr.addr.inet_v4.sin_addr, clientInfo.address, sizeof(clientInfo.address)); - clientInfo.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, clientInfo.address, sizeof(clientInfo.address)); - clientInfo.port = ntohs(packet->socket_addr.addr.inet_v6.sin6_port); - } - else if (packet->socket_type == SW_SOCK_UNIX_DGRAM) - { - strcpy(clientInfo.address, packet->socket_addr.addr.un.sun_path); - } - else - { - abort(); - return SW_ERR; - } - - DataBuffer _data; - _data.copy(data, length); - - Server *_this = (Server *) serv->ptr2; - _this->onPacket(_data, clientInfo); - - return SW_OK; -} - -void Server::_onStart(swServer *serv) -{ - Server *_this = (Server *) serv->ptr2; - _this->onStart(); -} - -void Server::_onShutdown(swServer *serv) -{ - Server *_this = (Server *) serv->ptr2; - _this->onShutdown(); -} - -void Server::_onConnect(swServer *serv, swDataHead *info) -{ - Server *_this = (Server *) serv->ptr2; - _this->onConnect(info->fd); -} - -void Server::_onClose(swServer *serv, swDataHead *info) -{ - Server *_this = (Server *) serv->ptr2; - _this->onClose(info->fd); -} - -void Server::_onPipeMessage(swServer *serv, swEventData *req) -{ - DataBuffer data = task_unpack(req); - Server *_this = (Server *) serv->ptr2; - _this->onPipeMessage(req->info.reactor_id, data); -} - -int Server::_onTask(swServer *serv, swEventData *task) -{ - Server *_this = (Server *) serv->ptr2; - DataBuffer data = task_unpack(task); - _this->onTask(task->info.fd, task->info.server_fd, data); - return SW_OK; -} - -int Server::_onFinish(swServer *serv, swEventData *task) -{ - Server *_this = (Server *) serv->ptr2; - DataBuffer data = task_unpack(task); - _this->onFinish(task->info.fd, data); - return SW_OK; -} - -DataBuffer Server::taskwait(const DataBuffer &data, double timeout, int dst_worker_id) -{ - swEventData buf; - DataBuffer retval; - - if (serv.gs->start == 0) - { - swWarn("server is not running"); - return retval; - } - - if (check_task_param(dst_worker_id) < 0) - { - return retval; - } - - task_pack(&buf, data); - - uint64_t notify; - swEventData *task_result = &(serv.task_result[SwooleWG.id]); - bzero(task_result, sizeof(swEventData)); - swPipe *task_notify_pipe = &serv.task_notify[SwooleWG.id]; - swSocket *task_notify_socket = task_notify_pipe->getSocket(task_notify_pipe, 0); - - //clear history task - while (read(task_notify_socket->fd, ¬ify, sizeof(notify)) > 0) {} - - if (swProcessPool_dispatch_blocking(&serv.gs->task_workers, &buf, &dst_worker_id) >= 0) - { - sw_atomic_fetch_add(&serv.stats->tasking_num, 1); - task_notify_pipe->timeout = timeout; - int ret = task_notify_pipe->read(task_notify_pipe, ¬ify, sizeof(notify)); - if (ret > 0) - { - return task_unpack(task_result); - } - else - { - swSysWarn("taskwait failed"); - } - } - return retval; -} - -map Server::taskWaitMulti(const vector &tasks, double timeout) -{ - swEventData buf; - map retval; - - if (serv.gs->start == 0) - { - swWarn("server is not running"); - return retval; - } - - int dst_worker_id; - int task_id; - int i = 0; - int n_task = tasks.size(); - - int list_of_id[1024]; - - uint64_t notify; - swEventData *task_result = &(serv.task_result[SwooleWG.id]); - bzero(task_result, sizeof(swEventData)); - swPipe *task_notify_pipe = &serv.task_notify[SwooleWG.id]; - swWorker *worker = swServer_get_worker(&serv, SwooleWG.id); - - char _tmpfile[sizeof(SW_TASK_TMP_FILE)] = SW_TASK_TMP_FILE; - int _tmpfile_fd = swoole_tmpfile(_tmpfile); - if (_tmpfile_fd < 0) - { - swSysWarn("mktemp(%s) failed", SW_TASK_TMP_FILE); - return retval; - } - - close(_tmpfile_fd); - int *finish_count = (int *) task_result->data; - - worker->lock.lock(&worker->lock); - *finish_count = 0; - memcpy(task_result->data + 4, _tmpfile, sizeof(_tmpfile)); - worker->lock.unlock(&worker->lock); - - //clear history task - swSocket *task_notify_socket = task_notify_pipe->getSocket(task_notify_pipe, 0); - while (read(task_notify_socket->fd, ¬ify, sizeof(notify)) > 0) {} - - for (auto task = tasks.begin(); task != tasks.end();) - { - task_id = task_pack(&buf, *task); - if (task_id < 0) - { - swWarn("task pack failed"); - goto _fail; - } - swTask_type(&buf) |= SW_TASK_WAITALL; - dst_worker_id = -1; - if (swProcessPool_dispatch_blocking(&serv.gs->task_workers, &buf, &dst_worker_id) >= 0) - { - sw_atomic_fetch_add(&serv.stats->tasking_num, 1); - list_of_id[i] = task_id; - } - else - { - swSysWarn("taskwait failed"); - _fail: - retval[i] = DataBuffer(); - n_task--; - } - i++; - } - - while (n_task > 0) - { - task_notify_pipe->timeout = timeout; - int ret = task_notify_pipe->read(task_notify_pipe, ¬ify, sizeof(notify)); - if (ret > 0) - { - if (*finish_count == n_task) - { - break; - } - } - else - { - swSysWarn("taskwait failed"); - unlink(_tmpfile); - return retval; - } - } - - swString *content = swoole_file_get_contents(_tmpfile); - if (content == NULL) - { - return retval; - } - - swEventData *result; - DataBuffer zdata; - int j; - - for (i = 0; i < n_task; i++) - { - result = (swEventData *) (content->str + content->offset); - task_id = result->info.fd; - zdata = task_unpack(result); - for (j = 0; j < n_task; j++) - { - if (list_of_id[j] == task_id) - { - break; - } - } - retval[j] = zdata; - content->offset += sizeof(swDataHead) + result->info.len; - } - unlink(_tmpfile); - swString_free(content); - return retval; -} - -} diff --git a/src/wrapper/timer.cc b/src/wrapper/timer.cc index 04f207794a5..6cfb31e4ef9 100644 --- a/src/wrapper/timer.cc +++ b/src/wrapper/timer.cc @@ -10,117 +10,124 @@ | to obtain it through the world-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 "wrapper/base.hpp" -#include "server.h" +#include "swoole_timer.h" -using namespace std; using namespace swoole; #ifdef __MACH__ -swTimer* sw_timer() -{ +Timer *sw_timer() { return SwooleTG.timer; } #endif -swTimer_node* swoole_timer_add(long ms, uchar persistent, swTimerCallback callback, void *private_data) -{ - if (sw_unlikely(SwooleTG.timer == nullptr)) - { - SwooleTG.timer = (swTimer *) sw_malloc(sizeof(swTimer)); - if (sw_unlikely(SwooleTG.timer == nullptr)) - { - return nullptr; - } - if (sw_unlikely(swTimer_init(SwooleTG.timer, ms) != SW_OK)) - { - sw_free(SwooleTG.timer); +bool swoole_timer_is_available() { + return SwooleTG.timer != nullptr; +} + +TimerNode *swoole_timer_add(double timeout, bool persistent, const TimerCallback &callback, void *private_data) { + if (timeout < SW_TIMER_MIN_SEC) { + return swoole_timer_add(1L, persistent, callback, private_data); + } + + return swoole_timer_add((long) (timeout * 1000), persistent, callback, private_data); +} + +TimerNode *swoole_timer_add(long ms, bool persistent, const TimerCallback &callback, void *private_data) { + if (sw_unlikely(!swoole_timer_is_available())) { + SwooleTG.timer = new Timer(); + if (sw_unlikely(!SwooleTG.timer->init())) { + delete SwooleTG.timer; SwooleTG.timer = nullptr; return nullptr; } } - return swTimer_add(SwooleTG.timer, ms, persistent, private_data, callback); + return SwooleTG.timer->add(ms, persistent, private_data, callback); } -uchar swoole_timer_del(swTimer_node* tnode) -{ - return swTimer_del(SwooleTG.timer, tnode); +bool swoole_timer_del(TimerNode *tnode) { + if (!swoole_timer_is_available()) { + swoole_warning("timer is not available"); + return false; + } + return SwooleTG.timer->remove(tnode); } -long swoole_timer_after(long ms, swTimerCallback callback, void *private_data) -{ - if (ms <= 0) - { - swWarn("Timer must be greater than 0"); - return SW_ERR; +void swoole_timer_delay(TimerNode *tnode, long delay_ms) { + if (!swoole_timer_is_available()) { + swoole_warning("timer is not available"); + return; } - swTimer_node *tnode = swoole_timer_add(ms, SW_FALSE, callback, private_data); - if (tnode == nullptr) - { + return SwooleTG.timer->delay(tnode, delay_ms); +} + +long swoole_timer_after(long ms, const TimerCallback &callback, void *private_data) { + if (ms <= 0) { + swoole_warning("Timer must be greater than 0"); return SW_ERR; } - else - { + TimerNode *tnode = swoole_timer_add(ms, false, callback, private_data); + if (tnode == nullptr) { + return SW_ERR; + } else { return tnode->id; } } -long swoole_timer_tick(long ms, swTimerCallback callback, void *private_data) -{ - if (ms <= 0) - { - swWarn("Timer must be greater than 0"); +long swoole_timer_tick(long ms, const TimerCallback &callback, void *private_data) { + if (ms <= 0) { + swoole_warning("Timer must be greater than 0"); return SW_ERR; } - swTimer_node *tnode = swoole_timer_add(ms, SW_TRUE, callback, private_data); - if (tnode == nullptr) - { + TimerNode *tnode = swoole_timer_add(ms, true, callback, private_data); + if (tnode == nullptr) { return SW_ERR; - } - else - { + } else { return tnode->id; } } -uchar swoole_timer_exists(long timer_id) -{ - if (!SwooleTG.timer) - { - swWarn("no timer"); +bool swoole_timer_exists(long timer_id) { + if (!swoole_timer_is_available()) { + swoole_warning("timer is not available"); return false; } - swTimer_node *tnode = swTimer_get(SwooleTG.timer, timer_id); + TimerNode *tnode = SwooleTG.timer->get(timer_id); return (tnode && !tnode->removed); } -uchar swoole_timer_clear(long timer_id) -{ - return swTimer_del(SwooleTG.timer, swTimer_get(SwooleTG.timer, 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)); } -swTimer_node* swoole_timer_get(long timer_id) -{ - if (!SwooleTG.timer) - { - swWarn("no timer"); +TimerNode *swoole_timer_get(long timer_id) { + if (!swoole_timer_is_available()) { + swoole_warning("timer is not available"); return nullptr; } - return swTimer_get(SwooleTG.timer, timer_id); + return SwooleTG.timer->get(timer_id); } -void swoole_timer_free() -{ - if (!SwooleTG.timer) - { +void swoole_timer_free() { + if (!swoole_timer_is_available()) { + swoole_warning("timer is not available"); return; } - swTimer_free(SwooleTG.timer); - sw_free(SwooleTG.timer); + delete SwooleTG.timer; SwooleTG.timer = nullptr; } + +int swoole_timer_select() { + if (!swoole_timer_is_available()) { + return SW_ERR; + } + return SwooleTG.timer->select(); +} diff --git a/swoole.cc b/swoole.cc deleted file mode 100644 index fe88d2b21e7..00000000000 --- a/swoole.cc +++ /dev/null @@ -1,1027 +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 "php_swoole_library.h" - -#if (HAVE_PCRE || HAVE_BUNDLED_PCRE) && !defined(COMPILE_DL_PCRE) -#include "ext/pcre/php_pcre.h" -#endif -#include "zend_exceptions.h" - -#include "mime_type.h" -#include "server.h" - -#include -#include -#include -#include -#include - -#ifdef SW_HAVE_ZLIB -#include -#endif -#ifdef SW_HAVE_BROTLI -#include -#include -#endif - -ZEND_DECLARE_MODULE_GLOBALS(swoole) - -extern sapi_module_struct sapi_module; - -static swoole::CallbackManager rshutdown_callbacks; - -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_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_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_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() - -static PHP_FUNCTION(swoole_hashcode); -static PHP_FUNCTION(swoole_mime_type_add); -static PHP_FUNCTION(swoole_mime_type_set); -static PHP_FUNCTION(swoole_mime_type_delete); -static PHP_FUNCTION(swoole_mime_type_get); -static PHP_FUNCTION(swoole_mime_type_exists); - -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) - /*------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) - /*------swoole_coroutine------*/ - PHP_FE(swoole_coroutine_create, arginfo_swoole_coroutine_create) - PHP_FE(swoole_coroutine_defer, arginfo_swoole_coroutine_defer) - /*------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_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_clear_dns_cache, arginfo_swoole_void) - PHP_FE(swoole_internal_call_user_shutdown_begin, arginfo_swoole_void) - PHP_FE_END /* Must be the last line in swoole_functions[] */ -}; - -#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; - -zend_module_entry swoole_module_entry = -{ - STANDARD_MODULE_HEADER_EX, - NULL, - NULL, - "swoole", - swoole_functions, - PHP_MINIT(swoole), - PHP_MSHUTDOWN(swoole), - PHP_RINIT(swoole), //RINIT - PHP_RSHUTDOWN(swoole), //RSHUTDOWN - PHP_MINFO(swoole), - PHP_SWOOLE_VERSION, - STANDARD_MODULE_PROPERTIES -}; - -#ifdef COMPILE_DL_SWOOLE -ZEND_GET_MODULE(swoole) -#endif - -/* {{{ PHP_INI - */ - -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) -/** - * enable swoole coroutine epreemptive scheduler - */ -STD_ZEND_INI_BOOLEAN("swoole.enable_preemptive_scheduler", "Off", PHP_INI_ALL, OnUpdateBool, enable_preemptive_scheduler, zend_swoole_globals, swoole_globals) -/** - * display error - */ -STD_ZEND_INI_BOOLEAN("swoole.display_errors", "On", PHP_INI_ALL, OnUpdateBool, display_errors, zend_swoole_globals, swoole_globals) -/** - * use short class name - */ -STD_ZEND_INI_BOOLEAN("swoole.use_shortname", "On", PHP_INI_SYSTEM, OnUpdateBool, use_shortname, zend_swoole_globals, swoole_globals) -/** - * unix socket buffer size - */ -STD_PHP_INI_ENTRY("swoole.unixsock_buffer_size", ZEND_TOSTR(SW_SOCKET_BUFFER_SIZE), PHP_INI_ALL, OnUpdateLong, socket_buffer_size, zend_swoole_globals, swoole_globals) -PHP_INI_END() - -static void php_swoole_init_globals(zend_swoole_globals *swoole_globals) -{ - swoole_globals->enable_coroutine = 1; - swoole_globals->enable_library = 1; - 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; -} - -void php_swoole_register_shutdown_function(const char *function) -{ - php_shutdown_function_entry shutdown_function_entry; - shutdown_function_entry.arg_count = 1; - shutdown_function_entry.arguments = (zval *) safe_emalloc(sizeof(zval), 1, 0); - ZVAL_STRING(&shutdown_function_entry.arguments[0], function); - register_user_shutdown_function((char *) function, ZSTR_LEN(Z_STR(shutdown_function_entry.arguments[0])), &shutdown_function_entry); -} - -void php_swoole_register_rshutdown_callback(swCallback cb, void *private_data) -{ - rshutdown_callbacks.append(cb, private_data); -} - -static void fatal_error(int code, const char *format, ...) -{ - va_list args; - zend_object *exception; - va_start(args, format); - exception = zend_throw_error_exception(swoole_error_ce, swoole::cpp_string::vformat(format, args).c_str(), code, E_ERROR); - va_end(args); - zend_exception_error(exception, E_ERROR); - // should never here - exit(1); -} - -/* {{{ PHP_MINIT_FUNCTION - */ -PHP_MINIT_FUNCTION(swoole) -{ - ZEND_INIT_MODULE_GLOBALS(swoole, php_swoole_init_globals, NULL); - REGISTER_INI_ENTRIES(); - - SW_REGISTER_STRING_CONSTANT("SWOOLE_VERSION", SWOOLE_VERSION); - SW_REGISTER_LONG_CONSTANT("SWOOLE_VERSION_ID", SWOOLE_VERSION_ID); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MAJOR_VERSION", SWOOLE_MAJOR_VERSION); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MINOR_VERSION", SWOOLE_MINOR_VERSION); - SW_REGISTER_LONG_CONSTANT("SWOOLE_RELEASE_VERSION", SWOOLE_RELEASE_VERSION); - SW_REGISTER_STRING_CONSTANT("SWOOLE_EXTRA_VERSION", SWOOLE_EXTRA_VERSION); -#ifndef SW_DEBUG - SW_REGISTER_BOOL_CONSTANT("SWOOLE_DEBUG", 0); -#else - SW_REGISTER_BOOL_CONSTANT("SWOOLE_DEBUG", 1); -#endif - -#ifdef SW_HAVE_COMPRESSION - SW_REGISTER_BOOL_CONSTANT("SWOOLE_HAVE_COMPRESSION", 1); -#endif -#ifdef SW_HAVE_ZLIB - SW_REGISTER_BOOL_CONSTANT("SWOOLE_HAVE_ZLIB", 1); -#endif -#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", SW_MODE_BASE); - SW_REGISTER_LONG_CONSTANT("SWOOLE_PROCESS", SW_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 - */ - SW_REGISTER_LONG_CONSTANT("SWOOLE_SOCK_TCP", SW_SOCK_TCP); - SW_REGISTER_LONG_CONSTANT("SWOOLE_SOCK_TCP6", SW_SOCK_TCP6); - SW_REGISTER_LONG_CONSTANT("SWOOLE_SOCK_UDP", SW_SOCK_UDP); - SW_REGISTER_LONG_CONSTANT("SWOOLE_SOCK_UDP6", SW_SOCK_UDP6); - SW_REGISTER_LONG_CONSTANT("SWOOLE_SOCK_UNIX_DGRAM", SW_SOCK_UNIX_DGRAM); - SW_REGISTER_LONG_CONSTANT("SWOOLE_SOCK_UNIX_STREAM", SW_SOCK_UNIX_STREAM); - - /** - * simple socket type alias - */ - SW_REGISTER_LONG_CONSTANT("SWOOLE_TCP", SW_SOCK_TCP); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TCP6", SW_SOCK_TCP6); - SW_REGISTER_LONG_CONSTANT("SWOOLE_UDP", SW_SOCK_UDP); - SW_REGISTER_LONG_CONSTANT("SWOOLE_UDP6", SW_SOCK_UDP6); - SW_REGISTER_LONG_CONSTANT("SWOOLE_UNIX_DGRAM", SW_SOCK_UNIX_DGRAM); - SW_REGISTER_LONG_CONSTANT("SWOOLE_UNIX_STREAM", SW_SOCK_UNIX_STREAM); - - /** - * simple api - */ - SW_REGISTER_BOOL_CONSTANT("SWOOLE_SOCK_SYNC", SW_SOCK_SYNC); - SW_REGISTER_BOOL_CONSTANT("SWOOLE_SOCK_ASYNC", SW_SOCK_ASYNC); - - SW_REGISTER_LONG_CONSTANT("SWOOLE_SYNC", SW_FLAG_SYNC); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ASYNC", SW_FLAG_ASYNC); - SW_REGISTER_LONG_CONSTANT("SWOOLE_KEEP", SW_FLAG_KEEP); - -#ifdef SW_USE_OPENSSL - SW_REGISTER_LONG_CONSTANT("SWOOLE_SSL", SW_SOCK_SSL); - - /** - * SSL methods - */ - SW_REGISTER_LONG_CONSTANT("SWOOLE_SSLv3_METHOD", SW_SSLv3_METHOD); - SW_REGISTER_LONG_CONSTANT("SWOOLE_SSLv3_SERVER_METHOD", SW_SSLv3_SERVER_METHOD); - SW_REGISTER_LONG_CONSTANT("SWOOLE_SSLv3_CLIENT_METHOD", SW_SSLv3_CLIENT_METHOD); - - SW_REGISTER_LONG_CONSTANT("SWOOLE_TLSv1_METHOD", SW_TLSv1_METHOD); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TLSv1_SERVER_METHOD", SW_TLSv1_SERVER_METHOD); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TLSv1_CLIENT_METHOD", SW_TLSv1_CLIENT_METHOD); - -#ifdef TLS1_1_VERSION - SW_REGISTER_LONG_CONSTANT("SWOOLE_TLSv1_1_METHOD", SW_TLSv1_1_METHOD); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TLSv1_1_SERVER_METHOD", SW_TLSv1_1_SERVER_METHOD); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TLSv1_1_CLIENT_METHOD", SW_TLSv1_1_CLIENT_METHOD); -#endif - -#ifdef TLS1_2_VERSION - SW_REGISTER_LONG_CONSTANT("SWOOLE_TLSv1_2_METHOD", SW_TLSv1_2_METHOD); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TLSv1_2_SERVER_METHOD", SW_TLSv1_2_SERVER_METHOD); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TLSv1_2_CLIENT_METHOD", SW_TLSv1_2_CLIENT_METHOD); -#endif - -#ifdef SW_SUPPORT_DTLS - SW_REGISTER_LONG_CONSTANT("SWOOLE_DTLS_SERVER_METHOD", SW_DTLS_SERVER_METHOD); - SW_REGISTER_LONG_CONSTANT("SWOOLE_DTLS_CLIENT_METHOD", SW_DTLS_CLIENT_METHOD); -#endif - - SW_REGISTER_LONG_CONSTANT("SWOOLE_SSLv23_METHOD", SW_SSLv23_METHOD); - SW_REGISTER_LONG_CONSTANT("SWOOLE_SSLv23_SERVER_METHOD", SW_SSLv23_SERVER_METHOD); - SW_REGISTER_LONG_CONSTANT("SWOOLE_SSLv23_CLIENT_METHOD", SW_SSLv23_CLIENT_METHOD); - /* SSLv23_method have been renamed to TLS_method */ - SW_REGISTER_LONG_CONSTANT("SWOOLE_TLS_METHOD", SW_SSLv23_METHOD); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TLS_SERVER_METHOD", SW_SSLv23_SERVER_METHOD); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TLS_CLIENT_METHOD", SW_SSLv23_CLIENT_METHOD); -#endif - - SW_REGISTER_LONG_CONSTANT("SWOOLE_EVENT_READ", SW_EVENT_READ); - SW_REGISTER_LONG_CONSTANT("SWOOLE_EVENT_WRITE", SW_EVENT_WRITE); - - /** - * Register ERROR types - */ - SW_REGISTER_LONG_CONSTANT("SWOOLE_STRERROR_SYSTEM", SW_STRERROR_SYSTEM); - SW_REGISTER_LONG_CONSTANT("SWOOLE_STRERROR_GAI", SW_STRERROR_GAI); - SW_REGISTER_LONG_CONSTANT("SWOOLE_STRERROR_DNS", SW_STRERROR_DNS); - SW_REGISTER_LONG_CONSTANT("SWOOLE_STRERROR_SWOOLE", SW_STRERROR_SWOOLE); - - /** - * Register ERROR constants - */ - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_MALLOC_FAIL", SW_ERROR_MALLOC_FAIL); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SYSTEM_CALL_FAIL", SW_ERROR_SYSTEM_CALL_FAIL); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_PHP_FATAL_ERROR", SW_ERROR_PHP_FATAL_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_NAME_TOO_LONG", SW_ERROR_NAME_TOO_LONG); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_INVALID_PARAMS", SW_ERROR_INVALID_PARAMS); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_QUEUE_FULL", SW_ERROR_QUEUE_FULL); - 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_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_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_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); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SESSION_CLOSING", SW_ERROR_SESSION_CLOSING); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SESSION_CLOSED", SW_ERROR_SESSION_CLOSED); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SESSION_NOT_EXIST", SW_ERROR_SESSION_NOT_EXIST); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SESSION_INVALID_ID", SW_ERROR_SESSION_INVALID_ID); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SESSION_DISCARD_TIMEOUT_DATA", SW_ERROR_SESSION_DISCARD_TIMEOUT_DATA); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SESSION_DISCARD_DATA", SW_ERROR_SESSION_DISCARD_DATA); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_OUTPUT_BUFFER_OVERFLOW", SW_ERROR_OUTPUT_BUFFER_OVERFLOW); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_OUTPUT_SEND_YIELD", SW_ERROR_OUTPUT_SEND_YIELD); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SSL_NOT_READY", SW_ERROR_SSL_NOT_READY); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SSL_CANNOT_USE_SENFILE", SW_ERROR_SSL_CANNOT_USE_SENFILE); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SSL_EMPTY_PEER_CERTIFICATE", SW_ERROR_SSL_EMPTY_PEER_CERTIFICATE); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SSL_VERIFY_FAILED", SW_ERROR_SSL_VERIFY_FAILED); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SSL_BAD_CLIENT", SW_ERROR_SSL_BAD_CLIENT); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SSL_BAD_PROTOCOL", SW_ERROR_SSL_BAD_PROTOCOL); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SSL_RESET", SW_ERROR_SSL_RESET); - 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_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); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP2_STREAM_ID_TOO_BIG", SW_ERROR_HTTP2_STREAM_ID_TOO_BIG); - 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_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); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CLIENT_NO_CONNECTION", SW_ERROR_CLIENT_NO_CONNECTION); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SOCKET_CLOSED", SW_ERROR_SOCKET_CLOSED); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SOCKS5_UNSUPPORT_VERSION", SW_ERROR_SOCKS5_UNSUPPORT_VERSION); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SOCKS5_UNSUPPORT_METHOD", SW_ERROR_SOCKS5_UNSUPPORT_METHOD); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SOCKS5_AUTH_FAILED", SW_ERROR_SOCKS5_AUTH_FAILED); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SOCKS5_SERVER_ERROR", SW_ERROR_SOCKS5_SERVER_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP_PROXY_HANDSHAKE_ERROR", SW_ERROR_HTTP_PROXY_HANDSHAKE_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_HTTP_INVALID_PROTOCOL", SW_ERROR_HTTP_INVALID_PROTOCOL); - 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_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); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_INVALID_LISTEN_PORT", SW_ERROR_SERVER_INVALID_LISTEN_PORT); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_TOO_MANY_LISTEN_PORT", SW_ERROR_SERVER_TOO_MANY_LISTEN_PORT); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_PIPE_BUFFER_FULL", SW_ERROR_SERVER_PIPE_BUFFER_FULL); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_NO_IDLE_WORKER", SW_ERROR_SERVER_NO_IDLE_WORKER); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_SERVER_ONLY_START_ONE", SW_ERROR_SERVER_ONLY_START_ONE); - 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_WORKER_EXIT_TIMEOUT", SW_ERROR_SERVER_WORKER_EXIT_TIMEOUT); - 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); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_MUTEX_DOUBLE_UNLOCK", SW_ERROR_CO_MUTEX_DOUBLE_UNLOCK); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_BLOCK_OBJECT_LOCKED", SW_ERROR_CO_BLOCK_OBJECT_LOCKED); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_BLOCK_OBJECT_WAITING", SW_ERROR_CO_BLOCK_OBJECT_WAITING); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_YIELD_FAILED", SW_ERROR_CO_YIELD_FAILED); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_GETCONTEXT_FAILED", SW_ERROR_CO_GETCONTEXT_FAILED); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_SWAPCONTEXT_FAILED", SW_ERROR_CO_SWAPCONTEXT_FAILED); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_MAKECONTEXT_FAILED", SW_ERROR_CO_MAKECONTEXT_FAILED); - SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_IOCPINIT_FAILED", SW_ERROR_CO_IOCPINIT_FAILED); - 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); - - /** - * trace log - */ - SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_SERVER", SW_TRACE_SERVER); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CLIENT", SW_TRACE_CLIENT); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_BUFFER", SW_TRACE_BUFFER); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CONN", SW_TRACE_CONN); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_EVENT", SW_TRACE_EVENT); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_WORKER", SW_TRACE_WORKER); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_REACTOR", SW_TRACE_REACTOR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_PHP", SW_TRACE_PHP); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_HTTP", SW_TRACE_HTTP); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_HTTP2", SW_TRACE_HTTP2); - 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_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); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_AIO", SW_TRACE_AIO); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_SSL", SW_TRACE_SSL); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_NORMAL", SW_TRACE_NORMAL); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CHANNEL", SW_TRACE_CHANNEL); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_TIMER", SW_TRACE_TIMER); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_SOCKET", SW_TRACE_SOCKET); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_COROUTINE", SW_TRACE_COROUTINE); - 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_ALL", SW_TRACE_ALL); - - /** - * log level - */ - SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_DEBUG", SW_LOG_DEBUG); - SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_TRACE", SW_LOG_TRACE); - SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_INFO", SW_LOG_INFO); - SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_NOTICE", SW_LOG_NOTICE); - SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_WARNING", SW_LOG_WARNING); - SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_ERROR", SW_LOG_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_LOG_NONE", SW_LOG_NONE); - - SW_REGISTER_LONG_CONSTANT("SWOOLE_IPC_NONE", SW_IPC_NONE); - SW_REGISTER_LONG_CONSTANT("SWOOLE_IPC_UNIXSOCK", SW_IPC_UNIXSOCK); - SW_REGISTER_LONG_CONSTANT("SWOOLE_IPC_SOCKET", SW_IPC_SOCKET); - - 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"); - } - - swoole_init(); - if (!SWOOLE_G(enable_coroutine)) - { - SwooleG.enable_coroutine = 0; - } - 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", NULL, NULL, zend_ce_exception, zend_get_std_object_handlers()); - - SW_INIT_CLASS_ENTRY_EX2(swoole_error, "Swoole\\Error", "swoole_error", NULL, NULL, zend_ce_error, zend_get_std_object_handlers()); - - /** **/ - php_swoole_event_minit(module_number); - // base - php_swoole_atomic_minit(module_number); - php_swoole_lock_minit(module_number); - php_swoole_process_minit(module_number); - php_swoole_process_pool_minit(module_number); - php_swoole_table_minit(module_number); - php_swoole_timer_minit(module_number); - // coroutine - php_swoole_async_coro_minit(module_number); - php_swoole_coroutine_minit(module_number); - php_swoole_coroutine_system_minit(module_number); - php_swoole_coroutine_scheduler_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_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_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); - - SwooleG.fatal_error = fatal_error; - SwooleG.socket_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) - PCRE_G(jit) = 0; -#endif - - return SUCCESS; -} -/* }}} */ - -/* {{{ PHP_MSHUTDOWN_FUNCTION - */ -PHP_MSHUTDOWN_FUNCTION(swoole) -{ - swoole_clean(); - - return SUCCESS; -} -/* }}} */ - - -/* {{{ PHP_MINFO_FUNCTION - */ -PHP_MINFO_FUNCTION(swoole) -{ - char buf[64]; - php_info_print_table_start(); - php_info_print_table_header(2, "Swoole", "enabled"); - php_info_print_table_row(2, "Author", "Swoole Team "); - 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"); -#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 -#ifdef HAVE_EVENTFD - php_info_print_table_row(2, "eventfd", "enabled"); -#endif -#ifdef HAVE_KQUEUE - php_info_print_table_row(2, "kqueue", "enabled"); -#endif -#ifdef HAVE_SIGNALFD - php_info_print_table_row(2, "signalfd", "enabled"); -#endif -#ifdef SW_USE_ACCEPT4 - php_info_print_table_row(2, "accept4", "enabled"); -#endif -#ifdef HAVE_CPU_AFFINITY - php_info_print_table_row(2, "cpu_affinity", "enabled"); -#endif -#ifdef HAVE_SPINLOCK - php_info_print_table_row(2, "spinlock", "enabled"); -#endif -#ifdef HAVE_RWLOCK - php_info_print_table_row(2, "rwlock", "enabled"); -#endif -#ifdef SW_SOCKETS - php_info_print_table_row(2, "sockets", "enabled"); -#endif -#ifdef SW_USE_OPENSSL -#ifdef OPENSSL_VERSION_TEXT - php_info_print_table_row(2, "openssl", OPENSSL_VERSION_TEXT); -#else - php_info_print_table_row(2, "openssl", "enabled"); -#endif -#endif -#ifdef SW_USE_HTTP2 - php_info_print_table_row(2, "http2", "enabled"); -#endif -#ifdef HAVE_PCRE - php_info_print_table_row(2, "pcre", "enabled"); -#endif -#ifdef SW_HAVE_ZLIB -#ifdef ZLIB_VERSION - php_info_print_table_row(2, "zlib", ZLIB_VERSION); -#else - php_info_print_table_row(2, "zlib", "enabled"); -#endif -#endif -#ifdef SW_HAVE_BROTLI - snprintf(buf, sizeof(buf), "E%u/D%u", BrotliEncoderVersion(), BrotliDecoderVersion()); - php_info_print_table_row(2, "brotli", buf); -#endif -#ifdef HAVE_MUTEX_TIMEDLOCK - php_info_print_table_row(2, "mutex_timedlock", "enabled"); -#endif -#ifdef HAVE_PTHREAD_BARRIER - php_info_print_table_row(2, "pthread_barrier", "enabled"); -#endif -#ifdef HAVE_FUTEX - php_info_print_table_row(2, "futex", "enabled"); -#endif -#ifdef SW_USE_MYSQLND - php_info_print_table_row(2, "mysqlnd", "enabled"); -#endif -#ifdef SW_USE_JEMALLOC - php_info_print_table_row(2, "jemalloc", "enabled"); -#endif -#ifdef SW_USE_TCMALLOC - php_info_print_table_row(2, "tcmalloc", "enabled"); -#endif -#ifdef SW_USE_HUGEPAGE - php_info_print_table_row(2, "hugepage", "enabled"); -#endif - php_info_print_table_row(2, "async_redis", "enabled"); -#ifdef SW_USE_POSTGRESQL - php_info_print_table_row(2, "coroutine_postgresql", "enabled"); -#endif - php_info_print_table_end(); - - DISPLAY_INI_ENTRIES(); -} -/* }}} */ - -PHP_RINIT_FUNCTION(swoole) -{ - SWOOLE_G(req_status) = PHP_SWOOLE_RINIT_BEGIN; - - SwooleG.running = 1; - - php_swoole_register_shutdown_function("swoole_internal_call_user_shutdown_begin"); - - if ( - SWOOLE_G(enable_library) && SWOOLE_G(cli) -#ifdef ZEND_COMPILE_PRELOAD - /* avoid execution of the code during RINIT of preloader */ - && !(CG(compiler_options) & ZEND_COMPILE_PRELOAD) -#endif - ) - { - php_swoole_load_library(); - } - -#ifdef ZEND_SIGNALS - /* Disable warning even in ZEND_DEBUG because we may register our own signal handlers */ - SIGG(check) = 0; -#endif - - SWOOLE_G(req_status) = PHP_SWOOLE_RINIT_END; - - return SUCCESS; -} - -PHP_RSHUTDOWN_FUNCTION(swoole) -{ - SWOOLE_G(req_status) = PHP_SWOOLE_RSHUTDOWN_BEGIN; - - rshutdown_callbacks.execute(); - - swoole_event_free(); - - php_swoole_server_rshutdown(); - php_swoole_async_coro_rshutdown(); - php_swoole_redis_server_rshutdown(); - php_swoole_coroutine_rshutdown(); - php_swoole_runtime_rshutdown(); - php_swoole_process_clean(); - - SwooleG.running = 0; - SWOOLE_G(req_status) = PHP_SWOOLE_RSHUTDOWN_END; - -#ifdef PHP_STREAM_FLAG_NO_CLOSE - auto php_swoole_set_stdio_no_close = [](const char *name, size_t name_len) - { - zval *zstream; - php_stream *stream; - - zstream = zend_get_constant_str(name, name_len); - if (!zstream) - { - return; - } - stream = (php_stream*) zend_fetch_resource2_ex((zstream), "stream", php_file_le_stream(), php_file_le_pstream()); - if (!stream) - { - return; - } - stream->flags |= PHP_STREAM_FLAG_NO_CLOSE; - }; - /* do not close the stdout and stderr */ - php_swoole_set_stdio_no_close(ZEND_STRL("STDOUT")); - php_swoole_set_stdio_no_close(ZEND_STRL("STDERR")); -#endif - - return SUCCESS; -} - -PHP_FUNCTION(swoole_version) -{ - RETURN_STRING(SWOOLE_VERSION); -} - -static uint32_t hashkit_one_at_a_time(const char *key, size_t key_length) -{ - const char *ptr = key; - uint32_t value = 0; - - while (key_length--) - { - uint32_t val = (uint32_t) *ptr++; - value += val; - value += (value << 10); - value ^= (value >> 6); - } - value += (value << 3); - value ^= (value >> 11); - value += (value << 15); - - return value; -} - -static PHP_FUNCTION(swoole_hashcode) -{ - char *data; - size_t l_data; - zend_long type = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STRING(data, l_data) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(type) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - switch(type) - { - case 1: - RETURN_LONG(hashkit_one_at_a_time(data, l_data)); - break; /* ide */ - default: - RETURN_LONG(zend_hash_func(data, l_data)); - } -} - -PHP_FUNCTION(swoole_last_error) -{ - RETURN_LONG(SwooleG.error); -} - -PHP_FUNCTION(swoole_cpu_num) -{ - RETURN_LONG(SW_CPU_NUM); -} - -PHP_FUNCTION(swoole_strerror) -{ - zend_long swoole_errno; - zend_long error_type = SW_STRERROR_SYSTEM; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_LONG(swoole_errno) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(error_type) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (error_type == SW_STRERROR_GAI) - { - RETURN_STRING(gai_strerror(swoole_errno)); - } - else if (error_type == SW_STRERROR_DNS) - { - RETURN_STRING(hstrerror(swoole_errno)); - } - else if (error_type == SW_STRERROR_SWOOLE || (swoole_errno > SW_ERROR_BEGIN && swoole_errno < SW_ERROR_END)) - { - RETURN_STRING(swoole_strerror(swoole_errno)); - } - else - { - RETURN_STRING(strerror(swoole_errno)); - } -} - -PHP_FUNCTION(swoole_mime_type_add) -{ - zend_string *suffix; - zend_string *mime_type; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STR(suffix) - Z_PARAM_STR(mime_type) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - RETURN_BOOL(swoole::mime_type::add(ZSTR_VAL(suffix), ZSTR_VAL(mime_type))); -} - -PHP_FUNCTION(swoole_mime_type_set) -{ - zend_string *suffix; - zend_string *mime_type; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STR(suffix) - Z_PARAM_STR(mime_type) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - swoole::mime_type::set(ZSTR_VAL(suffix), ZSTR_VAL(mime_type)); -} - -PHP_FUNCTION(swoole_mime_type_delete) -{ - zend_string *suffix; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STR(suffix) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - RETURN_BOOL(swoole::mime_type::del(ZSTR_VAL(suffix))); -} - -PHP_FUNCTION(swoole_mime_type_get) -{ - zend_string *filename; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STR(filename) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - RETURN_STRING(swoole::mime_type::get(ZSTR_VAL(filename)).c_str()); -} - -PHP_FUNCTION(swoole_mime_type_exists) -{ - zend_string *filename; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STR(filename) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - RETURN_BOOL(swoole::mime_type::exists(ZSTR_VAL(filename))); -} - -PHP_FUNCTION(swoole_mime_type_list) -{ - array_init(return_value); - for (auto &i : swoole::mime_type::list()) { - add_next_index_string(return_value, i.second.c_str()); - } -} - -PHP_FUNCTION(swoole_errno) -{ - RETURN_LONG(errno); -} - -PHP_FUNCTION(swoole_set_process_name) -{ - zend_function *cli_set_process_title = (zend_function *) zend_hash_str_find_ptr(EG(function_table), - ZEND_STRL("cli_set_process_title")); - if (!cli_set_process_title) - { - php_swoole_fatal_error(E_WARNING, "swoole_set_process_name only support in CLI mode"); - RETURN_FALSE; - } - cli_set_process_title->internal_function.handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); -} - -PHP_FUNCTION(swoole_get_local_ip) -{ - struct sockaddr_in *s4; - struct ifaddrs *ipaddrs, *ifa; - void *in_addr; - char ip[64]; - - if (getifaddrs(&ipaddrs) != 0) - { - php_swoole_sys_error(E_WARNING, "getifaddrs() failed"); - RETURN_FALSE; - } - array_init(return_value); - for (ifa = ipaddrs; ifa != NULL; ifa = ifa->ifa_next) - { - if (ifa->ifa_addr == NULL || !(ifa->ifa_flags & IFF_UP)) - { - continue; - } - - switch (ifa->ifa_addr->sa_family) - { - case AF_INET: - s4 = (struct sockaddr_in *)ifa->ifa_addr; - in_addr = &s4->sin_addr; - break; - case AF_INET6: - //struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)ifa->ifa_addr; - //in_addr = &s6->sin6_addr; - continue; - default: - continue; - } - if (!inet_ntop(ifa->ifa_addr->sa_family, in_addr, ip, sizeof(ip))) - { - php_error_docref(NULL, E_WARNING, "%s: inet_ntop failed", ifa->ifa_name); - } - else - { - //if (ifa->ifa_addr->sa_family == AF_INET && ntohl(((struct in_addr *) in_addr)->s_addr) == INADDR_LOOPBACK) - if (strcmp(ip, "127.0.0.1") == 0) - { - continue; - } - add_assoc_string(return_value, ifa->ifa_name, ip); - } - } - freeifaddrs(ipaddrs); -} - -PHP_FUNCTION(swoole_get_local_mac) -{ -#ifdef SIOCGIFHWADDR - struct ifconf ifc; - struct ifreq buf[16]; - char mac[32] = {}; - - int sock; - int i = 0,num = 0; - - if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) - { - php_swoole_sys_error(E_WARNING, "new socket failed"); - RETURN_FALSE; - } - array_init(return_value); - - ifc.ifc_len = sizeof (buf); - ifc.ifc_buf = (caddr_t) buf; - if (!ioctl(sock, SIOCGIFCONF, (char *) &ifc)) - { - num = ifc.ifc_len / sizeof (struct ifreq); - while (i < num) - { - if (!(ioctl(sock, SIOCGIFHWADDR, (char *) &buf[i]))) - { - sprintf(mac, "%02X:%02X:%02X:%02X:%02X:%02X", - (unsigned char) buf[i].ifr_hwaddr.sa_data[0], - (unsigned char) buf[i].ifr_hwaddr.sa_data[1], - (unsigned char) buf[i].ifr_hwaddr.sa_data[2], - (unsigned char) buf[i].ifr_hwaddr.sa_data[3], - (unsigned char) buf[i].ifr_hwaddr.sa_data[4], - (unsigned char) buf[i].ifr_hwaddr.sa_data[5]); - add_assoc_string(return_value, buf[i].ifr_name, mac); - } - i++; - } - } - close(sock); -#else - php_error_docref(NULL, E_WARNING, "swoole_get_local_mac is not supported"); - RETURN_FALSE; -#endif -} - -PHP_FUNCTION(swoole_internal_call_user_shutdown_begin) -{ - if (SWOOLE_G(req_status) == PHP_SWOOLE_RINIT_END) - { - SWOOLE_G(req_status) = PHP_SWOOLE_CALL_USER_SHUTDOWNFUNC_BEGIN; - RETURN_TRUE; - } - else - { - php_error_docref(NULL, E_WARNING, "can not call this function in user level"); - RETURN_FALSE; - } -} diff --git a/swoole_async_coro.cc b/swoole_async_coro.cc deleted file mode 100644 index f44c9232c66..00000000000 --- a/swoole_async_coro.cc +++ /dev/null @@ -1,219 +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 "php_streams.h" -#include "php_network.h" - -#include "ext/standard/file.h" -#include "ext/standard/basic_functions.h" - -#include -#include -#include - -using swoole::PHPCoroutine; -using swoole::Coroutine; -using swoole::coroutine::Socket; -using std::string; -using std::vector; - -typedef struct -{ - char address[16]; - time_t update_time; -} dns_cache; - -typedef struct -{ - zval *callback; - pid_t pid; - int fd; - swString *buffer; -} process_stream; - -static std::unordered_map request_cache_map; - -void php_swoole_async_coro_minit(int module_number) -{ - -} - -void php_swoole_async_coro_rshutdown() -{ - for(auto i = request_cache_map.begin(); i != request_cache_map.end(); i++) - { - efree(i->second); - } -} - -PHP_FUNCTION(swoole_async_set) -{ - if (SwooleTG.reactor) - { - php_swoole_fatal_error(E_ERROR, "eventLoop has already been created. unable to change settings"); - RETURN_FALSE; - } - - zval *zset = NULL; - HashTable *vht; - zval *ztmp; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY(zset) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - vht = Z_ARRVAL_P(zset); - if (php_swoole_array_get_value(vht, "enable_signalfd", ztmp)) - { - SwooleG.enable_signalfd = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "dns_cache_refresh_time", ztmp)) - { - SwooleG.dns_cache_refresh_time = zval_get_double(ztmp); - } - if (php_swoole_array_get_value(vht, "socket_buffer_size", ztmp)) - { - SwooleG.socket_buffer_size = zval_get_long(ztmp); - if (SwooleG.socket_buffer_size <= 0 || SwooleG.socket_buffer_size > INT_MAX) - { - SwooleG.socket_buffer_size = INT_MAX; - } - } - if (php_swoole_array_get_value(vht, "socket_send_timeout", ztmp)) - { - SwooleG.socket_send_timeout = zval_get_double(ztmp); - if (SwooleG.socket_send_timeout <= 0 || SwooleG.socket_send_timeout > INT_MAX) - { - SwooleG.socket_send_timeout = INT_MAX; - } - } - if (php_swoole_array_get_value(vht, "log_level", ztmp)) - { - zend_long level = zval_get_long(ztmp); - SwooleG.log_level = (uint32_t) (level < 0 ? UINT32_MAX : level); - } - if (php_swoole_array_get_value(vht, "thread_num", ztmp) || php_swoole_array_get_value(vht, "min_thread_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, "max_thread_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, "display_errors", ztmp)) - { - SWOOLE_G(display_errors) = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "socket_dontwait", ztmp)) - { - SwooleG.socket_dontwait = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "dns_lookup_random", ztmp)) - { - SwooleG.dns_lookup_random = zval_is_true(ztmp); - } - 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(); - } - if (php_swoole_array_get_value(vht, "use_async_resolver", ztmp)) - { - SwooleG.use_async_resolver = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "enable_coroutine", ztmp)) - { - SwooleG.enable_coroutine = zval_is_true(ztmp); - } -} - -PHP_FUNCTION(swoole_async_dns_lookup_coro) -{ - Coroutine::get_current_safe(); - - zval *domain; - double timeout = Socket::default_dns_timeout; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|d", &domain, &timeout) == FAILURE) - { - RETURN_FALSE; - } - - if (Z_TYPE_P(domain) != IS_STRING) - { - php_swoole_fatal_error(E_WARNING, "invalid domain name"); - RETURN_FALSE; - } - - if (Z_STRLEN_P(domain) == 0) - { - php_swoole_fatal_error(E_WARNING, "domain name empty"); - RETURN_FALSE; - } - - //find cache - std::string key(Z_STRVAL_P(domain), Z_STRLEN_P(domain)); - dns_cache *cache; - - if (request_cache_map.find(key) != request_cache_map.end()) - { - cache = request_cache_map[key]; - if (cache->update_time > swTimer_get_absolute_msec()) - { - RETURN_STRING(cache->address); - } - } - - php_swoole_check_reactor(); - - vector result = swoole::coroutine::dns_lookup(Z_STRVAL_P(domain), timeout); - if (result.empty()) - { - SwooleG.error = SW_ERROR_DNSLOOKUP_RESOLVE_FAILED; - RETURN_FALSE; - } - - if (SwooleG.dns_lookup_random) - { - RETVAL_STRING(result[rand() % result.size()].c_str()); - } - else - { - RETVAL_STRING(result[0].c_str()); - } - - auto cache_iterator = request_cache_map.find(key); - if (cache_iterator == request_cache_map.end()) - { - cache = (dns_cache *) emalloc(sizeof(dns_cache)); - bzero(cache, sizeof(dns_cache)); - request_cache_map[key] = cache; - } - else - { - cache = cache_iterator->second; - } - memcpy(cache->address, Z_STRVAL_P(return_value), Z_STRLEN_P(return_value)); - cache->address[Z_STRLEN_P(return_value)] = '\0'; - cache->update_time = swTimer_get_absolute_msec() + (int64_t) (SwooleG.dns_cache_refresh_time * 1000); -} diff --git a/swoole_atomic.cc b/swoole_atomic.cc deleted file mode 100644 index 17191de58ef..00000000000 --- a/swoole_atomic.cc +++ /dev/null @@ -1,432 +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.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, NULL, 0); - } - else - { - ret = syscall(SYS_futex, atomic, FUTEX_WAIT, 0, NULL, NULL, 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, NULL, NULL, 0); - } - else - { - return SW_OK; - } -} -#endif - -zend_class_entry *swoole_atomic_ce; -static zend_object_handlers swoole_atomic_handlers; - -zend_class_entry *swoole_atomic_long_ce; -static zend_object_handlers swoole_atomic_long_handlers; - -typedef struct -{ - sw_atomic_t *ptr; - zend_object std; -} atomic_t; - -static sw_inline atomic_t* php_swoole_atomic_fetch_object(zend_object *obj) -{ - return (atomic_t *) ((char *) obj - swoole_atomic_handlers.offset); -} - -static sw_atomic_t * php_swoole_atomic_get_ptr(zval *zobject) -{ - return php_swoole_atomic_fetch_object(Z_OBJ_P(zobject))->ptr; -} - -void php_swoole_atomic_set_ptr(zval *zobject, sw_atomic_t *ptr) -{ - php_swoole_atomic_fetch_object(Z_OBJ_P(zobject))->ptr = ptr; -} - -static void php_swoole_atomic_free_object(zend_object *object) -{ - zend_object_std_dtor(object); -} - -static zend_object *php_swoole_atomic_create_object(zend_class_entry *ce) -{ - atomic_t *atomic = (atomic_t *) ecalloc(1, sizeof(atomic_t) + zend_object_properties_size(ce)); - zend_object_std_init(&atomic->std, ce); - object_properties_init(&atomic->std, ce); - atomic->std.handlers = &swoole_atomic_handlers; - - atomic->ptr = (sw_atomic_t *) SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(sw_atomic_t)); - if (atomic == NULL) - { - zend_throw_exception(swoole_exception_ce, "global memory allocation failure", SW_ERROR_MALLOC_FAIL); - } - - return &atomic->std; -} - -typedef struct -{ - sw_atomic_long_t *ptr; - zend_object std; -} atomic_long_t; - -static sw_inline atomic_long_t* php_swoole_atomic_long_fetch_object(zend_object *obj) -{ - return (atomic_long_t *) ((char *) obj - swoole_atomic_long_handlers.offset); -} - -static sw_atomic_long_t * php_swoole_atomic_long_get_ptr(zval *zobject) -{ - return php_swoole_atomic_long_fetch_object(Z_OBJ_P(zobject))->ptr; -} - -void php_swoole_atomic_long_set_ptr(zval *zobject, sw_atomic_long_t *ptr) -{ - php_swoole_atomic_long_fetch_object(Z_OBJ_P(zobject))->ptr = ptr; -} - -static void php_swoole_atomic_long_free_object(zend_object *object) -{ - zend_object_std_dtor(object); -} - -static zend_object *php_swoole_atomic_long_create_object(zend_class_entry *ce) -{ - atomic_long_t *atomic_long = (atomic_long_t *) ecalloc(1, sizeof(atomic_long_t) + zend_object_properties_size(ce)); - zend_object_std_init(&atomic_long->std, ce); - object_properties_init(&atomic_long->std, ce); - atomic_long->std.handlers = &swoole_atomic_long_handlers; - - atomic_long->ptr = (sw_atomic_long_t *) SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(sw_atomic_long_t)); - if (atomic_long == NULL) - { - zend_throw_exception(swoole_exception_ce, "global memory allocation failure", SW_ERROR_MALLOC_FAIL); - } - - return &atomic_long->std; -} - -static PHP_METHOD(swoole_atomic, __construct); -static PHP_METHOD(swoole_atomic, add); -static PHP_METHOD(swoole_atomic, sub); -static PHP_METHOD(swoole_atomic, get); -static PHP_METHOD(swoole_atomic, set); -static PHP_METHOD(swoole_atomic, cmpset); -static PHP_METHOD(swoole_atomic, wait); -static PHP_METHOD(swoole_atomic, wakeup); - -static PHP_METHOD(swoole_atomic_long, __construct); -static PHP_METHOD(swoole_atomic_long, add); -static PHP_METHOD(swoole_atomic_long, sub); -static PHP_METHOD(swoole_atomic_long, get); -static PHP_METHOD(swoole_atomic_long, set); -static PHP_METHOD(swoole_atomic_long, cmpset); - -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_waitup, 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_waitup, ZEND_ACC_PUBLIC) - PHP_ME(swoole_atomic, cmpset, arginfo_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_FE_END -}; - -void php_swoole_atomic_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY(swoole_atomic, "Swoole\\Atomic", "swoole_atomic", NULL, swoole_atomic_methods); - SW_SET_CLASS_SERIALIZABLE(swoole_atomic, zend_class_serialize_deny, zend_class_unserialize_deny); - 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, atomic_t, std); - - SW_INIT_CLASS_ENTRY(swoole_atomic_long, "Swoole\\Atomic\\Long", "swoole_atomic_long", NULL, swoole_atomic_long_methods); - SW_SET_CLASS_SERIALIZABLE(swoole_atomic_long, zend_class_serialize_deny, zend_class_unserialize_deny); - 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, php_swoole_atomic_long_create_object, php_swoole_atomic_long_free_object, atomic_long_t, std); -} - -PHP_METHOD(swoole_atomic, __construct) -{ - sw_atomic_t *atomic = php_swoole_atomic_get_ptr(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); - - *atomic = (sw_atomic_t) value; -} - -PHP_METHOD(swoole_atomic, add) -{ - sw_atomic_t *atomic = php_swoole_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_atomic, sub) -{ - sw_atomic_t *atomic = php_swoole_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_atomic, get) -{ - sw_atomic_t *atomic = php_swoole_atomic_get_ptr(ZEND_THIS); - RETURN_LONG(*atomic); -} - -PHP_METHOD(swoole_atomic, set) -{ - sw_atomic_t *atomic = php_swoole_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_atomic, cmpset) -{ - sw_atomic_t *atomic = php_swoole_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_atomic, wait) -{ - sw_atomic_t *atomic = php_swoole_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); - -#ifdef HAVE_FUTEX - SW_CHECK_RETURN(swoole_futex_wait(atomic, timeout)); -#else - timeout = timeout <= 0 ? INT_MAX : timeout; - int32_t i = (int32_t) sw_atomic_add_fetch(atomic, 1); - while (timeout > 0) - { - if ((int32_t) *atomic < i) - { - RETURN_TRUE; - } - else - { - usleep(1000); - timeout -= 0.001; - } - } - RETURN_FALSE; -#endif -} - -PHP_METHOD(swoole_atomic, wakeup) -{ - sw_atomic_t *atomic = php_swoole_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); - -#ifdef HAVE_FUTEX - SW_CHECK_RETURN(swoole_futex_wakeup(atomic, (int ) n)); -#else - sw_atomic_fetch_sub(atomic, n); - RETURN_TRUE; -#endif -} - -PHP_METHOD(swoole_atomic_long, __construct) -{ - sw_atomic_long_t *atomic_long = php_swoole_atomic_long_get_ptr(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); - - *atomic_long = (sw_atomic_long_t) value; - RETURN_TRUE; -} - -PHP_METHOD(swoole_atomic_long, add) -{ - sw_atomic_long_t *atomic_long = php_swoole_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_atomic_long, sub) -{ - sw_atomic_long_t *atomic_long = php_swoole_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_atomic_long, get) -{ - sw_atomic_long_t *atomic_long = php_swoole_atomic_long_get_ptr(ZEND_THIS); - RETURN_LONG(*atomic_long); -} - -PHP_METHOD(swoole_atomic_long, set) -{ - sw_atomic_long_t *atomic_long = php_swoole_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_atomic_long, cmpset) -{ - sw_atomic_long_t *atomic_long = php_swoole_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)); -} diff --git a/swoole_channel_coro.cc b/swoole_channel_coro.cc deleted file mode 100644 index b568768a511..00000000000 --- a/swoole_channel_coro.cc +++ /dev/null @@ -1,276 +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: Xinyu Zhu | - | Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#include "php_swoole_cxx.h" - -#include "coroutine_channel.h" - -using swoole::coroutine::Channel; - -static zend_class_entry *swoole_channel_coro_ce; -static zend_object_handlers swoole_channel_coro_handlers; - -typedef struct -{ - Channel *chan; - zend_object std; -} channel_coro; - -static PHP_METHOD(swoole_channel_coro, __construct); -static PHP_METHOD(swoole_channel_coro, push); -static PHP_METHOD(swoole_channel_coro, pop); -static PHP_METHOD(swoole_channel_coro, close); -static PHP_METHOD(swoole_channel_coro, stats); -static PHP_METHOD(swoole_channel_coro, length); -static PHP_METHOD(swoole_channel_coro, isEmpty); -static PHP_METHOD(swoole_channel_coro, isFull); - -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_FE_END -}; - -enum swChannelErrorCode -{ - SW_CHANNEL_OK = 0, - SW_CHANNEL_TIMEOUT = -1, - SW_CHANNEL_CLOSED = -2, -}; - -static sw_inline channel_coro* php_swoole_channel_coro_fetch_object(zend_object *obj) -{ - return (channel_coro *) ((char *) obj - swoole_channel_coro_handlers.offset); -} - -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"); - } - return chan; -} - -static void php_swoole_channel_coro_dtor_object(zend_object *object) -{ - zend_objects_destroy_object(object); - - channel_coro *chan_coro = php_swoole_channel_coro_fetch_object(object); - Channel *chan = chan_coro->chan; - if (chan) - { - chan->close(); - zval *data; - while ((data = (zval *) chan->pop_data())) - { - sw_zval_free(data); - } - delete chan; - chan_coro->chan = nullptr; - } -} - -static void php_swoole_channel_coro_free_object(zend_object *object) -{ - channel_coro *chan_coro = php_swoole_channel_coro_fetch_object(object); - Channel *chan = chan_coro->chan; - if (chan) - { - delete chan; - } - zend_object_std_dtor(object); -} - -static zend_object *php_swoole_channel_coro_create_object(zend_class_entry *ce) -{ - channel_coro *chan_t = (channel_coro *) ecalloc(1, sizeof(channel_coro) + zend_object_properties_size(ce)); - zend_object_std_init(&chan_t->std, ce); - object_properties_init(&chan_t->std, ce); - chan_t->std.handlers = &swoole_channel_coro_handlers; - return &chan_t->std; -} - -void php_swoole_channel_coro_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY(swoole_channel_coro, "Swoole\\Coroutine\\Channel", NULL, "Co\\Channel", swoole_channel_coro_methods); - SW_SET_CLASS_SERIALIZABLE(swoole_channel_coro, zend_class_serialize_deny, zend_class_unserialize_deny); - 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, php_swoole_channel_coro_create_object, php_swoole_channel_coro_free_object, channel_coro, std); - SW_SET_CLASS_DTOR(swoole_channel_coro, php_swoole_channel_coro_dtor_object); - if (SWOOLE_G(use_shortname)) - { - SW_CLASS_ALIAS("Chan", swoole_channel_coro); - } - - 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); -} - -static PHP_METHOD(swoole_channel_coro, __construct) -{ - zend_long capacity = 1; - - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(capacity) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (capacity <= 0) - { - capacity = 1; - } - - channel_coro *chan_t = php_swoole_channel_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); - chan_t->chan = new Channel(capacity); - zend_update_property_long(swoole_channel_coro_ce, ZEND_THIS, ZEND_STRL("capacity"), capacity); -} - -static PHP_METHOD(swoole_channel_coro, push) -{ - Channel *chan = php_swoole_get_channel(ZEND_THIS); - if (chan->is_closed()) - { - zend_update_property_long(swoole_channel_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), SW_CHANNEL_CLOSED); - RETURN_FALSE; - } - else - { - zend_update_property_long(swoole_channel_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), SW_CHANNEL_OK); - } - - zval *zdata; - double timeout = -1; - - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 2) - Z_PARAM_ZVAL(zdata) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - Z_TRY_ADDREF_P(zdata); - zdata = sw_zval_dup(zdata); - if (chan->push(zdata, timeout)) - { - RETURN_TRUE; - } - else - { - zend_update_property_long(swoole_channel_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), chan->is_closed() ? SW_CHANNEL_CLOSED : SW_CHANNEL_TIMEOUT); - Z_TRY_DELREF_P(zdata); - efree(zdata); - RETURN_FALSE; - } -} - -static PHP_METHOD(swoole_channel_coro, pop) -{ - Channel *chan = php_swoole_get_channel(ZEND_THIS); - if (chan->is_closed()) - { - zend_update_property_long(swoole_channel_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), SW_CHANNEL_CLOSED); - RETURN_FALSE; - } - else - { - zend_update_property_long(swoole_channel_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), SW_CHANNEL_OK); - } - - double timeout = -1; - - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - zval *zdata = (zval *) chan->pop(timeout); - if (zdata) - { - RETVAL_ZVAL(zdata, 0, 0); - efree(zdata); - } - else - { - zend_update_property_long(swoole_channel_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), chan->is_closed() ? SW_CHANNEL_CLOSED : SW_CHANNEL_TIMEOUT); - RETURN_FALSE; - } -} - -static PHP_METHOD(swoole_channel_coro, close) -{ - Channel *chan = php_swoole_get_channel(ZEND_THIS); - RETURN_BOOL(chan->close()); -} - -static PHP_METHOD(swoole_channel_coro, length) -{ - Channel *chan = php_swoole_get_channel(ZEND_THIS); - RETURN_LONG(chan->length()); -} - -static PHP_METHOD(swoole_channel_coro, isEmpty) -{ - Channel *chan = php_swoole_get_channel(ZEND_THIS); - RETURN_BOOL(chan->is_empty()); -} - -static PHP_METHOD(swoole_channel_coro, isFull) -{ - Channel *chan = php_swoole_get_channel(ZEND_THIS); - RETURN_BOOL(chan->is_full()); -} - -static PHP_METHOD(swoole_channel_coro, stats) -{ - Channel *chan = php_swoole_get_channel(ZEND_THIS); - array_init(return_value); - add_assoc_long_ex(return_value, ZEND_STRL("consumer_num"), chan->consumer_num()); - 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/swoole_client.cc b/swoole_client.cc deleted file mode 100644 index 238ecf00633..00000000000 --- a/swoole_client.cc +++ /dev/null @@ -1,1926 +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 "client.h" -#include "socks5.h" -#include "mqtt.h" - -#include -#include -#include - -using namespace std; - -#include "ext/standard/basic_functions.h" - -typedef struct -{ - 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; -} client_callback; - -static unordered_map *> long_connections; - -zend_class_entry *swoole_client_ce; -static zend_object_handlers swoole_client_handlers; - -typedef struct -{ - swClient *cli; - zval *zsocket; - client_callback *cb; - zend_object std; -} client_t; - -static sw_inline client_t* php_swoole_client_fetch_object(zend_object *obj) -{ - return (client_t *) ((char *) obj - swoole_client_handlers.offset); -} - -static sw_inline swClient* 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, swClient *cli) -{ - php_swoole_client_fetch_object(Z_OBJ_P(zobject))->cli = cli; -} - -static sw_inline zval* php_swoole_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) -{ - php_swoole_client_fetch_object(Z_OBJ_P(zobject))->zsocket = zsocket; -} - -static sw_inline client_callback* 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, client_callback *cb) -{ - php_swoole_client_fetch_object(Z_OBJ_P(zobject))->cb = cb; -} - -static void php_swoole_client_free_object(zend_object *object) -{ - zend_object_std_dtor(object); -} - -static zend_object *php_swoole_client_create_object(zend_class_entry *ce) -{ - client_t *client = (client_t *) ecalloc(1, sizeof(client_t) + zend_object_properties_size(ce)); - zend_object_std_init(&client->std, ce); - object_properties_init(&client->std, ce); - client->std.handlers = &swoole_client_handlers; - return &client->std; -} - -static PHP_METHOD(swoole_client, __construct); -static PHP_METHOD(swoole_client, __destruct); -static PHP_METHOD(swoole_client, set); -static PHP_METHOD(swoole_client, connect); -static PHP_METHOD(swoole_client, recv); -static PHP_METHOD(swoole_client, send); -static PHP_METHOD(swoole_client, sendfile); -static PHP_METHOD(swoole_client, sendto); -#ifdef SW_USE_OPENSSL -static PHP_METHOD(swoole_client, enableSSL); -static PHP_METHOD(swoole_client, getPeerCert); -static PHP_METHOD(swoole_client, verifyPeerCert); -#endif -static PHP_METHOD(swoole_client, isConnected); -static PHP_METHOD(swoole_client, getsockname); -static PHP_METHOD(swoole_client, getpeername); -static PHP_METHOD(swoole_client, close); -static PHP_METHOD(swoole_client, shutdown); - -#ifdef SWOOLE_SOCKETS_SUPPORT -static PHP_METHOD(swoole_client, getSocket); -#endif - -#ifdef PHP_SWOOLE_CLIENT_USE_POLL -static int client_poll_add(zval *sock_array, int index, struct pollfd *fds, int maxevents, int event); -static int client_poll_wait(zval *sock_array, struct pollfd *fds, int maxevents, int n_event, int revent); -#else -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 swClient* client_get_ptr(zval *zobject) -{ - swClient *cli = php_swoole_client_get_cli(zobject); - if (cli && cli->socket && cli->active == 1) - { - return cli; - } - else - { - SwooleG.error = SW_ERROR_CLIENT_NO_CONNECTION; - zend_update_property_long(swoole_client_ce, zobject, ZEND_STRL("errCode"), SwooleG.error); - php_swoole_error(E_WARNING, "client is not connected to server"); - return NULL; - } -} - -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) -#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) -#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) -#ifdef SWOOLE_SOCKETS_SUPPORT - PHP_ME(swoole_client, getSocket, arginfo_swoole_client_void, ZEND_ACC_PUBLIC) -#endif - PHP_FE_END -}; - -void php_swoole_client_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY(swoole_client, "Swoole\\Client", "swoole_client", NULL, swoole_client_methods); - SW_SET_CLASS_SERIALIZABLE(swoole_client, zend_class_serialize_deny, zend_class_unserialize_deny); - 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, client_t, std); - - 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); - zend_declare_property_bool(swoole_client_ce, ZEND_STRL("reuse"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_client_ce, ZEND_STRL("reuseCount"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_client_ce, ZEND_STRL("type"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_client_ce, ZEND_STRL("id"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_client_ce, ZEND_STRL("setting"), ZEND_ACC_PUBLIC); - - zend_declare_class_constant_long(swoole_client_ce, ZEND_STRL("MSG_OOB"), MSG_OOB); - zend_declare_class_constant_long(swoole_client_ce, ZEND_STRL("MSG_PEEK"), MSG_PEEK); - zend_declare_class_constant_long(swoole_client_ce, ZEND_STRL("MSG_DONTWAIT"), MSG_DONTWAIT); - zend_declare_class_constant_long(swoole_client_ce, ZEND_STRL("MSG_WAITALL"), MSG_WAITALL); - - zend_declare_class_constant_long(swoole_client_ce, ZEND_STRL("SHUT_RDWR"), SHUT_RDWR); - zend_declare_class_constant_long(swoole_client_ce, ZEND_STRL("SHUT_RD"), SHUT_RD); - zend_declare_class_constant_long(swoole_client_ce, ZEND_STRL("SHUT_WR"), SHUT_WR); -} - -#ifdef SW_USE_OPENSSL -void php_swoole_client_check_ssl_setting(swClient *cli, zval *zset) -{ - HashTable *vht = Z_ARRVAL_P(zset); - zval *ztmp; - - if (php_swoole_array_get_value(vht, "ssl_method", ztmp)) - { - zend_long v = zval_get_long(ztmp); - cli->ssl_option.method = SW_MAX(0, SW_MIN(v, UINT8_MAX)); - } - if (php_swoole_array_get_value(vht, "ssl_protocols", ztmp)) - { - zend_long v = zval_get_long(ztmp); - cli->ssl_option.disable_protocols = - (SW_SSL_SSLv2 | SW_SSL_SSLv3 | SW_SSL_TLSv1 | SW_SSL_TLSv1_1 | SW_SSL_TLSv1_2) ^ v; - } - if (php_swoole_array_get_value(vht, "ssl_compress", ztmp)) - { - cli->ssl_option.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) - { - php_swoole_fatal_error(E_ERROR, "ssl cert file[%s] not found", str_v.val()); - return; - } - cli->ssl_option.cert_file = sw_strdup(str_v.val()); - } - if (php_swoole_array_get_value(vht, "ssl_key_file", ztmp)) - { - zend::string str_v(ztmp); - if (access(str_v.val(), R_OK) < 0) - { - php_swoole_fatal_error(E_ERROR, "ssl key file[%s] not found", str_v.val()); - return; - } - cli->ssl_option.key_file = sw_strdup(str_v.val()); - } - if (php_swoole_array_get_value(vht, "ssl_passphrase", ztmp)) - { - zend::string str_v(ztmp); - cli->ssl_option.passphrase = sw_strdup(str_v.val()); - } -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - if (php_swoole_array_get_value(vht, "ssl_host_name", ztmp)) - { - zend::string str_v(ztmp); - cli->ssl_option.tls_host_name = sw_strdup(str_v.val()); - } -#endif - if (php_swoole_array_get_value(vht, "ssl_verify_peer", ztmp)) - { - cli->ssl_option.verify_peer = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "ssl_allow_self_signed", ztmp)) - { - cli->ssl_option.allow_self_signed = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "ssl_cafile", ztmp)) - { - zend::string str_v(ztmp); - cli->ssl_option.cafile = sw_strdup(str_v.val()); - } - if (php_swoole_array_get_value(vht, "ssl_capath", ztmp)) - { - zend::string str_v(ztmp); - cli->ssl_option.capath = sw_strdup(str_v.val()); - } - if (php_swoole_array_get_value(vht, "ssl_verify_depth", ztmp)) - { - zend_long v = zval_get_long(ztmp); - cli->ssl_option.verify_depth = SW_MAX(0, SW_MIN(v, UINT8_MAX)); - } - if (cli->ssl_option.cert_file && !cli->ssl_option.key_file) - { - php_swoole_fatal_error(E_ERROR, "ssl require key file"); - return; - } -} -#endif - -static void php_swoole_client_socket_free_socks5_proxy(swClient *cli) -{ - if (cli->socks5_proxy) - { - efree((void* )cli->socks5_proxy->host); - if (cli->socks5_proxy->username) - { - efree((void* )cli->socks5_proxy->username); - } - if (cli->socks5_proxy->password) - { - efree((void* )cli->socks5_proxy->password); - } - efree(cli->socks5_proxy); - } -} - -static void php_swoole_client_socket_free_http_proxy(swClient *cli) -{ - if (cli->http_proxy) - { - efree((void* )cli->http_proxy->proxy_host); - if (cli->http_proxy->user) - { - efree((void* )cli->http_proxy->user); - } - if (cli->http_proxy->password) - { - efree((void* )cli->http_proxy->password); - } - efree(cli->http_proxy); - } -} - -void php_swoole_client_check_setting(swClient *cli, zval *zset) -{ - HashTable *vht; - zval *ztmp; - int value = 1; - - vht = Z_ARRVAL_P(zset); - - //buffer: eof check - if (php_swoole_array_get_value(vht, "open_eof_check", ztmp)) - { - cli->open_eof_check = zval_is_true(ztmp); - } - //buffer: split package with eof - if (php_swoole_array_get_value(vht, "open_eof_split", ztmp)) - { - cli->protocol.split_by_eof = zval_is_true(ztmp); - if (cli->protocol.split_by_eof) - { - cli->open_eof_check = 1; - } - } - //package eof - if (php_swoole_array_get_value(vht, "package_eof", ztmp)) - { - zend::string str_v(ztmp); - cli->protocol.package_eof_len = str_v.len(); - if (cli->protocol.package_eof_len == 0) - { - php_swoole_fatal_error(E_ERROR, "package_eof cannot be an empty string"); - return; - } - else if (cli->protocol.package_eof_len > SW_DATA_EOF_MAXLEN) - { - php_swoole_fatal_error(E_ERROR, "package_eof max length is %d", SW_DATA_EOF_MAXLEN); - return; - } - memcpy(cli->protocol.package_eof, str_v.val(), str_v.len()); - } - //open mqtt protocol - if (php_swoole_array_get_value(vht, "open_mqtt_protocol", ztmp)) - { - cli->open_length_check = zval_is_true(ztmp); - cli->protocol.package_length_size = SW_MQTT_MIN_LENGTH; - cli->protocol.package_length_offset = 0; - cli->protocol.package_body_offset = 0; - cli->protocol.get_package_length = swMqtt_get_package_length; - } - //open length check - if (php_swoole_array_get_value(vht, "open_length_check", ztmp)) - { - cli->open_length_check = zval_is_true(ztmp); - cli->protocol.get_package_length = swProtocol_get_package_length; - } - //package length size - if (php_swoole_array_get_value(vht, "package_length_type", ztmp)) - { - zend::string str_v(ztmp); - cli->protocol.package_length_type = str_v.val()[0]; - cli->protocol.package_length_size = swoole_type_size(cli->protocol.package_length_type); - - if (cli->protocol.package_length_size == 0) - { - php_swoole_fatal_error(E_ERROR, "Unknown package_length_type name '%c', see pack(). Link: http://php.net/pack", cli->protocol.package_length_type); - return; - } - } - //package length offset - if (php_swoole_array_get_value(vht, "package_length_offset", ztmp)) - { - zend_long v = zval_get_long(ztmp); - cli->protocol.package_length_offset = SW_MAX(0, SW_MIN(v, UINT16_MAX)); - } - //package body start - if (php_swoole_array_get_value(vht, "package_body_offset", ztmp)) - { - zend_long v = zval_get_long(ztmp); - cli->protocol.package_body_offset = SW_MAX(0, SW_MIN(v, UINT16_MAX)); - } - //length function - if (php_swoole_array_get_value(vht, "package_length_func", ztmp)) - { - while (1) - { - if (Z_TYPE_P(ztmp) == IS_STRING) - { - swProtocol_length_function func = (swProtocol_length_function) swoole_get_function(Z_STRVAL_P(ztmp), Z_STRLEN_P(ztmp)); - if (func != NULL) - { - 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, NULL, 0, &func_name, NULL, fci_cache, NULL)) - { - php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name); - return; - } - 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; - } - - cli->protocol.package_length_size = 0; - cli->protocol.package_length_type = '\0'; - cli->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); - cli->protocol.package_max_length = SW_MAX(0, SW_MIN(v, UINT32_MAX)); - } - else - { - cli->protocol.package_max_length = SW_INPUT_BUFFER_SIZE; - } - /** - * socket send/recv buffer size - */ - if (php_swoole_array_get_value(vht, "socket_buffer_size", ztmp)) - { - zend_long v = zval_get_long(ztmp); - value = SW_MAX(1, SW_MIN(v, INT_MAX)); - if (value <= 0) - { - value = INT_MAX; - } - swSocket_set_buffer_size(cli->socket, value); - cli->socket->buffer_size = value; - } - if (php_swoole_array_get_value(vht, "buffer_high_watermark", ztmp)) - { - zend_long v = zval_get_long(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); - value = SW_MAX(0, SW_MIN(v, UINT32_MAX)); - cli->buffer_low_watermark = value; - } - /** - * bind port - */ - if (php_swoole_array_get_value(vht, "bind_port", ztmp)) - { - zend_long v = zval_get_long(ztmp); - int bind_port = SW_MAX(0, SW_MIN(v, UINT16_MAX)); - /** - * bind address - */ - if (php_swoole_array_get_value(vht, "bind_address", ztmp)) - { - zend::string str_v(ztmp); - swSocket_bind(cli->socket, str_v.val(), &bind_port); - } - } - /** - * client: tcp_nodelay - */ - if (php_swoole_array_get_value(vht, "open_tcp_nodelay", ztmp)) - { - if (zval_is_true(ztmp)) - { - goto _open_tcp_nodelay; - } - } - else - { - _open_tcp_nodelay: - if (cli->type == SW_SOCK_TCP || cli->type == SW_SOCK_TCP6) - { - value = 1; - if (setsockopt(cli->socket->fd, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)) != 0) - { - swSysWarn("setsockopt(%d, TCP_NODELAY) failed", cli->socket->fd); - } - } - } - /** - * 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)) - { - php_swoole_client_socket_free_socks5_proxy(cli); - cli->socks5_proxy = (struct _swSocks5 *) ecalloc(1, sizeof(swSocks5)); - cli->socks5_proxy->host = estrdup(host.val()); - 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.val(); - cli->socks5_proxy->l_username = username.len(); - cli->socks5_proxy->password = password.val(); - cli->socks5_proxy->l_password = password.len(); - } - } - else - { - php_swoole_fatal_error(E_WARNING, "socks5_password should not be null"); - } - } - } - else - { - php_swoole_fatal_error(E_WARNING, "socks5_port should not be null"); - } - } - /** - * 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)) - { - php_swoole_client_socket_free_http_proxy(cli); - cli->http_proxy = (struct _http_proxy*) ecalloc(1, sizeof(struct _http_proxy)); - cli->http_proxy->proxy_host = estrdup(host.val()); - 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->user = estrdup(username.val()); - cli->http_proxy->l_user = username.len(); - cli->http_proxy->password = estrdup(password.val()); - cli->http_proxy->l_password = password.len(); - } - } - else - { - php_swoole_fatal_error(E_WARNING, "http_proxy_password should not be null"); - } - } - } - else - { - php_swoole_fatal_error(E_WARNING, "http_proxy_port should not be null"); - } - } - /** - * ssl - */ -#ifdef SW_USE_OPENSSL - if (cli->open_ssl) - { - php_swoole_client_check_ssl_setting(cli, zset); - } -#endif -} - -static void php_swoole_client_free(zval *zobject, swClient *cli) -{ - if (cli->timer) - { - swoole_timer_del(cli->timer); - cli->timer = NULL; - } - //socks5 proxy config - php_swoole_client_socket_free_socks5_proxy(cli); - //http proxy config - php_swoole_client_socket_free_http_proxy(cli); - 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; - } - //long tcp connection, delete from php_sw_long_connections - if (cli->keep) - { - string conn_key = string(cli->server_str, cli->server_strlen); - auto i = long_connections.find(conn_key); - if (i != long_connections.end()) - { - queue *q = i->second; - if (q->empty()) - { - delete q; - long_connections.erase(string(cli->server_str, cli->server_strlen)); - } - } - - sw_free((void *) cli->server_str); - swClient_free(cli); - pefree(cli, 1); - } - else - { - sw_free((void *) cli->server_str); - swClient_free(cli); - efree(cli); - } -#ifdef SWOOLE_SOCKETS_SUPPORT - zval *zsocket = php_swoole_client_get_zsocket(zobject); - if (zsocket) - { - sw_zval_free(zsocket); - php_swoole_client_set_zsocket(zobject, NULL); - } -#endif - //unset object - php_swoole_client_set_cli(zobject, NULL); -} - -ssize_t php_swoole_length_func(swProtocol *protocol, swSocket *_socket, char *data, uint32_t length) -{ - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) protocol->private_data; - zval zdata; - zval retval; - ssize_t ret = -1; - - // TODO: reduce memory copy - ZVAL_STRINGL(&zdata, data, length); - if (UNEXPECTED(sw_zend_call_function_ex2(NULL, fci_cache, 1, &zdata, &retval) != SUCCESS)) - { - php_swoole_fatal_error(E_WARNING, "length function handler error"); - } - else - { - ret = zval_get_long(&retval); - zval_ptr_dtor(&retval); - } - zval_ptr_dtor(&zdata); - - return ret; -} - -swClient* php_swoole_client_new(zval *zobject, char *host, int host_len, int port) -{ - zval *ztype; - uint64_t tmp_buf; - int ret; - - ztype = sw_zend_read_property(Z_OBJCE_P(zobject), zobject, ZEND_STRL("type"), 0); - - if (ztype == NULL || ZVAL_IS_NULL(ztype)) - { - php_swoole_fatal_error(E_ERROR, "failed to get swoole_client->type"); - return NULL; - } - - long type = Z_LVAL_P(ztype); - int client_type = php_swoole_socktype(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"); - SwooleG.error = SW_ERROR_INVALID_PARAMS; - return NULL; - } - - swClient *cli; - string conn_key; - zval *zconnection_id = sw_zend_read_property_not_null(Z_OBJCE_P(zobject), zobject, ZEND_STRL("id"), 1); - - if (zconnection_id && Z_TYPE_P(zconnection_id) == IS_STRING && Z_STRLEN_P(zconnection_id) > 0) - { - conn_key = string(Z_STRVAL_P(zconnection_id), Z_STRLEN_P(zconnection_id)); - } - else - { - size_t size = sw_snprintf(SwooleTG.buffer_stack->str, SwooleTG.buffer_stack->size, "%s:%d", host, port); - conn_key = string(SwooleTG.buffer_stack->str, size); - } - - //keep the tcp connection - if (type & SW_FLAG_KEEP) - { - auto i = long_connections.find(conn_key); - if (i == long_connections.end() || i->second->empty()) - { - cli = (swClient*) pemalloc(sizeof(swClient), 1); - goto _create_socket; - } - else - { - queue *q = i->second; - cli = q->front(); - q->pop(); - //try recv, check connection status - ret = recv(cli->socket->fd, &tmp_buf, sizeof(tmp_buf), MSG_DONTWAIT | MSG_PEEK); - if (ret == 0 || (ret < 0 && swSocket_error(errno) == SW_CLOSE)) - { - cli->close(cli); - php_swoole_client_free(zobject, cli); - cli = (swClient*) pemalloc(sizeof(swClient), 1); - goto _create_socket; - } - cli->reuse_count++; - zend_update_property_long(Z_OBJCE_P(zobject), zobject, ZEND_STRL("reuseCount"), cli->reuse_count); - } - } - else - { - cli = (swClient*) emalloc(sizeof(swClient)); - - _create_socket: - if (swClient_create(cli, php_swoole_socktype(type), 0) < 0) - { - php_swoole_sys_error(E_WARNING, "swClient_create() failed"); - zend_update_property_long(Z_OBJCE_P(zobject), zobject, ZEND_STRL("errCode"), errno); - return NULL; - } - - //don't forget free it - cli->server_str = sw_strndup(conn_key.c_str(), conn_key.length()); - cli->server_strlen = conn_key.length(); - } - - zend_update_property_long(Z_OBJCE_P(zobject), zobject, ZEND_STRL("sock"), cli->socket->fd); - - if (type & SW_FLAG_KEEP) - { - cli->keep = 1; - } - -#ifdef SW_USE_OPENSSL - if (type & SW_SOCK_SSL) - { - cli->open_ssl = 1; - } -#endif - - return cli; -} - -static PHP_METHOD(swoole_client, __construct) -{ - zend_long type = 0; - zend_bool async = 0; - char *id = NULL; - 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"); - RETURN_FALSE; - } - - if (async) - { - php_swoole_fatal_error(E_ERROR, "please install the ext-async extension, using Swoole\\Async\\Client"); - } - - int client_type = php_swoole_socktype(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; - } - - zend_update_property_long(swoole_client_ce, ZEND_THIS, ZEND_STRL("type"), type); - if (id) - { - zend_update_property_stringl(swoole_client_ce, ZEND_THIS, ZEND_STRL("id"), id, len); - } - //init - php_swoole_client_set_cli(ZEND_THIS, NULL); - php_swoole_client_set_cb(ZEND_THIS, NULL); -#ifdef SWOOLE_SOCKETS_SUPPORT - php_swoole_client_set_zsocket(ZEND_THIS, NULL); -#endif - RETURN_TRUE; -} - -static PHP_METHOD(swoole_client, __destruct) -{ - SW_PREVENT_USER_DESTRUCT(); - - swClient *cli = php_swoole_client_get_cli(ZEND_THIS); - //no keep connection - if (cli) - { - sw_zend_call_method_with_0_params(ZEND_THIS, swoole_client_ce, NULL, "close", NULL); - } - //free memory - client_callback *cb = php_swoole_client_get_cb(ZEND_THIS); - if (cb) - { - efree(cb); - php_swoole_client_set_cb(ZEND_THIS, NULL); - } -} - -static PHP_METHOD(swoole_client, set) -{ - zval *zset; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zset) == FAILURE) - { - RETURN_FALSE; - } - if (!ZVAL_IS_ARRAY(zset)) - { - RETURN_FALSE; - } - - zval *zsetting = sw_zend_read_and_convert_property_array(swoole_client_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - php_array_merge(Z_ARRVAL_P(zsetting), Z_ARRVAL_P(zset)); - - RETURN_TRUE; -} - -static PHP_METHOD(swoole_client, 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; - } - - swClient *cli = php_swoole_client_get_cli(ZEND_THIS); - if (cli) - { - php_swoole_fatal_error(E_WARNING, "connection to the server has already been established"); - RETURN_FALSE; - } - - cli = php_swoole_client_new(ZEND_THIS, host, host_len, port); - if (cli == NULL) - { - RETURN_FALSE; - } - php_swoole_client_set_cli(ZEND_THIS, cli); - - if (cli->keep && cli->active) - { - zend_update_property_bool(swoole_client_ce, ZEND_THIS, ZEND_STRL("reuse"), 1); - RETURN_TRUE; - } - else if (cli->active == 1) - { - php_swoole_fatal_error(E_WARNING, "connection to the server has already been established"); - RETURN_FALSE; - } - - zval *zset = sw_zend_read_property(swoole_client_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - if (zset && ZVAL_IS_ARRAY(zset)) - { - php_swoole_client_check_setting(cli, zset); - } - - - //nonblock async - if (cli->connect(cli, host, port, timeout, sock_flag) < 0) - { - if (errno == 0) - { - if (SwooleG.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(SwooleG.error), SwooleG.error); - } - zend_update_property_long(swoole_client_ce, ZEND_THIS, ZEND_STRL("errCode"), SwooleG.error); - } - else - { - php_swoole_sys_error(E_WARNING, "connect to server[%s:%d] failed", host, (int )port); - zend_update_property_long(swoole_client_ce, ZEND_THIS, ZEND_STRL("errCode"), errno); - } - php_swoole_client_free(ZEND_THIS, cli); - RETURN_FALSE; - } - RETURN_TRUE; -} - -static PHP_METHOD(swoole_client, send) -{ - char *data; - size_t data_len; - zend_long flags = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STRING(data, data_len) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(flags) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (data_len == 0) - { - php_swoole_fatal_error(E_WARNING, "data to send is empty"); - RETURN_FALSE; - } - - swClient *cli = client_get_ptr(ZEND_THIS); - if (!cli) - { - RETURN_FALSE; - } - - //clear errno - SwooleG.error = 0; - int ret = cli->send(cli, data, data_len, flags); - if (ret < 0) - { - php_swoole_sys_error(E_WARNING, "failed to send(%d) %zu bytes", cli->socket->fd, data_len); - zend_update_property_long(swoole_client_ce, ZEND_THIS, ZEND_STRL("errCode"), SwooleG.error); - RETVAL_FALSE; - } - else - { - RETURN_LONG(ret); - } -} - -static PHP_METHOD(swoole_client, sendto) -{ - char* ip; - size_t ip_len; - long port; - char *data; - size_t len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sls", &ip, &ip_len, &port, &data, &len) == FAILURE) - { - RETURN_FALSE; - } - - if (len == 0) - { - php_swoole_error(E_WARNING, "data to send is empty"); - RETURN_FALSE; - } - - swClient *cli = php_swoole_client_get_cli(ZEND_THIS); - if (!cli) - { - cli = php_swoole_client_new(ZEND_THIS, ip, ip_len, port); - if (cli == NULL) - { - RETURN_FALSE; - } - cli->active = 1; - php_swoole_client_set_cli(ZEND_THIS, cli); - } - - double ori_timeout = SwooleG.socket_send_timeout; - SwooleG.socket_send_timeout = cli->timeout; - - int ret = -1; - if (cli->type == SW_SOCK_UDP) - { - ret = swSocket_udp_sendto(cli->socket->fd, ip, port, data, len); - } - else if (cli->type == SW_SOCK_UDP6) - { - ret = swSocket_udp_sendto6(cli->socket->fd, ip, port, data, len); - } - else if (cli->type == SW_SOCK_UNIX_DGRAM) - { - ret = swSocket_unix_sendto(cli->socket->fd, ip, data, len); - } - else - { - php_swoole_fatal_error(E_WARNING, "only supports SWOOLE_SOCK_(UDP/UDP6/UNIX_DGRAM)"); - } - SwooleG.socket_send_timeout = ori_timeout; - SW_CHECK_RETURN(ret); -} - -static PHP_METHOD(swoole_client, sendfile) -{ - char *file; - size_t file_len; - 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; - } - if (file_len == 0) - { - php_swoole_fatal_error(E_WARNING, "file to send is empty"); - RETURN_FALSE; - } - - swClient *cli = client_get_ptr(ZEND_THIS); - if (!cli) - { - RETURN_FALSE; - } - //only stream socket can sendfile - if (!(cli->type == SW_SOCK_TCP || cli->type == SW_SOCK_TCP6 || cli->type == SW_SOCK_UNIX_STREAM)) - { - php_swoole_error(E_WARNING, "dgram socket cannot use sendfile"); - RETURN_FALSE; - } - //clear errno - SwooleG.error = 0; - int ret = cli->sendfile(cli, file, offset, length); - if (ret < 0) - { - SwooleG.error = errno; - php_swoole_fatal_error(E_WARNING, "sendfile() failed. Error: %s [%d]", swoole_strerror(SwooleG.error), SwooleG.error); - zend_update_property_long(swoole_client_ce, ZEND_THIS, ZEND_STRL("errCode"), SwooleG.error); - RETVAL_FALSE; - } - else - { - RETVAL_TRUE; - } -} - -static PHP_METHOD(swoole_client, recv) -{ - zend_long buf_len = SW_PHP_CLIENT_BUFFER_SIZE; - zend_long flags = 0; - int ret; - char *buf = NULL; - - ZEND_PARSE_PARAMETERS_START(0, 2) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(buf_len) - Z_PARAM_LONG(flags) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - //waitall - if (flags == 1) - { - flags = MSG_WAITALL; - } - - swClient *cli = client_get_ptr(ZEND_THIS); - if (!cli) - { - RETURN_FALSE; - } - - swProtocol *protocol = &cli->protocol; - - if (cli->open_eof_check) - { - if (cli->buffer == NULL) - { - cli->buffer = swString_new(SW_BUFFER_SIZE_BIG); - } - - swString *buffer = cli->buffer; - int eof = -1; - - if (buffer->length > 0) - { - goto _find_eof; - } - - while (1) - { - buf = buffer->str + buffer->length; - buf_len = buffer->size - buffer->length; - - if (buf_len > SW_BUFFER_SIZE_BIG) - { - buf_len = SW_BUFFER_SIZE_BIG; - } - - ret = cli->recv(cli, buf, buf_len, 0); - if (ret < 0) - { - php_swoole_sys_error(E_WARNING, "recv() failed"); - buffer->length = 0; - RETURN_FALSE; - } - else if (ret == 0) - { - buffer->length = 0; - RETURN_EMPTY_STRING(); - } - - buffer->length += ret; - - if (buffer->length < protocol->package_eof_len) - { - continue; - } - - _find_eof: - eof = swoole_strnpos(buffer->str, buffer->length, protocol->package_eof, protocol->package_eof_len); - if (eof >= 0) - { - eof += protocol->package_eof_len; - RETVAL_STRINGL(buffer->str, eof); - - if ((int) buffer->length > eof) - { - swString_pop_front(buffer, eof); - } - else - { - buffer->length = 0; - } - return; - } - else - { - if (buffer->length == protocol->package_max_length) - { - php_swoole_error(E_WARNING, "no package eof"); - buffer->length = 0; - RETURN_FALSE; - } - else if (buffer->length == buffer->size) - { - if (buffer->size < protocol->package_max_length) - { - uint32_t new_size = buffer->size * 2; - if (new_size > protocol->package_max_length) - { - new_size = protocol->package_max_length; - } - if (swString_extend(buffer, new_size) < 0) - { - buffer->length = 0; - RETURN_FALSE; - } - } - } - } - } - buffer->length = 0; - RETURN_FALSE; - } - else if (cli->open_length_check) - { - if (cli->buffer == NULL) - { - cli->buffer = swString_new(SW_BUFFER_SIZE_STD); - } - else - { - swString_clear(cli->buffer); - } - - uint32_t header_len = protocol->package_length_offset + protocol->package_length_size; - - while (1) - { - int retval = cli->recv(cli, cli->buffer->str + cli->buffer->length, header_len - cli->buffer->length, 0); - if (retval <= 0) - { - break; - } - cli->buffer->length += retval; - buf_len = protocol->get_package_length(protocol, cli->socket, cli->buffer->str, cli->buffer->length); - if (buf_len == 0) - { - continue; - } - else if (buf_len < 0) - { - break; - } - else - { - break; - } - } - - //error package - if (buf_len < 0) - { - RETURN_EMPTY_STRING(); - } - //empty package - else if (buf_len == header_len) - { - RETURN_STRINGL(cli->buffer->str, header_len); - } - else if (buf_len > protocol->package_max_length) - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, "Package is too big. package_length=%d", (int )buf_len); - RETURN_EMPTY_STRING(); - } - else if (buf_len == (zend_long) cli->buffer->length) - { - RETURN_STRINGL(cli->buffer->str, cli->buffer->length); - } - - buf = (char *) emalloc(buf_len + 1); - memcpy(buf, cli->buffer->str, cli->buffer->length); - SwooleG.error = 0; - ret = cli->recv(cli, buf + header_len, buf_len - cli->buffer->length, MSG_WAITALL); - if (ret > 0) - { - ret += header_len; - if (ret != buf_len) - { - ret = 0; - } - } - } - else - { - if (!(flags & MSG_WAITALL) && buf_len > SW_PHP_CLIENT_BUFFER_SIZE) - { - buf_len = SW_PHP_CLIENT_BUFFER_SIZE; - } - buf = (char *) emalloc(buf_len + 1); - SwooleG.error = 0; - ret = cli->recv(cli, buf, buf_len, flags); - } - - if (ret < 0) - { - SwooleG.error = errno; - php_swoole_error(E_WARNING, "recv() failed. Error: %s [%d]", swoole_strerror(SwooleG.error), SwooleG.error); - zend_update_property_long(swoole_client_ce, ZEND_THIS, ZEND_STRL("errCode"), SwooleG.error); - if (buf) - { - efree(buf); - } - RETURN_FALSE; - } - else - { - if (ret == 0) - { - if (buf) - { - efree(buf); - } - RETURN_EMPTY_STRING(); - } - else - { - buf[ret] = 0; - RETVAL_STRINGL(buf, ret); - efree(buf); - } - } -} - -static PHP_METHOD(swoole_client, isConnected) -{ - swClient *cli = 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, getsockname) -{ - swClient *cli = client_get_ptr(ZEND_THIS); - if (!cli) - { - RETURN_FALSE; - } - - if (cli->type == SW_SOCK_UNIX_STREAM || cli->type == SW_SOCK_UNIX_DGRAM) - { - php_swoole_fatal_error(E_WARNING, "getsockname() only support AF_INET family socket"); - RETURN_FALSE; - } - - cli->socket->info.len = sizeof(cli->socket->info.addr); - if (getsockname(cli->socket->fd, (struct sockaddr*) &cli->socket->info.addr, &cli->socket->info.len) < 0) - { - php_swoole_sys_error(E_WARNING, "getsockname() failed"); - RETURN_FALSE; - } - - array_init(return_value); - if (cli->type == SW_SOCK_UDP6 || cli->type == SW_SOCK_TCP6) - { - add_assoc_long(return_value, "port", ntohs(cli->socket->info.addr.inet_v6.sin6_port)); - char tmp[INET6_ADDRSTRLEN]; - if (inet_ntop(AF_INET6, &cli->socket->info.addr.inet_v6.sin6_addr, tmp, sizeof(tmp))) - { - add_assoc_string(return_value, "host", tmp); - } - else - { - php_swoole_fatal_error(E_WARNING, "inet_ntop() failed"); - } - } - 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)); - } -} - -#ifdef SWOOLE_SOCKETS_SUPPORT -static PHP_METHOD(swoole_client, getSocket) -{ - zval *zsocket = php_swoole_client_get_zsocket(ZEND_THIS); - if (zsocket) - { - RETURN_ZVAL(zsocket, 1, NULL); - } - swClient *cli = client_get_ptr(ZEND_THIS); - if (!cli) - { - RETURN_FALSE; - } - if (cli->keep) - { - php_swoole_fatal_error(E_WARNING, "the 'getSocket' method can't be used on persistent connection"); - RETURN_FALSE; - } - php_socket *socket_object = swoole_convert_to_socket(cli->socket->fd); - if (!socket_object) - { - RETURN_FALSE; - } - SW_ZEND_REGISTER_RESOURCE(return_value, (void * ) socket_object, php_sockets_le_socket()); - zsocket = sw_zval_dup(return_value); - Z_TRY_ADDREF_P(zsocket); - php_swoole_client_set_zsocket(ZEND_THIS, zsocket); -} -#endif - -static PHP_METHOD(swoole_client, getpeername) -{ - swClient *cli = client_get_ptr(ZEND_THIS); - if (!cli) - { - RETURN_FALSE; - } - - if (cli->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)); - } - else if (cli->type == SW_SOCK_UDP6) - { - array_init(return_value); - add_assoc_long(return_value, "port", ntohs(cli->remote_addr.addr.inet_v6.sin6_port)); - char tmp[INET6_ADDRSTRLEN]; - - if (inet_ntop(AF_INET6, &cli->remote_addr.addr.inet_v6.sin6_addr, tmp, sizeof(tmp))) - { - add_assoc_string(return_value, "host", tmp); - } - else - { - php_swoole_fatal_error(E_WARNING, "inet_ntop() failed"); - } - } - else if (cli->type == SW_SOCK_UNIX_DGRAM) - { - add_assoc_string(return_value, "host", cli->remote_addr.addr.un.sun_path); - } - else - { - php_swoole_fatal_error(E_WARNING, "only supports SWOOLE_SOCK_(UDP/UDP6/UNIX_DGRAM)"); - RETURN_FALSE; - } -} - -static PHP_METHOD(swoole_client, close) -{ - int ret = 1; - zend_bool force = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_BOOL(force) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - swClient *cli = 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; - } - //Connection error, or short tcp connection. - //No keep connection - if (force || !cli->keep || swSocket_error(SwooleG.error) == SW_CLOSE) - { - ret = cli->close(cli); - php_swoole_client_free(ZEND_THIS, cli); - } - else - { - if (cli->keep) - { - string conn_key(cli->server_str, cli->server_strlen); - queue *q; - auto i = long_connections.find(conn_key); - if (i == long_connections.end()) - { - q = new queue; - long_connections[conn_key] = q; - } - else - { - q = i->second; - } - q->push(cli); - } - //unset object - php_swoole_client_set_cli(ZEND_THIS, NULL); - } - SW_CHECK_RETURN(ret); -} - -#ifdef SW_USE_OPENSSL -static PHP_METHOD(swoole_client, enableSSL) -{ - swClient *cli = client_get_ptr(ZEND_THIS); - if (!cli) - { - RETURN_FALSE; - } - if (cli->type != SW_SOCK_TCP && cli->type != SW_SOCK_TCP6) - { - php_swoole_fatal_error(E_WARNING, "cannot use enableSSL"); - RETURN_FALSE; - } - if (cli->socket->ssl) - { - php_swoole_fatal_error(E_WARNING, "SSL has been enabled"); - RETURN_FALSE; - } - cli->open_ssl = 1; - zval *zset = sw_zend_read_property(swoole_client_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - if (ZVAL_IS_ARRAY(zset)) - { - php_swoole_client_check_ssl_setting(cli, zset); - } - if (swClient_enable_ssl_encrypt(cli) < 0) - { - RETURN_FALSE; - } - if (swClient_ssl_handshake(cli) < 0) - { - RETURN_FALSE; - } - RETURN_TRUE; -} - -static PHP_METHOD(swoole_client, getPeerCert) -{ - swClient *cli = client_get_ptr(ZEND_THIS); - if (!cli) - { - RETURN_FALSE; - } - if (!cli->socket->ssl) - { - php_swoole_fatal_error(E_WARNING, "SSL is not ready"); - RETURN_FALSE; - } - char buf[8192]; - int n = swSSL_get_peer_cert(cli->socket->ssl, buf, sizeof(buf)); - if (n < 0) - { - RETURN_FALSE; - } - RETURN_STRINGL(buf, n); -} - -static PHP_METHOD(swoole_client, verifyPeerCert) -{ - swClient *cli = client_get_ptr(ZEND_THIS); - if (!cli) - { - RETURN_FALSE; - } - if (!cli->socket->ssl) - { - php_swoole_fatal_error(E_WARNING, "SSL is not ready"); - RETURN_FALSE; - } - zend_bool allow_self_signed = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &allow_self_signed) == FAILURE) - { - RETURN_FALSE; - } - SW_CHECK_RETURN(swClient_ssl_verify(cli, allow_self_signed)); -} -#endif - -static PHP_METHOD(swoole_client, shutdown) -{ - swClient *cli = client_get_ptr(ZEND_THIS); - if (!cli) - { - RETURN_FALSE; - } - long __how; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &__how) == FAILURE) - { - RETURN_FALSE; - } - SW_CHECK_RETURN(swClient_shutdown(cli, __how)); -} - -PHP_FUNCTION(swoole_client_select) -{ -#ifdef PHP_SWOOLE_CLIENT_USE_POLL - zval *r_array, *w_array, *e_array; - int retval, 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; - } - - 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)); - struct pollfd *fds = (struct pollfd *) ecalloc(maxevents, sizeof(struct pollfd)); - - if (r_array != NULL && php_swoole_array_length(r_array) > 0) - { - index = client_poll_add(r_array, index, fds, maxevents, POLLIN); - } - if (w_array != NULL && php_swoole_array_length(w_array) > 0) - { - index = client_poll_add(w_array, index, fds, maxevents, POLLOUT); - } - if (e_array != NULL && php_swoole_array_length(e_array) > 0) - { - index = client_poll_add(e_array, index, fds, maxevents, POLLHUP); - } - if (index == 0) - { - efree(fds); - php_swoole_fatal_error(E_WARNING, "no resource arrays were passed to select"); - RETURN_FALSE; - } - - retval = poll(fds, maxevents, (int) (timeout * 1000)); - if (retval == -1) - { - efree(fds); - php_swoole_sys_error(E_WARNING, "unable to poll()"); - RETURN_FALSE; - } - - if (r_array != NULL && php_swoole_array_length(r_array) > 0) - { - client_poll_wait(r_array, fds, maxevents, retval, POLLIN); - } - if (w_array != NULL && php_swoole_array_length(w_array) > 0) - { - client_poll_wait(w_array, fds, maxevents, retval, POLLOUT); - } - if (e_array != NULL && php_swoole_array_length(e_array) > 0) - { - client_poll_wait(e_array, fds, maxevents, retval, POLLHUP); - } - efree(fds); - RETURN_LONG(retval); -#else - zval *r_array, *w_array, *e_array; - fd_set rfds, wfds, efds; - - int max_fd = 0; - int retval, sets = 0; - double timeout = SW_CLIENT_CONNECT_TIMEOUT; - struct timeval timeo; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a!a!a!|d", &r_array, &w_array, &e_array, &timeout) == FAILURE) - { - RETURN_FALSE; - } - - FD_ZERO(&rfds); - FD_ZERO(&wfds); - FD_ZERO(&efds); - - if (r_array != NULL) sets += client_select_add(r_array, &rfds, &max_fd); - if (w_array != NULL) sets += client_select_add(w_array, &wfds, &max_fd); - if (e_array != NULL) sets += client_select_add(e_array, &efds, &max_fd); - - if (!sets) - { - php_swoole_fatal_error(E_WARNING, "no resource arrays were passed to select"); - RETURN_FALSE; - } - - if (max_fd >= FD_SETSIZE) - { - php_swoole_fatal_error(E_WARNING, "select max_fd > FD_SETSIZE[%d]", FD_SETSIZE); - RETURN_FALSE; - } - timeo.tv_sec = (int) timeout; - timeo.tv_usec = (int) ((timeout - timeo.tv_sec) * 1000 * 1000); - - retval = select(max_fd + 1, &rfds, &wfds, &efds, &timeo); - if (retval == -1) - { - php_swoole_sys_error(E_WARNING, "unable to select"); - RETURN_FALSE; - } - if (r_array != NULL) - { - client_select_wait(r_array, &rfds); - } - if (w_array != NULL) - { - client_select_wait(w_array, &wfds); - } - if (e_array != NULL) - { - client_select_wait(e_array, &efds); - } - RETURN_LONG(retval); -#endif -} - -#ifdef PHP_SWOOLE_CLIENT_USE_POLL -static inline int client_poll_get(struct pollfd *fds, int maxevents, int fd) -{ - int i; - for (i = 0; i < maxevents; i++) - { - if (fds[i].fd == fd) - { - return i; - } - } - return -1; -} - -static int client_poll_wait(zval *sock_array, struct pollfd *fds, int maxevents, int n_event, int revent) -{ - zval *element = NULL; - int sock; - - ulong_t num = 0; - if (!ZVAL_IS_ARRAY(sock_array)) - { - return 0; - } - - zval new_array; - array_init(&new_array); - zend_ulong num_key; - zend_string *key; - zval *dest_element; - int poll_key; - - ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(sock_array), num_key, key, element) - { - sock = swoole_convert_to_fd(element); - if (sock < 0) - { - continue; - } - poll_key = client_poll_get(fds, maxevents, sock); - if (poll_key == -1) - { - php_swoole_fatal_error(E_WARNING, "bad fd[%d]", sock); - continue; - } - if (!(fds[poll_key].revents & revent)) - { - continue; - } - if (key) - { - dest_element = zend_hash_add(Z_ARRVAL(new_array), key, element); - } - else - { - dest_element = zend_hash_index_update(Z_ARRVAL(new_array), num_key, element); - } - if (dest_element) - { - Z_ADDREF_P(dest_element); - } - num++; - } ZEND_HASH_FOREACH_END(); - - zval_ptr_dtor(sock_array); - ZVAL_COPY_VALUE(sock_array, &new_array); - return num ? 1 : 0; -} - -static int client_poll_add(zval *sock_array, int index, struct pollfd *fds, int maxevents, int event) -{ - zval *element = NULL; - if (!ZVAL_IS_ARRAY(sock_array)) - { - return -1; - } - - int sock; - int key = -1; - - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(sock_array), element) - sock = swoole_convert_to_fd(element); - if (sock < 0) - { - continue; - } - if (event != POLLIN) - { - key = client_poll_get(fds, maxevents, sock); - } - if (key < 0) - { - fds[index].fd = sock; - fds[index].events = event; - index++; - } - else - { - fds[key].fd = sock; - fds[key].events |= event; - } - SW_HASHTABLE_FOREACH_END(); - - return index; -} -#else -static int client_select_wait(zval *sock_array, fd_set *fds) -{ - zval *element = NULL; - int sock; - - ulong_t num = 0; - if (!ZVAL_IS_ARRAY(sock_array)) - { - return 0; - } - - zval new_array; - array_init(&new_array); - zend_ulong num_key; - zend_string *key; - zval *dest_element; - - - ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(sock_array), num_key, key, element) - { - sock = swoole_convert_to_fd(element); - if (sock < 0) - { - continue; - } - if ((sock < FD_SETSIZE) && FD_ISSET(sock, fds)) - { - if (key) - { - dest_element = zend_hash_add(Z_ARRVAL(new_array), key, element); - } - else - { - dest_element = zend_hash_index_update(Z_ARRVAL(new_array), num_key, element); - } - if (dest_element) - { - Z_ADDREF_P(dest_element); - } - } - num++; - } ZEND_HASH_FOREACH_END(); - - zval_ptr_dtor(sock_array); - ZVAL_COPY_VALUE(sock_array, &new_array); - return num ? 1 : 0; -} - -static int client_select_add(zval *sock_array, fd_set *fds, int *max_fd) -{ - zval *element = NULL; - if (!ZVAL_IS_ARRAY(sock_array)) - { - return 0; - } - - int sock; - int num = 0; - - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(sock_array), element) - sock = swoole_convert_to_fd(element); - if (sock < 0) - { - continue; - } - if (sock < FD_SETSIZE) - { - FD_SET(sock, fds); - } - else - { - php_swoole_fatal_error(E_WARNING, "socket[%d] > FD_SETSIZE[%d]", sock, FD_SETSIZE); - continue; - } - if (sock > *max_fd) - { - *max_fd = sock; - } - num ++; - SW_HASHTABLE_FOREACH_END(); - - return num ? 1 : 0; -} -#endif diff --git a/swoole_client.h b/swoole_client.h deleted file mode 100644 index 19869921ce9..00000000000 --- a/swoole_client.h +++ /dev/null @@ -1,29 +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: Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#pragma once - -#include "php_swoole_cxx.h" -#include "client.h" - -void php_swoole_client_free(zval *zobject, swClient *cli); -swClient* php_swoole_client_new(zval *zobject, char *host, int host_len, int port); -void php_swoole_client_check_setting(swClient *cli, zval *zset); -#ifdef SW_USE_OPENSSL -void php_swoole_client_check_ssl_setting(swClient *cli, zval *zset); -#endif diff --git a/swoole_client_coro.cc b/swoole_client_coro.cc deleted file mode 100644 index 054e7e906ec..00000000000 --- a/swoole_client_coro.cc +++ /dev/null @@ -1,1136 +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 "socks5.h" -#include "mqtt.h" - -#include "ext/standard/basic_functions.h" - -using swoole::coroutine::Socket; - -using namespace swoole; - -static zend_class_entry *swoole_client_coro_ce; -static zend_object_handlers swoole_client_coro_handlers; - -typedef struct -{ - Socket *sock; - zend_object std; -} client_coro; - -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 PHP_METHOD(swoole_client_coro, __construct); -static PHP_METHOD(swoole_client_coro, __destruct); -static PHP_METHOD(swoole_client_coro, set); -static PHP_METHOD(swoole_client_coro, connect); -static PHP_METHOD(swoole_client_coro, recv); -static PHP_METHOD(swoole_client_coro, peek); -static PHP_METHOD(swoole_client_coro, send); -static PHP_METHOD(swoole_client_coro, sendfile); -static PHP_METHOD(swoole_client_coro, sendto); -static PHP_METHOD(swoole_client_coro, recvfrom); -#ifdef SW_USE_OPENSSL -static PHP_METHOD(swoole_client_coro, enableSSL); -static PHP_METHOD(swoole_client_coro, getPeerCert); -static PHP_METHOD(swoole_client_coro, verifyPeerCert); -#endif -static PHP_METHOD(swoole_client_coro, exportSocket); -static PHP_METHOD(swoole_client_coro, isConnected); -static PHP_METHOD(swoole_client_coro, getsockname); -static PHP_METHOD(swoole_client_coro, getpeername); -static PHP_METHOD(swoole_client_coro, close); - -static Socket* client_coro_new(zval *zobject, int port = 0); -void php_swoole_client_coro_socket_free(Socket *cli); - -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) -#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) -#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_FE_END -}; - -static sw_inline client_coro* php_swoole_client_coro_fetch_object(zend_object *obj) -{ - return (client_coro *) ((char *) obj - swoole_client_coro_handlers.offset); -} - -static sw_inline client_coro* php_swoole_get_client(zval *zobject) -{ - return php_swoole_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 void php_swoole_client_coro_free_object(zend_object *object) -{ - client_coro *client = php_swoole_client_coro_fetch_object(object); - if (client->sock) - { - php_swoole_client_coro_socket_free(client->sock); - } - zend_object_std_dtor(&client->std); -} - -static zend_object *php_swoole_client_coro_create_object(zend_class_entry *ce) -{ - client_coro *sock_t = (client_coro *) ecalloc(1, sizeof(client_coro) + zend_object_properties_size(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; -} - -void php_swoole_client_coro_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY(swoole_client_coro, "Swoole\\Coroutine\\Client", NULL, "Co\\Client", swoole_client_coro_methods); - SW_SET_CLASS_SERIALIZABLE(swoole_client_coro, zend_class_serialize_deny, zend_class_unserialize_deny); - 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, client_coro, 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_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); - - zend_declare_class_constant_long(swoole_client_coro_ce, ZEND_STRL("MSG_OOB"), MSG_OOB); - zend_declare_class_constant_long(swoole_client_coro_ce, ZEND_STRL("MSG_PEEK"), MSG_PEEK); - zend_declare_class_constant_long(swoole_client_coro_ce, ZEND_STRL("MSG_DONTWAIT"), MSG_DONTWAIT); - 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, zobject, ZEND_STRL("errCode"), SW_ERROR_CLIENT_NO_CONNECTION); - zend_update_property_string(swoole_client_coro_ce, zobject, ZEND_STRL("errMsg"), swoole_strerror(SW_ERROR_CLIENT_NO_CONNECTION)); - } - return nullptr; - } -} - -static Socket* client_coro_new(zval *zobject, int port) -{ - zval *ztype = sw_zend_read_property(Z_OBJCE_P(zobject), zobject, ZEND_STRL("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)) - { - php_swoole_fatal_error(E_WARNING, "The port is invalid"); - return NULL; - } - - 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), zobject, ZEND_STRL("errCode"), errno); - zend_update_property_string(Z_OBJCE_P(zobject), zobject, ZEND_STRL("errMsg"), swoole_strerror(errno)); - delete cli; - return NULL; - } - - zend_update_property_long(Z_OBJCE_P(zobject), zobject, ZEND_STRL("fd"), cli->get_fd()); - -#ifdef SW_USE_OPENSSL - if (type & SW_SOCK_SSL) - { - cli->open_ssl = true; - } -#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), 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_socks5_proxy(Socket *cli) -{ - if (cli->socks5_proxy) - { - if (cli->socks5_proxy->host) - { - efree((void* )cli->socks5_proxy->host); - cli->socks5_proxy->host = nullptr; - } - if (cli->socks5_proxy->username) - { - efree((void* )cli->socks5_proxy->username); - cli->socks5_proxy->username = nullptr; - } - if (cli->socks5_proxy->password) - { - efree((void* )cli->socks5_proxy->password); - cli->socks5_proxy->password = nullptr; - } - efree(cli->socks5_proxy); - cli->socks5_proxy = nullptr; - } -} - -void php_swoole_client_coro_socket_free_http_proxy(Socket *cli) -{ - if (cli->http_proxy) - { - if (cli->http_proxy->proxy_host) - { - efree((void* ) cli->http_proxy->proxy_host); - cli->http_proxy->proxy_host = nullptr; - } - if (cli->http_proxy->user) - { - efree((void* ) cli->http_proxy->user); - cli->http_proxy->user = nullptr; - } - if (cli->http_proxy->password) - { - efree((void* ) cli->http_proxy->password); - cli->http_proxy->password = nullptr; - } - efree(cli->http_proxy); - cli->http_proxy = nullptr; - } -} - -void php_swoole_client_coro_socket_free(Socket *cli) -{ - //FIXME: move to Socket method, we should not manage it externally - if (!cli->has_bound()) - { - php_swoole_client_coro_socket_free_socks5_proxy(cli); - php_swoole_client_coro_socket_free_http_proxy(cli); - 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), SW_TIMEOUT_CONNECT); - } - if (php_swoole_array_get_value(vht, "read_timeout", ztmp)) - { - cli->set_timeout(zval_get_double(ztmp), SW_TIMEOUT_READ); - } - if (php_swoole_array_get_value(vht, "write_timeout", ztmp)) - { - cli->set_timeout(zval_get_double(ztmp), SW_TIMEOUT_WRITE); - } - /** - * bind port - */ - if (php_swoole_array_get_value(vht, "bind_port", ztmp)) - { - zend_long v = zval_get_long(ztmp); - int bind_port = SW_MAX(0, SW_MIN(v, UINT16_MAX)); - /** - * bind address - */ - if (php_swoole_array_get_value(vht, "bind_address", ztmp)) - { - if (!cli->bind(zend::string(ztmp).val(), 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->set_option(IPPROTO_TCP, 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)) - { - php_swoole_client_coro_socket_free_socks5_proxy(cli); - cli->socks5_proxy = (struct _swSocks5 *) ecalloc(1, sizeof(swSocks5)); - cli->socks5_proxy->host = estrdup(host.val()); - 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 = estrdup(username.val()); - cli->socks5_proxy->l_username = username.len(); - cli->socks5_proxy->password = estrdup(password.val()); - cli->socks5_proxy->l_password = password.len(); - } - } - 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)) - { - php_swoole_client_coro_socket_free_http_proxy(cli); - cli->http_proxy = (struct _http_proxy*) ecalloc(1, sizeof(*cli->http_proxy) - sizeof(cli->http_proxy->buf)); - cli->http_proxy->proxy_host = estrdup(host.val()); - 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->user = estrdup(username.val()); - cli->http_proxy->l_user = username.len(); - cli->http_proxy->password = estrdup(password.val()); - cli->http_proxy->l_password = password.len(); - } - } - 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_method", ztmp)) - { - zend_long v = zval_get_long(ztmp); - sock->ssl_option.method = SW_MAX(0, SW_MIN(v, UINT8_MAX)); - } - if (php_swoole_array_get_value(vht, "ssl_protocols", ztmp)) - { - zend_long v = zval_get_long(ztmp); - sock->ssl_option.disable_protocols = (SW_SSL_SSLv2 | SW_SSL_SSLv3 | SW_SSL_TLSv1 | SW_SSL_TLSv1_1 - | SW_SSL_TLSv1_2) ^ v; - } - if (php_swoole_array_get_value(vht, "ssl_compress", ztmp)) - { - sock->ssl_option.disable_compress = !zval_is_true(ztmp); - } - else if (php_swoole_array_get_value(vht, "ssl_disable_compression", ztmp)) - { - sock->ssl_option.disable_compress = !zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "ssl_cert_file", ztmp)) - { - zend::string str_v(ztmp); - if (sock->ssl_option.cert_file) - { - sw_free(sock->ssl_option.cert_file); - sock->ssl_option.cert_file = nullptr; - } - if (access(str_v.val(), R_OK) == 0) - { - sock->ssl_option.cert_file = str_v.dup(); - } - else - { - php_swoole_fatal_error(E_WARNING, "ssl cert file[%s] not found", sock->ssl_option.cert_file); - ret = false; - } - } - if (php_swoole_array_get_value(vht, "ssl_key_file", ztmp)) - { - zend::string str_v(ztmp); - if (sock->ssl_option.key_file) - { - sw_free(sock->ssl_option.key_file); - sock->ssl_option.key_file = nullptr; - } - if (access(str_v.val(), R_OK) == 0) - { - sock->ssl_option.key_file = str_v.dup(); - } - else - { - php_swoole_fatal_error(E_WARNING, "ssl key file[%s] not found", sock->ssl_option.key_file); - ret = false; - } - } - if (sock->ssl_option.cert_file && !sock->ssl_option.key_file) - { - php_swoole_fatal_error(E_WARNING, "ssl require key file"); - } - else if (sock->ssl_option.key_file && !sock->ssl_option.cert_file) - { - php_swoole_fatal_error(E_WARNING, "ssl require cert file"); - } - if (php_swoole_array_get_value(vht, "ssl_passphrase", ztmp)) - { - if (sock->ssl_option.passphrase) - { - sw_free(sock->ssl_option.passphrase); - } - sock->ssl_option.passphrase = zend::string(ztmp).dup(); - } -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - if (php_swoole_array_get_value(vht, "ssl_host_name", ztmp)) - { - if (sock->ssl_option.tls_host_name) - { - sw_free(sock->ssl_option.tls_host_name); - } - sock->ssl_option.tls_host_name = zend::string(ztmp).dup(); - /* if user set empty ssl_host_name, disable it, otherwise the underlying may set it automatically */ - sock->ssl_option.disable_tls_host_name = !sock->ssl_option.tls_host_name; - } -#endif - if (php_swoole_array_get_value(vht, "ssl_verify_peer", ztmp)) - { - sock->ssl_option.verify_peer = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "ssl_allow_self_signed", ztmp)) - { - sock->ssl_option.allow_self_signed = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "ssl_cafile", ztmp)) - { - if (sock->ssl_option.cafile) - { - sw_free(sock->ssl_option.cafile); - } - sock->ssl_option.cafile = zend::string(ztmp).dup(); - } - if (php_swoole_array_get_value(vht, "ssl_capath", ztmp)) - { - if (sock->ssl_option.capath) - { - sw_free( sock->ssl_option.capath); - } - sock->ssl_option.capath = zend::string(ztmp).dup(); - } - if (php_swoole_array_get_value(vht, "ssl_verify_depth", ztmp)) - { - zend_long v = zval_get_long(ztmp); - sock->ssl_option.verify_depth = SW_MAX(0, SW_MIN(v, UINT8_MAX)); - } - - return ret; -} -#endif - -static PHP_METHOD(swoole_client_coro, __construct) -{ - if (php_swoole_get_client(ZEND_THIS)->sock) - { - php_swoole_fatal_error(E_ERROR, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - } - - zend_long type = 0; - - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1) - Z_PARAM_LONG(type) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - int client_type = php_swoole_socktype(type); - if (client_type < SW_SOCK_TCP || client_type > SW_SOCK_UNIX_DGRAM) - { - 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; - } - - zend_update_property_long(swoole_client_coro_ce, 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) - Z_PARAM_ARRAY(zset) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (php_swoole_array_length(zset) == 0) - { - RETURN_FALSE; - } - 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)); - if (cli) - { - RETURN_BOOL(php_swoole_client_set(cli, zset)); - } - RETURN_TRUE; - } -} - -static PHP_METHOD(swoole_client_coro, connect) -{ - char *host; - size_t host_len; - zend_long port = 0; - double timeout = 0; - 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; - } - - Socket *cli = php_swoole_get_sock(ZEND_THIS); - if (cli) - { - RETURN_FALSE; - } - - cli = client_coro_new(ZEND_THIS, (int) port); - if (!cli) - { - RETURN_FALSE; - } - - zval *zset = sw_zend_read_property(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - if (zset && ZVAL_IS_ARRAY(zset)) - { - php_swoole_client_set(cli, zset); - } - - cli->set_timeout(timeout, SW_TIMEOUT_CONNECT); - if (!cli->connect(host, port, sock_flag)) - { - zend_update_property_long(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("errMsg"), cli->errMsg); - client_coro_close(ZEND_THIS); - RETURN_FALSE; - } - cli->set_timeout(timeout, SW_TIMEOUT_RDWR); - zend_update_property_bool(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("connected"), 1); - RETURN_TRUE; -} - -static PHP_METHOD(swoole_client_coro, send) -{ - char *data; - size_t data_len; - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STRING(data, data_len) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (data_len == 0) - { - php_swoole_fatal_error(E_WARNING, "data to send is empty"); - RETURN_FALSE; - } - - Socket *cli = client_get_ptr(ZEND_THIS); - if (!cli) - { - RETURN_FALSE; - } - - Socket::timeout_setter ts(cli, timeout, SW_TIMEOUT_WRITE); - ssize_t ret = cli->send_all(data, data_len); - if (ret < 0) - { - zend_update_property_long(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, 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, ZEND_THIS, ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("errMsg"), cli->errMsg); - } - RETURN_LONG(ret); - } -} - -static PHP_METHOD(swoole_client_coro, sendto) -{ - char* host; - size_t host_len; - long port; - char *data; - size_t len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sls", &host, &host_len, &port, &data, &len) == FAILURE) - { - RETURN_FALSE; - } - - if (len == 0) - { - RETURN_FALSE; - } - - Socket *cli = php_swoole_get_sock(ZEND_THIS); - if (!cli) - { - cli = client_coro_new(ZEND_THIS, (int) port); - if (!cli) - { - RETURN_FALSE; - } - } - - ssize_t ret = cli->sendto(std::string(host, host_len), port, data, len); - if (ret < 0) - { - zend_update_property_long(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("errMsg"), cli->errMsg); - RETURN_FALSE; - } - RETURN_TRUE; -} - -static PHP_METHOD(swoole_client_coro, recvfrom) -{ - zend_long length; - zval *address, *port; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz/|z/", &length, &address, &port) == FAILURE) - { - RETURN_FALSE; - } - - if (length <= 0) - { - RETURN_FALSE; - } - - Socket *cli = php_swoole_get_sock(ZEND_THIS); - if (!cli) - { - cli = client_coro_new(ZEND_THIS); - if (!cli) - { - RETURN_FALSE; - } - } - - zend_string *retval = zend_string_alloc(length + 1, 0); - ssize_t n_bytes = cli->recvfrom(ZSTR_VAL(retval), length); - if (n_bytes < 0) - { - zend_string_free(retval); - zend_update_property_long(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("errMsg"), cli->errMsg); - RETURN_FALSE; - } - else - { - zval_ptr_dtor(address); - ZVAL_STRING(address, cli->get_ip()); - if (port) - { - zval_ptr_dtor(port); - ZVAL_LONG(port, cli->get_port()); - } - - ZSTR_LEN(retval) = n_bytes; - ZSTR_VAL(retval)[ZSTR_LEN(retval)] = '\0'; - RETURN_STR(retval); - } -} - -static PHP_METHOD(swoole_client_coro, sendfile) -{ - char *file; - size_t file_len; - 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; - } - if (file_len == 0) - { - php_swoole_fatal_error(E_WARNING, "file to send is empty"); - RETURN_FALSE; - } - - Socket *cli = client_get_ptr(ZEND_THIS); - if (!cli) - { - RETURN_FALSE; - } - //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)) - { - zend_update_property_long(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), EINVAL); - zend_update_property_string(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("errMsg"), "dgram socket cannot use sendfile"); - RETURN_FALSE; - } - if (!cli->sendfile(file, offset, length)) - { - zend_update_property_long(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("errMsg"), cli->errMsg); - RETVAL_FALSE; - } - else - { - RETVAL_TRUE; - } -} - -static PHP_METHOD(swoole_client_coro, recv) -{ - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - Socket *cli = client_get_ptr(ZEND_THIS); - if (!cli) - { - RETURN_FALSE; - } - ssize_t retval ; - if (cli->open_length_check || cli->open_eof_check) - { - retval = cli->recv_packet(timeout); - if (retval > 0) - { - RETVAL_STRINGL(cli->get_read_buffer()->str, retval); - } - } - else - { - zend_string *result = zend_string_alloc(SW_PHP_CLIENT_BUFFER_SIZE - sizeof(zend_string), 0); - Socket::timeout_setter ts(cli, timeout, SW_TIMEOUT_READ); - retval = cli->recv(ZSTR_VAL(result), SW_PHP_CLIENT_BUFFER_SIZE - sizeof(zend_string)); - if (retval > 0) - { - ZSTR_VAL(result)[retval] = '\0'; - ZSTR_LEN(result) = retval; - RETURN_STR(result); - } - else - { - zend_string_free(result); - } - } - if (retval < 0) - { - zend_update_property_long(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("errMsg"), cli->errMsg); - RETURN_FALSE; - } - else if (retval == 0) - { - RETURN_EMPTY_STRING(); - } -} - -static PHP_METHOD(swoole_client_coro, peek) -{ - zend_long buf_len = SW_PHP_CLIENT_BUFFER_SIZE; - int ret; - char *buf = NULL; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(buf_len) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - Socket *cli = client_get_ptr(ZEND_THIS); - if (!cli) - { - RETURN_FALSE; - } - - buf = (char *) emalloc(buf_len + 1); - ret = cli->peek(buf, buf_len); - if (ret < 0) - { - zend_update_property_long(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("errMsg"), cli->errMsg); - efree(buf); - RETURN_FALSE; - } - else - { - buf[ret] = 0; - RETVAL_STRINGL(buf, ret); - efree(buf); - } -} - -static PHP_METHOD(swoole_client_coro, isConnected) -{ - Socket *cli = php_swoole_get_sock(ZEND_THIS); - if (cli && cli->is_connect()) - { - RETURN_TRUE; - } - else - { - RETURN_FALSE; - } -} - -static PHP_METHOD(swoole_client_coro, getsockname) -{ - Socket *cli = client_get_ptr(ZEND_THIS); - if (!cli) - { - RETURN_FALSE; - } - - swSocketAddress sa; - if (!cli->getsockname(&sa)) - { - zend_update_property_long(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("errMsg"), cli->errMsg); - RETURN_FALSE; - } - - array_init(return_value); - zval zaddress; - ZVAL_STRING(&zaddress, swSocket_get_ip(cli->get_type(), &sa)); - add_assoc_zval(return_value, "host", &zaddress); /* backward compatibility */ - Z_ADDREF(zaddress); - add_assoc_zval(return_value, "address", &zaddress); - add_assoc_long(return_value, "port", swSocket_get_port(cli->get_type(), &sa)); -} - -/** - * export Swoole\Coroutine\Socket object - */ -static PHP_METHOD(swoole_client_coro, exportSocket) -{ - zval rv; - zval *zsocket = zend_read_property(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("socket"), 1, &rv); - if (!ZVAL_IS_NULL(zsocket)) - { - RETURN_ZVAL(zsocket, 1, NULL); - } - - 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, ZEND_THIS, ZEND_STRL("socket"), return_value); -} - -static PHP_METHOD(swoole_client_coro, getpeername) -{ - Socket *cli = client_get_ptr(ZEND_THIS); - if (!cli) - { - RETURN_FALSE; - } - - swSocketAddress sa; - if (!cli->getpeername(&sa)) - { - zend_update_property_long(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), cli->errCode); - zend_update_property_string(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("errMsg"), cli->errMsg); - RETURN_FALSE; - } - - array_init(return_value); - zval zaddress; - ZVAL_STRING(&zaddress, swSocket_get_ip(cli->get_type(), &sa)); - add_assoc_zval(return_value, "host", &zaddress); /* backward compatibility */ - Z_ADDREF(zaddress); - add_assoc_zval(return_value, "address", &zaddress); - add_assoc_long(return_value, "port", swSocket_get_port(cli->get_type(), &sa)); -} - -static PHP_METHOD(swoole_client_coro, close) -{ - RETURN_BOOL(client_coro_close(ZEND_THIS)); -} - -#ifdef SW_USE_OPENSSL -static PHP_METHOD(swoole_client_coro, enableSSL) -{ - Socket *cli = client_get_ptr(ZEND_THIS); - if (!cli) - { - RETURN_FALSE; - } - if (cli->get_type() != SW_SOCK_TCP && cli->get_type() != SW_SOCK_TCP6) - { - php_swoole_fatal_error(E_WARNING, "cannot use enableSSL"); - RETURN_FALSE; - } - if (cli->socket->ssl) - { - php_swoole_fatal_error(E_WARNING, "SSL has been enabled"); - RETURN_FALSE; - } - zval *zset = sw_zend_read_property(swoole_client_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - if (php_swoole_array_length_safe(zset) > 0) - { - php_swoole_socket_set_ssl(cli, zset); - } - RETURN_BOOL(cli->ssl_handshake()); -} - -static PHP_METHOD(swoole_client_coro, getPeerCert) -{ - Socket *cli = client_get_ptr(ZEND_THIS); - if (!cli) - { - RETURN_FALSE; - } - if (!cli->socket->ssl) - { - php_swoole_fatal_error(E_WARNING, "SSL is not ready"); - RETURN_FALSE; - } - char buf[8192]; - int n = swSSL_get_peer_cert(cli->socket->ssl, buf, sizeof(buf)); - if (n < 0) - { - RETURN_FALSE; - } - RETURN_STRINGL(buf, n); -} - -static PHP_METHOD(swoole_client_coro, verifyPeerCert) -{ - Socket *cli = client_get_ptr(ZEND_THIS); - if (!cli) - { - RETURN_FALSE; - } - if (!cli->socket->ssl) - { - php_swoole_fatal_error(E_WARNING, "SSL is not ready"); - RETURN_FALSE; - } - zend_bool allow_self_signed = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &allow_self_signed) == FAILURE) - { - RETURN_FALSE; - } - RETURN_BOOL(cli->ssl_verify(allow_self_signed)); -} -#endif diff --git a/swoole_coroutine.cc b/swoole_coroutine.cc deleted file mode 100644 index a192b6022b7..00000000000 --- a/swoole_coroutine.cc +++ /dev/null @@ -1,1120 +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: Xinyu Zhu | - | shiguangqi | - | Twosee | - | Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#include "php_swoole_cxx.h" - -#include "server.h" -#include "swoole_coroutine_system.h" - -#include "zend_builtin_functions.h" -#include "ext/spl/spl_array.h" - -#include - -using swoole::coroutine::System; -using swoole::coroutine::Socket; -using swoole::Coroutine; -using swoole::PHPCoroutine; -using std::unordered_map; - -#define PHP_CORO_TASK_SLOT ((int)((ZEND_MM_ALIGNED_SIZE(sizeof(php_coro_task)) + ZEND_MM_ALIGNED_SIZE(sizeof(zval)) - 1) / ZEND_MM_ALIGNED_SIZE(sizeof(zval)))) - -enum sw_exit_flags -{ - SW_EXIT_IN_COROUTINE = 1 << 1, - SW_EXIT_IN_SERVER = 1 << 2 -}; - -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_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() - -bool PHPCoroutine::active = false; - -swoole::coroutine::Config PHPCoroutine::config = -{ - SW_DEFAULT_MAX_CORO_NUM, - 0, - /* TODO: enable hook in v5.0.0 */ - // SW_HOOK_ALL - false, -}; - -php_coro_task PHPCoroutine::main_task = {}; -pthread_t PHPCoroutine::interrupt_thread_id; -bool PHPCoroutine::interrupt_thread_running = false; - -static zend_bool* zend_vm_interrupt = nullptr; -static user_opcode_handler_t ori_exit_handler = nullptr; -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; - -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, const char *format, va_list args) = nullptr; - -static zend_class_entry *swoole_coroutine_util_ce; -static zend_class_entry *swoole_exit_exception_ce; -static zend_object_handlers swoole_exit_exception_handlers; -static zend_class_entry *swoole_coroutine_iterator_ce; -static zend_class_entry *swoole_coroutine_context_ce; - -static PHP_METHOD(swoole_coroutine, exists); -static PHP_METHOD(swoole_coroutine, yield); -static PHP_METHOD(swoole_coroutine, resume); -static PHP_METHOD(swoole_coroutine, stats); -static PHP_METHOD(swoole_coroutine, getCid); -static PHP_METHOD(swoole_coroutine, getPcid); -static PHP_METHOD(swoole_coroutine, getContext); -static PHP_METHOD(swoole_coroutine, getBackTrace); -static PHP_METHOD(swoole_coroutine, getElapsed); -static PHP_METHOD(swoole_coroutine, list); -static PHP_METHOD(swoole_coroutine, enableScheduler); -static PHP_METHOD(swoole_coroutine, disableScheduler); - -static const zend_function_entry swoole_coroutine_util_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, 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, 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) - /** - * 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, fread, arginfo_swoole_coroutine_system_fread, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, fgets, arginfo_swoole_coroutine_system_fgets, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, fwrite, arginfo_swoole_coroutine_system_fwrite, 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, 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, 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) - PHP_FE_END -}; - -/** - * Exit Exception - */ -static PHP_METHOD(swoole_exit_exception, getFlags); -static PHP_METHOD(swoole_exit_exception, getStatus); - -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 -}; - -static int coro_exit_handler(zend_execute_data *execute_data) -{ - zval ex; - zend_object *obj; - zend_long flags = 0; - if (Coroutine::get_current()) - { - flags |= SW_EXIT_IN_COROUTINE; - } - if (sw_server() && sw_server()->gs->start) - { - flags |= SW_EXIT_IN_SERVER; - } - if (flags) - { - const zend_op *opline = EX(opline); - zval _exit_status; - zval *exit_status = NULL; - - if (opline->op1_type != IS_UNUSED) - { - if (opline->op1_type == IS_CONST) - { - // see: https://github.com/php/php-src/commit/e70618aff6f447a298605d07648f2ce9e5a284f5 -#ifdef EX_CONSTANT - exit_status = EX_CONSTANT(opline->op1); -#else - exit_status = RT_CONSTANT(opline, opline->op1); -#endif - } - else - { - exit_status = EX_VAR(opline->op1.var); - } - if (Z_ISREF_P(exit_status)) - { - exit_status = Z_REFVAL_P(exit_status); - } - ZVAL_DUP(&_exit_status, exit_status); - exit_status = &_exit_status; - } - else - { - exit_status = &_exit_status; - ZVAL_NULL(exit_status); - } - obj = zend_throw_error_exception(swoole_exit_exception_ce, "swoole exit", 0, E_ERROR); - ZVAL_OBJ(&ex, obj); - zend_update_property_long(swoole_exit_exception_ce, &ex, ZEND_STRL("flags"), flags); - Z_TRY_ADDREF_P(exit_status); - zend_update_property(swoole_exit_exception_ce, &ex, ZEND_STRL("status"), exit_status); - } - - return ZEND_USER_OPCODE_DISPATCH; -} - -static int coro_begin_silence_handler(zend_execute_data *execute_data) -{ - php_coro_task *task = PHPCoroutine::get_task(); - task->in_silence = true; - task->ori_error_reporting = EG(error_reporting); - return ZEND_USER_OPCODE_DISPATCH; -} - -static int coro_end_silence_handler(zend_execute_data *execute_data) -{ - php_coro_task *task = PHPCoroutine::get_task(); - task->in_silence = false; - return ZEND_USER_OPCODE_DISPATCH; -} - -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()); - co->resume(); - } -} - -static void coro_interrupt_function(zend_execute_data *execute_data) -{ - php_coro_task *task = PHPCoroutine::get_task(); - if (task && task->co && PHPCoroutine::is_schedulable(task)) - { - swoole_event_defer(coro_interrupt_resume, (void *) task->co); - task->co->yield(); - } - if (orig_interrupt_function) - { - orig_interrupt_function(execute_data); - } -} - -void PHPCoroutine::init() -{ - Coroutine::set_on_yield(on_yield); - Coroutine::set_on_resume(on_resume); - Coroutine::set_on_close(on_close); -} - -void PHPCoroutine::deactivate(void *ptr) -{ - PHPCoroutine::interrupt_thread_stop(); - /** - * reset runtime hook - */ - PHPCoroutine::disable_hook(); - config.hook_flags = 0; - - zend_interrupt_function = orig_interrupt_function; - zend_error_cb = orig_error_function; - - active = false; -} - -inline void PHPCoroutine::activate() -{ - if (sw_unlikely(active)) - { - 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!"); - } - - /* init reactor and register event wait */ - php_swoole_check_reactor(); - - /* replace interrupt function */ - 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 = error; - - 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(); - } - - if (config.hook_flags) - { - enable_hook(config.hook_flags); - } - - /** - * deactivate when reactor free. - */ - swReactor_add_destroy_callback(SwooleTG.reactor, deactivate, nullptr); - active = true; -} - -void PHPCoroutine::error(int type, const char *error_filename, const uint32_t error_lineno, const char *format, va_list args) -{ - if (sw_unlikely(type & E_FATAL_ERRORS)) - { - if (active) - { - /* update the last coroutine's info */ - save_task(get_task()); - } - swoole_event_free(); - } - if (sw_likely(orig_error_function)) - { - orig_error_function(type, error_filename, error_lineno, format, args); - } -} - -void PHPCoroutine::shutdown() -{ - interrupt_thread_stop(); - Coroutine::bailout(nullptr); -} - -void PHPCoroutine::interrupt_thread_stop() -{ - if (!interrupt_thread_running) - { - return; - } - interrupt_thread_running = false; - if (pthread_join(interrupt_thread_id, NULL) < 0) - { - swSysWarn("pthread_join(%ld) failed", (ulong_t )interrupt_thread_id); - interrupt_thread_running = true; - } -} - -void PHPCoroutine::interrupt_thread_start() -{ - if (interrupt_thread_running) - { - return; - } - zend_vm_interrupt = &EG(vm_interrupt); - interrupt_thread_running = true; - if (pthread_create(&interrupt_thread_id, NULL, (void * (*)(void *)) interrupt_thread_loop, NULL) < 0) - { - swSysError("pthread_create[PHPCoroutine Scheduler] failed"); - interrupt_thread_running = false; - } -} - -void PHPCoroutine::interrupt_thread_loop() -{ - static const useconds_t interval = (MAX_EXEC_MSEC / 2) * 1000; - swSignal_none(); - while (interrupt_thread_running) - { - *zend_vm_interrupt = 1; - usleep(interval); - } - pthread_exit(0); -} - -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 = NULL; - - 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 != NULL) - { - zend_vm_stack p = stack->prev; - efree(stack); - stack = p; - } -} - -/** - * The meaning of the task argument in coro switch functions - * - * create: origin_task - * yield: current_task - * resume: target_task - * close: current_task - * - */ -inline void PHPCoroutine::save_vm_stack(php_coro_task *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); - task->error_handling = EG(error_handling); - task->exception_class = EG(exception_class); - task->exception = EG(exception); - if (UNEXPECTED(BG(array_walk_fci).size != 0)) - { - if (!task->array_walk_fci) - { - task->array_walk_fci = (php_swoole_fci *) emalloc(sizeof(*task->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)); - } - if (UNEXPECTED(task->in_silence)) - { - task->tmp_error_reporting = EG(error_reporting); - EG(error_reporting) = task->ori_error_reporting; - } -} - -inline void PHPCoroutine::restore_vm_stack(php_coro_task *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; - EG(error_handling) = task->error_handling; - EG(exception_class) = task->exception_class; - EG(exception) = task->exception; - 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(task->in_silence)) - { - EG(error_reporting) = task->tmp_error_reporting; - } -} - -inline void PHPCoroutine::save_og(php_coro_task *task) -{ - if (OG(handlers).elements) - { - task->output_ptr = (zend_output_globals *) emalloc(sizeof(zend_output_globals)); - memcpy(task->output_ptr, SWOG, sizeof(zend_output_globals)); - php_output_activate(); - } - else - { - task->output_ptr = NULL; - } -} - -inline void PHPCoroutine::restore_og(php_coro_task *task) -{ - if (task->output_ptr) - { - memcpy(SWOG, task->output_ptr, sizeof(zend_output_globals)); - efree(task->output_ptr); - task->output_ptr = NULL; - } -} - -void PHPCoroutine::save_task(php_coro_task *task) -{ - save_vm_stack(task); - save_og(task); -} - -void PHPCoroutine::restore_task(php_coro_task *task) -{ - restore_vm_stack(task); - restore_og(task); -} - -void PHPCoroutine::on_yield(void *arg) -{ - php_coro_task *task = (php_coro_task *) arg; - php_coro_task *origin_task = get_origin_task(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); -} - -void PHPCoroutine::on_resume(void *arg) -{ - php_coro_task *task = (php_coro_task *) arg; - php_coro_task *current_task = get_task(); - 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()); -} - -void PHPCoroutine::on_close(void *arg) -{ - php_coro_task *task = (php_coro_task *) arg; - php_coro_task *origin_task = get_origin_task(task); -#ifdef SW_LOG_TRACE_OPEN - long cid = task->co->get_cid(); - long origin_cid = task->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 (OG(handlers).elements) - { - if (OG(active)) - { - php_output_end_all(); - } - php_output_deactivate(); - php_output_activate(); - } - if (task->array_walk_fci) - { - efree(task->array_walk_fci); - } - 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; - php_coro_args *php_arg = (php_coro_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; - php_coro_task *task; - zend_execute_data *call; - zval _retval, *retval = &_retval; - - if (fci_cache.object) - { - GC_ADDREF(fci_cache.object); - } - - vm_stack_init(); - call = (zend_execute_data *) (EG(vm_stack_top)); - task = (php_coro_task *) EG(vm_stack_top); - EG(vm_stack_top) = (zval *) ((char *) call + PHP_CORO_TASK_SLOT * sizeof(zval)); - -#if PHP_VERSION_ID < 70400 - call = zend_vm_stack_push_call_frame( - ZEND_CALL_TOP_FUNCTION | ZEND_CALL_ALLOCATED, - func, argc, fci_cache.called_scope, fci_cache.object - ); -#else - do { - uint32_t call_info; - void *object_or_called_scope; - if ((func->common.fn_flags & ZEND_ACC_STATIC) || !fci_cache.object) { - object_or_called_scope = fci_cache.called_scope; - call_info = ZEND_CALL_TOP_FUNCTION | ZEND_CALL_DYNAMIC; - } else { - object_or_called_scope = fci_cache.object; - call_info = ZEND_CALL_TOP_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_HAS_THIS; - } - call = zend_vm_stack_push_call_frame(call_info, func, argc, object_or_called_scope); - } while (0); -#endif - - 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); - } - - call->symbol_table = NULL; - - if (func->op_array.fn_flags & ZEND_ACC_CLOSURE) - { - uint32_t call_info; - GC_ADDREF(ZEND_CLOSURE_OBJECT(func)); - call_info = ZEND_CALL_CLOSURE; - ZEND_ADD_CALL_FLAG(call, call_info); - } - -#if defined(SW_CORO_SWAP_BAILOUT) && !defined(SW_CORO_SUPPORT_BAILOUT) - EG(bailout) = NULL; -#endif - EG(current_execute_data) = call; - EG(error_handling) = EH_NORMAL; - EG(exception_class) = NULL; - EG(exception) = NULL; - - task->output_ptr = NULL; - task->array_walk_fci = NULL; - 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 = 1; - - 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); - } - - 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) = NULL; -#if PHP_VERSION_ID >= 70200 - zend_init_func_execute_data(call, &func->op_array, retval); -#else - zend_init_execute_data(call, &func->op_array, retval); -#endif - zend_execute_ex(EG(current_execute_data)); - } - else /* ZEND_INTERNAL_FUNCTION */ - { - ZVAL_NULL(retval); - call->prev_execute_data = NULL; - call->return_value = NULL; /* this is not a constructor call */ - execute_internal(call, retval); - zend_vm_stack_free_args(call); - } - - if (task->defer_tasks) - { - std::stack *tasks = task->defer_tasks; - while (!tasks->empty()) - { - php_swoole_fci *defer_fci = tasks->top(); - tasks->pop(); - defer_fci->fci.param_count = 1; - defer_fci->fci.params = retval; - 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"); - } - 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); - } - -#ifdef SW_CORO_SUPPORT_BAILOUT - } zend_catch { - Coroutine::bailout([](){ - swoole_event_free(); - sw_zend_bailout(); - }); - } zend_end_try(); -#endif -} - -long PHPCoroutine::create(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv) -{ - 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 SW_CORO_ERR_LIMIT; - } - if (sw_unlikely(!fci_cache || !fci_cache->function_handler)) - { - php_swoole_fatal_error(E_ERROR, "invalid function call info cache"); - return SW_CORO_ERR_INVALID; - } - zend_uchar type = fci_cache->function_handler->type; - if (sw_unlikely(type != ZEND_USER_FUNCTION && type != ZEND_INTERNAL_FUNCTION)) - { - php_swoole_fatal_error(E_ERROR, "invalid function type %u", fci_cache->function_handler->type); - return SW_CORO_ERR_INVALID; - } - - if (sw_unlikely(!active)) - { - activate(); - } - - php_coro_args php_coro_args; - php_coro_args.fci_cache = fci_cache; - php_coro_args.argv = argv; - php_coro_args.argc = argc; - save_task(get_task()); - - return Coroutine::create(main_func, (void*) &php_coro_args); -} - -void PHPCoroutine::defer(php_swoole_fci *fci) -{ - php_coro_task *task = get_task(); - if (task->defer_tasks == nullptr) - { - task->defer_tasks = new std::stack; - } - task->defer_tasks->push(fci); -} - -void PHPCoroutine::yield_m(zval *return_value, php_coro_context *sw_current_context) -{ - php_coro_task *task = get_task(); - sw_current_context->current_coro_return_value_ptr = return_value; - sw_current_context->current_task = task; - on_yield(task); - task->co->yield_naked(); -} - -int PHPCoroutine::resume_m(php_coro_context *sw_current_context, zval *retval, zval *coro_retval) -{ - php_coro_task *task = sw_current_context->current_task; - on_resume(task); - if (retval) - { - ZVAL_COPY(sw_current_context->current_coro_return_value_ptr, retval); - } - task->co->resume_naked(); - return SW_CORO_ERR_END; -} - -void php_swoole_coroutine_minit(int module_number) -{ - PHPCoroutine::init(); - - SW_INIT_CLASS_ENTRY_BASE(swoole_coroutine_util, "Swoole\\Coroutine", NULL, "Co", swoole_coroutine_util_methods, NULL); - SW_SET_CLASS_CREATE(swoole_coroutine_util, sw_zend_create_object_deny); - - SW_INIT_CLASS_ENTRY_BASE(swoole_coroutine_iterator, "Swoole\\Coroutine\\Iterator", NULL, "Co\\Iterator", NULL, spl_ce_ArrayIterator); - SW_INIT_CLASS_ENTRY_BASE(swoole_coroutine_context, "Swoole\\Coroutine\\Context", NULL, "Co\\Context", NULL, 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", SW_CORO_MAX_NUM_LIMIT); - SW_REGISTER_LONG_CONSTANT("SWOOLE_CORO_INIT", SW_CORO_INIT); - SW_REGISTER_LONG_CONSTANT("SWOOLE_CORO_WAITING", SW_CORO_WAITING); - SW_REGISTER_LONG_CONSTANT("SWOOLE_CORO_RUNNING", SW_CORO_RUNNING); - SW_REGISTER_LONG_CONSTANT("SWOOLE_CORO_END", SW_CORO_END); - - //prohibit exit in coroutine - SW_INIT_CLASS_ENTRY_EX(swoole_exit_exception, "Swoole\\ExitException", NULL, NULL, 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); - - if (SWOOLE_G(cli)) - { - ori_exit_handler = zend_get_user_opcode_handler(ZEND_EXIT); - zend_set_user_opcode_handler(ZEND_EXIT, coro_exit_handler); - - ori_begin_silence_handler = zend_get_user_opcode_handler(ZEND_BEGIN_SILENCE); - zend_set_user_opcode_handler(ZEND_BEGIN_SILENCE, coro_begin_silence_handler); - - ori_end_silence_handler = zend_get_user_opcode_handler(ZEND_END_SILENCE); - zend_set_user_opcode_handler(ZEND_END_SILENCE, coro_end_silence_handler); - } -} - -void php_swoole_coroutine_rshutdown() -{ - PHPCoroutine::shutdown(); -} - -static PHP_METHOD(swoole_exit_exception, getFlags) -{ - SW_RETURN_PROPERTY("flags"); -} - -static PHP_METHOD(swoole_exit_exception, getStatus) -{ - SW_RETURN_PROPERTY("status"); -} - -PHP_FUNCTION(swoole_coroutine_create) -{ - zend_fcall_info fci; - zend_fcall_info_cache fci_cache; - - ZEND_PARSE_PARAMETERS_START(1, -1) - Z_PARAM_FUNC(fci, fci_cache) - Z_PARAM_VARIADIC('*', fci.params, fci.param_count) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (sw_unlikely(SWOOLE_G(req_status) == PHP_SWOOLE_CALL_USER_SHUTDOWNFUNC_BEGIN)) - { - zend_function *func = (zend_function *) EG(current_execute_data)->prev_execute_data->func; - if (func->common.function_name && sw_unlikely(memcmp(ZSTR_VAL(func->common.function_name), ZEND_STRS("__destruct")) == 0)) - { - php_swoole_fatal_error(E_ERROR, "can not use coroutine in __destruct after php_request_shutdown"); - RETURN_FALSE; - } - } - - long cid = PHPCoroutine::create(&fci_cache, fci.param_count, fci.params); - if (sw_likely(cid > 0)) - { - RETURN_LONG(cid); - } - else - { - RETURN_FALSE; - } -} - -PHP_FUNCTION(swoole_coroutine_defer) -{ - zend_fcall_info fci; - zend_fcall_info_cache fci_cache; - php_swoole_fci *defer_fci; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_FUNC(fci, fci_cache) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - Coroutine::get_current_safe(); - defer_fci = (php_swoole_fci *) emalloc(sizeof(php_swoole_fci)); - defer_fci->fci = fci; - defer_fci->fci_cache = fci_cache; - sw_zend_fci_cache_persist(&defer_fci->fci_cache); - PHPCoroutine::defer(defer_fci); -} - -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("signal_listener_num"), - SwooleTG.reactor ? (SwooleTG.reactor->signal_listener_num + SwooleTG.reactor->co_signal_listener_num) : 0 - ); - 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"), swAio_thread_count()); - 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()); - add_assoc_long_ex(return_value, ZEND_STRL("coroutine_last_cid"), Coroutine::get_last_cid()); -} - -PHP_METHOD(swoole_coroutine, getCid) -{ - RETURN_LONG(PHPCoroutine::get_cid()); -} - -PHP_METHOD(swoole_coroutine, getPcid) -{ - zend_long cid = 0; - zend_long ret; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(cid) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - ret = PHPCoroutine::get_pcid(cid); - if (ret == 0) - { - RETURN_FALSE; - } - - RETURN_LONG(ret); -} - -PHP_METHOD(swoole_coroutine, getContext) -{ - zend_long cid = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(cid) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - php_coro_task *task = (php_coro_task *) (EXPECTED(cid == 0) ? Coroutine::get_current_task() : Coroutine::get_task_by_cid(cid)); - if (UNEXPECTED(!task)) - { - RETURN_NULL(); - } - if (UNEXPECTED(task->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)) - { - object_init_ex(return_value, swoole_coroutine_context_ce); - task->context = Z_OBJ_P(return_value); - } - GC_ADDREF(task->context); - RETURN_OBJ(task->context); -} - -PHP_METHOD(swoole_coroutine, getElapsed) -{ - zend_long cid = 0; - zend_long ret; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(cid) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - ret = PHPCoroutine::get_elapsed(cid); - RETURN_LONG(ret); -} - -PHP_METHOD(swoole_coroutine, exists) -{ - zend_long cid; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_LONG(cid) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - 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; - } - - 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"); - RETURN_FALSE; - } - - Coroutine* co = coroutine_iterator->second; - user_yield_coros.erase(cid); - co->resume(); - RETURN_TRUE; -} - -PHP_METHOD(swoole_coroutine, yield) -{ - Coroutine* co = Coroutine::get_current_safe(); - user_yield_coros[co->get_cid()] = co; - co->yield(); - RETURN_TRUE; -} - -PHP_METHOD(swoole_coroutine, getBackTrace) -{ - 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); - - if (!cid || cid == PHPCoroutine::get_cid()) - { - zend_fetch_debug_backtrace(return_value, 0, options, limit); - } - else - { - php_coro_task *task = (php_coro_task *) PHPCoroutine::get_task_by_cid(cid); - if (UNEXPECTED(!task)) - { - RETURN_FALSE; - } - zend_execute_data *ex_backup = EG(current_execute_data); - EG(current_execute_data) = task->execute_data; - zend_fetch_debug_backtrace(return_value, 0, options, limit); - EG(current_execute_data) = ex_backup; - } -} - -PHP_METHOD(swoole_coroutine, list) -{ - zval zlist; - array_init(&zlist); - for (auto &co : Coroutine::coroutines) { - add_next_index_long(&zlist, co.second->get_cid()); - } - object_init_ex(return_value, swoole_coroutine_iterator_ce); - sw_zend_call_method_with_1_params( - return_value, - swoole_coroutine_iterator_ce, - &swoole_coroutine_iterator_ce->constructor, - (const char *) "__construct", - NULL, - &zlist - ); - zval_ptr_dtor(&zlist); -} - -PHP_METHOD(swoole_coroutine, enableScheduler) -{ - RETURN_BOOL(PHPCoroutine::enable_scheduler()); -} - -PHP_METHOD(swoole_coroutine, disableScheduler) -{ - RETURN_BOOL(PHPCoroutine::disable_scheduler()); -} - -/** - * for gdb - */ -zend_executor_globals* php_swoole_get_executor_globals() -{ - return (zend_executor_globals *) &EG(uninitialized_zval); -} diff --git a/swoole_coroutine.h b/swoole_coroutine.h deleted file mode 100644 index 52510ad1b50..00000000000 --- a/swoole_coroutine.h +++ /dev/null @@ -1,238 +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: Xinyu Zhu | - | shiguangqi | - | Twosee | - | Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#pragma once - -#include "coroutine_cxx_api.h" -#include "zend_vm.h" -#include "zend_closures.h" - -#include - -#define SW_DEFAULT_MAX_CORO_NUM 100000 -#define SW_DEFAULT_PHP_STACK_PAGE_SIZE 8192 - -#define SWOG ((zend_output_globals *) &OG(handlers)) - -enum sw_coro_hook_type -{ - SW_HOOK_TCP = 1u << 1, - SW_HOOK_UDP = 1u << 2, - SW_HOOK_UNIX = 1u << 3, - SW_HOOK_UDG = 1u << 4, - SW_HOOK_SSL = 1u << 5, - SW_HOOK_TLS = 1u << 6, - SW_HOOK_STREAM_FUNCTION = 1u << 7, - SW_HOOK_FILE = 1u << 8, - SW_HOOK_SLEEP = 1u << 9, - SW_HOOK_PROC = 1u << 10, - SW_HOOK_CURL = 1u << 28, - SW_HOOK_BLOCKING_FUNCTION = 1u << 30, - - SW_HOOK_ALL = 0x7fffffff ^ SW_HOOK_CURL /* TODO: remove it */ -}; - -struct php_coro_task -{ - 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; - zend_error_handling_t error_handling; - zend_class_entry *exception_class; - zend_object *exception; - zend_output_globals *output_ptr; - /* for array_walk non-reentrancy */ - php_swoole_fci *array_walk_fci; - /* for error control `@` */ - bool in_silence; - int ori_error_reporting; - int tmp_error_reporting; - swoole::Coroutine *co; - std::stack *defer_tasks; - long pcid; - zend_object *context; - int64_t last_msec; - zend_bool enable_scheduler; -}; - -struct php_coro_args -{ - zend_fcall_info_cache *fci_cache; - zval *argv; - uint32_t argc; -}; - -// TODO: remove php coro context -struct php_coro_context -{ - zval coro_params; - zval *current_coro_return_value_ptr; - void *private_data; - swTimer_node *timer; - php_coro_task *current_task; -}; - -PHP_METHOD(swoole_coroutine_scheduler, set); - -namespace swoole -{ - -namespace coroutine -{ -struct Config -{ - uint64_t max_num; - long hook_flags; - bool enable_preemptive_scheduler; -}; -} - -class PHPCoroutine -{ -public: - static const uint8_t MAX_EXEC_MSEC = 10; - static coroutine::Config config; - - 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 void defer(php_swoole_fci *fci); - - static bool enable_hook(int flags); - static bool disable_hook(); - - static void interrupt_thread_stop(); - - // TODO: remove old coro APIs (Manual) - static void yield_m(zval *return_value, php_coro_context *sw_php_context); - static int resume_m(php_coro_context *sw_current_context, zval *retval, zval *coro_retval); - - static inline long get_cid() - { - return sw_likely(active) ? Coroutine::get_current_cid() : -1; - } - - static inline long get_pcid(long cid = 0) - { - php_coro_task *task = cid == 0 ? get_task() : get_task_by_cid(cid); - return sw_likely(task) ? task->pcid : 0; - } - - static inline long get_elapsed(long cid = 0) - { - return sw_likely(active) ? Coroutine::get_elapsed(cid) : -1; - } - - static inline php_coro_task* get_task() - { - php_coro_task *task = (php_coro_task *) Coroutine::get_current_task(); - return task ? task : &main_task; - } - - static inline php_coro_task* get_origin_task(php_coro_task *task) - { - Coroutine *co = task->co->get_origin(); - return co ? (php_coro_task *) co->get_task() : &main_task; - } - - static inline php_coro_task* get_task_by_cid(long cid) - { - return cid == -1 ? &main_task : (php_coro_task *) Coroutine::get_task_by_cid(cid); - } - - static inline uint64_t get_max_num() - { - return config.max_num; - } - - static inline void set_max_num(uint64_t n) - { - config.max_num = n; - } - - static inline bool is_schedulable(php_coro_task *task) - { - return task->enable_scheduler && (swTimer_get_absolute_msec() - task->last_msec > MAX_EXEC_MSEC); - } - - static inline bool enable_scheduler() - { - php_coro_task *task = (php_coro_task *) Coroutine::get_current_task(); - if (task && task->enable_scheduler == 0) - { - task->enable_scheduler = 1; - return true; - } - return false; - } - - static inline bool disable_scheduler() - { - php_coro_task *task = (php_coro_task *) Coroutine::get_current_task(); - if (task && task->enable_scheduler == 1) - { - task->enable_scheduler = 0; - return true; - } - return false; - } - -protected: - static bool active; - static php_coro_task main_task; - - static bool interrupt_thread_running; - static pthread_t interrupt_thread_id; - - static void activate(); - static void error(int type, const char *error_filename, const uint32_t error_lineno, const char *format, va_list args); - - static inline void vm_stack_init(void); - static inline void vm_stack_destroy(void); - static inline void save_vm_stack(php_coro_task *task); - static inline void restore_vm_stack(php_coro_task *task); - static inline void save_og(php_coro_task *task); - static inline void restore_og(php_coro_task *task); - static inline void save_task(php_coro_task *task); - static inline void restore_task(php_coro_task *task); - static void on_yield(void *arg); - static void on_resume(void *arg); - static void on_close(void *arg); - static void main_func(void *arg); - - static void interrupt_thread_start(); - static void interrupt_thread_loop(); - static inline void record_last_msec(php_coro_task *task) - { - if (interrupt_thread_running) - { - task->last_msec = swTimer_get_absolute_msec(); - } - } -}; -} - -/** - * for gdb - */ -zend_executor_globals* php_swoole_get_executor_globals(); diff --git a/swoole_coroutine_scheduler.cc b/swoole_coroutine_scheduler.cc deleted file mode 100644 index 0e60c2ee90c..00000000000 --- a/swoole_coroutine_scheduler.cc +++ /dev/null @@ -1,393 +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: Xinyu Zhu | - | shiguangqi | - | Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#include "php_swoole_cxx.h" -#include "coroutine_c_api.h" - -#include - -using namespace std; -using swoole::coroutine::System; -using swoole::coroutine::Socket; -using swoole::Coroutine; -using swoole::PHPCoroutine; - -struct scheduler_task_t -{ - zend_long count; - zend_fcall_info fci; - zend_fcall_info_cache fci_cache; -}; - -struct scheduler_t -{ - queue *list; - bool started; - zend_object std; -}; - -static zend_class_entry *swoole_coroutine_scheduler_ce; -static zend_object_handlers swoole_coroutine_scheduler_handlers; - -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 PHP_METHOD(swoole_coroutine_scheduler, add); -static PHP_METHOD(swoole_coroutine_scheduler, parallel); -static PHP_METHOD(swoole_coroutine_scheduler, start); - -static sw_inline scheduler_t* scheduler_get_object(zend_object *obj) -{ - return (scheduler_t *) ((char *) obj - swoole_coroutine_scheduler_handlers.offset); -} - -static zend_object *scheduler_create_object(zend_class_entry *ce) -{ - scheduler_t *s = (scheduler_t *) ecalloc(1, sizeof(scheduler_t) + zend_object_properties_size(ce)); - zend_object_std_init(&s->std, ce); - object_properties_init(&s->std, ce); - s->std.handlers = &swoole_coroutine_scheduler_handlers; - return &s->std; -} - -static void scheduler_free_object(zend_object *object) -{ - scheduler_t *s = scheduler_get_object(object); - if (s->list) - { - while(!s->list->empty()) - { - scheduler_task_t *task = s->list->front(); - s->list->pop(); - sw_zend_fci_cache_discard(&task->fci_cache); - sw_zend_fci_params_discard(&task->fci); - efree(task); - } - delete s->list; - s->list = nullptr; - } - zend_object_std_dtor(&s->std); -} - -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, start, arginfo_swoole_void, ZEND_ACC_PUBLIC) - PHP_FE_END -}; - -void php_swoole_coroutine_scheduler_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY(swoole_coroutine_scheduler, "Swoole\\Coroutine\\Scheduler", NULL, "Co\\Scheduler", swoole_coroutine_scheduler_methods); - SW_SET_CLASS_SERIALIZABLE(swoole_coroutine_scheduler, zend_class_serialize_deny, zend_class_unserialize_deny); - 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); - SW_SET_CLASS_CUSTOM_OBJECT(swoole_coroutine_scheduler, scheduler_create_object, scheduler_free_object, scheduler_t, std); - swoole_coroutine_scheduler_ce->ce_flags |= ZEND_ACC_FINAL; -} - -static zend_fcall_info_cache exit_condition_fci_cache; -static bool exit_condition_cleaner; - -static int php_swoole_coroutine_reactor_can_exit(swReactor *reactor) -{ - zval retval; - int success; - - SW_ASSERT(exit_condition_fci_cache.function_handler); - ZVAL_NULL(&retval); - success = sw_zend_call_function_ex(NULL, &exit_condition_fci_cache, 0, nullptr, &retval); - if (UNEXPECTED(success != SUCCESS)) - { - php_swoole_fatal_error(E_ERROR, "Coroutine can_exit callback handler error"); - } - if (UNEXPECTED(EG(exception))) - { - zend_exception_error(EG(exception), E_ERROR); - } - return !(Z_TYPE_P(&retval) == IS_FALSE); -} - -PHP_METHOD(swoole_coroutine_scheduler, set) -{ - zval *zset = NULL; - HashTable *vht = NULL; - zval *ztmp; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY(zset) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - vht = Z_ARRVAL_P(zset); - if (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); - } - if (php_swoole_array_get_value(vht, "hook_flags", ztmp)) - { - PHPCoroutine::config.hook_flags = zval_get_long(ztmp); - } - if (php_swoole_array_get_value(vht, "enable_preemptive_scheduler", ztmp)) - { - PHPCoroutine::config.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)); - } - if (php_swoole_array_get_value(vht, "socket_dns_timeout", ztmp)) - { - double t = zval_get_double(ztmp); - if (t != 0) { Socket::default_dns_timeout = t; } - } - if (php_swoole_array_get_value(vht, "socket_connect_timeout", ztmp)) - { - double t = zval_get_double(ztmp); - if (t != 0) { Socket::default_connect_timeout = t; } - } - if (php_swoole_array_get_value(vht, "socket_timeout", ztmp)) - { - double t = zval_get_double(ztmp); - if (t != 0) { Socket::default_read_timeout = Socket::default_write_timeout = t; } - } - if (php_swoole_array_get_value(vht, "socket_read_timeout", ztmp)) - { - double t = zval_get_double(ztmp); - if (t != 0) { Socket::default_read_timeout = t; } - } - if (php_swoole_array_get_value(vht, "socket_write_timeout", ztmp)) - { - double t = zval_get_double(ztmp); - if (t != 0) { Socket::default_write_timeout = t; } - } - if (php_swoole_array_get_value(vht, "log_level", ztmp)) - { - zend_long level = zval_get_long(ztmp); - SwooleG.log_level = (uint32_t) (level < 0 ? UINT32_MAX : level); - } - if (php_swoole_array_get_value(vht, "trace_flags", ztmp)) - { - SwooleG.trace_flags = (uint32_t) SW_MAX(0, zval_get_long(ztmp)); - } - if (php_swoole_array_get_value(vht, "dns_cache_expire", ztmp)) - { - System::set_dns_cache_expire((time_t) zval_get_long(ztmp)); - } - if (php_swoole_array_get_value(vht, "dns_cache_capacity", ztmp)) - { - System::set_dns_cache_capacity((size_t) zval_get_long(ztmp)); - } - 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(); - } - if (php_swoole_array_get_value(vht, "display_errors", ztmp)) - { - SWOOLE_G(display_errors) = zval_is_true(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); - } - /* 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 (!ZVAL_IS_NULL(ztmp)) - { - if (!sw_zend_is_callable_ex(ztmp, NULL, 0, &func_name, NULL, &exit_condition_fci_cache, NULL)) - { - 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.reactor_can_exit = php_swoole_coroutine_reactor_can_exit; - if (SwooleTG.reactor) - { - SwooleTG.reactor->can_exit = php_swoole_coroutine_reactor_can_exit; - } - } - } - else - { - SwooleG.reactor_can_exit = nullptr; - if (SwooleTG.reactor) - { - SwooleTG.reactor->can_exit = nullptr; - } - } - } -} - -static void scheduler_add_task(scheduler_t *s, scheduler_task_t *task) -{ - if (!s->list) - { - s->list = new queue; - } - sw_zend_fci_cache_persist(&task->fci_cache); - sw_zend_fci_params_persist(&task->fci); - s->list->push(task); -} - -static PHP_METHOD(swoole_coroutine_scheduler, add) -{ - scheduler_t *s = scheduler_get_object(Z_OBJ_P(ZEND_THIS)); - if (s->started) - { - php_swoole_fatal_error(E_WARNING, "scheduler is running, unable to execute %s->add", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - RETURN_FALSE; - } - - scheduler_task_t *task = (scheduler_task_t *) ecalloc(1, sizeof(scheduler_task_t)); - - ZEND_PARSE_PARAMETERS_START(1, -1) - Z_PARAM_FUNC(task->fci, task->fci_cache) - Z_PARAM_VARIADIC('*', task->fci.params, task->fci.param_count) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - task->count = 1; - scheduler_add_task(s, task); -} - -static PHP_METHOD(swoole_coroutine_scheduler, parallel) -{ - scheduler_t *s = scheduler_get_object(Z_OBJ_P(ZEND_THIS)); - if (s->started) - { - php_swoole_fatal_error(E_WARNING, "scheduler is running, unable to execute %s->parallel", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - RETURN_FALSE; - } - - scheduler_task_t *task = (scheduler_task_t *) ecalloc(1, sizeof(scheduler_task_t)); - zend_long count; - - ZEND_PARSE_PARAMETERS_START(2, -1) - Z_PARAM_LONG(count) - Z_PARAM_FUNC(task->fci, task->fci_cache) - Z_PARAM_VARIADIC('*', task->fci.params, task->fci.param_count) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - task->count = count; - scheduler_add_task(s, task); -} - -static PHP_METHOD(swoole_coroutine_scheduler, start) -{ - scheduler_t *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)); - RETURN_FALSE; - } - if (php_swoole_reactor_init() < 0) - { - RETURN_FALSE; - } - - s->started = true; - - if (!s->list) - { - php_swoole_fatal_error(E_WARNING, "no coroutine task"); - RETURN_FALSE; - } - - while (!s->list->empty()) - { - scheduler_task_t *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); - } - sw_zend_fci_cache_discard(&task->fci_cache); - sw_zend_fci_params_discard(&task->fci); - efree(task); - } - php_swoole_event_wait(); - delete s->list; - s->list = nullptr; - s->started = false; - RETURN_TRUE; -} diff --git a/swoole_coroutine_system.cc b/swoole_coroutine_system.cc deleted file mode 100644 index 9817175acfe..00000000000 --- a/swoole_coroutine_system.cc +++ /dev/null @@ -1,886 +0,0 @@ -#include "swoole_coroutine_system.h" - -#include "ext/standard/file.h" -#include - -#include "coroutine_c_api.h" -#include "async.h" - -#include - -using namespace std; -using swoole::coroutine::System; -using swoole::coroutine::Socket; -using swoole::Coroutine; -using swoole::PHPCoroutine; - -struct tmp_socket -{ - php_coro_context context; - swSocket socket; - zend_string *buf; - uint32_t nbytes; - swTimer_node *timer; -}; - -static zend_class_entry *swoole_coroutine_system_ce; - -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, fread, arginfo_swoole_coroutine_system_fread, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, fwrite, arginfo_swoole_coroutine_system_fwrite, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(swoole_coroutine_system, fgets, arginfo_swoole_coroutine_system_fgets, 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, 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, statvfs, arginfo_swoole_coroutine_system_statvfs, 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) - PHP_FE_END -}; - -void php_swoole_coroutine_system_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY_BASE(swoole_coroutine_system, "Swoole\\Coroutine\\System", NULL, "Co\\System", swoole_coroutine_system_methods, NULL); - SW_SET_CLASS_CREATE(swoole_coroutine_system, sw_zend_create_object_deny); -} - -PHP_METHOD(swoole_coroutine_system, sleep) -{ - double seconds; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_DOUBLE(seconds) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (UNEXPECTED(seconds < SW_TIMER_MIN_SEC)) - { - php_swoole_fatal_error(E_WARNING, "Timer must be greater than or equal to " ZEND_TOSTR(SW_TIMER_MIN_SEC)); - RETURN_FALSE; - } - RETURN_BOOL(System::sleep(seconds) == 0); -} - -static void aio_onReadCompleted(swAio_event *event) -{ - zval *retval = NULL; - zval result; - - if (event->error == 0) - { - // TODO: Optimization: reduce memory copy - ZVAL_STRINGL(&result, (char* )event->buf, event->ret); - } - else - { - SwooleG.error = event->error; - ZVAL_FALSE(&result); - } - - php_coro_context *context = (php_coro_context *) event->object; - int ret = PHPCoroutine::resume_m(context, &result, retval); - if (ret == SW_CORO_ERR_END && retval) - { - zval_ptr_dtor(retval); - } - zval_ptr_dtor(&result); - efree(event->buf); - efree(context); -} - -static void aio_onFgetsCompleted(swAio_event *event) -{ - zval *retval = NULL; - zval result; - - if (event->ret != -1) - { - ZVAL_STRING(&result, (char* )event->buf); - } - else - { - SwooleG.error = event->error; - ZVAL_FALSE(&result); - } - - php_coro_context *context = (php_coro_context *) event->object; - php_stream *stream; - php_stream_from_zval_no_verify(stream, &context->coro_params); - - if (event->flags & SW_AIO_EOF) - { - stream->eof = 1; - } - - int ret = PHPCoroutine::resume_m(context, &result, retval); - if (ret == SW_CORO_ERR_END && retval) - { - zval_ptr_dtor(retval); - } - zval_ptr_dtor(&result); - efree(context); -} - -static void aio_onWriteCompleted(swAio_event *event) -{ - zval *retval = NULL; - zval result; - - if (event->ret < 0) - { - SwooleG.error = event->error; - ZVAL_FALSE(&result); - } - else - { - ZVAL_LONG(&result, event->ret); - } - - php_coro_context *context = (php_coro_context *) event->object; - int ret = PHPCoroutine::resume_m(context, &result, retval); - if (ret == SW_CORO_ERR_END && retval) - { - zval_ptr_dtor(retval); - } - efree(event->buf); - efree(context); -} - -static int co_socket_onReadable(swReactor *reactor, swEvent *event) -{ - tmp_socket *sock = (tmp_socket *) event->socket->object; - php_coro_context *context = &sock->context; - - zval *retval = NULL; - zval result; - - swoole_event_del(event->socket); - - if (sock->timer) - { - swoole_timer_del(sock->timer); - sock->timer = NULL; - } - - int n = read(event->fd, 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); - } - int ret = PHPCoroutine::resume_m(context, &result, retval); - zval_ptr_dtor(&result); - if (ret == SW_CORO_ERR_END && retval) - { - zval_ptr_dtor(retval); - } - efree(sock); - return SW_OK; -} - -static int co_socket_onWritable(swReactor *reactor, swEvent *event) -{ - tmp_socket *sock = (tmp_socket *) event->socket->object; - php_coro_context *context = &sock->context; - - zval *retval = NULL; - zval result; - - swoole_event_del(event->socket); - - if (sock->timer) - { - swoole_timer_del(sock->timer); - sock->timer = NULL; - } - - int n = write(event->fd, context->private_data, sock->nbytes); - if (n < 0) - { - SwooleG.error = errno; - ZVAL_FALSE(&result); - } - else - { - ZVAL_LONG(&result, n); - } - int ret = PHPCoroutine::resume_m(context, &result, retval); - if (ret == SW_CORO_ERR_END && retval) - { - zval_ptr_dtor(retval); - } - efree(sock); - return SW_OK; -} - -static void co_socket_read(int fd, zend_long length, INTERNAL_FUNCTION_PARAMETERS) -{ - php_swoole_check_reactor(); - if (!swReactor_isset_handler(SwooleTG.reactor, PHP_SWOOLE_FD_SOCKET)) - { - swReactor_set_handler(SwooleTG.reactor, PHP_SWOOLE_FD_CO_UTIL | SW_EVENT_READ, co_socket_onReadable); - swReactor_set_handler(SwooleTG.reactor, PHP_SWOOLE_FD_CO_UTIL | SW_EVENT_WRITE, co_socket_onWritable); - } - - tmp_socket *sock = (tmp_socket *) ecalloc(1, sizeof(tmp_socket)); - - sock->socket.fd = fd; - sock->socket.fdtype = (enum swFd_type) PHP_SWOOLE_FD_CO_UTIL; - sock->socket.object = sock; - - if (swoole_event_add(&sock->socket, SW_EVENT_READ) < 0) - { - SwooleG.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) -{ - int ret = write(fd, str, l_str); - if (ret < 0) - { - if (errno == EAGAIN) - { - goto _yield; - } - SwooleG.error = errno; - RETURN_FALSE; - } - else - { - RETURN_LONG(ret); - } - - tmp_socket *sock; - - _yield: - sock = (tmp_socket *) ecalloc(1, sizeof(tmp_socket)); - - sock->socket.fd = fd; - sock->socket.fdtype = (enum swFd_type) PHP_SWOOLE_FD_CO_UTIL; - sock->socket.object = sock; - - if (swoole_event_add(&sock->socket, SW_EVENT_WRITE) < 0) - { - SwooleG.error = errno; - RETURN_FALSE; - } - - php_coro_context *context = &sock->context; - context->private_data = str; - - sock->nbytes = l_str; - - PHPCoroutine::yield_m(return_value, context); -} - -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 = 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) - { - SwooleG.error = errno; - RETURN_FALSE; - } - off_t _seek = swoole_coroutine_lseek(fd, 0, SEEK_CUR); - if (_seek < 0) - { - SwooleG.error = errno; - RETURN_FALSE; - } - if (_seek >= file_stat.st_size) - { - length = SW_BUFFER_SIZE_STD; - } - else - { - length = file_stat.st_size - _seek; - } - } - - swAio_event ev; - bzero(&ev, sizeof(swAio_event)); - - ev.nbytes = length; - ev.buf = emalloc(ev.nbytes + 1); - if (!ev.buf) - { - RETURN_FALSE; - } - - php_coro_context *context = (php_coro_context *) emalloc(sizeof(php_coro_context)); - - ((char *) ev.buf)[length] = 0; - ev.flags = 0; - ev.object = context; - ev.handler = swAio_handler_fread; - ev.callback = aio_onReadCompleted; - ev.fd = fd; - - swTrace("fd=%d, offset=%jd, length=%ld", fd, (intmax_t) ev.offset, ev.nbytes); - - php_swoole_check_reactor(); - ssize_t ret = swAio_dispatch(&ev); - if (ret < 0) - { - efree(context); - RETURN_FALSE; - } - PHPCoroutine::yield_m(return_value, context); -} - -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 = 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; - } - - swAio_event ev; - bzero(&ev, sizeof(swAio_event)); - - 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 == NULL) - { - RETURN_FALSE; - } - } - - if (stream->readbuf == NULL) - { - stream->readbuflen = stream->chunk_size; - stream->readbuf = (uchar *) emalloc(stream->chunk_size); - } - - ev.nbytes = stream->readbuflen; - ev.buf = stream->readbuf; - if (!ev.buf) - { - RETURN_FALSE; - } - - php_coro_context *context = (php_coro_context *) emalloc(sizeof(php_coro_context)); - - ev.flags = 0; - ev.object = context; - ev.callback = aio_onFgetsCompleted; - ev.handler = swAio_handler_fgets; - ev.fd = fd; - ev.req = (void *) file; - - swTrace("fd=%d, offset=%jd, length=%ld", fd, (intmax_t) ev.offset, ev.nbytes); - - php_swoole_check_reactor(); - ssize_t ret = swAio_dispatch(&ev); - if (ret < 0) - { - efree(context); - RETURN_FALSE; - } - - context->coro_params = *handle; - - PHPCoroutine::yield_m(return_value, context); -} - -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 = 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; - } - - swAio_event ev; - bzero(&ev, sizeof(swAio_event)); - - ev.nbytes = length; - ev.buf = estrndup(str, length); - - if (!ev.buf) - { - RETURN_FALSE; - } - - php_coro_context *context = (php_coro_context *) emalloc(sizeof(php_coro_context)); - - ev.flags = 0; - ev.object = context; - ev.handler = swAio_handler_fwrite; - ev.callback = aio_onWriteCompleted; - ev.fd = fd; - - swTrace("fd=%d, offset=%jd, length=%ld", fd, (intmax_t) ev.offset, ev.nbytes); - - php_swoole_check_reactor(); - ssize_t ret = swAio_dispatch(&ev); - if (ret < 0) - { - efree(context); - RETURN_FALSE; - } - PHPCoroutine::yield_m(return_value, context); -} - -PHP_METHOD(swoole_coroutine_system, readFile) -{ - char *filename; - size_t l_filename; - zend_long flags = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STRING(filename, l_filename) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(flags) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - swString *result = System::read_file(filename, flags & LOCK_EX); - if (result == NULL) - { - RETURN_FALSE; - } - else - { - RETVAL_STRINGL(result->str, result->length); - swString_free(result); - } -} - -PHP_METHOD(swoole_coroutine_system, writeFile) -{ - char *filename; - size_t l_filename; - char *data; - size_t l_data; - zend_long flags = 0; - - ZEND_PARSE_PARAMETERS_START(2, 3) - Z_PARAM_STRING(filename, l_filename) - Z_PARAM_STRING(data, l_data) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(flags) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - int _flags = O_CREAT | O_WRONLY; - if (flags & PHP_FILE_APPEND) - { - _flags |= O_APPEND; - } - else - { - _flags |= O_TRUNC; - } - - ssize_t retval = System::write_file(filename, data, l_data, flags & LOCK_EX, _flags); - if (retval < 0) - { - RETURN_FALSE; - } - else - { - RETURN_LONG(retval); - } -} - -PHP_FUNCTION(swoole_coroutine_gethostbyname) -{ - Coroutine::get_current_safe(); - - char *domain_name; - size_t l_domain_name; - 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; - } - - if (l_domain_name == 0) - { - php_swoole_fatal_error(E_WARNING, "domain name is empty"); - RETURN_FALSE; - } - - if (family != AF_INET && family != AF_INET6) - { - php_swoole_fatal_error(E_WARNING, "unknown protocol family, must be AF_INET or AF_INET6"); - RETURN_FALSE; - } - - string address = System::gethostbyname(string(domain_name, l_domain_name), family, timeout); - if (address.empty()) - { - RETURN_FALSE; - } - else - { - RETURN_STRINGL(address.c_str(), address.length()); - } -} - -PHP_FUNCTION(swoole_clear_dns_cache) -{ - System::clear_dns_cache(); -} - -PHP_METHOD(swoole_coroutine_system, getaddrinfo) -{ - char *hostname; - size_t l_hostname; - zend_long family = AF_INET; - zend_long socktype = SOCK_STREAM; - zend_long protocol = IPPROTO_TCP; - char *service = NULL; - 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; - } - - if (l_hostname == 0) - { - php_swoole_fatal_error(E_WARNING, "hostname is empty"); - RETURN_FALSE; - } - - if (family != AF_INET && family != AF_INET6) - { - php_swoole_fatal_error(E_WARNING, "unknown protocol family, must be AF_INET or AF_INET6"); - RETURN_FALSE; - } - - string str_service(service ? service : ""); - vector result = System::getaddrinfo(hostname, family, socktype, protocol, str_service, timeout); - - if (result.empty()) - { - RETURN_FALSE; - } - - array_init(return_value); - for (auto i = result.begin(); i != result.end(); i++) - { - add_next_index_stringl(return_value, i->c_str(), i->length()); - } -} - -PHP_METHOD(swoole_coroutine_system, statvfs) -{ - char *path; - size_t l_path; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(path, l_path) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - struct statvfs _stat; - swoole_coroutine_statvfs(path, &_stat); - - array_init(return_value); - add_assoc_long(return_value, "bsize", _stat.f_bsize); - add_assoc_long(return_value, "frsize", _stat.f_frsize); - add_assoc_long(return_value, "blocks", _stat.f_blocks); - add_assoc_long(return_value, "bfree", _stat.f_bfree); - add_assoc_long(return_value, "bavail", _stat.f_bavail); - add_assoc_long(return_value, "files", _stat.f_files); - add_assoc_long(return_value, "ffree", _stat.f_ffree); - add_assoc_long(return_value, "favail", _stat.f_favail); - add_assoc_long(return_value, "fsid", _stat.f_fsid); - add_assoc_long(return_value, "flag", _stat.f_flag); - add_assoc_long(return_value, "namemax", _stat.f_namemax); -} - -PHP_METHOD(swoole_coroutine_system, exec) -{ - char *command; - size_t command_len; - zend_bool get_error_stream = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STRING(command, command_len) - Z_PARAM_OPTIONAL - 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); - RETURN_FALSE; - } - - swString *buffer = swString_new(1024); - if (buffer == NULL) - { - 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 (swString_extend(buffer, buffer->size * 2) < 0) - { - break; - } - } - } - else - { - break; - } - } - socket.close(); - - zval zdata; - if (buffer->length == 0) - { - ZVAL_EMPTY_STRING(&zdata); - } - else - { - ZVAL_STRINGL(&zdata, buffer->str, buffer->length); - } - swString_free(buffer); - - 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; - } -} - -static void swoole_coroutine_system_wait(INTERNAL_FUNCTION_PARAMETERS, pid_t pid, double timeout) -{ - int status; - - Coroutine::get_current_safe(); - - if (pid < 0) - { - pid = System::wait(&status, timeout); - } - else - { - pid = System::waitpid(pid, &status, 0, timeout); - } - if (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)); - } - else - { - SwooleG.error = errno; - RETURN_FALSE; - } -} - -PHP_METHOD(swoole_coroutine_system, wait) -{ - double timeout = -1; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - swoole_coroutine_system_wait(INTERNAL_FUNCTION_PARAM_PASSTHRU, -1, timeout); -} - -PHP_METHOD(swoole_coroutine_system, waitPid) -{ - zend_long pid; - double timeout = -1; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_LONG(pid) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - swoole_coroutine_system_wait(INTERNAL_FUNCTION_PARAM_PASSTHRU, pid, timeout); -} - -PHP_METHOD(swoole_coroutine_system, waitSignal) -{ - zend_long signo; - double timeout = -1; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_LONG(signo) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (!System::wait_signal(signo, timeout)) - { - if (errno == 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); - } - SwooleG.error = errno; - RETURN_FALSE; - } - - RETURN_TRUE; -} - -PHP_METHOD(swoole_coroutine_system, waitEvent) -{ - zval *zfd; - zend_long events = SW_EVENT_READ; - double timeout = -1; - - ZEND_PARSE_PARAMETERS_START(1, 3) - Z_PARAM_ZVAL(zfd) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(events) - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - int fd = swoole_convert_to_fd(zfd); - if (fd < 0) - { - php_swoole_fatal_error(E_WARNING, "unknow fd type"); - RETURN_FALSE; - } - - events = System::wait_event(fd, events, timeout); - - RETURN_LONG(events); -} diff --git a/swoole_coroutine_system.h b/swoole_coroutine_system.h deleted file mode 100644 index 20aac0fa449..00000000000 --- a/swoole_coroutine_system.h +++ /dev/null @@ -1,111 +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: Xinyu Zhu | - | shiguangqi | - | Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#include "php_swoole_cxx.h" - -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() - -PHP_METHOD(swoole_coroutine_system, exec); -PHP_METHOD(swoole_coroutine_system, sleep); -PHP_METHOD(swoole_coroutine_system, fread); -PHP_METHOD(swoole_coroutine_system, fgets); -PHP_METHOD(swoole_coroutine_system, fwrite); -PHP_METHOD(swoole_coroutine_system, statvfs); -PHP_METHOD(swoole_coroutine_system, getaddrinfo); -PHP_METHOD(swoole_coroutine_system, readFile); -PHP_METHOD(swoole_coroutine_system, writeFile); -PHP_METHOD(swoole_coroutine_system, wait); -PHP_METHOD(swoole_coroutine_system, waitPid); -PHP_METHOD(swoole_coroutine_system, waitSignal); -PHP_METHOD(swoole_coroutine_system, waitEvent); diff --git a/swoole_event.cc b/swoole_event.cc deleted file mode 100644 index 1952d1e3cb7..00000000000 --- a/swoole_event.cc +++ /dev/null @@ -1,890 +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: Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#include "php_swoole_cxx.h" -#include "server.h" - -using namespace swoole; -using namespace std; - -static unordered_map event_socket_map; - -zend_class_entry *swoole_event_ce; -static zend_object_handlers swoole_event_handlers; - -typedef struct -{ - zval zsocket; - zend_fcall_info_cache fci_cache_read; - zend_fcall_info_cache fci_cache_write; -} php_event_object; - -static int php_swoole_event_onRead(swReactor *reactor, swEvent *event); -static int php_swoole_event_onWrite(swReactor *reactor, swEvent *event); -static int php_swoole_event_onError(swReactor *reactor, swEvent *event); -static void php_swoole_event_onDefer(void *data); -static void php_swoole_event_onEndCallback(void *data); - -static PHP_FUNCTION(swoole_event_add); -static PHP_FUNCTION(swoole_event_set); -static PHP_FUNCTION(swoole_event_del); -static PHP_FUNCTION(swoole_event_write); -static PHP_FUNCTION(swoole_event_wait); -static PHP_FUNCTION(swoole_event_rshutdown); -static PHP_FUNCTION(swoole_event_exit); -static PHP_FUNCTION(swoole_event_defer); -static PHP_FUNCTION(swoole_event_cycle); -static PHP_FUNCTION(swoole_event_dispatch); -static PHP_FUNCTION(swoole_event_isset); - -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) - PHP_FE_END -}; - -void php_swoole_event_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY(swoole_event, "Swoole\\Event", "swoole_event", NULL, 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"); -} - -static void php_event_object_free(void* data) -{ - php_event_object *peo = (php_event_object*) data; - if (peo->fci_cache_read.function_handler) - { - sw_zend_fci_cache_discard(&peo->fci_cache_read); - } - if (peo->fci_cache_write.function_handler) - { - sw_zend_fci_cache_discard(&peo->fci_cache_write); - } - zval_ptr_dtor((&peo->zsocket)); - efree(peo); -} - -static int php_swoole_event_onRead(swReactor *reactor, swEvent *event) -{ - php_event_object *peo = (php_event_object *) event->socket->object; - - if (UNEXPECTED(sw_zend_call_function_ex2(NULL, &peo->fci_cache_read, 1, &peo->zsocket, NULL) != SUCCESS)) - { - php_swoole_fatal_error(E_WARNING, "%s: onRead callback handler error, fd [%d] will be removed from reactor", ZSTR_VAL(swoole_event_ce->name), swoole_convert_to_fd(&peo->zsocket)); - event->socket->object = NULL; - swoole_event_defer(php_event_object_free, peo); - swoole_event_del(event->socket); - return SW_ERR; - } - - return SW_OK; -} - -static int php_swoole_event_onWrite(swReactor *reactor, swEvent *event) -{ - php_event_object *peo = (php_event_object *) event->socket->object; - - if (UNEXPECTED(sw_zend_call_function_ex2(NULL, &peo->fci_cache_write, 1, &peo->zsocket, NULL) != SUCCESS)) - { - php_swoole_fatal_error(E_WARNING, "%s: onWrite callback handler error, fd [%d] will be removed from reactor", ZSTR_VAL(swoole_event_ce->name), swoole_convert_to_fd(&peo->zsocket)); - event->socket->object = NULL; - swoole_event_defer(php_event_object_free, peo); - swoole_event_del(event->socket); - return SW_ERR; - } - - return SW_OK; -} - -static int php_swoole_event_onError(swReactor *reactor, swEvent *event) -{ - if (!(event->socket->events & SW_EVENT_ERROR)) - { - if (event->socket->events & SW_EVENT_READ) - { - return swReactor_get_handler(reactor, SW_EVENT_READ, event->socket->fdtype)(reactor, event); - } - else - { - return swReactor_get_handler(reactor, SW_EVENT_WRITE, event->socket->fdtype)(reactor, event); - } - } - - int error; - socklen_t len = sizeof(error); - - if (getsockopt(event->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) - { - php_swoole_sys_error(E_WARNING, "swoole_event->onError[1]: getsockopt[sock=%d] failed", event->fd); - } - - if (error != 0) - { - php_swoole_fatal_error(E_WARNING, "swoole_event->onError[1]: socket error. Error: %s [%d]", strerror(error), error); - } - - php_event_object_free(event->socket->object); - swoole_event_del(event->socket); - - return SW_OK; -} - -static void php_swoole_event_onDefer(void *data) -{ - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) data; - - if (UNEXPECTED(sw_zend_call_function_ex2(NULL, fci_cache, 0, NULL, NULL) != SUCCESS)) - { - 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); -} - -static void php_swoole_event_onEndCallback(void *data) -{ - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) data; - - if (UNEXPECTED(sw_zend_call_function_ex2(NULL, (zend_fcall_info_cache *) fci_cache, 0, NULL, NULL) != SUCCESS)) - { - php_swoole_error(E_WARNING, "%s::defer callback handler error", ZSTR_VAL(swoole_event_ce->name)); - } -} - -int php_swoole_reactor_init() -{ - if (!SWOOLE_G(cli)) - { - php_swoole_fatal_error(E_ERROR, "async-io must be used in PHP CLI mode"); - return SW_ERR; - } - - if (sw_server()) - { - if (swIsTaskWorker() && !sw_server()->task_enable_coroutine) - { - php_swoole_fatal_error(E_ERROR, "Unable to use async-io in task processes, please set `task_enable_coroutine` to true"); - return SW_ERR; - } - if (swIsManager()) - { - php_swoole_fatal_error(E_ERROR, "Unable to use async-io in manager process"); - return SW_ERR; - } - } - if (!SwooleTG.reactor) - { - swTraceLog(SW_TRACE_PHP, "init reactor"); - - swoole_event_init(); - SwooleTG.reactor->wait_exit = 1; - - php_swoole_register_shutdown_function("Swoole\\Event::rshutdown"); - } - return SW_OK; -} - -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 (!SwooleTG.reactor) - { - return; - } - -#ifdef HAVE_SIGNALFD - if (SwooleTG.reactor->check_signalfd) - { - swSignalfd_setup(SwooleTG.reactor); - } -#endif - if (!swReactor_empty(SwooleTG.reactor)) - { - // 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) - zend_bool in_shutdown = EG(flags) & EG_FLAGS_IN_SHUTDOWN; - EG(flags) &= ~EG_FLAGS_IN_SHUTDOWN; -#endif - int ret = SwooleTG.reactor->wait(SwooleTG.reactor, NULL); - if (ret < 0) - { - php_swoole_sys_error(E_ERROR, "reactor wait failed"); - } -#if defined(EG_FLAGS_IN_SHUTDOWN) && !defined(EG_FLAGS_OBJECT_STORE_NO_REUSE) - if (in_shutdown) - { - EG(flags) |= EG_FLAGS_IN_SHUTDOWN; - } -#endif - } - swoole_event_free(); -} - -void php_swoole_event_exit() -{ - if (SwooleTG.reactor) - { - SwooleTG.reactor->running = 0; - } -} - -int swoole_convert_to_fd(zval *zsocket) -{ - int fd = -1; - - switch (Z_TYPE_P(zsocket)) - { - case IS_RESOURCE: - { - php_stream *stream; - if ((php_stream_from_zval_no_verify(stream, zsocket))) - { - if (php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void **) &fd, 1) == SUCCESS && fd >= 0) - { - return fd; - } - } -#ifdef SWOOLE_SOCKETS_SUPPORT - else - { - php_socket *php_sock; - if ((php_sock = (php_socket *) zend_fetch_resource_ex(zsocket, NULL, php_sockets_le_socket()))) - { - 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; - } - case IS_LONG: - { - fd = Z_LVAL_P(zsocket); - if (fd < 0) - { - php_swoole_fatal_error(E_WARNING, "invalid file descriptor#%d passed", fd); - return SW_ERR; - } - return fd; - } - case IS_OBJECT: - { - zval *zfd = NULL; - if (instanceof_function(Z_OBJCE_P(zsocket), swoole_socket_coro_ce)) - { - zfd = sw_zend_read_property(Z_OBJCE_P(zsocket), zsocket, ZEND_STRL("fd"), 0); - } - else if (instanceof_function(Z_OBJCE_P(zsocket), swoole_client_ce)) - { - zfd = sw_zend_read_property(Z_OBJCE_P(zsocket), zsocket, ZEND_STRL("sock"), 0); - } - else if (instanceof_function(Z_OBJCE_P(zsocket), swoole_process_ce)) - { - zfd = sw_zend_read_property(Z_OBJCE_P(zsocket), zsocket, ZEND_STRL("pipe"), 0); - } - if (zfd == NULL || Z_TYPE_P(zfd) != IS_LONG) - { - return SW_ERR; - } - return Z_LVAL_P(zfd); - } - default: - php_swoole_fatal_error(E_WARNING, "invalid file descriptor passed"); - return SW_ERR; - } -} - -int swoole_convert_to_fd_ex(zval *zsocket, int *async) -{ - int fd; - - *async = 0; - if (Z_TYPE_P(zsocket) == IS_RESOURCE) - { - php_stream *stream; - if ((php_stream_from_zval_no_verify(stream, zsocket))) - { - if (php_stream_cast(stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void **)&fd, 1) == SUCCESS && fd >= 0) - { - *async = (stream->wrapper && (stream->wrapper->wops == php_plain_files_wrapper.wops)) ? 0 : 1; - return fd; - } - } -#ifdef SWOOLE_SOCKETS_SUPPORT - else - { - php_socket *php_sock; - if ((php_sock = (php_socket *) zend_fetch_resource_ex(zsocket, NULL, php_sockets_le_socket()))) - { - fd = php_sock->bsd_socket; - *async = 1; - 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; -} - -#ifdef SWOOLE_SOCKETS_SUPPORT -php_socket* swoole_convert_to_socket(int sock) -{ - php_socket *socket_object = (php_socket *) emalloc(sizeof *socket_object); - bzero(socket_object, sizeof(php_socket)); - 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 NULL; - } - - 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); - } - return socket_object; -} - -void swoole_php_socket_free(zval *zsocket) -{ - php_socket *php_sock; - php_sock = (php_socket *) zend_fetch_resource_ex(zsocket, NULL, php_sockets_le_socket()); - php_sock->bsd_socket = -1; - sw_zval_free(zsocket); -} -#endif - -static void check_reactor() -{ - php_swoole_check_reactor(); - - if (!swReactor_isset_handler(SwooleTG.reactor, SW_FD_USER)) - { - swReactor_set_handler(SwooleTG.reactor, SW_FD_USER | SW_EVENT_READ, php_swoole_event_onRead); - swReactor_set_handler(SwooleTG.reactor, SW_FD_USER | SW_EVENT_WRITE, php_swoole_event_onWrite); - swReactor_set_handler(SwooleTG.reactor, SW_FD_USER | SW_EVENT_ERROR, php_swoole_event_onError); - } -} - -static swSocket* get_event_socket(int socket_fd) -{ - auto i = event_socket_map.find(socket_fd); - if (i == event_socket_map.end()) - { - return nullptr; - } - return i->second; -} - -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; - - 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_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 emoty"); - RETURN_FALSE; - } - - int socket_fd = swoole_convert_to_fd(zfd); - if (socket_fd < 0) - { - php_swoole_fatal_error(E_WARNING, "unknow fd type"); - RETURN_FALSE; - } - if (socket_fd == 0 && (events & SW_EVENT_WRITE)) - { - php_swoole_fatal_error(E_WARNING, "invalid socket fd [%d]", socket_fd); - RETURN_FALSE; - } - if (event_socket_map.find(socket_fd) != event_socket_map.end()) - { - php_swoole_fatal_error(E_WARNING, "already exist"); - RETURN_FALSE; - } - if (!(events & (SW_EVENT_WRITE | SW_EVENT_READ))) - { - php_swoole_fatal_error(E_WARNING, "invalid events"); - RETURN_FALSE; - } - - php_event_object *peo = (php_event_object *) ecalloc(1, sizeof(php_event_object)); - - 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; - } - - check_reactor(); - - swSocket *socket = swSocket_new(socket_fd, SW_FD_USER); - if (!socket) - { - RETURN_FALSE; - } - - swSocket_set_nonblock(socket); - socket->object = peo; - - if (swoole_event_add(socket, events) < 0) - { - php_swoole_fatal_error(E_WARNING, "swoole_event_add failed"); - efree(socket); - php_event_object_free(peo); - RETURN_FALSE; - } - - event_socket_map[socket_fd] = socket; - - RETURN_LONG(socket_fd); -} - -static PHP_FUNCTION(swoole_event_write) -{ - zval *zfd; - char *data; - size_t len; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "zs", &zfd, &data, &len) == FAILURE) - { - RETURN_FALSE; - } - - if (len == 0) - { - php_swoole_fatal_error(E_WARNING, "data empty"); - RETURN_FALSE; - } - - int socket_fd = swoole_convert_to_fd(zfd); - if (socket_fd < 0) - { - php_swoole_fatal_error(E_WARNING, "unknow type"); - RETURN_FALSE; - } - - swSocket *socket = get_event_socket(socket_fd); - if (socket == nullptr) - { - php_swoole_fatal_error(E_WARNING, "socket[%d] is not found in the reactor", socket_fd); - RETURN_FALSE; - } - - check_reactor(); - if (swoole_event_write(socket, data, len) < 0) - { - RETURN_FALSE; - } - else - { - RETURN_TRUE; - } -} - -static PHP_FUNCTION(swoole_event_set) -{ - if (!SwooleTG.reactor) - { - php_swoole_fatal_error(E_WARNING, "reactor is not ready, cannot call swoole_event_set"); - RETURN_FALSE; - } - - 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; - - 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_LONG(events) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - int socket_fd = swoole_convert_to_fd(zfd); - if (socket_fd < 0) - { - php_swoole_fatal_error(E_WARNING, "unknow type"); - RETURN_FALSE; - } - - swSocket *socket = get_event_socket(socket_fd); - if (socket == nullptr) - { - php_swoole_fatal_error(E_WARNING, "socket[%d] is not found in the reactor", socket_fd); - RETURN_FALSE; - } - - php_event_object *reactor_fd = (php_event_object *) 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); - } - reactor_fd->fci_cache_read = fci_cache_read; - } - if (fci_write.size != 0) - { - if (reactor_fd->fci_cache_write.function_handler) - { - sw_zend_fci_cache_discard(&reactor_fd->fci_cache_write); - } - reactor_fd->fci_cache_write = fci_cache_write; - } - - if ((events & SW_EVENT_READ) && reactor_fd->fci_cache_read.function_handler == NULL) - { - php_swoole_fatal_error(E_WARNING, "%s: unable to find read 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 == NULL) - { - php_swoole_fatal_error(E_WARNING, "%s: unable to find write callback of fd [%d]", ZSTR_VAL(swoole_event_ce->name), socket_fd); - RETURN_FALSE; - } - if (swoole_event_set(socket, events) < 0) - { - php_swoole_fatal_error(E_WARNING, "%s::set failed", ZSTR_VAL(swoole_event_ce->name)); - RETURN_FALSE; - } - - RETURN_TRUE; -} - -static PHP_FUNCTION(swoole_event_del) -{ - zval *zfd; - - if (!SwooleTG.reactor) - { - php_swoole_fatal_error(E_WARNING, "reactor is not ready, cannot call swoole_event_del"); - RETURN_FALSE; - } - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zfd) == FAILURE) - { - RETURN_FALSE; - } - - int socket_fd = swoole_convert_to_fd(zfd); - if (socket_fd < 0) - { - php_swoole_fatal_error(E_WARNING, "unknow type"); - RETURN_FALSE; - } - - swSocket *socket = get_event_socket(socket_fd); - if (!socket) - { - RETURN_FALSE; - } - swoole_event_defer(php_event_object_free, socket->object); - int retval = swoole_event_del(socket); - event_socket_map.erase(socket_fd); - socket->fd = -1; - swSocket_free(socket); - RETURN_BOOL(retval == SW_OK); -} - -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)); - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_FUNC(fci, *fci_cache) - ZEND_PARSE_PARAMETERS_END_EX(efree(fci_cache); RETURN_FALSE); - - php_swoole_check_reactor(); - - sw_zend_fci_cache_persist(fci_cache); - swoole_event_defer(php_swoole_event_onDefer, fci_cache); - - RETURN_TRUE; -} - -static PHP_FUNCTION(swoole_event_cycle) -{ - if (!SwooleTG.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; - zend_bool before = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_FUNC_EX(_fci, _fci_cache, 1, 0) - Z_PARAM_OPTIONAL - Z_PARAM_BOOL(before) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (_fci.size == 0) - { - if (SwooleTG.reactor->idle_task.callback == NULL) - { - RETURN_FALSE; - } - else - { - swoole_event_defer(sw_zend_fci_cache_free, SwooleTG.reactor->idle_task.data); - SwooleTG.reactor->idle_task.callback = NULL; - SwooleTG.reactor->idle_task.data = NULL; - 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); - - if (!before) - { - if (SwooleTG.reactor->idle_task.data != NULL) - { - swoole_event_defer(sw_zend_fci_cache_free, SwooleTG.reactor->idle_task.data); - } - - SwooleTG.reactor->idle_task.callback = php_swoole_event_onEndCallback; - SwooleTG.reactor->idle_task.data = fci_cache; - } - else - { - if (SwooleTG.reactor->future_task.data != NULL) - { - swoole_event_defer(sw_zend_fci_cache_free, SwooleTG.reactor->future_task.data); - } - - SwooleTG.reactor->future_task.callback = php_swoole_event_onEndCallback; - SwooleTG.reactor->future_task.data = fci_cache; - //Registration onBegin callback function - swReactor_activate_future_task(SwooleTG.reactor); - } - - RETURN_TRUE; -} - -static PHP_FUNCTION(swoole_event_exit) -{ - php_swoole_event_exit(); -} - -static PHP_FUNCTION(swoole_event_wait) -{ - if (!SwooleTG.reactor) - { - return; - } - php_swoole_event_wait(); -} - -static PHP_FUNCTION(swoole_event_rshutdown) -{ - /* prevent the program from jumping out of the rshutdown */ - zend_try - { - PHP_FN(swoole_event_wait)(INTERNAL_FUNCTION_PARAM_PASSTHRU); - } - zend_end_try(); -} - -static PHP_FUNCTION(swoole_event_dispatch) -{ - if (!SwooleTG.reactor) - { - RETURN_FALSE; - } - SwooleTG.reactor->once = 1; - -#ifdef HAVE_SIGNALFD - if (SwooleTG.reactor->check_signalfd) - { - swSignalfd_setup(SwooleTG.reactor); - } -#endif - - int ret = SwooleTG.reactor->wait(SwooleTG.reactor, NULL); - if (ret < 0) - { - php_swoole_sys_error(E_ERROR, "reactor wait failed"); - } - - SwooleTG.reactor->once = 0; - RETURN_TRUE; -} - -static PHP_FUNCTION(swoole_event_isset) -{ - if (!SwooleTG.reactor) - { - RETURN_FALSE; - } - - zval *zfd; - zend_long events = SW_EVENT_READ | SW_EVENT_WRITE; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|l", &zfd, &events) == FAILURE) - { - RETURN_FALSE; - } - - int socket_fd = swoole_convert_to_fd(zfd); - if (socket_fd < 0) - { - php_swoole_fatal_error(E_WARNING, "unknow type"); - RETURN_FALSE; - } - - swSocket *_socket = get_event_socket(socket_fd); - if (_socket == nullptr || _socket->removed) - { - RETURN_FALSE; - } - if (_socket->events & events) - { - RETURN_TRUE; - } - else - { - RETURN_FALSE; - } -} diff --git a/swoole_http.h b/swoole_http.h deleted file mode 100644 index 7144057efff..00000000000 --- a/swoole_http.h +++ /dev/null @@ -1,368 +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: Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#pragma once - -#include "thirdparty/swoole_http_parser.h" -#include "thirdparty/multipart_parser.h" - -#include - -#ifdef SW_HAVE_ZLIB -#include -#define SW_ZLIB_ENCODING_RAW -0xf -#define SW_ZLIB_ENCODING_GZIP 0x1f -#define SW_ZLIB_ENCODING_DEFLATE 0x0f -#define SW_ZLIB_ENCODING_ANY 0x2f -#endif - -#ifdef SW_USE_HTTP2 -#include "thirdparty/nghttp2/nghttp2.h" -#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, - HTTP_HEADER_TRANSFER_ENCODING = 1u << 6, - HTTP_HEADER_ACCEPT_ENCODING = 1u << 7, -}; - -enum http_compress_method -{ - HTTP_COMPRESS_NONE, - HTTP_COMPRESS_GZIP, - HTTP_COMPRESS_DEFLATE, - HTTP_COMPRESS_BR, -}; - -struct http_request -{ - int version; - char *path; - uint32_t path_len; - const char *ext; - uint32_t ext_len; - uint8_t post_form_urlencoded; - - zval zdata; - size_t body_length; - swString *chunked_body; -#ifdef SW_USE_HTTP2 - swString *h2_data_buffer; -#endif - - // Notice: Do not change the order - zval *zobject; - zval _zobject; - zval *zserver; - zval _zserver; - zval *zheader; - zval _zheader; - zval *zget; - zval _zget; - zval *zpost; - zval _zpost; - zval *zcookie; - zval _zcookie; - zval *zfiles; - zval _zfiles; - zval *ztmpfiles; - zval _ztmpfiles; -}; - -struct http_response -{ - enum swoole_http_method method; - int version; - int status; - char* reason; - - // Notice: Do not change the order - zval *zobject; - zval _zobject; - zval *zheader; - zval _zheader; - zval *zcookie; - zval _zcookie; - zval *ztrailer; - zval _ztrailer; -}; - -#ifdef SW_USE_HTTP2 -class http2_stream; -#endif - -struct http_context -{ - int fd; - uchar completed :1; - uchar end :1; - uchar send_header :1; -#ifdef SW_HAVE_COMPRESSION - uchar enable_compression :1; - uchar accept_compression :1; -#endif - uchar send_chunked :1; - uchar recv_chunked :1; - uchar keepalive :1; - uchar websocket :1; -#ifdef SW_HAVE_ZLIB - uchar websocket_compression :1; -#endif - uchar upgrade :1; - uchar detached :1; - uchar parse_cookie :1; - uchar parse_body :1; - uchar parse_files :1; - uchar co_socket :1; - -#ifdef SW_USE_HTTP2 - uchar http2 :1; - http2_stream* stream; -#endif - -#ifdef SW_HAVE_COMPRESSION - int8_t compression_level; - int8_t compression_method; -#endif - - http_request request; - http_response response; - - swoole_http_parser parser; - multipart_parser *mt_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; - zval *current_multipart_header; - - const char *upload_tmp_dir; - - void *private_data; - bool (*send)(http_context* ctx, const char *data, size_t length); - bool (*sendfile)(http_context* ctx, const char *file, uint32_t l_file, off_t offset, size_t length); - bool (*close)(http_context* ctx); -}; - -#ifdef SW_USE_HTTP2 -class http2_session; -class http2_stream -{ -public: - http_context* ctx; - // uint8_t priority; // useless now - uint32_t id; - // flow control - uint32_t send_window; - uint32_t recv_window; - - http2_stream(http2_session *client, uint32_t _id); - ~http2_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_trailer(); - - void reset(uint32_t error_code); -}; - -class http2_session -{ -public: - int fd; - std::unordered_map streams; - - 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; - - http_context *default_ctx = nullptr; - void *private_data = nullptr; - - void (*handle)(http2_session *, http2_stream *) = nullptr; - - http2_session(int _fd); - ~http2_session(); -}; -#endif - -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 swString *swoole_http_buffer; -extern swString *swoole_http_form_data_buffer; -#ifdef SW_HAVE_COMPRESSION -extern swString *swoole_zlib_buffer; -#endif - -http_context* swoole_http_context_new(int fd); -http_context* php_swoole_http_request_get_and_check_context(zval *zobject); -http_context* php_swoole_http_response_get_and_check_context(zval *zobject); -void swoole_http_context_free(http_context *ctx); -void swoole_http_context_copy(http_context *src, http_context *dst); - -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) -{ - if (UNEXPECTED(!*zproperty_store_pp)) - { - // Notice: swoole http server properties can not be unset anymore, so we can read it without checking - zval rv, *property = zend_read_property(ce, zobject, name, name_len, 0, &rv); - array_init(property); - *zproperty_store_pp = (zval *) (zproperty_store_pp + 1); - **zproperty_store_pp = *property; - } - return *zproperty_store_pp; -} -int swoole_http_parse_form_data(http_context *ctx, const char *boundary_str, int boundary_len); -void swoole_http_parse_cookie(zval *array, const char *at, size_t length); - -http_context * php_swoole_http_request_get_context(zval *zobject); -void php_swoole_http_request_set_context(zval *zobject, http_context *context); -http_context * php_swoole_http_response_get_context(zval *zobject); -void php_swoole_http_response_set_context(zval *zobject, http_context *context); -size_t swoole_http_requset_parse(http_context *ctx, const char *data, size_t length); - -bool swoole_http_response_set_header(http_context *ctx, const char *k, size_t klen, const char *v, size_t vlen, bool ucwords); -void swoole_http_response_end(http_context *ctx, zval *zdata, zval *return_value); - -#ifdef SW_HAVE_COMPRESSION -int swoole_http_response_compress(swString *body, int method, int level); -void swoole_http_get_compression_method(http_context *ctx, const char *accept_encoding, size_t length); -const char* swoole_http_get_content_encoding(http_context *ctx); -#endif - -#ifdef SW_HAVE_ZLIB -voidpf php_zlib_alloc(voidpf opaque, uInt items, uInt size); -void php_zlib_free(voidpf opaque, voidpf address); -#endif - -#ifdef SW_HAVE_BROTLI -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); }, - [](void *ptr, void *mem_user_data){ return efree(ptr); }, - [](size_t nmemb, size_t size, void *mem_user_data){ return ecalloc(nmemb, size); }, - [](void *ptr, size_t size, void *mem_user_data){ return erealloc(ptr, size); } - }; - return &mem; -} - -void swoole_http2_response_end(http_context *ctx, zval *zdata, zval *return_value); - -namespace swoole { namespace http2 { -//-----------------------------------namespace begin-------------------------------------------- -class headers -{ -public: - headers(size_t size) : size(size), index(0) - { - nvs = (nghttp2_nv *) ecalloc(size, sizeof(nghttp2_nv)); - } - - inline nghttp2_nv* get() - { - return nvs; - } - - inline size_t len() - { - return index; - } - - void reserve_one() - { - index++; - } - - inline void add( - size_t index, - const char *name, size_t name_len, - const char *value, size_t value_len, - 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); - 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); - } - else - { - php_swoole_fatal_error(E_WARNING, "unexpect http2 header [%.*s] (duplicated or overflow)", (int) name_len, name); - } - } - - inline void add( - const char *name, size_t name_len, - const char *value, size_t value_len, - const uint8_t flags = NGHTTP2_NV_FLAG_NONE - ) - { - add(index++, name, name_len, value, value_len, flags); - } - - ~headers() - { - for (size_t i = 0; i < size; ++i) - { - if (sw_likely(nvs[i].name/* && nvs[i].value */)) - { - efree((void *) nvs[i].name); - efree((void *) nvs[i].value); - } - } - efree(nvs); - } - -private: - nghttp2_nv *nvs; - size_t size; - size_t index; -}; -//-----------------------------------namespace end-------------------------------------------- -}} -#endif diff --git a/swoole_http2_client_coro.cc b/swoole_http2_client_coro.cc deleted file mode 100644 index 93099cadb57..00000000000 --- a/swoole_http2_client_coro.cc +++ /dev/null @@ -1,1609 +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 "swoole_http.h" - -#ifdef SW_USE_HTTP2 - -#include "http.h" -#include "http2.h" - -#define HTTP2_CLIENT_HOST_HEADER_INDEX 3 - -#include - -using namespace swoole; -using swoole::coroutine::Socket; - -static zend_class_entry *swoole_http2_client_coro_ce; -static zend_object_handlers swoole_http2_client_coro_handlers; - -static zend_class_entry *swoole_http2_client_coro_exception_ce; -static zend_object_handlers swoole_http2_client_coro_exception_handlers; - -static zend_class_entry *swoole_http2_request_ce; -static zend_object_handlers swoole_http2_request_handlers; - -static zend_class_entry *swoole_http2_response_ce; -static zend_object_handlers swoole_http2_response_handlers; - -struct http2_client_stream -{ - uint32_t stream_id; - uint8_t gzip; - uint8_t flags; - swString *buffer; -#ifdef SW_HAVE_ZLIB - z_stream gzip_stream; - swString *gzip_buffer; -#endif - zval zresponse; - - // flow control - uint32_t remote_window_size; - uint32_t local_window_size; -}; - -class http2_client -{ -public: - std::string host; - int port; - bool ssl; - double timeout = 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 = {}; - - std::unordered_map streams; - - /* safety zval */ - zval _zobject; - zval *zobject; - - http2_client(const char *_host, size_t _host_len, int _port, bool _ssl, zval *__zobject) - { - host = std::string(_host, _host_len); - port = _port; - ssl = _ssl; - _zobject = *__zobject; - zobject = &_zobject; - swHttp2_init_settings(&local_settings); - } - - inline http2_client_stream* get_stream(uint32_t stream_id) - { - auto i = streams.find(stream_id); - if (i == streams.end()) - { - return nullptr; - } - else - { - return i->second; - } - } - - inline void update_error_properties(int code, const char *msg) - { - zend_update_property_long(swoole_http2_client_coro_ce, zobject, ZEND_STRL("errCode"), code); - zend_update_property_string(swoole_http2_client_coro_ce, zobject, ZEND_STRL("errMsg"), msg); - } - - inline void io_error() - { - update_error_properties(client->errCode, client->errMsg); - } - - inline void nghttp2_error(int code, const char *msg) - { - update_error_properties(code, cpp_string::format("%s with error: %s", msg, nghttp2_strerror(code)).c_str()); - } - - inline bool is_available() - { - if (sw_unlikely(!client)) - { - SwooleG.error = SW_ERROR_CLIENT_NO_CONNECTION; - zend_update_property_long(swoole_http2_client_coro_ce, zobject, ZEND_STRL("errCode"), ECONNRESET); - zend_update_property_string(swoole_http2_client_coro_ce, zobject, ZEND_STRL("errMsg"), "client is not connected to server"); - return false; - } - return true; - } - - inline void apply_setting(zval *zset) - { - if (client && ZVAL_IS_ARRAY(zset)) - { - php_swoole_client_set(client, zset); - } - } - - inline bool recv_packet(double timeout) - { - if (sw_unlikely(client->recv_packet(timeout) <= 0)) - { - io_error(); - return false; - } - return true; - } - - bool connect(); - http2_client_stream* create_stream(uint32_t stream_id, bool pipeline); - void destroy_stream(http2_client_stream *stream); - - inline bool delete_stream(uint32_t stream_id) - { - auto i = streams.find(stream_id); - if (i == streams.end()) - { - return false; - } - - destroy_stream(i->second); - streams.erase(i); - - return true; - } - - bool send_window_update(int stream_id, uint32_t size); - bool send_ping_frame(); - bool send_data(uint32_t stream_id, const char *p, size_t len, int flag); - uint32_t send_request(zval *req); - 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); - bool close(); - - ~http2_client() - { - close(); - } -private: - bool send_setting(); - int parse_header(http2_client_stream *stream , int flags, char *in, size_t inlen); - - inline bool send(const char *buf, size_t len) - { - if (sw_unlikely(client->send_all(buf, len) != (ssize_t )len)) - { - io_error(); - return false; - } - return true; - } -}; - -typedef struct -{ - http2_client *h2c; - zend_object std; -} http2_client_coro_t; - -static sw_inline http2_client_coro_t* php_swoole_http2_client_coro_fetch_object(zend_object *obj) -{ - return (http2_client_coro_t *) ((char *) obj - swoole_http2_client_coro_handlers.offset); -} - -static sw_inline http2_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, http2_client *h2c) -{ - php_swoole_http2_client_coro_fetch_object(Z_OBJ_P(zobject))->h2c = h2c; -} - -static void php_swoole_http2_client_coro_free_object(zend_object *object) -{ - http2_client_coro_t *request = php_swoole_http2_client_coro_fetch_object(object); - http2_client *h2c = request->h2c; - - if (h2c) - { - delete h2c; - } - zend_object_std_dtor(&request->std); -} - -static zend_object *php_swoole_http2_client_coro_create_object(zend_class_entry *ce) -{ - http2_client_coro_t *request = (http2_client_coro_t *) ecalloc(1, sizeof(http2_client_coro_t) + zend_object_properties_size(ce)); - zend_object_std_init(&request->std, ce); - object_properties_init(&request->std, ce); - request->std.handlers = &swoole_http2_client_coro_handlers; - return &request->std; -} - -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, 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 PHP_METHOD(swoole_http2_client_coro, __construct); -static PHP_METHOD(swoole_http2_client_coro, __destruct); -static PHP_METHOD(swoole_http2_client_coro, set); -static PHP_METHOD(swoole_http2_client_coro, connect); -static PHP_METHOD(swoole_http2_client_coro, stats); -static PHP_METHOD(swoole_http2_client_coro, isStreamExist); -static PHP_METHOD(swoole_http2_client_coro, send); -static PHP_METHOD(swoole_http2_client_coro, write); -static PHP_METHOD(swoole_http2_client_coro, recv); -static PHP_METHOD(swoole_http2_client_coro, read); -static PHP_METHOD(swoole_http2_client_coro, ping); -static PHP_METHOD(swoole_http2_client_coro, goaway); -static PHP_METHOD(swoole_http2_client_coro, close); - -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_FE_END -}; - -void php_swoole_http2_client_coro_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY(swoole_http2_client_coro, "Swoole\\Coroutine\\Http2\\Client", NULL, "Co\\Http2\\Client", swoole_http2_client_methods); - SW_SET_CLASS_SERIALIZABLE(swoole_http2_client_coro, zend_class_serialize_deny, zend_class_unserialize_deny); - 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_t, std); - - SW_INIT_CLASS_ENTRY_EX(swoole_http2_client_coro_exception, "Swoole\\Coroutine\\Http2\\Client\\Exception", NULL, "Co\\Http2\\Client\\Exception", NULL, swoole_exception); - - SW_INIT_CLASS_ENTRY(swoole_http2_request, "Swoole\\Http2\\Request", "swoole_http2_request", NULL, NULL); - SW_SET_CLASS_SERIALIZABLE(swoole_http2_request, zend_class_serialize_deny, zend_class_unserialize_deny); - 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", NULL, NULL); - SW_SET_CLASS_SERIALIZABLE(swoole_http2_response, zend_class_serialize_deny, zend_class_unserialize_deny); - 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_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_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_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); - zend_declare_property_null(swoole_http2_request_ce, ZEND_STRL("headers"), ZEND_ACC_PUBLIC); - 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_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); - zend_declare_property_long(swoole_http2_response_ce, ZEND_STRL("statusCode"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_bool(swoole_http2_response_ce, ZEND_STRL("pipeline"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_http2_response_ce, ZEND_STRL("headers"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_http2_response_ce, ZEND_STRL("set_cookie_headers"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_http2_response_ce, ZEND_STRL("cookies"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_http2_response_ce, ZEND_STRL("data"), ZEND_ACC_PUBLIC); - - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_DATA", SW_HTTP2_TYPE_DATA); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_HEADERS", SW_HTTP2_TYPE_HEADERS); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_PRIORITY", SW_HTTP2_TYPE_PRIORITY); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_RST_STREAM", SW_HTTP2_TYPE_RST_STREAM); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_SETTINGS", SW_HTTP2_TYPE_SETTINGS); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_PUSH_PROMISE", SW_HTTP2_TYPE_PUSH_PROMISE); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_PING", SW_HTTP2_TYPE_PING); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_GOAWAY", SW_HTTP2_TYPE_GOAWAY); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_WINDOW_UPDATE", SW_HTTP2_TYPE_WINDOW_UPDATE); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_TYPE_CONTINUATION", SW_HTTP2_TYPE_CONTINUATION); - - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_NO_ERROR", SW_HTTP2_ERROR_NO_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_PROTOCOL_ERROR", SW_HTTP2_ERROR_PROTOCOL_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_INTERNAL_ERROR", SW_HTTP2_ERROR_INTERNAL_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_FLOW_CONTROL_ERROR", SW_HTTP2_ERROR_FLOW_CONTROL_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_SETTINGS_TIMEOUT", SW_HTTP2_ERROR_SETTINGS_TIMEOUT); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_STREAM_CLOSED", SW_HTTP2_ERROR_STREAM_CLOSED); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_FRAME_SIZE_ERROR", SW_HTTP2_ERROR_FRAME_SIZE_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_REFUSED_STREAM", SW_HTTP2_ERROR_REFUSED_STREAM); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_CANCEL", SW_HTTP2_ERROR_CANCEL); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HTTP2_ERROR_COMPRESSION_ERROR", SW_HTTP2_ERROR_COMPRESSION_ERROR); - 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); -} - -bool http2_client::connect() -{ - if (sw_unlikely(client != nullptr)) - { - return false; - } - - client = new Socket(SW_SOCK_TCP); - if (UNEXPECTED(client->get_fd() < 0)) - { - php_swoole_sys_error(E_WARNING, "new Socket() failed"); - zend_update_property_long(swoole_http2_client_coro_ce, zobject, ZEND_STRL("errCode"), errno); - zend_update_property_string(swoole_http2_client_coro_ce, zobject, ZEND_STRL("errMsg"), swoole_strerror(errno)); - delete client; - client = nullptr; - return false; - } -#ifdef SW_USE_OPENSSL - client->open_ssl = ssl; -#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; - - apply_setting(sw_zend_read_property(swoole_http2_client_coro_ce, zobject, ZEND_STRL("setting"), 0)); - - if (!client->connect(host, port)) - { - io_error(); - close(); - return false; - } - - stream_id = 1; - // [init]: we must set default value, server is not always send all the settings - swHttp2_init_settings(&remote_settings); - - int ret = nghttp2_hd_inflate_new2(&inflater, php_nghttp2_mem()); - if (ret != 0) - { - nghttp2_error(ret, "nghttp2_hd_inflate_new2() failed"); - close(); - return false; - } - ret = nghttp2_hd_deflate_new2(&deflater, local_settings.header_table_size, php_nghttp2_mem()); - if (ret != 0) - { - nghttp2_error(ret, "nghttp2_hd_deflate_new2() failed"); - close(); - return false; - } - - if (!send(ZEND_STRL(SW_HTTP2_PRI_STRING))) - { - close(); - return false; - } - - if (!send_setting()) - { - close(); - return false; - } - - zend_update_property_bool(swoole_http2_client_coro_ce, zobject, ZEND_STRL("connected"), 1); - - return true; -} - -bool http2_client::close() -{ - Socket *_client = client; - if (!_client) - { - return false; - } - zend_update_property_bool(swoole_http2_client_coro_ce, 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 = NULL; - } - if (deflater) - { - nghttp2_hd_deflate_del(deflater); - deflater = NULL; - } - client = nullptr; - } - if (_client->close()) - { - delete _client; - } - return true; -} - -enum swReturn_code http2_client::parse_frame(zval *return_value, bool pipeline_read) -{ - char *buf = client->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); - buf += SW_HTTP2_FRAME_HEADER_SIZE; - - char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE]; - - if (stream_id > last_stream_id) - { - last_stream_id = stream_id; - } - - uint16_t id = 0; - uint32_t value = 0; - - switch (type) - { - case SW_HTTP2_TYPE_SETTINGS: - { - if (flags & SW_HTTP2_FLAG_ACK) - { - swHttp2FrameTraceLog(recv, "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); - switch (id) - { - case SW_HTTP2_SETTING_HEADER_TABLE_SIZE: - if (value != remote_settings.header_table_size) - { - remote_settings.header_table_size = value; - int ret = nghttp2_hd_deflate_change_table_size(deflater, value); - if (ret != 0) - { - nghttp2_error(ret, "nghttp2_hd_deflate_change_table_size() failed"); - return SW_ERROR; - } - } - swTraceLog(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); - break; - case SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE: - remote_settings.window_size = value; - swTraceLog(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); - break; - case SW_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: - if (value != remote_settings.max_header_list_size) - { - remote_settings.max_header_list_size = value; - /* - int ret = nghttp2_hd_inflate_change_table_size(inflater, value); - if (ret != 0) - { - nghttp2_error(ret, "nghttp2_hd_inflate_change_table_size() failed"); - return SW_ERROR; - } - */ - } - swTraceLog(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); - 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); - if (!send(frame, SW_HTTP2_FRAME_HEADER_SIZE)) - { - return SW_ERROR; - } - return SW_CONTINUE; - } - case SW_HTTP2_TYPE_WINDOW_UPDATE: - { - value = ntohl(*(uint32_t *) buf); - swHttp2FrameTraceLog(recv, "window_size_increment=%d", value); - if (stream_id == 0) - { - remote_settings.window_size += value; - } - else - { - http2_client_stream *stream = get_stream(stream_id); - if (stream) - { - stream->remote_window_size += value; - } - } - return SW_CONTINUE; - } - case SW_HTTP2_TYPE_PING: - { - swHttp2FrameTraceLog(recv, "ping"); - if (!(flags & SW_HTTP2_FLAG_ACK)) - { - swHttp2_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); - if (!send(frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE)) - { - return SW_ERROR; - } - } - return SW_CONTINUE; - } - case SW_HTTP2_TYPE_GOAWAY: - { - uint32_t server_last_stream_id = ntohl(*(uint32_t *) (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); - - // update goaway error code and error msg - zend_update_property_long(swoole_http2_client_coro_ce, zobject, ZEND_STRL("errCode"), value); - zend_update_property_stringl(swoole_http2_client_coro_ce, zobject, ZEND_STRL("errMsg"), buf, length - SW_HTTP2_GOAWAY_SIZE); - zend_update_property_long(swoole_http2_client_coro_ce, zobject, ZEND_STRL("serverLastStreamId"), server_last_stream_id); - close(); - return SW_CLOSE; - } - case SW_HTTP2_TYPE_RST_STREAM: - { - value = ntohl(*(uint32_t *) (buf)); - swHttp2FrameTraceLog(recv, "error_code=%d", value); - - // delete and free quietly - delete_stream(stream_id); - - return SW_CONTINUE; - } - /** - * TODO not support push_promise - */ - 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); -#endif - // auto promise_stream = create_stream(promise_stream_id, false); - // RETVAL_ZVAL(promise_stream->response_object, 0, 0); - // return SW_READY; - return SW_CONTINUE; - } - default: - { - swHttp2FrameTraceLog(recv, ""); - } - } - - http2_client_stream *stream = get_stream(stream_id); - // The stream is not found or has closed - if (stream == NULL) - { - swNotice("http2 stream#%d belongs to an unknown type or it never registered", stream_id); - return SW_CONTINUE; - } - if (type == SW_HTTP2_TYPE_HEADERS) - { - parse_header(stream, flags, buf, length); - } - else if (type == SW_HTTP2_TYPE_DATA) - { - if (!(flags & SW_HTTP2_FLAG_END_STREAM)) - { - stream->flags |= SW_HTTP2_STREAM_PIPELINE_RESPONSE; - } - if (length > 0) - { - if (!stream->buffer) - { - stream->buffer = swString_new(SW_HTTP2_DATA_BUFFER_SIZE); - } -#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"); - return SW_ERROR; - } - swString_append_ptr(stream->buffer, stream->gzip_buffer->str, stream->gzip_buffer->length); - } - else -#endif - { - swString_append_ptr(stream->buffer, buf, length); - } - - // 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; - 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)) - { - return SW_ERROR; - } - local_settings.window_size = SW_HTTP2_MAX_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)) - { - return SW_ERROR; - } - stream->local_window_size = SW_HTTP2_MAX_WINDOW_SIZE; - } - } - } - - bool end = (flags & SW_HTTP2_FLAG_END_STREAM) || - type == SW_HTTP2_TYPE_RST_STREAM || - type == SW_HTTP2_TYPE_GOAWAY; - pipeline_read = (pipeline_read && (stream->flags & SW_HTTP2_STREAM_PIPELINE_RESPONSE)); - if (end || pipeline_read) - { - zval *zresponse = &stream->zresponse; - if (type == SW_HTTP2_TYPE_RST_STREAM) - { - zend_update_property_long(swoole_http2_response_ce, zresponse, ZEND_STRL("statusCode"), -3 /* HTTP_CLIENT_ESTATUS_SERVER_RESET */); - zend_update_property_long(swoole_http2_response_ce, zresponse, ZEND_STRL("errCode"), value); - } - if (stream->buffer && stream->buffer->length > 0) - { - zend_update_property_stringl(swoole_http2_response_ce, zresponse, ZEND_STRL("data"), stream->buffer->str, stream->buffer->length); - swString_clear(stream->buffer); - } - if (!end) - { - zend_update_property_bool(swoole_http2_response_ce, &stream->zresponse, ZEND_STRL("pipeline"), 1); - } - RETVAL_ZVAL(zresponse, end, 0); - if (!end) - { - // reinit response object for the following frames - object_init_ex(zresponse, swoole_http2_response_ce); - zend_update_property_long(swoole_http2_response_ce, &stream->zresponse, ZEND_STRL("streamId"), stream_id); - } - else - { - delete_stream(stream_id); - } - - return SW_READY; - } - - return SW_CONTINUE; -} - -#ifdef SW_HAVE_ZLIB -int php_swoole_zlib_decompress(z_stream *stream, swString *buffer, char *body, int length) -{ - int status = 0; - - stream->avail_in = length; - stream->next_in = (Bytef *) body; - 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 - - swString_clear(buffer); - - while (1) - { - stream->avail_out = buffer->size - buffer->length; - stream->next_out = (Bytef *) (buffer->str + buffer->length); - - 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 - if (status >= 0) - { - buffer->length = stream->total_out; - } - if (status == Z_STREAM_END) - { - return SW_OK; - } - else if (status == Z_OK) - { - if (buffer->length + 4096 >= buffer->size) - { - if (swString_extend(buffer, buffer->size * 2) < 0) - { - return SW_ERR; - } - } - if (stream->avail_in == 0) - { - return SW_OK; - } - } - else - { - return SW_ERR; - } - } - return SW_ERR; -} -#endif - -static PHP_METHOD(swoole_http2_client_coro, __construct) -{ - char *host; - size_t host_len; - zend_long port = 80; - zend_bool ssl = SW_FALSE; - - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 3) - Z_PARAM_STRING(host, host_len) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(port) - Z_PARAM_BOOL(ssl) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (host_len == 0) - { - zend_throw_exception(swoole_http2_client_coro_exception_ce, "host is empty", SW_ERROR_INVALID_PARAMS); - RETURN_FALSE; - } - - http2_client *h2c = new http2_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; - RETURN_FALSE; -#endif - } - - php_swoole_set_h2c(ZEND_THIS, h2c); - - zend_update_property_stringl(swoole_http2_client_coro_ce, ZEND_THIS, ZEND_STRL("host"), host, host_len); - zend_update_property_long(swoole_http2_client_coro_ce, ZEND_THIS, ZEND_STRL("port"), port); - zend_update_property_bool(swoole_http2_client_coro_ce, ZEND_THIS, ZEND_STRL("ssl"), ssl); -} - -static PHP_METHOD(swoole_http2_client_coro, set) -{ - http2_client *h2c = php_swoole_get_h2c(ZEND_THIS); - zval *zset; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY(zset) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - zval *zsetting = sw_zend_read_and_convert_property_array(swoole_http2_client_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - php_array_merge(Z_ARRVAL_P(zsetting), Z_ARRVAL_P(zset)); - - h2c->apply_setting(zset); - - RETURN_TRUE; -} - -bool http2_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); - *(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); - return send(frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_WINDOW_UPDATE_SIZE); -} - -bool http2_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); -} - -void http_parse_set_cookies(const char *at, size_t length, zval *zcookies, zval *zset_cookie_headers); - -int http2_client::parse_header(http2_client_stream *stream, int flags, char *in, size_t inlen) -{ - zval *zresponse = &stream->zresponse; - - if (flags & SW_HTTP2_FLAG_PRIORITY) - { - // int stream_deps = ntohl(*(int *) (in)); - // uint8_t weight = in[4]; - in += 5; - inlen -= 5; - } - - zval *zheaders = sw_zend_read_and_convert_property_array(swoole_http2_response_ce, zresponse, ZEND_STRL("headers"), 0); - zval *zcookies = sw_zend_read_and_convert_property_array(swoole_http2_response_ce, zresponse, ZEND_STRL("cookies"), 0); - zval *zset_cookie_headers = sw_zend_read_and_convert_property_array(swoole_http2_response_ce, zresponse, ZEND_STRL("set_cookie_headers"), 0); - - ssize_t rv; - while (true) - { - nghttp2_nv nv; - int inflate_flags = 0; - - rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, (uchar *) in, inlen, 1); - if (rv < 0) - { - nghttp2_error(rv, "nghttp2_hd_inflate_hd failed"); - return SW_ERR; - } - - in += (size_t) rv; - inlen -= (size_t) rv; - - swTraceLog( - SW_TRACE_HTTP2, "[" SW_ECHO_GREEN "] %.*s[%d]: %.*s[%d]", "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] == ':') - { - if (SW_STRCASEEQ((char *) nv.name + 1, nv.namelen - 1, "status")) - { - zend_update_property_long(swoole_http2_response_ce, zresponse, ZEND_STRL("statusCode"), atoi((char *) nv.value)); - goto _check_end; - } - } -#ifdef SW_HAVE_ZLIB - else if ( - SW_STRCASEEQ((char *) nv.name, nv.namelen, "content-encoding") && - SW_STRCASECT((char *) nv.value, nv.valuelen, "gzip") - ) - { - /** - * init zlib stream - */ - stream->gzip = 1; - memset(&stream->gzip_stream, 0, sizeof(stream->gzip_stream)); - stream->gzip_buffer = swString_new(8192); - stream->gzip_stream.zalloc = php_zlib_alloc; - stream->gzip_stream.zfree = php_zlib_free; - /** - * zlib decode - */ - if (Z_OK != inflateInit2(&stream->gzip_stream, MAX_WBITS + 16)) - { - swWarn("inflateInit2() failed"); - return SW_ERR; - } - } -#endif - else if (SW_STRCASEEQ((char *) nv.name, nv.namelen, "set-cookie")) - { - 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); - } - _check_end: - - if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) - { - nghttp2_hd_inflate_end_headers(inflater); - break; - } - - if (inlen == 0) - { - break; - } - } - - return SW_OK; -} - -static ssize_t http2_client_build_header(zval *zobject, zval *zrequest, char *buffer) -{ - http2_client *h2c = php_swoole_get_h2c(zobject); - zval *zmethod = sw_zend_read_property(swoole_http2_request_ce, zrequest, ZEND_STRL("method"), 0); - zval *zpath = sw_zend_read_property(swoole_http2_request_ce, zrequest, ZEND_STRL("path"), 0); - zval *zheaders = sw_zend_read_property(swoole_http2_request_ce, zrequest, ZEND_STRL("headers"), 0); - zval *zcookies = sw_zend_read_property(swoole_http2_request_ce, zrequest, ZEND_STRL("cookies"), 0); - http2::headers headers(8 + php_swoole_array_length_safe(zheaders) + php_swoole_array_length_safe(zcookies)); - bool find_host = 0; - - if (Z_TYPE_P(zmethod) != IS_STRING || Z_STRLEN_P(zmethod) == 0) - { - headers.add(ZEND_STRL(":method"), ZEND_STRL("GET")); - } - else - { - headers.add(ZEND_STRL(":method"), Z_STRVAL_P(zmethod), Z_STRLEN_P(zmethod)); - } - if (Z_TYPE_P(zpath) != IS_STRING || Z_STRLEN_P(zpath) == 0) - { - headers.add(ZEND_STRL(":path"), "/", 1); - } - else - { - headers.add(ZEND_STRL(":path"), Z_STRVAL_P(zpath), Z_STRLEN_P(zpath)); - } - if (h2c->ssl) - { - headers.add(ZEND_STRL(":scheme"), ZEND_STRL("https")); - } - else - { - headers.add(ZEND_STRL(":scheme"), ZEND_STRL("http")); - } - // Host - headers.reserve_one(); - - if (ZVAL_IS_ARRAY(zheaders)) - { - zend_string *key; - zval *zvalue; - - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheaders), key, zvalue) - { - if (UNEXPECTED(!key || *ZSTR_VAL(key) == ':' || ZVAL_IS_NULL(zvalue))) - { - continue; - } - zend::string str_value(zvalue); - if (SW_STRCASEEQ(ZSTR_VAL(key), ZSTR_LEN(key), "host")) - { - headers.add(HTTP2_CLIENT_HOST_HEADER_INDEX, ZEND_STRL(":authority"), str_value.val(), str_value.len()); - find_host = true; - } - else - { - headers.add(ZSTR_VAL(key), ZSTR_LEN(key), str_value.val(), str_value.len()); - } - } - ZEND_HASH_FOREACH_END(); - } - if (!find_host) - { - headers.add(HTTP2_CLIENT_HOST_HEADER_INDEX,ZEND_STRL(":authority"), h2c->host.c_str(), h2c->host.length()); - } - // http cookies - if (ZVAL_IS_ARRAY(zcookies)) - { - zend_string *key; - zval *zvalue; - char *encoded_value; - int encoded_value_len; - swString *buffer = SwooleTG.buffer_stack; - - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zcookies), key, zvalue) - { - if (UNEXPECTED(!key || ZVAL_IS_NULL(zvalue))) - { - continue; - } - zend::string str_value(zvalue); - swString_clear(buffer); - swString_append_ptr(buffer, ZSTR_VAL(key), ZSTR_LEN(key)); - swString_append_ptr(buffer, "=", 1); - encoded_value = php_swoole_url_encode(str_value.val(), str_value.len(), &encoded_value_len); - if (encoded_value) - { - swString_append_ptr(buffer, encoded_value, encoded_value_len); - efree(encoded_value); - headers.add(ZEND_STRL("cookie"), buffer->str, buffer->length); - } - } - ZEND_HASH_FOREACH_END(); - } - - 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; - } - */ - 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"); - return -1; - } - return rv; -} - -void http2_client::destroy_stream(http2_client_stream *stream) -{ - if (stream->buffer) - { - swString_free(stream->buffer); - } -#ifdef SW_HAVE_ZLIB - if (stream->gzip) - { - inflateEnd(&stream->gzip_stream); - swString_free(stream->gzip_buffer); - } -#endif - zval_ptr_dtor(&stream->zresponse); - efree(stream); -} - -http2_client_stream* http2_client::create_stream(uint32_t stream_id, bool pipeline) -{ - // malloc - http2_client_stream *stream = (http2_client_stream *) ecalloc(1, sizeof(http2_client_stream)); - // init - stream->stream_id = stream_id; - stream->flags = pipeline ? SW_HTTP2_STREAM_PIPELINE_REQUEST : SW_HTTP2_STREAM_NORMAL; - stream->remote_window_size = SW_HTTP2_DEFAULT_WINDOW_SIZE; - stream->local_window_size = SW_HTTP2_DEFAULT_WINDOW_SIZE; - streams.emplace(stream_id, stream); - // create response object - object_init_ex(&stream->zresponse, swoole_http2_response_ce); - zend_update_property_long(swoole_http2_response_ce, &stream->zresponse, ZEND_STRL("streamId"), stream_id); - - return stream; -} - -bool http2_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); - return send(frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE); -} - -bool http2_client::send_data(uint32_t stream_id, const char *p, size_t len, int flag) -{ - uint8_t send_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; - 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); - if (!send(header, SW_HTTP2_FRAME_HEADER_SIZE)) - { - return false; - } - if (!send(p, send_len)) - { - return false; - } - len -= send_len; - p += send_len; - } - return true; -} - -uint32_t http2_client::send_request(zval *req) -{ - zval *zheaders = sw_zend_read_and_convert_property_array(swoole_http2_request_ce, req, ZEND_STRL("headers"), 0); - zval *zdata = sw_zend_read_property(swoole_http2_request_ce, req, ZEND_STRL("data"), 0); - zval *zpipeline = sw_zend_read_property(swoole_http2_request_ce, req, ZEND_STRL("pipeline"), 0); - bool is_data_empty = Z_TYPE_P(zdata) == IS_STRING ? Z_STRLEN_P(zdata) == 0 : !zval_is_true(zdata); - - if (ZVAL_IS_ARRAY(zdata)) - { - add_assoc_stringl_ex(zheaders, ZEND_STRL("content-type"), (char *) ZEND_STRL("application/x-www-form-urlencoded")); - } - - /** - * send headers - */ - char* buffer = SwooleTG.buffer_stack->str; - ssize_t bytes = http2_client_build_header(zobject, req, buffer + SW_HTTP2_FRAME_HEADER_SIZE); - - if (bytes <= 0) - { - return 0; - } - - auto stream = create_stream(stream_id, Z_BVAL_P(zpipeline)); - - if (is_data_empty) - { - if (stream->flags & SW_HTTP2_STREAM_PIPELINE_REQUEST) - { - swHttp2_set_frame_header(buffer, SW_HTTP2_TYPE_HEADERS, bytes, SW_HTTP2_FLAG_END_HEADERS, stream->stream_id); - } - else - { - swHttp2_set_frame_header(buffer, SW_HTTP2_TYPE_HEADERS, bytes, SW_HTTP2_FLAG_END_STREAM | SW_HTTP2_FLAG_END_HEADERS, stream->stream_id); - } - } - else - { - swHttp2_set_frame_header(buffer, SW_HTTP2_TYPE_HEADERS, bytes, SW_HTTP2_FLAG_END_HEADERS, 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); - if (!send(buffer, SW_HTTP2_FRAME_HEADER_SIZE + bytes)) - { - return 0; - } - - /** - * send body - */ - if (!is_data_empty) - { - char *p; - size_t len; - smart_str formstr_s = {}; - zend::string str_zpost_data; - - int flag = (stream->flags & SW_HTTP2_STREAM_PIPELINE_REQUEST) ? 0 : SW_HTTP2_FLAG_END_STREAM; - if (ZVAL_IS_ARRAY(zdata)) - { - p = php_swoole_http_build_query(zdata, &len, &formstr_s); - if (p == NULL) - { - php_swoole_error(E_WARNING, "http_build_query failed"); - return 0; - } - } - else - { - str_zpost_data = zdata; - p = str_zpost_data.val(); - 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); - - if (!send_data(stream->stream_id, p, len, flag)) - { - return 0; - } - - if (formstr_s.s) - { - smart_str_free(&formstr_s); - } - } - - stream_id += 2; - - return stream->stream_id; -} - -bool http2_client::write_data(uint32_t stream_id, zval *zdata, bool end) -{ - char buffer[SW_HTTP2_FRAME_HEADER_SIZE]; - http2_client_stream *stream = get_stream(stream_id); - int flag = end ? SW_HTTP2_FLAG_END_STREAM : 0; - - if (stream == NULL || !(stream->flags & SW_HTTP2_STREAM_PIPELINE_REQUEST) || (stream->flags & SW_HTTP2_STREAM_REQUEST_END)) - { - update_error_properties(EINVAL, cpp_string::format("unable to found active pipeline stream#%u", stream_id).c_str()); - return false; - } - - if (ZVAL_IS_ARRAY(zdata)) - { - size_t len; - smart_str formstr_s = {}; - char *formstr = php_swoole_http_build_query(zdata, &len, &formstr_s); - if (formstr == NULL) - { - 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); - if (!send(buffer, SW_HTTP2_FRAME_HEADER_SIZE) || !send(formstr, len)) - { - smart_str_free(&formstr_s); - return false; - } - 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()); - if (!send(buffer, SW_HTTP2_FRAME_HEADER_SIZE) || !send(data.val(), data.len())) - { - return false; - } - } - - if (end) - { - stream->flags |= SW_HTTP2_STREAM_REQUEST_END; - } - - return true; -} - -bool http2_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); - *(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=%d, error-code=%d", swHttp2_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) -{ - zval *request; - http2_client *h2c = php_swoole_get_h2c(ZEND_THIS); - - if (!h2c->is_available()) - { - RETURN_FALSE; - } - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &request) == FAILURE) - { - RETURN_FALSE; - } - if (Z_TYPE_P(request) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(request), swoole_http2_request_ce)) - { - zend_throw_exception_ex(swoole_http2_client_coro_exception_ce, SW_ERROR_INVALID_PARAMS, "Object is not a instanceof %s", ZSTR_VAL(swoole_http2_request_ce->name)); - RETURN_FALSE; - } - - uint32_t stream_id = h2c->send_request(request); - if (stream_id == 0) - { - RETURN_FALSE; - } - else - { - RETURN_LONG(stream_id); - } -} - -static void php_swoole_http2_client_coro_recv(INTERNAL_FUNCTION_PARAMETERS, bool pipeline_read) -{ - http2_client *h2c = php_swoole_get_h2c(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); - - while (true) - { - if (!h2c->is_available()) - { - RETURN_FALSE; - } - if (!h2c->recv_packet(timeout)) - { - RETURN_FALSE; - } - enum swReturn_code ret = h2c->parse_frame(return_value, pipeline_read); - if (ret == SW_CONTINUE) - { - continue; - } - else if (ret == SW_READY) - { - break; - } - else - { - RETURN_FALSE; - } - } -} - -static PHP_METHOD(swoole_http2_client_coro, recv) -{ - php_swoole_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) -{ - http2_client *h2c = php_swoole_get_h2c(ZEND_THIS); - RETURN_BOOL(h2c->close()); -} - -static PHP_METHOD(swoole_http2_client_coro, connect) -{ - http2_client *h2c = php_swoole_get_h2c(ZEND_THIS); - RETURN_BOOL(h2c->connect()); -} - -static sw_inline void http2_settings_to_array(swHttp2_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("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) -{ - http2_client *h2c = php_swoole_get_h2c(ZEND_THIS); - zval _zarray, *zarray = &_zarray; - swString key = {}; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &key.str, &key.length) == FAILURE) - { - 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); - RETURN_ZVAL(zarray, 0, 0); - } - else if (SW_STREQ(key.str, key.length, "remote_settings")) - { - http2_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()); - } - } - else - { - 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); - add_assoc_zval_ex(return_value, ZEND_STRL("local_settings"), zarray); - http2_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()); - } -} - -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; - } - if (stream_id < 0) - { - RETURN_FALSE; - } - - http2_client *h2c = php_swoole_get_h2c(ZEND_THIS); - if (!h2c->client) - { - RETURN_FALSE; - } - else if (stream_id == 0) - { - RETURN_TRUE; - } - http2_client_stream *stream = h2c->get_stream(stream_id); - RETURN_BOOL(stream ? 1 : 0); -} - -static PHP_METHOD(swoole_http2_client_coro, write) -{ - http2_client *h2c = php_swoole_get_h2c(ZEND_THIS); - - if (!h2c->is_available()) - { - RETURN_FALSE; - } - - 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; - } - 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); -} - -static PHP_METHOD(swoole_http2_client_coro, ping) -{ - http2_client *h2c = php_swoole_get_h2c(ZEND_THIS); - - if (!h2c->is_available()) - { - RETURN_FALSE; - } - - RETURN_BOOL(h2c->send_ping_frame()); -} - -/** - * +-+-------------------------------------------------------------+ - * |R| Last-Stream-ID (31) | - * +-+-------------------------------------------------------------+ - * | Error Code (32) | - * +---------------------------------------------------------------+ - * | Additional Debug Data (*) | - * +---------------------------------------------------------------+ - */ -static PHP_METHOD(swoole_http2_client_coro, goaway) -{ - http2_client *h2c = php_swoole_get_h2c(ZEND_THIS); - zend_long error_code = SW_HTTP2_ERROR_NO_ERROR; - char* debug_data = NULL; - size_t debug_data_len = 0; - - if (!h2c->is_available()) - { - RETURN_FALSE; - } - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ls", &error_code, &debug_data, &debug_data_len) == FAILURE) - { - RETURN_FALSE; - } - - RETURN_BOOL(h2c->send_goaway_frame(error_code, debug_data, debug_data_len)); -} - -#endif diff --git a/swoole_http2_server.cc b/swoole_http2_server.cc deleted file mode 100644 index ae5a39b450e..00000000000 --- a/swoole_http2_server.cc +++ /dev/null @@ -1,1123 +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 "swoole_http_server.h" - -#ifdef SW_USE_HTTP2 - -#include "static_handler.h" -#include "http2.h" - -#include "main/php_variables.h" - -#include - -extern swString *swoole_http_buffer; - -using namespace swoole; -using swoole::http::StaticHandler; -using std::string; -using swoole::coroutine::System; - -static std::unordered_map http2_sessions; - -static bool swoole_http2_server_respond(http_context *ctx, swString *body); - -http2_stream::http2_stream(http2_session *client, uint32_t _id) -{ - ctx = swoole_http_context_new(client->fd); - swoole_http_context_copy(client->default_ctx, 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; -} - -http2_stream::~http2_stream() -{ - ctx->stream = nullptr; - ctx->end = true; - swoole_http_context_free(ctx); -} - -void http2_stream::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); - *(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); - ctx->send(ctx, frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_RST_STREAM_SIZE); -} - -http2_session::http2_session(int _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_sessions[_fd] = this; -} - -http2_session::~http2_session() -{ - for (auto iter = streams.begin(); iter != streams.end(); iter++) - { - delete iter->second; - } - if (inflater) - { - nghttp2_hd_inflate_del(inflater); - } - if (deflater) - { - nghttp2_hd_deflate_del(deflater); - } - if (default_ctx) - { - efree(default_ctx); - } - http2_sessions.erase(fd); -} - -static void http2_server_send_window_update(http_context *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); - *(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); - 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) -{ - zval *ztrailer = sw_zend_read_property(swoole_http_response_ce, ctx->response.zobject, ZEND_STRL("trailer"), 0); - uint32_t size = php_swoole_array_length_safe(ztrailer); - - if (size > 0) - { - http2::headers trailer(size); - zend_string *key; - zval *zvalue; - - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(ztrailer), key, zvalue) - { - if (UNEXPECTED(!key || ZVAL_IS_NULL(zvalue))) - { - continue; - } - zend::string str_value(zvalue); - trailer.add(ZSTR_VAL(key), ZSTR_LEN(key), str_value.val(), str_value.len()); - } - ZEND_HASH_FOREACH_END(); - - ssize_t rv; - size_t buflen; - http2_session *client = http2_sessions[ctx->fd]; - nghttp2_hd_deflater *deflater = client->deflater; - - if (!deflater) - { - int ret = nghttp2_hd_deflate_new2(&deflater, SW_HTTP2_DEFAULT_HEADER_TABLE_SIZE, php_nghttp2_mem()); - if (ret != 0) - { - swWarn("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; - } - */ - 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)); - return -1; - } - return rv; - } - return 0; -} - -static bool swoole_http2_is_static_file(swServer *serv, http_context *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) - { - StaticHandler handler(serv, Z_STRVAL_P(zrequest_uri), Z_STRLEN_P(zrequest_uri)); - if (!handler.hit()) - { - return false; - } - - if (handler.status_code == SW_HTTP_NOT_FOUND) - { - swString null_body; - null_body.length = 0; - null_body.str = nullptr; - - ctx->response.status = SW_HTTP_NOT_FOUND; - swoole_http2_server_respond(ctx, &null_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 *zdate_if_modified_since = zend_hash_str_find(Z_ARR_P(zheader), ZEND_STRL("if-modified-since")); - if (zdate_if_modified_since) - { - string date_if_modified_since(Z_STRVAL_P(zdate_if_modified_since), Z_STRLEN_P(zdate_if_modified_since)); - if (!date_if_modified_since.empty() && handler.is_modified(date_if_modified_since)) - { - ctx->response.status = SW_HTTP_NOT_MODIFIED; - return true; - } - } - - 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, NULL, "sendfile", &retval, &zfilename); - - return true; - } - - return false; -} - -static void swoole_http2_onRequest(http2_session *client, http2_stream *stream) -{ - http_context *ctx = stream->ctx; - zval *zserver = ctx->request.zserver; - swServer *serv = (swServer *) ctx->private_data; - - swConnection *conn = swWorker_get_connection(serv, ctx->fd); - int server_fd = conn->server_fd; - swConnection *serv_sock = swServer_connection_get(serv, server_fd); - - ctx->request.version = SW_HTTP_OK; - - 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; - } - - add_assoc_long(zserver, "request_time", time(NULL)); - add_assoc_double(zserver, "request_time_float", swoole_microtime()); - if (serv_sock) - { - add_assoc_long(zserver, "server_port", swSocket_get_port(serv_sock->socket_type, &serv_sock->info)); - } - add_assoc_long(zserver, "remote_port", swSocket_get_port(conn->socket_type, &conn->info)); - add_assoc_string(zserver, "remote_addr", (char * ) swSocket_get_ip(conn->socket_type, &conn->info)); - add_assoc_long(zserver, "master_time", conn->last_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, NULL, SwooleG.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]); -} - -static ssize_t http2_build_header(http_context *ctx, uchar *buffer, size_t body_length) -{ - zval *zheader = sw_zend_read_property(swoole_http_response_ce, ctx->response.zobject, ZEND_STRL("header"), 0); - zval *zcookie = sw_zend_read_property(swoole_http_response_ce, ctx->response.zobject, ZEND_STRL("cookie"), 0); - http2::headers headers(8 + php_swoole_array_length_safe(zheader) + php_swoole_array_length_safe(zcookie)); - char *date_str = NULL; - char intbuf[2][16]; - int ret; - - assert(ctx->send_header == 0); - - // status code - if (ctx->response.status == 0) - { - ctx->response.status = SW_HTTP_OK; - } - ret = swoole_itoa(intbuf[0], ctx->response.status); - headers.add(ZEND_STRL(":status"), intbuf[0], ret); - - // headers - if (ZVAL_IS_ARRAY(zheader)) - { - uint32_t header_flag = 0x0; - zend_string *key; - zval *zvalue; - - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheader), key, 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; - } - headers.add(c_key, c_keylen, str_value.val(), str_value.len()); - } - ZEND_HASH_FOREACH_END(); - - 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(NULL), 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")); - } - } - else - { - 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(NULL), 0); - headers.add(ZEND_STRL("date"), date_str, strlen(date_str)); - } - if (date_str) - { - efree(date_str); - } - - // cookies - if (ZVAL_IS_ARRAY(zcookie)) - { - zval *zvalue; - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(zcookie), zvalue) - { - if (Z_TYPE_P(zvalue) != IS_STRING) - { - continue; - } - headers.add(ZEND_STRL("set-cookie"), Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue)); - } - SW_HASHTABLE_FOREACH_END(); - } - - // content encoding -#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)); - } -#endif - - // content length -#ifdef SW_HAVE_COMPRESSION - if (ctx->accept_compression) - { - body_length = swoole_zlib_buffer->length; - } -#endif - ret = swoole_itoa(intbuf[1], body_length); - headers.add(ZEND_STRL("content-length"), intbuf[1], ret); - - http2_session *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()); - if (ret != 0) - { - swWarn("nghttp2_hd_deflate_new2() failed with error: %s", nghttp2_strerror(ret)); - return -1; - } - client->deflater = deflater; - } - - size_t buflen = nghttp2_hd_deflate_bound(deflater, headers.get(), headers.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; - } - */ - 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)); - return -1; - } - - ctx->send_header = 1; - return rv; -} - -int swoole_http2_server_ping(http_context *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); - return ctx->send(ctx, frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE) ? SW_OK : SW_ERR; -} - -bool http2_stream::send_header(size_t body_length, bool end_stream) -{ - char header_buffer[SW_BUFFER_SIZE_STD]; - ssize_t bytes = http2_build_header(ctx, (uchar *) header_buffer, body_length); - if (bytes < 0) - { - return false; - } - - swString_clear(swoole_http_buffer); - - /** - +---------------+ - |Pad Length? (8)| - +-+-------------+-----------------------------------------------+ - |E| Stream Dependency? (31) | - +-+-------------+-----------------------------------------------+ - | Weight? (8) | - +-+-------------+-----------------------------------------------+ - | Header Block Fragment (*) ... - +---------------------------------------------------------------+ - | Padding (*) ... - +---------------------------------------------------------------+ - */ - char frame_header[SW_HTTP2_FRAME_HEADER_SIZE]; - - if (end_stream && body_length == 0) - { - swHttp2_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); - } - - swString_append_ptr(swoole_http_buffer, frame_header, SW_HTTP2_FRAME_HEADER_SIZE); - swString_append_ptr(swoole_http_buffer, header_buffer, bytes); - - if (!ctx->send(ctx, swoole_http_buffer->str, swoole_http_buffer->length)) - { - ctx->send_header = 0; - return false; - } - - return true; -} - -bool http2_stream::send_body(swString *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; - - while (l > 0) - { - size_t send_n; - int _send_flag; - swString_clear(swoole_http_buffer); - if (l > max_frame_size) - { - send_n = max_frame_size; - _send_flag = 0; - } - else - { - send_n = l; - _send_flag = flag; - } - swHttp2_set_frame_header(frame_header, SW_HTTP2_TYPE_DATA, send_n, _send_flag, id); - swString_append_ptr(swoole_http_buffer, frame_header, SW_HTTP2_FRAME_HEADER_SIZE); - swString_append_ptr(swoole_http_buffer, p, send_n); - - if (!ctx->send(ctx, swoole_http_buffer->str, swoole_http_buffer->length)) - { - return false; - } - else - { - l -= send_n; - p += send_n; - } - } - - return true; -} - -bool http2_stream::send_trailer() -{ - char header_buffer[SW_BUFFER_SIZE_STD] = {}; - char frame_header[SW_HTTP2_FRAME_HEADER_SIZE]; - - swString_clear(swoole_http_buffer); - ssize_t bytes = http2_build_trailer(ctx, (uchar *) header_buffer); - if (bytes > 0) - { - swHttp2_set_frame_header(frame_header, SW_HTTP2_TYPE_HEADERS, bytes, SW_HTTP2_FLAG_END_HEADERS | SW_HTTP2_FLAG_END_STREAM, id); - swString_append_ptr(swoole_http_buffer, frame_header, SW_HTTP2_FRAME_HEADER_SIZE); - swString_append_ptr(swoole_http_buffer, header_buffer, bytes); - if (!ctx->send(ctx, swoole_http_buffer->str, swoole_http_buffer->length)) - { - return false; - } - } - - return true; -} - -static bool swoole_http2_server_respond(http_context *ctx, swString *body) -{ - http2_session *client = http2_sessions[ctx->fd]; - http2_stream *stream = ctx->stream; - -#ifdef SW_HAVE_COMPRESSION - if (ctx->accept_compression) - { - if (body->length == 0 || swoole_http_response_compress(body, ctx->compression_method, ctx->compression_level) != SW_OK) - { - ctx->accept_compression = 0; - } - else - { - body = swoole_zlib_buffer; - } - } -#endif - - zval *ztrailer = sw_zend_read_property(swoole_http_response_ce, ctx->response.zobject, ZEND_STRL("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; - } - - /* headers has already been sent, retries are no longer allowed (even if send body failed) */ - ctx->end = 1; - - bool error = false; - - if (body->length != 0) - { - if (!stream->send_body(body, end_stream, client->max_frame_size)) - { - error = true; - } - else - { - client->send_window -= body->length; // TODO: flow control? - } - } - - if (!error && ztrailer) - { - if (!stream->send_trailer()) - { - error = true; - } - } - - if (error) - { - ctx->close(ctx); - } - else - { - client->streams.erase(stream->id); - delete stream; - } - - return !error; -} - -static bool http2_context_sendfile(http_context* ctx, const char *file, uint32_t l_file, off_t offset, size_t length) -{ - http2_session *client = http2_sessions[ctx->fd]; - http2_stream *stream = (http2_stream *) ctx->stream; - swString *body; - -#ifdef SW_HAVE_COMPRESSION - ctx->accept_compression = 0; -#endif - if (swoole_coroutine_is_in()) - { - body = System::read_file(file, false); - if (!body) - { - return false; - } - if (!ctx->stream) - { - /* closed */ - return false; - } - } - else - { - int fd = open(file, O_RDONLY); - if (fd < 0) - { - return false; - } - body = swoole_sync_readfile_eof(fd); - close(fd); - if (!body) - { - return false; - } - } - body->length = SW_MIN(length, body->length); - - zval *ztrailer = sw_zend_read_property(swoole_http_response_ce, ctx->response.zobject, ZEND_STRL("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); - - bool end_stream = (ztrailer == nullptr); - if (!stream->send_header(length, end_stream)) - { - return false; - } - - /* headers has already been sent, retries are no longer allowed (even if send body failed) */ - ctx->end = 1; - - bool error = false; - - if (body->length > 0) - { - if (!stream->send_body(body, end_stream, client->max_frame_size, offset, length)) - { - error = true; - } - else - { - client->send_window -= length; // TODO: flow control? - } - } - - swString_free(body); - - if (!error && ztrailer) - { - if (!stream->send_trailer()) - { - error = true; - } - } - - if (error) - { - ctx->close(ctx); - } - else - { - client->streams.erase(stream->id); - delete stream; - } - - return true; -} - -static int http2_parse_header(http2_session *client, http_context *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); - return SW_ERR; - } - client->inflater = inflater; - } - - if (flags & SW_HTTP2_FLAG_PRIORITY) - { - //int stream_deps = ntohl(*(int *) (in)); - //uint8_t weight = in[4]; - in += 5; - inlen -= 5; - } - - zval *zheader = ctx->request.zheader; - zval *zserver = ctx->request.zserver; - - ssize_t rv; - for (;;) - { - nghttp2_nv nv; - int inflate_flags = 0; - size_t proclen; - - 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); - return SW_ERR; - } - - proclen = (size_t) rv; - - in += proclen; - 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); - - if (nv.name[0] == ':') - { - if (SW_STRCASEEQ((char *) nv.name + 1, nv.namelen - 1, "method")) - { - add_assoc_stringl_ex(zserver, ZEND_STRL("request_method"), (char *) nv.value, nv.valuelen); - } - else if (SW_STRCASEEQ((char *) nv.name + 1, nv.namelen - 1, "path")) - { - char *pathbuf = SwooleTG.buffer_stack->str; - char *v_str = strchr((char *) nv.value, '?'); - zend_string *zstr_path; - if (v_str) - { - v_str++; - int k_len = v_str - (char *) nv.value - 1; - int v_len = nv.valuelen - k_len - 1; - memcpy(pathbuf, nv.value, k_len); - pathbuf[k_len] = 0; - add_assoc_stringl_ex(zserver, ZEND_STRL("query_string"), v_str, v_len); - zstr_path = zend_string_init(pathbuf, k_len, 0); - // parse url params - sapi_module.treat_data( - PARSE_STRING, - estrndup(v_str, v_len), // 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")) - ); - } - else - { - zstr_path = zend_string_init((char *) nv.value, nv.valuelen, 0); - } - ctx->request.path = (char*) estrndup((char* )nv.value, nv.valuelen); - ctx->request.path_len = nv.valuelen; - add_assoc_str_ex(zserver, ZEND_STRL("request_uri"), zstr_path); - // path_info should be decoded - zstr_path = zend_string_dup(zstr_path, 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); - } - else if (SW_STRCASEEQ((char *) nv.name + 1, nv.namelen - 1, "authority")) - { - add_assoc_stringl_ex(zheader, ZEND_STRL("host"), (char * ) nv.value, nv.valuelen); - } - } - else - { - if (SW_STRCASEEQ((char *) nv.name, nv.namelen, "content-type")) - { - if (SW_STRCASECT((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:%d", ctx->fd); - return SW_ERR; - } - swoole_http_parse_form_data(ctx, (char*) nv.value + nv.valuelen - boundary_len, boundary_len); - ctx->parser.data = ctx; - } - } - else if (SW_STRCASEEQ((char *) nv.name, nv.namelen, "cookie")) - { - swoole_http_parse_cookie( - swoole_http_init_and_read_property(swoole_http_request_ce, ctx->request.zobject, &ctx->request.zcookie, ZEND_STRL("cookie")), - (const char *) nv.value, nv.valuelen - ); - continue; - } -#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); - } -#endif - add_assoc_stringl_ex(zheader, (char *) nv.name, nv.namelen, (char *) nv.value, nv.valuelen); - } - } - - if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) - { - nghttp2_hd_inflate_end_headers(inflater); - break; - } - - if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) - { - break; - } - } - - return SW_OK; -} - -int swoole_http2_server_parse(http2_session *client, const char *buf) -{ - http2_stream *stream = nullptr; - int type = buf[3]; - int flags = buf[4]; - uint32_t stream_id = ntohl((*(int *) (buf + 5))) & 0x7fffffff; - ssize_t length = swHttp2_get_length(buf); - buf += SW_HTTP2_FRAME_HEADER_SIZE; - - uint16_t id = 0; - uint32_t value = 0; - - switch (type) - { - case SW_HTTP2_TYPE_SETTINGS: - { - if (flags & SW_HTTP2_FLAG_ACK) - { - swHttp2FrameTraceLog(recv, "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); - switch (id) - { - case SW_HTTP2_SETTING_HEADER_TABLE_SIZE: - if (value != client->header_table_size) - { - client->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=%s, errmsg=%s", ret, - nghttp2_strerror(ret)); - return SW_ERROR; - } - } - } - swTraceLog(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); - break; - case SW_HTTP2_SETTINGS_INIT_WINDOW_SIZE: - client->send_window = value; - swTraceLog(SW_TRACE_HTTP2, "setting: init_send_window=%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); - 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); - break; - default: - // disable warning and ignore it because some websites are not following http2 protocol totally - // swWarn("unknown option[%d]: %d", id, value); - break; - } - buf += sizeof(id) + sizeof(value); - length -= sizeof(id) + sizeof(value); - } - break; - } - case SW_HTTP2_TYPE_HEADERS: - { - stream = client->streams[stream_id]; - swHttp2FrameTraceLog(recv, "%s", (stream ? "exist stream" : "new stream")); - http_context *ctx; - if (!stream) - { - stream = new http2_stream(client, stream_id); - if (sw_unlikely(!stream->ctx)) - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_HTTP2_STREAM_NO_HEADER, "http2 create stream#%d context error", stream_id); - return SW_ERR; - } - ctx = stream->ctx; - client->streams[stream_id] = stream; - zend_update_property_long(swoole_http_request_ce, ctx->request.zobject, ZEND_STRL("streamId"), stream_id); - } - else - { - ctx = stream->ctx; - } - if (http2_parse_header(client, ctx, flags, buf, length) < 0) - { - return SW_ERR; - } - - if (flags & SW_HTTP2_FLAG_END_STREAM) - { - client->handle(client, stream); - } - else - { - // need continue frame - } - break; - } - case SW_HTTP2_TYPE_DATA: - { - swHttp2FrameTraceLog(recv, "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; - - zend_update_property_long(swoole_http_request_ce, ctx->request.zobject, ZEND_STRL("streamId"), stream_id); - - swString *buffer = ctx->request.h2_data_buffer; - if (!buffer) - { - buffer = swString_new(SW_HTTP2_DATA_BUFFER_SIZE); - ctx->request.h2_data_buffer = buffer; - } - swString_append_ptr(buffer, buf, length); - - // flow control - client->recv_window -= length; - stream->recv_window -= 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 (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 (flags & SW_HTTP2_FLAG_END_STREAM) - { - if (ctx->parse_body && ctx->request.post_form_urlencoded) - { - sapi_module.treat_data( - PARSE_STRING, estrndup(buffer->str, buffer->length), // 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 != NULL) - { - 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); - } - } - client->handle(client, stream); - } - break; - } - case SW_HTTP2_TYPE_PING: - { - swHttp2FrameTraceLog(recv, "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(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(client->default_ctx, ping_frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE); - } - break; - } - 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]; - stream->send_window += value; - } - swHttp2FrameTraceLog(recv, "window_size_increment=%d", value); - break; - } - case SW_HTTP2_TYPE_RST_STREAM: - { - value = ntohl(*(int *) (buf)); - swHttp2FrameTraceLog(recv, "error_code=%d", value); - if (client->streams.find(stream_id) != client->streams.end()) - { - // TODO: i onRequest and use request->recv - // stream exist - stream = client->streams[stream_id]; - client->streams.erase(stream_id); - delete stream; - } - break; - } - case SW_HTTP2_TYPE_GOAWAY: - { - uint32_t server_last_stream_id = ntohl(*(uint32_t *) (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); - //TODO: onRequest - (void) server_last_stream_id; - - break; - } - default: - { - swHttp2FrameTraceLog(recv, ""); - } - } - return SW_OK; -} - -/** - * Http2 - */ -int swoole_http2_server_onFrame(swServer *serv, swConnection *conn, swEventData *req) -{ - int session_id = req->info.fd; - http2_session *client = http2_sessions[session_id]; - if (client == nullptr) - { - client = new http2_session(session_id); - } - - client->handle = swoole_http2_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->fd = session_id; - client->default_ctx->http2 = true; - client->default_ctx->stream = (http2_stream *) -1; - client->default_ctx->keepalive = true; - client->default_ctx->sendfile = http2_context_sendfile; - } - - zval zdata; - php_swoole_get_recv_data(serv, &zdata, req); - swoole_http2_server_parse(client, Z_STRVAL(zdata)); - zval_ptr_dtor(&zdata); - - return SW_OK; -} - -void swoole_http2_server_session_free(swConnection *conn) -{ - auto session_iterator = http2_sessions.find(conn->session_id); - if (session_iterator == http2_sessions.end()) - { - return; - } - http2_session *client = session_iterator->second; - delete client; -} - -void swoole_http2_response_end(http_context *ctx, zval *zdata, zval *return_value) -{ - swString http_body; - if (zdata) - { - http_body.length = php_swoole_get_send_data(zdata, &http_body.str); - } - else - { - http_body.length = 0; - http_body.str = NULL; - } - - RETURN_BOOL(swoole_http2_server_respond(ctx, &http_body)); -} - -#endif diff --git a/swoole_http_client.h b/swoole_http_client.h deleted file mode 100644 index 56df178e929..00000000000 --- a/swoole_http_client.h +++ /dev/null @@ -1,72 +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 | - +----------------------------------------------------------------------+ -*/ - -#pragma once - -#include "swoole_http.h" - -#ifdef __cplusplus -extern "C" { -#endif -#include "websocket.h" -#include "thirdparty/swoole_http_parser.h" - -#include "ext/standard/basic_functions.h" -#include "ext/standard/php_http.h" -#include "ext/standard/base64.h" - -#ifdef SW_HAVE_ZLIB -#include -#endif - -#ifdef __cplusplus -} -#endif - -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, -}; - -static sw_inline void http_client_create_token(int length, char *buf) -{ - char characters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"§$%&/()=[]{}"; - int i; - assert(length < 1024); - for (i = 0; i < length; i++) - { - buf[i] = characters[rand() % (sizeof(characters) - 1)]; - } - buf[length] = '\0'; -} - -static sw_inline void http_client_swString_append_headers(swString* swStr, const char* key, size_t key_len, const char* data, size_t data_len) -{ - swString_append_ptr(swStr, key, key_len); - swString_append_ptr(swStr, ZEND_STRL(": ")); - swString_append_ptr(swStr, data, data_len); - swString_append_ptr(swStr, ZEND_STRL("\r\n")); -} - -static sw_inline void http_client_append_content_length(swString* buf, int length) -{ - char content_length_str[32]; - int n = snprintf(SW_STRS(content_length_str), "Content-Length: %d\r\n\r\n", length); - swString_append_ptr(buf, content_length_str, n); -} diff --git a/swoole_http_client_coro.cc b/swoole_http_client_coro.cc deleted file mode 100644 index 6afaafff95b..00000000000 --- a/swoole_http_client_coro.cc +++ /dev/null @@ -1,2238 +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 | - | Author: Twosee | - | Author: Fang | - | Author: Yuanyi Zhi | - +----------------------------------------------------------------------+ - */ - -#include "php_swoole_cxx.h" -#include "coroutine_c_api.h" -#include "swoole_http_client.h" - -#include "mime_type.h" -#include "base64.h" - -#ifdef SW_HAVE_BROTLI -#include -#endif - -using namespace swoole; -using swoole::coroutine::Socket; - -extern void php_swoole_client_coro_socket_free(Socket *cli); - -static int http_parser_on_header_field(swoole_http_parser *parser, const char *at, size_t length); -static int http_parser_on_header_value(swoole_http_parser *parser, const char *at, size_t length); -static int http_parser_on_headers_complete(swoole_http_parser *parser); -static int http_parser_on_body(swoole_http_parser *parser, const char *at, size_t length); -static int http_parser_on_message_complete(swoole_http_parser *parser); - -static const swoole_http_parser_settings http_parser_settings = -{ - NULL, - NULL, - NULL, - NULL, - NULL, - http_parser_on_header_field, - http_parser_on_header_value, - http_parser_on_headers_complete, - http_parser_on_body, - http_parser_on_message_complete -}; - -class http_client -{ -public: - /* request info */ - std::string host = "127.0.0.1"; - uint16_t port = 80; -#ifdef SW_USE_OPENSSL - uint8_t ssl = false; -#endif - double connect_timeout = Socket::default_connect_timeout; - bool defer = false; - - int8_t method = SW_HTTP_GET; - std::string path; - std::string basic_auth; - - /* for response parser */ - char *tmp_header_field_name = nullptr; - int tmp_header_field_name_len = 0; - swString *body = nullptr; -#ifdef SW_HAVE_COMPRESSION - enum http_compress_method compress_method = HTTP_COMPRESS_NONE; - bool compression_error = false; -#endif - - /* options */ - uint8_t reconnect_interval = 1; - uint8_t reconnected_count = 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 -#ifdef SW_HAVE_ZLIB - bool websocket_compression = false; // allow to compress websocket messages -#endif - int download_file_fd = 0; // save http response to file - bool has_upload_files = false; - - /* safety zval */ - zval _zobject; - zval *zobject = &_zobject; - - http_client(zval* zobject, std::string host, zend_long port = 80, zend_bool ssl = false); - -private: -#ifdef SW_HAVE_ZLIB - bool gzip_stream_active = false; - z_stream gzip_stream; -#endif -#ifdef SW_HAVE_BROTLI - BrotliDecoderState *brotli_decoder_state = nullptr; -#endif - bool connect(); - bool keep_liveness(); - bool send(); - void reset(); - -public: -#ifdef SW_HAVE_COMPRESSION - bool decompress_response(const char *in, size_t in_len); -#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 upgrade(std::string path); - bool push(zval *zdata, zend_long opcode = WEBSOCKET_OPCODE_TEXT, uint8_t flags = SW_WEBSOCKET_FLAG_FIN); - bool close(const bool should_be_reset = true); - - void get_header_out(zval *return_value) - { - swString *buffer = socket->get_write_buffer(); - if (buffer == nullptr) - { - RETURN_FALSE; - } - off_t offset = swoole_strnpos(buffer->str, buffer->length, ZEND_STRL("\r\n\r\n")); - if (offset <= 0) - { - RETURN_FALSE; - } - - RETURN_STRINGL(buffer->str, offset); - } - - void getsockname(zval *return_value) - { - swSocketAddress sa; - if (!socket || !socket->getsockname(&sa)) - { - ZVAL_FALSE(return_value); - return; - } - - array_init(return_value); - add_assoc_string(return_value, "address", (char *) swSocket_get_ip(socket->get_type(), &sa)); - add_assoc_long(return_value, "port", swSocket_get_port(socket->get_type(), &sa)); - } - - void getpeername(zval *return_value) - { - swSocketAddress sa; - if (!socket || !socket->getpeername(&sa)) - { - ZVAL_FALSE(return_value); - return; - } - - array_init(return_value); - add_assoc_string(return_value, "address", (char *) swSocket_get_ip(socket->get_type(), &sa)); - add_assoc_long(return_value, "port", swSocket_get_port(socket->get_type(), &sa)); - } - -#ifdef SW_USE_OPENSSL - void getpeercert(zval *return_value) - { - auto cert = socket->ssl_get_peer_cert(); - if (cert.empty()) - { - ZVAL_FALSE(return_value); - return; - } - else - { - ZVAL_STRINGL(return_value, cert.c_str(), cert.length()); - } - } -#endif - - ~http_client(); - -private: - Socket* socket = nullptr; - swSocket_type socket_type = SW_SOCK_TCP; - swoole_http_parser parser = {}; - bool wait = false; -}; - -static zend_class_entry *swoole_http_client_coro_ce; -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; - -typedef struct -{ - http_client* phc; - zend_object std; -} http_client_coro; - -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 PHP_METHOD(swoole_http_client_coro, __construct); -static PHP_METHOD(swoole_http_client_coro, __destruct); -static PHP_METHOD(swoole_http_client_coro, set); -static PHP_METHOD(swoole_http_client_coro, getDefer); -static PHP_METHOD(swoole_http_client_coro, setDefer); -static PHP_METHOD(swoole_http_client_coro, setMethod); -static PHP_METHOD(swoole_http_client_coro, setHeaders); -static PHP_METHOD(swoole_http_client_coro, setBasicAuth); -static PHP_METHOD(swoole_http_client_coro, setCookies); -static PHP_METHOD(swoole_http_client_coro, setData); -static PHP_METHOD(swoole_http_client_coro, addFile); -static PHP_METHOD(swoole_http_client_coro, addData); -static PHP_METHOD(swoole_http_client_coro, execute); -static PHP_METHOD(swoole_http_client_coro, getsockname); -static PHP_METHOD(swoole_http_client_coro, getpeername); -static PHP_METHOD(swoole_http_client_coro, get); -static PHP_METHOD(swoole_http_client_coro, post); -static PHP_METHOD(swoole_http_client_coro, download); -static PHP_METHOD(swoole_http_client_coro, getBody); -static PHP_METHOD(swoole_http_client_coro, getHeaders); -static PHP_METHOD(swoole_http_client_coro, getCookies); -static PHP_METHOD(swoole_http_client_coro, getStatusCode); -static PHP_METHOD(swoole_http_client_coro, getHeaderOut); -#ifdef SW_USE_OPENSSL -static PHP_METHOD(swoole_http_client_coro, getPeerCert); -#endif -static PHP_METHOD(swoole_http_client_coro, upgrade); -static PHP_METHOD(swoole_http_client_coro, push); -static PHP_METHOD(swoole_http_client_coro, recv); -static PHP_METHOD(swoole_http_client_coro, close); - -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) -#ifdef SW_USE_OPENSSL - PHP_ME(swoole_http_client_coro, getPeerCert, arginfo_swoole_void, 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_FE_END -}; - -void 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; - - // key - p = (char *) memchr(at, '=', length); - if (p) - { - key_len = p - at; - p++; // point to value - } - else - { - p = at; // key is empty - } - // value - eof = (char*) memchr(p, ';', at + length - p); - if (!eof) - { - eof = at + length; - } - value_len = eof - p; - if (value_len != 0) - { - ZVAL_STRINGL(&zvalue, p, value_len); - Z_STRLEN(zvalue) = php_url_decode(Z_STRVAL(zvalue), value_len); - } - else - { - ZVAL_EMPTY_STRING(&zvalue); - } - if (key_len == 0) - { - add_next_index_zval(zcookies, &zvalue); - } - else - { - add_assoc_zval_ex(zcookies, at, key_len, &zvalue); - } - - // set_cookie_headers - add_next_index_stringl(zset_cookie_headers, (char *) at, length); -} - -static int http_parser_on_header_field(swoole_http_parser *parser, const char *at, size_t length) -{ - http_client* http = (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) -{ - http_client* http = (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 = zend_str_tolower_dup(http->tmp_header_field_name, http->tmp_header_field_name_len); - size_t header_len = http->tmp_header_field_name_len; - - add_assoc_stringl_ex(zheaders, header_name, http->tmp_header_field_name_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")) - { - http->websocket = true; - } - /* TODO: protocol error? */ - } -#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; - } - } -#endif - else if (SW_STREQ(header_name, header_len, "set-cookie")) - { - zval *zcookies = 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); - } -#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")) - { - http->compress_method = HTTP_COMPRESS_BR; - } -#endif -#ifdef SW_HAVE_ZLIB - else if (SW_STRCASECT(at, length, "gzip")) - { - http->compress_method = HTTP_COMPRESS_GZIP; - } - else if (SW_STRCASECT(at, length, "deflate")) - { - http->compress_method = HTTP_COMPRESS_DEFLATE; - } -#endif - } -#endif - else if (SW_STREQ(header_name, header_len, "transfer-encoding") && SW_STRCASECT(at, length, "chunked")) - { - http->chunked = true; - } - - efree(header_name); - return 0; -} - -static int http_parser_on_headers_complete(swoole_http_parser *parser) -{ - http_client* http = (http_client*) parser->data; - if (http->method == SW_HTTP_HEAD || parser->status_code == SW_HTTP_NO_CONTENT) - { - return 1; - } - return 0; -} - -static int http_parser_on_body(swoole_http_parser *parser, const char *at, size_t length) -{ - http_client* http = (http_client*) parser->data; -#ifdef SW_HAVE_COMPRESSION - if (!http->compression_error && http->compress_method != HTTP_COMPRESS_NONE) - { - if (!http->decompress_response(at, length)) - { - http->compression_error = true; - goto _append_raw; - } - } - else -#endif - { -#ifdef SW_HAVE_COMPRESSION - _append_raw: -#endif - if (swString_append_ptr(http->body, at, length) < 0) - { - return -1; - } - } - if (http->download_file_fd > 0 && http->body->length > 0) - { - if (swoole_coroutine_write(http->download_file_fd, SW_STRINGL(http->body)) != (ssize_t) http->body->length) - { - return -1; - } - swString_clear(http->body); - } - return 0; -} - -static int http_parser_on_message_complete(swoole_http_parser *parser) -{ - http_client* http = (http_client*) parser->data; - zval* zobject = (zval*) http->zobject; - - if (parser->upgrade && !http->websocket) - { - // not support, continue. - parser->upgrade = 0; - return 0; - } - - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("statusCode"), parser->status_code); - if (http->download_file_fd <= 0) - { - zend_update_property_stringl(swoole_http_client_coro_ce, zobject, ZEND_STRL("body"), SW_STRINGL(http->body)); - } - - if (parser->upgrade) - { - // return 1 will finish the parser and means yes we support it. - return 1; - } - else - { - return 0; - } -} - -http_client::http_client(zval* zobject, std::string host, zend_long port, zend_bool ssl) -{ - this->socket_type = Socket::convert_to_type(host); - this->host = host; - this->port = port; -#ifdef SW_USE_OPENSSL - this->ssl = ssl; -#endif - _zobject = *zobject; - // TODO: zend_read_property cache here (strong type properties) -} - -#ifdef SW_HAVE_COMPRESSION -bool http_client::decompress_response(const char *in, size_t in_len) -{ - if (in_len == 0) - { - return false; - } - - size_t reserved_body_length = body->length; - - switch(compress_method) - { -#ifdef SW_HAVE_ZLIB - case HTTP_COMPRESS_GZIP: - case HTTP_COMPRESS_DEFLATE: - { - int status; - int encoding = compress_method == HTTP_COMPRESS_GZIP ? SW_ZLIB_ENCODING_GZIP : SW_ZLIB_ENCODING_DEFLATE; - bool first_decompress = !gzip_stream_active; - size_t total_out; - - if (!gzip_stream_active) - { - _retry: - memset(&gzip_stream, 0, sizeof(gzip_stream)); - gzip_stream.zalloc = php_zlib_alloc; - gzip_stream.zfree = php_zlib_free; - // gzip_stream.total_out = 0; - status = inflateInit2(&gzip_stream, encoding); - if (status != Z_OK) - { - swWarn("inflateInit2() failed by %s", zError(status)); - return false; - } - gzip_stream_active = true; - } - - gzip_stream.next_in = (Bytef *) in; - gzip_stream.avail_in = in_len; - gzip_stream.total_in = 0; - - while (1) - { - total_out = gzip_stream.total_out; - gzip_stream.avail_out = body->size - body->length; - gzip_stream.next_out = (Bytef *) (body->str + body->length); - SW_ASSERT(body->length <= body->size); - status = inflate(&gzip_stream, Z_SYNC_FLUSH); - if (status >= 0) - { - body->length += (gzip_stream.total_out - total_out); - if (body->length + (SW_BUFFER_SIZE_STD / 2) >= body->size) - { - if (swString_extend(body, body->size * 2) < 0) - { - status = Z_MEM_ERROR; - break; - } - } - } - if (status == Z_STREAM_END || (status == Z_OK && gzip_stream.avail_in == 0)) - { - return true; - } - if (status != Z_OK) - { - break; - } - } - - if (status == Z_DATA_ERROR && first_decompress) - { - first_decompress = false; - inflateEnd(&gzip_stream); - encoding = SW_ZLIB_ENCODING_RAW; - body->length = reserved_body_length; - goto _retry; - } - - swWarn("http_client::decompress_response failed by %s", zError(status)); - body->length = reserved_body_length; - return false; - } -#endif -#ifdef SW_HAVE_BROTLI - case HTTP_COMPRESS_BR: - { - if (!brotli_decoder_state) { - brotli_decoder_state = BrotliDecoderCreateInstance(php_brotli_alloc, php_brotli_free, NULL); - if (!brotli_decoder_state) - { - swWarn("BrotliDecoderCreateInstance() failed"); - return false; - } - } - - const char *next_in = in; - size_t available_in = in_len; - while (1) { - size_t available_out = body->size - body->length, reserved_available_out = available_out; - char * next_out = body->str + body->length; - size_t total_out; - BrotliDecoderResult result; - SW_ASSERT(body->length <= body->size); - result = BrotliDecoderDecompressStream( - brotli_decoder_state, - &available_in, (const uint8_t **) &next_in, - &available_out, (uint8_t **) &next_out, - &total_out - ); - body->length += reserved_available_out - available_out; - if (result == BROTLI_DECODER_RESULT_SUCCESS || result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) - { - return true; - } - else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) - { - if (swString_extend_align(body, body->size * 2) < 0) - { - swWarn("BrotliDecoderDecompressStream() failed, no memory is available"); - break; - } - } - else - { - swWarn("BrotliDecoderDecompressStream() failed, %s", BrotliDecoderErrorString(BrotliDecoderGetErrorCode(brotli_decoder_state))); - break; - } - } - - body->length = reserved_body_length; - return false; - } -#endif - default: - break; - } - - swWarn("http_client::decompress_response unknown compress method [%d]", compress_method); - return false; -} -#endif - -void http_client::apply_setting(zval *zset, const bool check_all) -{ - if (!ZVAL_IS_ARRAY(zset) || php_swoole_array_length(zset) == 0) - { - return; - } - if (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 */) - { - 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, "defer", ztmp)) - { - defer = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "keep_alive", ztmp)) - { - keep_alive = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "websocket_mask", ztmp)) - { - websocket_mask = 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 (socket) - { - php_swoole_client_set(socket, zset); -#ifdef SW_USE_OPENSSL - if (socket->http_proxy && !socket->open_ssl) -#else - if (socket->http_proxy) -#endif - { - socket->http_proxy->dont_handshake = 1; - } - } -} - -void http_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); - if (sw_likely(output)) - { - size_t output_len = sprintf(output, "Basic "); - output_len += swBase64_encode((const unsigned char *) input.c_str(), input.size(), output + output_len); - basic_auth = std::string((const char *) output, output_len); - efree(output); - } -} - -bool http_client::connect() -{ - if (!socket) - { - if (!body) - { - body = swString_new(SW_HTTP_RESPONSE_INIT_SIZE); - if (!body) - { - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("errCode"), ENOMEM); - zend_update_property_string(swoole_http_client_coro_ce, zobject, ZEND_STRL("errMsg"), swoole_strerror(ENOMEM)); - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("statusCode"), HTTP_CLIENT_ESTATUS_CONNECT_FAILED); - return false; - } - } - - 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, zobject, ZEND_STRL("errCode"), errno); - zend_update_property_string(swoole_http_client_coro_ce, zobject, ZEND_STRL("errMsg"), swoole_strerror(errno)); - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("statusCode"), HTTP_CLIENT_ESTATUS_CONNECT_FAILED); - delete socket; - socket = nullptr; - return false; - } -#ifdef SW_USE_OPENSSL - socket->open_ssl = ssl; -#endif - // apply settings - apply_setting(sw_zend_read_property(swoole_http_client_coro_ce, zobject, ZEND_STRL("setting"), 0), false); - - // connect - socket->set_timeout(connect_timeout, SW_TIMEOUT_CONNECT); - if (!socket->connect(host, port)) - { - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("errCode"), socket->errCode); - zend_update_property_string(swoole_http_client_coro_ce, zobject, ZEND_STRL("errMsg"), socket->errMsg); - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("statusCode"), HTTP_CLIENT_ESTATUS_CONNECT_FAILED); - close(); - return false; - } - reconnected_count = 0; - zend_update_property_bool(swoole_http_client_coro_ce, zobject, ZEND_STRL("connected"), 1); - } - return true; -} - -bool http_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, zobject, ZEND_STRL("errCode"), socket->errCode); - zend_update_property_string(swoole_http_client_coro_ce, zobject, ZEND_STRL("errMsg"), socket->errMsg); - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("statusCode"), HTTP_CLIENT_ESTATUS_SERVER_RESET); - close(false); - } - for (; reconnected_count < reconnect_interval; reconnected_count++) - { - if (connect()) - { - return true; - } - } - return false; - } - return true; -} - -bool http_client::send() -{ - zval *zvalue = NULL; - 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"); - return false; - } - - // when new request, clear all properties about the last response - { - zval *zattr; - zattr = sw_zend_read_property(swoole_http_client_coro_ce, zobject, ZEND_STRL("headers"), 0); - if (ZVAL_IS_ARRAY(zattr)) - { - zend_hash_clean(Z_ARRVAL_P(zattr)); - } - zattr = sw_zend_read_property(swoole_http_client_coro_ce, zobject, ZEND_STRL("set_cookie_headers"), 0); - if (ZVAL_IS_ARRAY(zattr)) - { - zend_hash_clean(Z_ARRVAL_P(zattr)); - } - zend_update_property_string(swoole_http_client_coro_ce, zobject, ZEND_STRL("body"), ""); - } - - if (!keep_liveness()) - { - return false; - } - else - { - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("errCode"), 0); - zend_update_property_string(swoole_http_client_coro_ce, zobject, ZEND_STRL("errMsg"), ""); - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("statusCode"), 0); - } - - /* another coroutine is connecting */ - socket->check_bound_co(SW_EVENT_WRITE); - - //clear errno - SwooleG.error = 0; - //alloc buffer - swString *buffer = socket->get_write_buffer(); - swString_clear(buffer); - // clear body - swString_clear(body); - - zmethod = sw_zend_read_property_not_null(swoole_http_client_coro_ce, zobject, ZEND_STRL("requestMethod"), 0); - zheaders = sw_zend_read_property(swoole_http_client_coro_ce, zobject, ZEND_STRL("requestHeaders"), 0); - zbody = sw_zend_read_property_not_null(swoole_http_client_coro_ce, zobject, ZEND_STRL("requestBody"), 0); - zupload_files = sw_zend_read_property(swoole_http_client_coro_ce, zobject, ZEND_STRL("uploadFiles"), 0); - zcookies = sw_zend_read_property(swoole_http_client_coro_ce, zobject, ZEND_STRL("cookies"), 0); - z_download_file = sw_zend_read_property_not_null(swoole_http_client_coro_ce, zobject, ZEND_STRL("downloadFile"), 0); - - // ============ download ============ - if (z_download_file) - { - zend::string str_download_file(z_download_file); - char *download_file_name = str_download_file.val(); - zval *z_download_offset = sw_zend_read_property(swoole_http_client_coro_ce, zobject, ZEND_STRL("downloadOffset"), 0); - off_t download_offset = zval_get_long(z_download_offset); - - int fd = ::open(download_file_name, O_CREAT | O_WRONLY, 0664); - if (fd < 0) - { - swSysWarn("open(%s, O_CREAT | O_WRONLY) failed", download_file_name); - return false; - } - if (download_offset == 0) - { - if (ftruncate(fd, 0) < 0) - { - swSysWarn("ftruncate(%s) failed", download_file_name); - ::close(fd); - return false; - } - } - else - { - if (lseek(fd, download_offset, SEEK_SET) < 0) - { - swSysWarn("fseek(%s, %jd) failed", download_file_name, (intmax_t) download_offset); - ::close(fd); - return false; - } - } - download_file_fd = fd; - } - - // ============ method ============ - { - zend::string str_method; - const char *method; - size_t method_len; - if (zmethod) - { - str_method = zmethod; - method = str_method.val(); - method_len = str_method.len(); - } - else - { - method = zbody ? "POST" : "GET"; - method_len = strlen(method); - } - this->method = swHttp_get_method(method, method_len); - swString_append_ptr(buffer, method, method_len); - swString_append_ptr(buffer, ZEND_STRL(" ")); - } - - // ============ path & proxy ============ -#ifdef SW_USE_OPENSSL - if (socket->http_proxy && !socket->open_ssl) -#else - if (socket->http_proxy) -#endif - { - zend::string str_host; - const static char *pre = "https://"; - char *_host = (char *) host.c_str(); - size_t _host_len = host.length(); - if (ZVAL_IS_ARRAY(zheaders)) - { - if ((zvalue = zend_hash_str_find(Z_ARRVAL_P(zheaders), ZEND_STRL("Host")))) - { - str_host = zvalue; - _host = str_host.val(); - _host_len = str_host.len(); - } - } - 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()); - swString_append_ptr(buffer, proxy_uri, proxy_uri_len); - efree(proxy_uri); - } - else - { - swString_append_ptr(buffer, path.c_str(), path.length()); - } - - // ============ protocol ============ - swString_append_ptr(buffer, ZEND_STRL(" HTTP/1.1\r\n")); - - // ============ headers ============ - char *key; - uint32_t keylen; - int keytype; - - // As much as possible to ensure that Host is the first header. - // See: http://tools.ietf.org/html/rfc7230#section-5.4 - if ((ZVAL_IS_ARRAY(zheaders)) && ((zvalue = zend_hash_str_find(Z_ARRVAL_P(zheaders), ZEND_STRL("Host"))) || (zvalue = zend_hash_str_find(Z_ARRVAL_P(zheaders), ZEND_STRL("host"))))) - { - zend::string str_value(zvalue); - http_client_swString_append_headers(buffer, ZEND_STRL("Host"), str_value.val(), str_value.len()); - } - else - { - // See: https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.23 - const std::string *_host = &host; - std::string __host; - if (port != 80 && port != 443) - { - __host = cpp_string::format("%s:%u", host.c_str(), port); - _host = &__host; - } - http_client_swString_append_headers(buffer, ZEND_STRL("Host"), _host->c_str(), _host->length()); - } - - if (ZVAL_IS_ARRAY(zheaders)) - { - SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zheaders), key, keylen, keytype, zvalue) - { - if (UNEXPECTED(HASH_KEY_IS_STRING != keytype || ZVAL_IS_NULL(zvalue))) - { - continue; - } - if (SW_STRCASEEQ(key, keylen, "Host")) - { - continue; - } - if (SW_STRCASEEQ(key, keylen, "Content-Length")) - { - 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")) - { - header_flag |= HTTP_HEADER_ACCEPT_ENCODING; - } - zend::string str_value(zvalue); - http_client_swString_append_headers(buffer, key, keylen, str_value.val(), str_value.len()); - } - SW_HASHTABLE_FOREACH_END(); - } - - if (!basic_auth.empty()) - { - http_client_swString_append_headers(buffer, ZEND_STRL("Authorization"), basic_auth.c_str(), basic_auth.size()); - } - if (!(header_flag & HTTP_HEADER_CONNECTION)) - { - if (keep_alive) - { - http_client_swString_append_headers(buffer, ZEND_STRL("Connection"), ZEND_STRL("keep-alive")); - } - else - { - http_client_swString_append_headers(buffer, ZEND_STRL("Connection"), ZEND_STRL("closed")); - } - } -#ifdef SW_HAVE_COMPRESSION - if (!(header_flag & HTTP_HEADER_ACCEPT_ENCODING)) - { - http_client_swString_append_headers( - buffer, ZEND_STRL("Accept-Encoding"), -#if defined(SW_HAVE_ZLIB) && defined(SW_HAVE_BROTLI) - ZEND_STRL("gzip, deflate, br") -#else -#ifdef SW_HAVE_ZLIB - ZEND_STRL("gzip, deflate") -#else -#ifdef SW_HAVE_BROTLI - ZEND_STRL("br") -#endif -#endif -#endif - ); - } -#endif - - // ============ cookies ============ - if (ZVAL_IS_ARRAY(zcookies)) - { - swString_append_ptr(buffer, ZEND_STRL("Cookie: ")); - int n_cookie = php_swoole_array_length(zcookies); - int i = 0; - char *encoded_value; - - SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zcookies), key, keylen, keytype, zvalue) - { - i++; - if (HASH_KEY_IS_STRING != keytype) - { - continue; - } - zend::string str_value(zvalue); - if (str_value.len() == 0) - { - continue; - } - swString_append_ptr(buffer, key, keylen); - swString_append_ptr(buffer, "=", 1); - - int encoded_value_len; - encoded_value = php_swoole_url_encode(str_value.val(), str_value.len(), &encoded_value_len); - if (encoded_value) - { - swString_append_ptr(buffer, encoded_value, encoded_value_len); - efree(encoded_value); - } - if (i < n_cookie) - { - swString_append_ptr(buffer, "; ", 2); - } - } - SW_HASHTABLE_FOREACH_END(); - swString_append_ptr(buffer, ZEND_STRL("\r\n")); - } - - // ============ multipart/form-data ============ - if ((has_upload_files = (php_swoole_array_length_safe(zupload_files) > 0))) - { - char header_buf[2048]; - char boundary_str[SW_HTTP_CLIENT_BOUNDARY_TOTAL_SIZE]; - int n; - - // ============ content-type ============ - memcpy(boundary_str, SW_HTTP_CLIENT_BOUNDARY_PREKEY, sizeof(SW_HTTP_CLIENT_BOUNDARY_PREKEY) - 1); - swoole_random_string( - boundary_str + sizeof(SW_HTTP_CLIENT_BOUNDARY_PREKEY) - 1, - sizeof(boundary_str) - sizeof(SW_HTTP_CLIENT_BOUNDARY_PREKEY) - ); - n = sw_snprintf( - header_buf, - sizeof(header_buf), "Content-Type: multipart/form-data; boundary=%.*s\r\n", - (int)(sizeof(boundary_str) - 1), boundary_str - ); - swString_append_ptr(buffer, header_buf, n); - - // ============ content-length ============ - size_t content_length = 0; - - // calculate length before encode array - if (zbody && ZVAL_IS_ARRAY(zbody)) - { - SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zbody), key, keylen, keytype, zvalue) - if (UNEXPECTED(HASH_KEY_IS_STRING != keytype || ZVAL_IS_NULL(zvalue))) - { - continue; - } - zend::string str_value(zvalue); - //strlen("%.*s")*2 = 8 - //header + body + CRLF(2) - content_length += (sizeof(SW_HTTP_FORM_RAW_DATA_FMT) - SW_HTTP_FORM_RAW_DATA_FMT_LEN -1) + (sizeof(boundary_str) - 1) + keylen + str_value.len() + 2; - SW_HASHTABLE_FOREACH_END(); - } - - zval *zname; - zval *ztype; - zval *zsize = NULL; - zval *zpath = NULL; - zval *zcontent = NULL; - zval *zfilename; - zval *zoffset; - - // calculate length of files - { - //upload files - SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zupload_files), key, keylen, keytype, zvalue) - { - HashTable *ht = Z_ARRVAL_P(zvalue); - if (!(zname = zend_hash_str_find(ht, ZEND_STRL("name")))) - { - continue; - } - if (!(zfilename = zend_hash_str_find(ht, ZEND_STRL("filename")))) - { - continue; - } - if (!(zsize = zend_hash_str_find(ht, ZEND_STRL("size")))) - { - continue; - } - if (!(ztype = zend_hash_str_find(ht, ZEND_STRL("type")))) - { - continue; - } - //strlen("%.*s")*4 = 16 - //header + body + CRLF(2) - content_length += - (sizeof(SW_HTTP_FORM_FILE_DATA_FMT) - SW_HTTP_FORM_FILE_DATA_FMT_LEN - 1) + (sizeof(boundary_str) - 1) + - Z_STRLEN_P(zname) + Z_STRLEN_P(zfilename) + Z_STRLEN_P(ztype) + Z_LVAL_P(zsize) + 2; - } - SW_HASHTABLE_FOREACH_END(); - } - - http_client_append_content_length(buffer, content_length + sizeof(boundary_str) - 1 + 6); - - // ============ form-data body ============ - if (zbody && ZVAL_IS_ARRAY(zbody)) - { - SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zbody), key, keylen, keytype, zvalue) - { - if (UNEXPECTED(HASH_KEY_IS_STRING != keytype || ZVAL_IS_NULL(zvalue))) - { - continue; - } - zend::string str_value(zvalue); - n = sw_snprintf( - header_buf, sizeof(header_buf), - SW_HTTP_FORM_RAW_DATA_FMT, (int)(sizeof(boundary_str) - 1), - boundary_str, keylen, key - ); - swString_append_ptr(buffer, header_buf, n); - swString_append_ptr(buffer, str_value.val(), str_value.len()); - swString_append_ptr(buffer, ZEND_STRL("\r\n")); - } - SW_HASHTABLE_FOREACH_END(); - } - - if (socket->send_all(buffer->str, buffer->length) != (ssize_t) buffer->length) - { - goto _send_fail; - } - - { - //upload files - SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zupload_files), key, keylen, keytype, zvalue) - { - if (!(zname = zend_hash_str_find(Z_ARRVAL_P(zvalue), ZEND_STRL("name")))) - { - continue; - } - if (!(zfilename = zend_hash_str_find(Z_ARRVAL_P(zvalue), ZEND_STRL("filename")))) - { - continue; - } - /** - * from disk file - */ - if (!(zcontent = zend_hash_str_find(Z_ARRVAL_P(zvalue), ZEND_STRL("content")))) - { - //file path - if (!(zpath = zend_hash_str_find(Z_ARRVAL_P(zvalue), ZEND_STRL("path")))) - { - continue; - } - //file offset - if (!(zoffset = zend_hash_str_find(Z_ARRVAL_P(zvalue), ZEND_STRL("offset")))) - { - continue; - } - zcontent = NULL; - } - else - { - zpath = NULL; - zoffset = NULL; - } - if (!(zsize = zend_hash_str_find(Z_ARRVAL_P(zvalue), ZEND_STRL("size")))) - { - continue; - } - if (!(ztype = zend_hash_str_find(Z_ARRVAL_P(zvalue), ZEND_STRL("type")))) - { - continue; - } - /** - * part header - */ - n = sw_snprintf( - header_buf, sizeof(header_buf), SW_HTTP_FORM_FILE_DATA_FMT, - (int) (sizeof(boundary_str) - 1), boundary_str, - (int) Z_STRLEN_P(zname), Z_STRVAL_P(zname), - (int) Z_STRLEN_P(zfilename), Z_STRVAL_P(zfilename), - (int) Z_STRLEN_P(ztype), Z_STRVAL_P(ztype) - ); - /** - * from memory - */ - if (zcontent) - { - swString_clear(buffer); - swString_append_ptr(buffer, header_buf, n); - swString_append_ptr(buffer, Z_STRVAL_P(zcontent), Z_STRLEN_P(zcontent)); - swString_append_ptr(buffer, "\r\n", 2); - - if (socket->send_all(buffer->str, buffer->length) != (ssize_t) buffer->length) - { - goto _send_fail; - } - } - /** - * from disk file - */ - else - { - if (socket->send_all(header_buf, n) != n) - { - goto _send_fail; - } - if (!socket->sendfile(Z_STRVAL_P(zpath), Z_LVAL_P(zoffset), Z_LVAL_P(zsize))) - { - goto _send_fail; - } - if (socket->send_all("\r\n", 2) != 2) - { - goto _send_fail; - } - } - } - SW_HASHTABLE_FOREACH_END(); - } - - n = sw_snprintf(header_buf, sizeof(header_buf), "--%.*s--\r\n", (int)(sizeof(boundary_str) - 1), boundary_str); - if (socket->send_all(header_buf, n) != n) - { - goto _send_fail; - } - wait = true; - return true; - } - // ============ x-www-form-urlencoded or raw ============ - else if (zbody) - { - if (ZVAL_IS_ARRAY(zbody)) - { - size_t len; - http_client_swString_append_headers(buffer, ZEND_STRL("Content-Type"), ZEND_STRL("application/x-www-form-urlencoded")); - if (php_swoole_array_length(zbody) > 0) - { - smart_str formstr_s = {}; - char *formstr = php_swoole_http_build_query(zbody, &len, &formstr_s); - if (formstr == NULL) - { - php_swoole_error(E_WARNING, "http_build_query failed"); - return false; - } - http_client_append_content_length(buffer, len); - swString_append_ptr(buffer, formstr, len); - smart_str_free(&formstr_s); - } - else - { - http_client_append_content_length(buffer, 0); - } - } - else - { - char *body; - size_t body_length = php_swoole_get_send_data(zbody, &body); - http_client_append_content_length(buffer, body_length); - swString_append_ptr(buffer, body, body_length); - } - } - // ============ no body ============ - else - { - if (header_flag & HTTP_HEADER_CONTENT_LENGTH) - { - http_client_append_content_length(buffer, 0); - } - else - { - swString_append_ptr(buffer, ZEND_STRL("\r\n")); - } - } - - 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 - ); - - if (socket->send_all(buffer->str, buffer->length) != (ssize_t) buffer->length) - { - _send_fail: - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("errCode"), socket->errCode); - zend_update_property_string(swoole_http_client_coro_ce, zobject, ZEND_STRL("errMsg"), socket->errMsg); - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("statusCode"), HTTP_CLIENT_ESTATUS_SEND_FAILED); - close(); - return false; - } - wait = true; - return true; -} - -bool http_client::exec(std::string path) -{ - this->path = path; - // bzero when make a new reqeust - reconnected_count = 0; - if (defer) - { - return send(); - } - else - { - return send() && recv(); - } -} - -bool http_client::recv(double timeout) -{ - if (!wait) - { - return false; - } - if (!socket || !socket->is_connect()) - { - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("errCode"), SwooleG.error = SW_ERROR_CLIENT_NO_CONNECTION); - zend_update_property_string(swoole_http_client_coro_ce, zobject, ZEND_STRL("errMsg"), "connection is not available"); - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("statusCode"), HTTP_CLIENT_ESTATUS_SERVER_RESET); - return false; - } - if (!recv_http_response(timeout)) - { - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("errCode"), socket->errCode); - zend_update_property_string(swoole_http_client_coro_ce, zobject, ZEND_STRL("errMsg"), socket->errMsg); - zend_update_property_long( - swoole_http_client_coro_ce, 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 http_client::recv(zval *zframe, double timeout) -{ - SW_ASSERT(websocket); - ZVAL_FALSE(zframe); - if (!socket || !socket->is_connect()) - { - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("errCode"), SwooleG.error = SW_ERROR_CLIENT_NO_CONNECTION); - zend_update_property_string(swoole_http_client_coro_ce, zobject, ZEND_STRL("errMsg"), "connection is not available"); - zend_update_property_long(swoole_http_client_coro_ce, 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, zobject, ZEND_STRL("errCode"), socket->errCode); - zend_update_property_string(swoole_http_client_coro_ce, zobject, ZEND_STRL("errMsg"), socket->errMsg); - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("statusCode"), HTTP_CLIENT_ESTATUS_SERVER_RESET); - if (socket->errCode != ETIMEDOUT) - { - close(); - } - } - 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, zframe, ZEND_STRL("fd"), socket->get_fd()); - } -} - -bool http_client::recv_http_response(double timeout) -{ - ssize_t retval = 0; - size_t total_bytes = 0, parsed_n = 0; - swString *buffer = socket->get_read_buffer(); - - // re-init http response parser - swoole_http_parser_init(&parser, PHP_HTTP_RESPONSE); - parser.data = this; - - if (timeout == 0) - { - timeout = socket->get_timeout(SW_TIMEOUT_READ); - } - Socket::timeout_controller tc(socket, timeout, SW_TIMEOUT_READ); - while (true) - { - if (sw_unlikely(tc.has_timedout(SW_TIMEOUT_READ))) - { - return false; - } - retval = socket->recv(buffer->str, buffer->size); - if (sw_unlikely(retval <= 0)) - { - if (retval == 0) - { - socket->set_err(ECONNRESET); - if (total_bytes > 0 && !swoole_http_should_keep_alive(&parser)) - { - http_parser_on_message_complete(&parser); - return true; - } - } - return false; - } - 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); - if (parser.state == s_start_res) - { - // handle redundant data (websocket packet) - if (parser.upgrade && (size_t) retval > parsed_n + SW_WEBSOCKET_HEADER_LEN) - { - swString_sub(buffer, parsed_n, retval - parsed_n); - } - return true; - } - if (sw_unlikely(parser.state == s_dead)) - { - socket->set_err(EPROTO); - return false; - } - } -} - -bool http_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, zobject, ZEND_STRL("requestMethod"), "GET"); - http_client_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); - } - return websocket; -} - -bool http_client::push(zval *zdata, zend_long opcode, uint8_t flags) -{ - if (!websocket) - { - php_swoole_fatal_error(E_WARNING, "websocket handshake failed, cannot push data"); - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("errCode"), SwooleG.error = SW_ERROR_WEBSOCKET_HANDSHAKE_FAILED); - zend_update_property_string(swoole_http_client_coro_ce, zobject, ZEND_STRL("errMsg"), "websocket handshake failed, cannot push data"); - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("statusCode"), HTTP_CLIENT_ESTATUS_CONNECT_FAILED); - return false; - } - if (!socket || !socket->is_connect()) - { - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("errCode"), SwooleG.error = SW_ERROR_CLIENT_NO_CONNECTION); - zend_update_property_string(swoole_http_client_coro_ce, zobject, ZEND_STRL("errMsg"), "connection is not available"); - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("statusCode"), HTTP_CLIENT_ESTATUS_SERVER_RESET); - return false; - } - - swString *buffer = socket->get_write_buffer(); - swString_clear(buffer); - if (php_swoole_websocket_frame_is_object(zdata)) - { - if (php_swoole_websocket_frame_object_pack(buffer, zdata, websocket_mask, websocket_compression) < 0) - { - return false; - } - } - else - { - if (php_swoole_websocket_frame_pack(buffer, zdata, opcode, flags, websocket_mask, websocket_compression) < 0) - { - return false; - } - } - - if (socket->send_all(buffer->str, buffer->length) != (ssize_t) buffer->length) - { - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("errCode"), socket->errCode); - zend_update_property_string(swoole_http_client_coro_ce, zobject, ZEND_STRL("errMsg"), socket->errMsg); - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("statusCode"), HTTP_CLIENT_ESTATUS_SERVER_RESET); - close(); - return false; - } - else - { - return true; - } -} - -void http_client::reset() -{ - wait = false; -#ifdef SW_HAVE_COMPRESSION - compress_method = HTTP_COMPRESS_NONE; - compression_error = false; -#endif -#ifdef SW_HAVE_ZLIB - if (gzip_stream_active) - { - inflateEnd(&gzip_stream); - gzip_stream_active = false; - } -#endif -#ifdef SW_HAVE_BROTLI - if (brotli_decoder_state) - { - BrotliDecoderDestroyInstance(brotli_decoder_state); - brotli_decoder_state = nullptr; - } -#endif - if (has_upload_files) - { - zend_update_property_null(swoole_http_client_coro_ce, zobject, ZEND_STRL("uploadFiles")); - } - if (download_file_fd > 0) - { - ::close(download_file_fd); - download_file_fd = 0; - zend_update_property_null(swoole_http_client_coro_ce, zobject, ZEND_STRL("downloadFile")); - zend_update_property_long(swoole_http_client_coro_ce, zobject, ZEND_STRL("downloadOffset"), 0); - } -} - -bool http_client::close(const bool should_be_reset) -{ - Socket *_socket = socket; - if (_socket) - { - zend_update_property_bool(swoole_http_client_coro_ce, 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 - socket = nullptr; - } - php_swoole_client_coro_socket_free(_socket); - return true; - } - return false; -} - -http_client::~http_client() -{ - close(); - if (body) - { - swString_free(body); - } -} - -static sw_inline http_client_coro* php_swoole_http_client_coro_fetch_object(zend_object *obj) -{ - return (http_client_coro *) ((char *) obj - swoole_http_client_coro_handlers.offset); -} - -static sw_inline http_client * php_swoole_get_phc(zval *zobject) -{ - http_client *phc = php_swoole_http_client_coro_fetch_object(Z_OBJ_P(zobject))->phc; - if (UNEXPECTED(!phc)) - { - php_swoole_fatal_error(E_ERROR, "you must call Http Client constructor first"); - } - return phc; -} - -static void php_swoole_http_client_coro_free_object(zend_object *object) -{ - http_client_coro *hcc = php_swoole_http_client_coro_fetch_object(object); - if (hcc->phc) - { - delete hcc->phc; - hcc->phc = nullptr; - } - zend_object_std_dtor(&hcc->std); -} - -static zend_object *php_swoole_http_client_coro_create_object(zend_class_entry *ce) -{ - http_client_coro *hcc = (http_client_coro *) ecalloc(1, sizeof(http_client_coro) + zend_object_properties_size(ce)); - zend_object_std_init(&hcc->std, ce); - object_properties_init(&hcc->std, ce); - hcc->std.handlers = &swoole_http_client_coro_handlers; - return &hcc->std; -} - -void php_swoole_http_client_coro_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY(swoole_http_client_coro, "Swoole\\Coroutine\\Http\\Client", NULL, "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_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, http_client_coro, std); - - // client status - zend_declare_property_long(swoole_http_client_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_string(swoole_http_client_coro_ce, ZEND_STRL("errMsg"), "", ZEND_ACC_PUBLIC); - zend_declare_property_bool(swoole_http_client_coro_ce, ZEND_STRL("connected"), 0, ZEND_ACC_PUBLIC); - - // client info - zend_declare_property_string(swoole_http_client_coro_ce, ZEND_STRL("host"), "", ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_http_client_coro_ce, ZEND_STRL("port"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_bool(swoole_http_client_coro_ce, ZEND_STRL("ssl"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL("setting"), ZEND_ACC_PUBLIC); - - // request properties - zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL("requestMethod"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL("requestHeaders"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL("requestBody"), ZEND_ACC_PUBLIC); - // always set by API (make it private?) - zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL("uploadFiles"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL("downloadFile"), ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_http_client_coro_ce, ZEND_STRL("downloadOffset"), 0, ZEND_ACC_PUBLIC); - - // response properties - zend_declare_property_long(swoole_http_client_coro_ce, ZEND_STRL("statusCode"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL("headers"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL("set_cookie_headers"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_http_client_coro_ce, ZEND_STRL("cookies"), ZEND_ACC_PUBLIC); - zend_declare_property_string(swoole_http_client_coro_ce, ZEND_STRL("body"), "", ZEND_ACC_PUBLIC); - - SW_INIT_CLASS_ENTRY_EX(swoole_http_client_coro_exception, "Swoole\\Coroutine\\Http\\Client\\Exception", NULL, "Co\\Http\\Client\\Exception", NULL, 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); - -#ifdef SW_HAVE_COMPRESSION - swoole_zlib_buffer = swString_new(SW_HTTP_RESPONSE_INIT_SIZE); - if (!swoole_zlib_buffer) - { - php_swoole_fatal_error(E_ERROR, "[2] swString_new(%d) failed", SW_HTTP_RESPONSE_INIT_SIZE); - } -#endif -} - -static PHP_METHOD(swoole_http_client_coro, __construct) -{ - http_client_coro *hcc = php_swoole_http_client_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); - char *host; - size_t host_len; - zend_long port = 80; - zend_bool ssl = 0; - - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 3) - Z_PARAM_STRING(host, host_len) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(port) - Z_PARAM_BOOL(ssl) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - zend_update_property_stringl(swoole_http_client_coro_ce, ZEND_THIS, ZEND_STRL("host"), host, host_len); - zend_update_property_long(swoole_http_client_coro_ce, ZEND_THIS, ZEND_STRL("port"), port); - zend_update_property_bool(swoole_http_client_coro_ce, ZEND_THIS, ZEND_STRL("ssl"), ssl); - // check host - if (host_len == 0) - { - zend_throw_exception_ex(swoole_http_client_coro_exception_ce, EINVAL, "host is empty"); - RETURN_FALSE; - } - // check ssl -#ifndef SW_USE_OPENSSL - if (ssl) - { - zend_throw_exception_ex( - swoole_http_client_coro_exception_ce, - EPROTONOSUPPORT, "you must configure with `--enable-openssl` to support ssl connection when compiling Swoole" - ); - RETURN_FALSE; - } -#endif - hcc->phc = new http_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) -{ - http_client* phc = php_swoole_get_phc(ZEND_THIS); - zval *zset; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY(zset) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (php_swoole_array_length(zset) == 0) - { - RETURN_FALSE; - } - else - { - zval *zsettings = sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - php_array_merge(Z_ARRVAL_P(zsettings), Z_ARRVAL_P(zset)); - phc->apply_setting(zset); - RETURN_TRUE; - } -} - -static PHP_METHOD(swoole_http_client_coro, getDefer) -{ - http_client *phc = php_swoole_get_phc(ZEND_THIS); - - RETURN_BOOL(phc->defer); -} - -static PHP_METHOD(swoole_http_client_coro, setDefer) -{ - http_client *phc = php_swoole_get_phc(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); - - phc->defer = defer; - - RETURN_TRUE; -} - -static PHP_METHOD(swoole_http_client_coro, setMethod) -{ - char *method; - size_t method_length; - - // Notice: maybe string or array - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(method, method_length) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - zend_update_property_stringl(swoole_http_client_coro_ce, ZEND_THIS, ZEND_STRL("requestMethod"), method, method_length); - - RETURN_TRUE; -} - -static PHP_METHOD(swoole_http_client_coro, setHeaders) -{ - zval *headers; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY_EX(headers, 0, 1) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - zend_update_property(swoole_http_client_coro_ce, ZEND_THIS, ZEND_STRL("requestHeaders"), headers); - - RETURN_TRUE; -} - -static PHP_METHOD(swoole_http_client_coro, setBasicAuth) -{ - http_client* phc = php_swoole_get_phc(ZEND_THIS); - char *username, *password; - size_t username_len, password_len; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STRING(username, username_len) - Z_PARAM_STRING(password, password_len) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - phc->set_basic_auth(std::string(username, username_len), std::string(password, password_len)); -} - -static PHP_METHOD(swoole_http_client_coro, setCookies) -{ - zval *cookies; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY_EX(cookies, 0, 1) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - zend_update_property(swoole_http_client_coro_ce, ZEND_THIS, ZEND_STRL("cookies"), cookies); - - RETURN_TRUE; -} - -static PHP_METHOD(swoole_http_client_coro, setData) -{ - zval *zdata; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ZVAL(zdata) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - zend_update_property(swoole_http_client_coro_ce, ZEND_THIS, ZEND_STRL("requestBody"), zdata); - - RETURN_TRUE; -} - -static PHP_METHOD(swoole_http_client_coro, addFile) -{ - char *path; - size_t l_path; - char *name; - size_t l_name; - char *type = NULL; - size_t l_type = 0; - char *filename = NULL; - size_t l_filename = 0; - zend_long offset = 0; - zend_long length = 0; - - ZEND_PARSE_PARAMETERS_START(2, 6) - Z_PARAM_STRING(path, l_path) - Z_PARAM_STRING(name, l_name) - Z_PARAM_OPTIONAL - Z_PARAM_STRING(type, l_type) - Z_PARAM_STRING(filename, l_filename) - Z_PARAM_LONG(offset) - Z_PARAM_LONG(length) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (offset < 0) - { - offset = 0; - } - if (length < 0) - { - length = 0; - } - struct stat file_stat; - if (stat(path, &file_stat) < 0) - { - php_swoole_sys_error(E_WARNING, "stat(%s) failed", path); - RETURN_FALSE; - } - if (file_stat.st_size == 0) - { - php_swoole_sys_error(E_WARNING, "cannot send empty file[%s]", filename); - 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; - } - if (length > file_stat.st_size - offset) - { - php_swoole_sys_error(E_WARNING, "parameter $length[" ZEND_LONG_FMT "] exceeds the file size", length); - RETURN_FALSE; - } - if (length == 0) - { - length = file_stat.st_size - offset; - } - if (l_type == 0) - { - type = (char *) swoole::mime_type::get(path).c_str(); - l_type = strlen(type); - } - if (l_filename == 0) - { - char *dot = strrchr(path, '/'); - if (dot == NULL) - { - filename = path; - l_filename = l_path; - } - else - { - filename = dot + 1; - l_filename = strlen(filename); - } - } - - zval *zupload_files = sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, ZEND_THIS, ZEND_STRL("uploadFiles"), 0); - zval zupload_file; - array_init(&zupload_file); - add_assoc_stringl_ex(&zupload_file, ZEND_STRL("path"), path, l_path); - add_assoc_stringl_ex(&zupload_file, ZEND_STRL("name"), name, l_name); - add_assoc_stringl_ex(&zupload_file, ZEND_STRL("filename"), filename, l_filename); - add_assoc_stringl_ex(&zupload_file, ZEND_STRL("type"), type, l_type); - add_assoc_long(&zupload_file, "size", length); - add_assoc_long(&zupload_file, "offset", offset); - - RETURN_BOOL(add_next_index_zval(zupload_files, &zupload_file) == SUCCESS); -} - -static PHP_METHOD(swoole_http_client_coro, addData) -{ - char *data; - size_t l_data; - char *name; - size_t l_name; - char *type = NULL; - size_t l_type = 0; - char *filename = NULL; - size_t l_filename = 0; - - ZEND_PARSE_PARAMETERS_START(2, 4) - Z_PARAM_STRING(data, l_data) - Z_PARAM_STRING(name, l_name) - Z_PARAM_OPTIONAL - Z_PARAM_STRING(type, l_type) - Z_PARAM_STRING(filename, l_filename) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (l_type == 0) - { - type = (char *) "application/octet-stream"; - l_type = strlen(type); - } - if (l_filename == 0) - { - filename = name; - l_filename = l_name; - } - - zval *zupload_files = sw_zend_read_and_convert_property_array(swoole_http_client_coro_ce, ZEND_THIS, ZEND_STRL("uploadFiles"), 0); - zval zupload_file; - array_init(&zupload_file); - add_assoc_stringl_ex(&zupload_file, ZEND_STRL("content"), data, l_data); - add_assoc_stringl_ex(&zupload_file, ZEND_STRL("name"), name, l_name); - add_assoc_stringl_ex(&zupload_file, ZEND_STRL("filename"), filename, l_filename); - add_assoc_stringl_ex(&zupload_file, ZEND_STRL("type"), type, l_type); - add_assoc_long(&zupload_file, "size", l_data); - - RETURN_BOOL(add_next_index_zval(zupload_files, &zupload_file) == SUCCESS); -} - -static PHP_METHOD(swoole_http_client_coro, execute) -{ - http_client* phc = php_swoole_get_phc(ZEND_THIS); - char *path = NULL; - size_t path_len = 0; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(path, path_len) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - RETURN_BOOL(phc->exec(std::string(path, path_len))); -} - -static PHP_METHOD(swoole_http_client_coro, get) -{ - http_client* phc = php_swoole_get_phc(ZEND_THIS); - char *path = NULL; - size_t path_len = 0; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(path, path_len) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - zend_update_property_string(swoole_http_client_coro_ce, ZEND_THIS, ZEND_STRL("requestMethod"), "GET"); - - RETURN_BOOL(phc->exec(std::string(path, path_len))); -} - -static PHP_METHOD(swoole_http_client_coro, post) -{ - http_client* phc = php_swoole_get_phc(ZEND_THIS); - char *path = NULL; - size_t path_len = 0; - zval *post_data; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STRING(path, path_len) - Z_PARAM_ZVAL(post_data) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - zend_update_property_string(swoole_http_client_coro_ce, ZEND_THIS, ZEND_STRL("requestMethod"), "POST"); - zend_update_property(swoole_http_client_coro_ce, ZEND_THIS, ZEND_STRL("requestBody"), post_data); - - RETURN_BOOL(phc->exec(std::string(path, path_len))); -} - -static PHP_METHOD(swoole_http_client_coro, download) -{ - http_client* phc = php_swoole_get_phc(ZEND_THIS); - char *path; - size_t path_len; - zval *download_file; - zend_long offset = 0; - - ZEND_PARSE_PARAMETERS_START(2, 3) - Z_PARAM_STRING(path, path_len) - Z_PARAM_ZVAL(download_file) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(offset) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - zend_update_property(swoole_http_client_coro_ce, ZEND_THIS, ZEND_STRL("downloadFile"), download_file); - zend_update_property_long(swoole_http_client_coro_ce, ZEND_THIS, ZEND_STRL("downloadOffset"), offset); - - RETURN_BOOL(phc->exec(std::string(path, path_len))); -} - -static PHP_METHOD(swoole_http_client_coro, upgrade) -{ - http_client* phc = php_swoole_get_phc(ZEND_THIS); - char *path = NULL; - size_t path_len = 0; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(path, path_len) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - RETURN_BOOL(phc->upgrade(std::string(path, path_len))); -} - -static PHP_METHOD(swoole_http_client_coro, push) -{ - http_client* phc = php_swoole_get_phc(ZEND_THIS); - zval *zdata; - zend_long opcode = WEBSOCKET_OPCODE_TEXT; - zval *zflags = NULL; - zend_long flags = SW_WEBSOCKET_FLAG_FIN; - - ZEND_PARSE_PARAMETERS_START(1, 3) - Z_PARAM_ZVAL(zdata) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(opcode) - Z_PARAM_ZVAL_EX(zflags, 1, 0) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (zflags != NULL) - { - flags = zval_get_long(zflags); - } - - RETURN_BOOL(phc->push(zdata, opcode, flags & SW_WEBSOCKET_FLAGS_ALL)); -} - -static PHP_METHOD(swoole_http_client_coro, recv) -{ - http_client *phc = php_swoole_get_phc(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 (phc->websocket) - { - phc->recv(return_value, timeout); - return; - } - else - { - RETURN_BOOL(phc->recv(timeout)); - } -} - -static PHP_METHOD(swoole_http_client_coro, close) -{ - http_client* phc = php_swoole_get_phc(ZEND_THIS); - - RETURN_BOOL(phc->close()); -} - -static PHP_METHOD(swoole_http_client_coro, getBody) -{ - SW_RETURN_PROPERTY("body"); -} - -static PHP_METHOD(swoole_http_client_coro, getHeaders) -{ - SW_RETURN_PROPERTY("headers"); -} - -static PHP_METHOD(swoole_http_client_coro, getCookies) -{ - SW_RETURN_PROPERTY("cookies"); -} - -static PHP_METHOD(swoole_http_client_coro, getStatusCode) -{ - SW_RETURN_PROPERTY("statusCode"); -} - -static PHP_METHOD(swoole_http_client_coro, getHeaderOut) -{ - http_client *phc = php_swoole_get_phc(ZEND_THIS); - phc->get_header_out(return_value); -} - -static PHP_METHOD(swoole_http_client_coro, getsockname) -{ - http_client *phc = php_swoole_get_phc(ZEND_THIS); - phc->getsockname(return_value); -} - -static PHP_METHOD(swoole_http_client_coro, getpeername) -{ - http_client *phc = php_swoole_get_phc(ZEND_THIS); - phc->getpeername(return_value); -} - -#ifdef SW_USE_OPENSSL -static PHP_METHOD(swoole_http_client_coro, getPeerCert) -{ - http_client *phc = php_swoole_get_phc(ZEND_THIS); - phc->getpeercert(return_value); -} -#endif \ No newline at end of file diff --git a/swoole_http_request.cc b/swoole_http_request.cc deleted file mode 100644 index e1cdb74096e..00000000000 --- a/swoole_http_request.cc +++ /dev/null @@ -1,1055 +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 "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 "main/rfc1867.h" -#include "main/php_variables.h" - -#include "websocket.h" -#include "base64.h" - -#ifdef SW_HAVE_ZLIB -#include -#endif - -#ifdef SW_HAVE_BROTLI -#include -#endif - -#ifdef SW_USE_HTTP2 -#include "http2.h" -#endif - -enum http_upload_errno -{ - HTTP_UPLOAD_ERR_OK = 0, - HTTP_UPLOAD_ERR_INI_SIZE, - HTTP_UPLOAD_ERR_FORM_SIZE, - HTTP_UPLOAD_ERR_PARTIAL, - HTTP_UPLOAD_ERR_NO_FILE, - HTTP_UPLOAD_ERR_NO_TMP_DIR = 6, - HTTP_UPLOAD_ERR_CANT_WRITE, -}; - -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); - -static int http_request_on_path(swoole_http_parser *parser, const char *at, size_t length) -{ - http_context *ctx = (http_context *) 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; - char *tmp = ptr; - - //ltrim('"') - for (i = 0; i < *len; i++) - { - if (tmp[0] == '"') - { - (*len)--; - tmp++; - continue; - } - else - { - break; - } - } - //rtrim('"') - for (i = (*len) - 1; i >= 0; i--) - { - if (tmp[i] == '"') - { - tmp[i] = 0; - (*len)--; - continue; - } - else - { - break; - } - } - 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 NULL; - } -} - -static const swoole_http_parser_settings http_parser_settings = -{ - NULL, - http_request_on_path, - http_request_on_query_string, - NULL, - NULL, - 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, - NULL, - multipart_body_on_header_complete, - multipart_body_on_data_end, - NULL, -}; - -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); -} - -zend_class_entry *swoole_http_request_ce; -static zend_object_handlers swoole_http_request_handlers; - -typedef struct -{ - http_context *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); -} - -http_context * 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) -{ - 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(swoole_http_request_ce, &zobject, ZEND_STRL("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)); - } - } - SW_HASHTABLE_FOREACH_END(); - } - if (ctx) - { - ctx->request.zobject = NULL; - swoole_http_context_free(ctx); - } - - 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 *) ecalloc(1, sizeof(http_request_t) + zend_object_properties_size(ce)); - zend_object_std_init(&request->std, ce); - object_properties_init(&request->std, ce); - request->std.handlers = &swoole_http_request_handlers; - return &request->std; -} - -static PHP_METHOD(swoole_http_request, getData); -static PHP_METHOD(swoole_http_request, rawContent); -static PHP_METHOD(swoole_http_request, __destruct); - -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_FE_END -}; - -void php_swoole_http_request_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY(swoole_http_request, "Swoole\\Http\\Request", "swoole_http_request", NULL, swoole_http_request_methods); - SW_SET_CLASS_SERIALIZABLE(swoole_http_request, zend_class_serialize_deny, zend_class_unserialize_deny); - 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, 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); - zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("get"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("files"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("post"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("tmpfiles"), ZEND_ACC_PUBLIC); -} - -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); - //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")) - ); - 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; - 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); - if (!mt_parser) - { - php_swoole_fatal_error(E_WARNING, "multipart_parser_init() failed"); - return SW_ERR; - } - - ctx->mt_parser = mt_parser; - mt_parser->data = ctx; - - return SW_OK; -} - -void swoole_http_parse_cookie(zval *zarray, const char *at, size_t length) -{ - char keybuf[SW_HTTP_COOKIE_KEYLEN]; - char valbuf[SW_HTTP_COOKIE_VALLEN]; - char *_c = (char *) at; - - char *_value; - int klen = 0; - int vlen = 0; - int state = -1; - - int i = 0, j = 0; - while (_c < at + length) - { - if (state <= 0 && *_c == '=') - { - klen = i - j + 1; - if (klen >= SW_HTTP_COOKIE_KEYLEN) - { - swWarn("cookie[%.*s...] name length %d is exceed the max name len %d", 8, (char *) at + j, klen, SW_HTTP_COOKIE_KEYLEN); - return; - } - memcpy(keybuf, (char *) at + j, klen - 1); - keybuf[klen - 1] = 0; - - j = i + 1; - state = 1; - } - else if (state == 1 && *_c == ';') - { - vlen = i - j; - if (vlen >= SW_HTTP_COOKIE_VALLEN) - { - swWarn("cookie[%s]'s value[v=%.*s...] length %d is exceed the max value len %d", keybuf, 8, (char *) at + j, vlen, SW_HTTP_COOKIE_VALLEN); - return; - } - memcpy(valbuf, (char *) at + j, vlen); - valbuf[vlen] = 0; - _value = http_trim_double_quote(valbuf, &vlen); - vlen = php_url_decode(_value, vlen); - if (klen > 1) - { - add_assoc_stringl_ex(zarray, keybuf, klen - 1, _value, vlen); - } - j = i + 1; - state = -1; - } - else if (state < 0) - { - if (isspace(*_c)) - { - //Remove leading spaces from cookie names - j++; - } - else - { - state = 0; - } - } - _c++; - i++; - } - if (j < (off_t) length) - { - vlen = i - j; - if (klen >= SW_HTTP_COOKIE_KEYLEN) - { - swWarn("cookie[%.*s...] name length %d is exceed the max name len %d", 8, keybuf, klen, SW_HTTP_COOKIE_KEYLEN); - return; - } - keybuf[klen - 1] = 0; - if (vlen >= SW_HTTP_COOKIE_VALLEN) - { - swWarn("cookie[%s]'s value[v=%.*s...] length %d is exceed the max value len %d", keybuf, 8, (char *) at + j, vlen, SW_HTTP_COOKIE_VALLEN); - return; - } - memcpy(valbuf, (char *) at + j, vlen); - valbuf[vlen] = 0; - _value = http_trim_double_quote(valbuf, &vlen); - vlen = php_url_decode(_value, vlen); - if (klen > 1) - { - add_assoc_stringl_ex(zarray, keybuf, klen - 1, _value, vlen); - } - } -} - -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; - zval *zheader = ctx->request.zheader; - 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")) - { - zval *zcookie = swoole_http_init_and_read_property(swoole_http_request_ce, ctx->request.zobject, &ctx->request.zcookie, ZEND_STRL("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")) - { - ctx->websocket = 1; - if (ctx->co_socket) - { - goto _add_header; - } - swServer *serv = (swServer *) ctx->private_data; - swConnection *conn = swWorker_get_connection(serv, ctx->fd); - if (!conn) - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED, "session[%d] is closed", ctx->fd); - efree(header_name); - return -1; - } - swListenPort *port = (swListenPort *) serv->connection_list[conn->server_fd].object; - if (port->open_websocket_protocol) - { - conn->websocket_status = 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")) - { - ctx->request.post_form_urlencoded = 1; - } - else if (SW_STRCASECT(at, length, "multipart/form-data")) - { - offset = sizeof("multipart/form-data") - 1; - while (at[offset] == ' ' || at[offset] == ';') - { - offset++; - } - offset += sizeof("boundary=") - 1; - - int boundary_len = length - offset; - char *boundary_str = (char *) at + offset; - - // 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:%d", ctx->fd); - 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); - } - } -#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); - } -#endif - else if (SW_STREQ(header_name, header_len, "transfer-encoding") && SW_STRCASECT(at, length, "chunked")) - { - ctx->recv_chunked = 1; - } - - _add_header: - add_assoc_stringl_ex(zheader, header_name, header_len, (char *) at, length); - efree(header_name); - - return 0; -} - -static int http_request_on_headers_complete(swoole_http_parser *parser) -{ - http_context *ctx = (http_context *) 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; - ctx->request.ext_len = 0; - - while (p > vpath) - { - --p; - if (*p == '.') - { - ++p; - ctx->request.ext = p; - ctx->request.ext_len = end - p; - break; - } - } - - ctx->keepalive = swoole_http_should_keep_alive(parser); - - 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(NULL)); - add_assoc_double_ex(zserver, ZEND_STRL("request_time_float"), swoole_microtime()); - add_assoc_string(zserver, "server_protocol", (char *) (ctx->request.version == 101 ? "HTTP/1.1" : "HTTP/1.0")); - - ctx->current_header_name = NULL; - - 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; - 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; - int ret = 0; - - http_context *ctx = (http_context *) p->data; - /** - * Hash collision attack - */ - if (ctx->input_var_num > PG(max_input_vars)) - { - php_swoole_error(E_WARNING, - "Input variables exceeded " ZEND_LONG_FMT ". " - "To increase the limit change max_input_vars in php.ini", - PG(max_input_vars) - ); - return SW_OK; - } - else - { - ctx->input_var_num++; - } - - size_t header_len = ctx->current_header_name_len; - char *header_name = zend_str_tolower_dup(ctx->current_header_name, header_len); - - if (SW_STRCASEEQ(header_name, header_len, "content-disposition")) - { - //not form data - if (swoole_strnpos(at, length, ZEND_STRL("form-data;")) < 0) - { - goto _end; - } - - zval tmp_array; - array_init(&tmp_array); - swoole_http_parse_cookie(&tmp_array, at + sizeof("form-data;") - 1, length - sizeof("form-data;") + 1); - - zval *zform_name; - if (!(zform_name = zend_hash_str_find(Z_ARRVAL(tmp_array), ZEND_STRL("name")))) - { - goto _end; - } - - if (Z_STRLEN_P(zform_name) >= SW_HTTP_FORM_KEYLEN) - { - swWarn("form_name[%s] is too large", Z_STRVAL_P(zform_name)); - ret = -1; - goto _end; - } - - strncpy(value_buf, Z_STRVAL_P(zform_name), Z_STRLEN_P(zform_name)); - value_len = Z_STRLEN_P(zform_name); - char *tmp = http_trim_double_quote(value_buf, &value_len); - - zval *zfilename; - //POST form data - if (!(zfilename = zend_hash_str_find(Z_ARRVAL(tmp_array), ZEND_STRL("filename")))) - { - ctx->current_form_data_name = estrndup(tmp, value_len); - ctx->current_form_data_name_len = value_len; - } - //upload file - else - { - if (Z_STRLEN_P(zfilename) >= SW_HTTP_FORM_KEYLEN) - { - swWarn("filename[%s] is too large", Z_STRVAL_P(zfilename)); - ret = -1; - goto _end; - } - ctx->current_input_name = estrndup(tmp, value_len); - ctx->current_input_name_len = value_len; - - 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 *) ""); - 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); - - strncpy(value_buf, Z_STRVAL_P(zfilename), Z_STRLEN_P(zfilename)); - value_len = Z_STRLEN_P(zfilename); - tmp = http_trim_double_quote(value_buf, &value_len); - - 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); - } - - 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); - } - } - - _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; - if (ctx->current_form_data_name) - { - swString_append_ptr(swoole_http_form_data_buffer, (char*) at, length); - return 0; - } - if (p->fp == NULL) - { - return 0; - } - int n = fwrite(at, sizeof(char), length, (FILE *) 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); - p->fp = NULL; - - swSysWarn("write upload file failed"); - } - return 0; -} - -#if 0 -static void get_random_file_name(char *des, const char *src) -{ - unsigned char digest[16] = {}; - char buf[19] = {}; - int n = sprintf(buf, "%s%d", src, swoole_system_random(0, 9999)); - - PHP_MD5_CTX ctx; - PHP_MD5Init(&ctx); - PHP_MD5Update(&ctx, buf, n); - PHP_MD5Final(digest, &ctx); - make_digest_ex(des, digest, 16); -} -#endif - -static int multipart_body_on_header_complete(multipart_parser* p) -{ - http_context *ctx = (http_context *) p->data; - if (!ctx->current_input_name) - { - return 0; - } - - zval *z_multipart_header = ctx->current_multipart_header; - zval *zerr = NULL; - if (!(zerr = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL("error")))) - { - return 0; - } - if (Z_TYPE_P(zerr) == IS_LONG && Z_LVAL_P(zerr) != HTTP_UPLOAD_ERR_OK) - { - return 0; - } - - char file_path[SW_HTTP_UPLOAD_TMPDIR_SIZE]; - snprintf(file_path, SW_HTTP_UPLOAD_TMPDIR_SIZE, "%s/swoole.upfile.XXXXXX", ctx->upload_tmp_dir); - int tmpfile = swoole_tmpfile(file_path); - if (tmpfile < 0) - { - return 0; - } - - FILE *fp = fdopen(tmpfile, "wb+"); - if (fp == NULL) - { - add_assoc_long(z_multipart_header, "error", HTTP_UPLOAD_ERR_NO_TMP_DIR); - swSysWarn("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); - - return 0; -} - -static int multipart_body_on_data_end(multipart_parser* p) -{ - http_context *ctx = (http_context *) 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, - swoole_http_init_and_read_property(swoole_http_request_ce, ctx->request.zobject, &ctx->request.zpost, ZEND_STRL("post")) - ); - - efree(ctx->current_form_data_name); - ctx->current_form_data_name = NULL; - ctx->current_form_data_name_len = 0; - swString_clear(swoole_http_form_data_buffer); - return 0; - } - - if (!ctx->current_input_name) - { - return 0; - } - - zval *z_multipart_header = ctx->current_multipart_header; - if (p->fp != NULL) - { - long size = swoole_file_get_size((FILE *) p->fp); - add_assoc_long(z_multipart_header, "size", size); - - fclose((FILE *) p->fp); - p->fp = NULL; - } - - zval *zfiles = swoole_http_init_and_read_property(swoole_http_request_ce, ctx->request.zobject, &ctx->request.zfiles, ZEND_STRL("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) - { - char meta_name[SW_HTTP_FORM_KEYLEN + sizeof("[tmp_name]") - 1]; - char *input_path = ctx->current_input_name + input_path_pos; - char *meta_path = meta_name + input_path_pos; - size_t meta_path_len = sizeof(meta_name) - input_path_pos; - - strncpy(meta_name, ctx->current_input_name, input_path_pos); - - zval *zname = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL("name")); - zval *ztype = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL("type")); - zval *zfile = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL("tmp_name")); - zval *zerr = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL("error")); - zval *zsize = zend_hash_str_find(Z_ARRVAL_P(z_multipart_header), ZEND_STRL("size")); - - sw_snprintf(meta_path, meta_path_len, "[name]%s", input_path); - php_register_variable_ex(meta_name, zname, zfiles); - - sw_snprintf(meta_path, meta_path_len, "[type]%s", input_path); - php_register_variable_ex(meta_name, ztype, zfiles); - - sw_snprintf(meta_path, meta_path_len, "[tmp_name]%s", input_path); - php_register_variable_ex(meta_name, zfile, zfiles); - - sw_snprintf(meta_path, meta_path_len, "[error]%s", input_path); - php_register_variable_ex(meta_name, zerr, zfiles); - - sw_snprintf(meta_path, meta_path_len, "[size]%s", input_path); - php_register_variable_ex(meta_name, zsize, zfiles); - } - else - { - php_register_variable_ex(ctx->current_input_name, z_multipart_header, zfiles); - } - - efree(ctx->current_input_name); - ctx->current_input_name = NULL; - ctx->current_input_name_len = 0; - efree(ctx->current_multipart_header); - ctx->current_multipart_header = NULL; - - return 0; -} - -static int http_request_on_body(swoole_http_parser *parser, const char *at, size_t length) -{ - if (length == 0) - { - return 0; - } - - http_context *ctx = (http_context *) parser->data; - bool is_beginning = (ctx->request.chunked_body ? ctx->request.chunked_body->length : ctx->request.body_length) == 0; - - if (ctx->recv_chunked) - { - if (ctx->request.chunked_body == nullptr) - { - ctx->request.chunked_body = swString_new(SW_BUFFER_SIZE_STD); - if (ctx->request.chunked_body == nullptr) - { - return -1; - } - } - swString_append_ptr(ctx->request.chunked_body, at, length); - } - else - { - 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 != NULL) - { - multipart_parser *multipart_parser = ctx->mt_parser; - if (is_beginning) - { - /* Compatibility: some clients may send extra EOL */ - do - { - if (*at != '\r' && *at != '\n') - { - break; - } - at++; - 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); - } - } - - return 0; -} - -static int http_request_message_complete(swoole_http_parser *parser) -{ - http_context *ctx = (http_context *) 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( - 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")) - ); - } - if (ctx->mt_parser) - { - multipart_parser_free(ctx->mt_parser); - ctx->mt_parser = NULL; - } - ctx->completed = 1; - - swTraceLog(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) -{ -#ifdef SW_HAVE_BROTLI - if (swoole_strnpos(accept_encoding, length, ZEND_STRL("br")) >= 0) - { - ctx->accept_compression = 1; - ctx->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; - } - else if (swoole_strnpos(accept_encoding, length, ZEND_STRL("deflate")) >= 0) - { - ctx->accept_compression = 1; - ctx->compression_method = HTTP_COMPRESS_DEFLATE; - } - else - { - ctx->accept_compression = 0; - } -} - -const char* swoole_http_get_content_encoding(http_context *ctx) -{ - if (ctx->compression_method == HTTP_COMPRESS_GZIP) - { - return "gzip"; - } - else if (ctx->compression_method == HTTP_COMPRESS_DEFLATE) - { - return "deflate"; - } -#ifdef SW_HAVE_BROTLI - else if (ctx->compression_method == HTTP_COMPRESS_BR) - { - return "br"; - } -#endif - else - { - return NULL; - } -} -#endif - -static PHP_METHOD(swoole_http_request, rawContent) -{ - http_context *ctx = php_swoole_http_request_get_and_check_context(ZEND_THIS); - if (UNEXPECTED(!ctx)) - { - RETURN_FALSE; - } - - http_request *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) - { - 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); - 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); - } - - RETURN_EMPTY_STRING(); -} - -static PHP_METHOD(swoole_http_request, __destruct) { } diff --git a/swoole_http_response.cc b/swoole_http_response.cc deleted file mode 100644 index e47823acad6..00000000000 --- a/swoole_http_response.cc +++ /dev/null @@ -1,1438 +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 "swoole_http_server.h" - -#include "mime_type.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 "websocket.h" -#include "base64.h" - -#ifdef SW_HAVE_ZLIB -#include -#endif - -#ifdef SW_HAVE_BROTLI -#include -#endif - -#ifdef SW_USE_HTTP2 -#include "http2.h" -#endif - -using namespace swoole; -using swoole::coroutine::Socket; - -zend_class_entry *swoole_http_response_ce; -static zend_object_handlers swoole_http_response_handlers; - -static void http_build_header(http_context *, swString *response, int body_length); - -static inline void http_header_key_format(char *key, int length) -{ - int i, state = 0; - for (i = 0; i < length; i++) - { - if (state == 0) - { - if (key[i] >= 97 && key[i] <= 122) - { - key[i] -= 32; - } - state = 1; - } - else if (key[i] == '-') - { - state = 0; - } - else - { - if (key[i] >= 65 && key[i] <= 90) - { - key[i] += 32; - } - } - } -} - -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 != NULL) - { - return buffer; - } - } - return swoole_http_buffer; -} - -typedef struct -{ - http_context *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); -} - -http_context * 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) -{ - 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; - zval ztmp; /* bool, not required to release it */ - - if (ctx) - { - if (!ctx->end && !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); - } - } - else -#endif - { - if (ctx->co_socket) - { - swoole_http_response_end(ctx, nullptr, &ztmp); - } - else - { - swServer *serv = (swServer *) ctx->private_data; - swConnection *conn = swWorker_get_connection(serv, ctx->fd); - if (conn && !conn->closed && !conn->peer_closed) - { - swoole_http_response_end(ctx, nullptr, &ztmp); - } - } - } - } - ctx->response.zobject = NULL; - swoole_http_context_free(ctx); - } - - 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 *) ecalloc(1, sizeof(http_response_t) + zend_object_properties_size(ce)); - zend_object_std_init(&response->std, ce); - object_properties_init(&response->std, ce); - response->std.handlers = &swoole_http_response_handlers; - return &response->std; -} - -static PHP_METHOD(swoole_http_response, write); -static PHP_METHOD(swoole_http_response, end); -static PHP_METHOD(swoole_http_response, sendfile); -static PHP_METHOD(swoole_http_response, redirect); -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, detach); -static PHP_METHOD(swoole_http_response, create); -/** - * for WebSocket Client - */ -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); -#endif -static PHP_METHOD(swoole_http_response, status); -static PHP_METHOD(swoole_http_response, __destruct); - -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, ucwords) -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_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) -#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) - /** - * 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_FE_END -}; - -void php_swoole_http_response_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY(swoole_http_response, "Swoole\\Http\\Response", "swoole_http_response", NULL, swoole_http_response_methods); - SW_SET_CLASS_SERIALIZABLE(swoole_http_response, zend_class_serialize_deny, zend_class_unserialize_deny); - 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, std); - - zend_declare_property_long(swoole_http_response_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_http_response_ce, ZEND_STRL("socket"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_http_response_ce, ZEND_STRL("header"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_http_response_ce, ZEND_STRL("cookie"), ZEND_ACC_PUBLIC); -#ifdef SW_USE_HTTP2 - zend_declare_property_null(swoole_http_response_ce, ZEND_STRL("trailer"), ZEND_ACC_PUBLIC); -#endif -} - -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; - } - -#ifdef SW_USE_HTTP2 - if (ctx->http2) - { - php_swoole_error(E_WARNING, "HTTP2 client does not support HTTP-CHUNK"); - RETURN_FALSE; - } -#endif - -#ifdef SW_HAVE_COMPRESSION - ctx->accept_compression = 0; -#endif - - swString *http_buffer = http_get_write_buffer(ctx); - - if (!ctx->send_header) - { - ctx->send_chunked = 1; - swString_clear(http_buffer); - http_build_header(ctx, http_buffer, -1); - if (!ctx->send(ctx, http_buffer->str, http_buffer->length)) - { - ctx->send_chunked = 0; - ctx->send_header = 0; - RETURN_FALSE; - } - } - - swString 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. - swString_clear(http_buffer); - 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 - swString_append_ptr(http_buffer, hex_string, hex_len); - swString_append_ptr(http_buffer, ZEND_STRL("\r\n")); - swString_append_ptr(http_buffer, http_body.str, http_body.length); - swString_append_ptr(http_buffer, ZEND_STRL("\r\n")); - sw_free(hex_string); - - RETURN_BOOL(ctx->send(ctx, http_buffer->str, http_buffer->length)); -} - -static void http_build_header(http_context *ctx, swString *response, int body_length) -{ - char *buf = SwooleTG.buffer_stack->str; - size_t l_buf = SwooleTG.buffer_stack->size; - int n; - char *date_str; - - assert(ctx->send_header == 0); - - /** - * 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)); - } - else - { - n = sw_snprintf(buf, l_buf, "HTTP/1.1 %d %s\r\n", ctx->response.status, ctx->response.reason); - } - swString_append_ptr(response, buf, n); - - /** - * http header - */ - zval *zheader = sw_zend_read_property(swoole_http_response_ce, ctx->response.zobject, ZEND_STRL("header"), 0); - uint32_t header_flag = 0x0; - if (ZVAL_IS_ARRAY(zheader)) - { - const char *key; - uint32_t keylen; - int type; - zval *zvalue; - - 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; - } - if (SW_STRCASEEQ(key, keylen, "Server")) - { - header_flag |= HTTP_HEADER_SERVER; - } - else if (SW_STRCASEEQ(key, keylen, "Connection")) - { - header_flag |= HTTP_HEADER_CONNECTION; - } - else if (SW_STRCASEEQ(key, keylen, "Date")) - { - header_flag |= HTTP_HEADER_DATE; - } - else if (SW_STRCASEEQ(key, keylen, "Content-Length") && ctx->parser.method != PHP_HTTP_HEAD) - { - continue; // ignore - } - else if (SW_STRCASEEQ(key, keylen, "Content-Type")) - { - header_flag |= HTTP_HEADER_CONTENT_TYPE; - } - else if (SW_STRCASEEQ(key, keylen, "Transfer-Encoding")) - { - header_flag |= HTTP_HEADER_TRANSFER_ENCODING; - } - if (!ZVAL_IS_NULL(zvalue)) - { - 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()); - swString_append_ptr(response, buf, n); - } - } - SW_HASHTABLE_FOREACH_END(); - (void)type; - } - - //http cookies - zval *zcookie = sw_zend_read_property(swoole_http_response_ce, ctx->response.zobject, ZEND_STRL("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) - { - continue; - } - swString_append_ptr(response, ZEND_STRL("Set-Cookie: ")); - swString_append_ptr(response, Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue)); - swString_append_ptr(response, ZEND_STRL("\r\n")); - } - SW_HASHTABLE_FOREACH_END(); - } - - if (!(header_flag & HTTP_HEADER_SERVER)) - { - swString_append_ptr(response, ZEND_STRL("Server: " SW_HTTP_SERVER_SOFTWARE "\r\n")); - } - - // websocket protocol (subsequent header info is unnecessary) - if (ctx->upgrade == 1) - { - swString_append_ptr(response, ZEND_STRL("\r\n")); - ctx->send_header = 1; - return; - } - - if (!(header_flag & HTTP_HEADER_CONNECTION)) - { - if (ctx->keepalive) - { - swString_append_ptr(response, ZEND_STRL("Connection: keep-alive\r\n")); - } - else - { - swString_append_ptr(response, ZEND_STRL("Connection: close\r\n")); - } - } - if (!(header_flag & HTTP_HEADER_CONTENT_TYPE)) - { - swString_append_ptr(response, ZEND_STRL("Content-Type: text/html\r\n")); - } - if (!(header_flag & HTTP_HEADER_DATE)) - { - date_str = php_swoole_format_date((char *) ZEND_STRL(SW_HTTP_DATE_FORMAT), time(NULL), 0); - n = sw_snprintf(buf, l_buf, "Date: %s\r\n", date_str); - swString_append_ptr(response, buf, n); - efree(date_str); - } - - if (ctx->send_chunked) - { - if (!(header_flag & HTTP_HEADER_TRANSFER_ENCODING)) - { - swString_append_ptr(response, ZEND_STRL("Transfer-Encoding: chunked\r\n")); - } - } - // Content-Length - else if (body_length > 0 || ctx->parser.method != PHP_HTTP_HEAD) - { -#ifdef SW_HAVE_COMPRESSION - if (ctx->accept_compression) - { - body_length = swoole_zlib_buffer->length; - } -#endif - n = sw_snprintf(buf, l_buf, "Content-Length: %d\r\n", body_length); - swString_append_ptr(response, buf, n); - } -#ifdef SW_HAVE_COMPRESSION - //http compress - if (ctx->accept_compression) - { - const char *content_encoding = swoole_http_get_content_encoding(ctx); - swString_append_ptr(response, ZEND_STRL("Content-Encoding: ")); - swString_append_ptr(response, (char*) content_encoding, strlen(content_encoding)); - swString_append_ptr(response, ZEND_STRL("\r\n")); - } -#endif - swString_append_ptr(response, ZEND_STRL("\r\n")); - ctx->send_header = 1; -} - -#ifdef SW_HAVE_ZLIB -voidpf php_zlib_alloc(voidpf opaque, uInt items, uInt size) -{ - return (voidpf) safe_emalloc(items, size, 0); -} - -void php_zlib_free(voidpf opaque, voidpf address) -{ - efree((void *)address); -} -#endif - -#ifdef SW_HAVE_BROTLI -void* php_brotli_alloc(void* opaque, size_t size) -{ - return emalloc(size); -} - -void php_brotli_free(void* opaque, void* address) -{ - efree(address); -} -#endif - -#ifdef SW_HAVE_COMPRESSION -int swoole_http_response_compress(swString *body, int method, int level) -{ -#ifdef SW_HAVE_ZLIB - int encoding; -#endif - - if (0) { } -#ifdef SW_HAVE_ZLIB - //gzip: 0x1f - else if (method == HTTP_COMPRESS_GZIP) - { - encoding = 0x1f; - } - //deflate: -0xf - else if (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; - } - - size_t memory_size = BrotliEncoderMaxCompressedSize(body->length); - if (memory_size > swoole_zlib_buffer->size) - { - if (swString_extend(swoole_zlib_buffer, memory_size) < 0) - { - return SW_ERR; - } - } - - size_t input_size = body->length; - const uint8_t *input_buffer = (const uint8_t *) body->str; - size_t encoded_size = swoole_zlib_buffer->size; - uint8_t *encoded_buffer = (uint8_t *) swoole_zlib_buffer->str; - - if (BROTLI_TRUE != BrotliEncoderCompress( - level, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, - input_size, input_buffer, &encoded_size, encoded_buffer - )) - { - swWarn("BrotliEncoderCompress() failed"); - return SW_ERR; - } - else - { - swoole_zlib_buffer->length = encoded_size; - return SW_OK; - } - } -#endif - else - { - swWarn("Unknown compression method"); - return SW_ERR; - } -#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) body->length * (double) 1.015)) + 10 + 8 + 4 + 1; - if (memory_size > swoole_zlib_buffer->size) - { - if (swString_extend(swoole_zlib_buffer, memory_size) < 0) - { - return SW_ERR; - } - } - - 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); - if (status != Z_OK) - { - swWarn("deflateInit2() failed, Error: [%d]", status); - return SW_ERR; - } - - zstream.next_in = (Bytef *) body->str; - zstream.avail_in = body->length; - zstream.next_out = (Bytef *) swoole_zlib_buffer->str; - zstream.avail_out = swoole_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_zlib_buffer->length = zstream.total_out; - return SW_OK; -#endif -} -#endif - -static PHP_METHOD(swoole_http_response, initHeader) -{ - http_context *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); - if (UNEXPECTED(!ctx)) - { - RETURN_FALSE; - } - zval *zresponse_object = ctx->response.zobject; - swoole_http_init_and_read_property(swoole_http_response_ce, zresponse_object, &ctx->response.zheader, ZEND_STRL("header")); - swoole_http_init_and_read_property(swoole_http_response_ce, zresponse_object, &ctx->response.zcookie, ZEND_STRL("cookie")); -#ifdef SW_USE_HTTP2 - swoole_http_init_and_read_property(swoole_http_response_ce, zresponse_object, &ctx->response.ztrailer, ZEND_STRL("trailer")); -#endif - RETURN_TRUE; -} - -static PHP_METHOD(swoole_http_response, end) -{ - http_context *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); - if (UNEXPECTED(!ctx)) - { - RETURN_FALSE; - } - - zval *zdata = NULL; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_ZVAL_EX(zdata, 1, 0) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - -#ifdef SW_USE_HTTP2 - if (ctx->http2) - { - swoole_http2_response_end(ctx, zdata, return_value); - } - else -#endif - { - swoole_http_response_end(ctx, zdata, return_value); - } -} - -void swoole_http_response_end(http_context *ctx, zval *zdata, zval *return_value) -{ - swString http_body; - if (zdata) - { - http_body.length = php_swoole_get_send_data(zdata, &http_body.str); - } - else - { - http_body.length = 0; - http_body.str = NULL; - } - - if (ctx->send_chunked) - { - if (!ctx->send(ctx, 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); - - swString_clear(http_buffer); -#ifdef SW_HAVE_COMPRESSION - if (ctx->accept_compression) - { - if (http_body.length == 0 || swoole_http_response_compress(&http_body, ctx->compression_method, ctx->compression_level) != SW_OK) - { - ctx->accept_compression = 0; - } - } -#endif - http_build_header(ctx, http_buffer, http_body.length); - - char *send_body_str; - size_t send_body_len; - - if (http_body.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; - } - /** - * - */ -#ifdef SW_HTTP_SEND_TWICE - if (send_body_len < SwooleG.pagesize) -#endif - { - if (swString_append_ptr(http_buffer, send_body_str, send_body_len) < 0) - { - ctx->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; - RETURN_FALSE; - } - if (!ctx->send(ctx, send_body_str, send_body_len)) - { - ctx->end = 1; - ctx->close(ctx); - RETURN_FALSE; - } - goto _skip_copy; - } -#endif - } - - if (!ctx->send(ctx, http_buffer->str, http_buffer->length)) - { - ctx->end = 1; - ctx->close(ctx); - RETURN_FALSE; - } - } - -#ifdef SW_HTTP_SEND_TWICE - _skip_copy: -#endif - if (ctx->upgrade && !ctx->co_socket) - { - swServer *serv = (swServer*) ctx->private_data; - swConnection *conn = swWorker_get_connection(serv, ctx->fd); - if (conn && conn->websocket_status == WEBSOCKET_STATUS_HANDSHAKE) - { - if (ctx->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; - } - } - } - if (!ctx->keepalive) - { - ctx->close(ctx); - } - ctx->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 ucwords) -{ - if (UNEXPECTED(klen > SW_HTTP_HEADER_KEY_SIZE - 1)) - { - php_swoole_error(E_WARNING, "header key is too long"); - return false; - } - zval *zheader = swoole_http_init_and_read_property(swoole_http_response_ce, ctx->response.zobject, &ctx->response.zheader, ZEND_STRL("header")); - if (ucwords) - { - char key_buf[SW_HTTP_HEADER_KEY_SIZE]; - strncpy(key_buf, k, klen)[klen] = '\0'; -#ifdef SW_USE_HTTP2 - if (ctx->http2) - { - swoole_strtolower(key_buf, klen); - } - else -#endif - { - http_header_key_format(key_buf, klen); - } - if (UNEXPECTED(!v)) - { - add_assoc_null_ex(zheader, key_buf, klen); - } - else - { - add_assoc_stringl_ex(zheader, key_buf, klen, (char *) v, vlen); - } - } - else - { - if (UNEXPECTED(!v)) - { - add_assoc_null_ex(zheader, k, klen); - } - else - { - add_assoc_stringl_ex(zheader, k, klen, (char *) v, vlen); - } - } - return true; -} - -static PHP_METHOD(swoole_http_response, sendfile) -{ - http_context *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); - if (UNEXPECTED(!ctx)) - { - RETURN_FALSE; - } - - if (ctx->send_chunked) - { - php_swoole_fatal_error(E_WARNING, "can't use sendfile when HTTP chunk is enabled"); - RETURN_FALSE; - } - - char *file; - size_t l_file; - 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; - } - - if (l_file == 0) - { - php_swoole_error(E_WARNING, "file name is empty"); - RETURN_FALSE; - } - - struct stat file_stat; - if (stat(file, &file_stat) < 0) - { - php_swoole_sys_error(E_WARNING, "stat(%s) failed", file); - RETURN_FALSE; - } - if (file_stat.st_size == 0) - { - php_swoole_sys_error(E_WARNING, "can't send empty file[%s]", 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; - } - if (length > file_stat.st_size - offset) - { - php_swoole_sys_error(E_WARNING, "parameter $length[" ZEND_LONG_FMT "] exceeds the file size", length); - RETURN_FALSE; - } - if (length == 0) - { - 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); - - swString_clear(http_buffer); - - 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 (!ctx->sendfile(ctx, file, l_file, offset, length)) - { - ctx->close(ctx); - RETURN_FALSE; - } - - ctx->end = 1; - - if (!ctx->keepalive) - { - ctx->close(ctx); - } - - RETURN_TRUE; -} - -static void php_swoole_http_response_cookie(INTERNAL_FUNCTION_PARAMETERS, const bool url_encode) -{ - char *name, *value = NULL, *path = NULL, *domain = NULL, *samesite = NULL; - zend_long expires = 0; - size_t name_len, value_len = 0, path_len = 0, domain_len = 0, samesite_len = 0; - zend_bool secure = 0, httponly = 0; - - ZEND_PARSE_PARAMETERS_START(1, 8) - Z_PARAM_STRING(name, name_len) - Z_PARAM_OPTIONAL - Z_PARAM_STRING(value, value_len) - Z_PARAM_LONG(expires) - Z_PARAM_STRING(path, path_len) - Z_PARAM_STRING(domain, domain_len) - Z_PARAM_BOOL(secure) - Z_PARAM_BOOL(httponly) - Z_PARAM_STRING(samesite, samesite_len) - 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; - } - - int cookie_size = name_len /* + value_len */ + path_len + domain_len + 100; - char *cookie = NULL, *date = NULL; - - if (name_len > 0 && strpbrk(name, "=,; \t\r\n\013\014") != NULL) - { - php_swoole_error(E_WARNING, "Cookie names can't contain any of the following '=,; \\t\\r\\n\\013\\014'"); - 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); - } - 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); - snprintf(cookie, cookie_size, "%s=%s", name, encoded_value); - efree(encoded_value); - } - else - { - cookie_size += value_len; - cookie = (char *) emalloc(cookie_size); - 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); - } - if (samesite_len > 0) - { - strlcat(cookie, "; samesite=", cookie_size); - strlcat(cookie, samesite, 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; -} - -static PHP_METHOD(swoole_http_response, cookie) -{ - php_swoole_http_response_cookie(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); -} - -static PHP_METHOD(swoole_http_response, rawcookie) -{ - php_swoole_http_response_cookie(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); -} - -static PHP_METHOD(swoole_http_response, status) -{ - zend_long http_status; - char* reason = NULL; - size_t reason_len = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_LONG(http_status) - Z_PARAM_OPTIONAL - 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); - if (UNEXPECTED(!ctx)) - { - RETURN_FALSE; - } - - ctx->response.status = http_status; - ctx->response.reason = reason_len > 0 ? estrndup(reason, reason_len) : NULL; - RETURN_TRUE; -} - -static PHP_METHOD(swoole_http_response, header) -{ - char *k, *v; - size_t klen, vlen; - zend_bool ucwords = 1; - - ZEND_PARSE_PARAMETERS_START(2, 3) - Z_PARAM_STRING(k, klen) - Z_PARAM_STRING_EX(v, vlen, 1, 0) - Z_PARAM_OPTIONAL - Z_PARAM_BOOL(ucwords) - 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; - } - - RETURN_BOOL(swoole_http_response_set_header(ctx, k, klen, v, vlen, ucwords)); -} - -#ifdef SW_USE_HTTP2 -static PHP_METHOD(swoole_http_response, trailer) -{ - char *k, *v; - size_t klen, vlen; - char key_buf[SW_HTTP_HEADER_KEY_SIZE]; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STRING(k, klen) - 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); - if (!ctx || !ctx->http2) - { - RETURN_FALSE; - } - if (UNEXPECTED(klen > SW_HTTP_HEADER_KEY_SIZE - 1)) - { - php_swoole_error(E_WARNING, "trailer key is too long"); - RETURN_FALSE; - } - zval *ztrailer = swoole_http_init_and_read_property(swoole_http_response_ce, ctx->response.zobject, &ctx->response.ztrailer, ZEND_STRL("trailer")); - strncpy(key_buf, k, klen)[klen] = '\0'; - swoole_strtolower(key_buf, klen); - if (UNEXPECTED(!v)) - { - add_assoc_null_ex(ztrailer, key_buf, klen); - } - else - { - add_assoc_stringl_ex(ztrailer, key_buf, klen, v, vlen); - } - RETURN_TRUE; -} - -static PHP_METHOD(swoole_http_response, ping) -{ - http_context *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); - if (UNEXPECTED(!ctx)) - { - RETURN_FALSE; - } - if (UNEXPECTED(!ctx->http2)) - { - php_swoole_fatal_error(E_WARNING, "fd[%d] is not a HTTP2 conncetion", ctx->fd); - RETURN_FALSE; - } - SW_CHECK_RETURN(swoole_http2_server_ping(ctx)); -} -#endif - -static PHP_METHOD(swoole_http_response, upgrade) -{ - http_context *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); - if (UNEXPECTED(!ctx)) - { - RETURN_FALSE; - } - if (UNEXPECTED(!ctx->co_socket)) - { - php_swoole_fatal_error(E_WARNING, "async server dose not support protocol upgrade"); - RETURN_FALSE; - } - RETVAL_BOOL(swoole_websocket_handshake(ctx)); -} - -static PHP_METHOD(swoole_http_response, push) -{ - http_context *ctx = php_swoole_http_response_get_context(ZEND_THIS); - if (UNEXPECTED(!ctx)) - { - SwooleG.error = SW_ERROR_SESSION_CLOSED; - RETURN_FALSE; - } - if (UNEXPECTED(!ctx->co_socket || !ctx->upgrade)) - { - php_swoole_fatal_error(E_WARNING, "fd[%d] is not a websocket conncetion", ctx->fd); - RETURN_FALSE; - } - - zval *zdata; - zend_long opcode = WEBSOCKET_OPCODE_TEXT; - zval *zflags = NULL; - zend_long flags = SW_WEBSOCKET_FLAG_FIN; - - ZEND_PARSE_PARAMETERS_START(1, 3) - Z_PARAM_ZVAL(zdata) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(opcode) - Z_PARAM_ZVAL_EX(zflags, 1, 0) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (zflags != NULL) - { - flags = zval_get_long(zflags); - } - - swString *http_buffer = http_get_write_buffer(ctx); - swString_clear(http_buffer); - if (php_swoole_websocket_frame_is_object(zdata)) - { - if (php_swoole_websocket_frame_object_pack(http_buffer, zdata, 0, ctx->websocket_compression) < 0) - { - RETURN_FALSE; - } - } - else - { - if (php_swoole_websocket_frame_pack(http_buffer, zdata, opcode, flags & SW_WEBSOCKET_FLAGS_ALL, 0, ctx->websocket_compression) < 0) - { - RETURN_FALSE; - } - } - RETURN_BOOL(ctx->send(ctx, http_buffer->str, http_buffer->length)); -} - -static PHP_METHOD(swoole_http_response, close) -{ - http_context *ctx = php_swoole_http_response_get_context(ZEND_THIS); - if (UNEXPECTED(!ctx)) - { - SwooleG.error = SW_ERROR_SESSION_CLOSED; - RETURN_FALSE; - } - RETURN_BOOL(ctx->close(ctx)); -} - -static PHP_METHOD(swoole_http_response, recv) -{ - http_context *ctx = php_swoole_http_response_get_context(ZEND_THIS); - if (UNEXPECTED(!ctx)) - { - SwooleG.error = SW_ERROR_SESSION_CLOSED; - RETURN_FALSE; - } - if (UNEXPECTED(!ctx->co_socket || !ctx->upgrade)) - { - php_swoole_fatal_error(E_WARNING, "fd[%d] is not a websocket conncetion", ctx->fd); - RETURN_FALSE; - } - - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - Socket *sock = (Socket *) ctx->private_data; - ssize_t retval = sock->recv_packet(timeout); - swString _tmp; - - if (retval < 0) - { - SwooleG.error = sock->errCode; - RETURN_FALSE; - } - else if (retval == 0) - { - RETURN_EMPTY_STRING(); - } - else - { - _tmp.str = sock->get_read_buffer()->str; - _tmp.length = retval; - -#ifdef SW_HAVE_ZLIB - php_swoole_websocket_frame_unpack_ex(&_tmp, return_value, ctx->websocket_compression); -#else - php_swoole_websocket_frame_unpack(&_tmp, return_value); -#endif - } -} - -static PHP_METHOD(swoole_http_response, detach) -{ - http_context *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); - if (!ctx) - { - RETURN_FALSE; - } - ctx->detached = 1; - RETURN_TRUE; -} - -static PHP_METHOD(swoole_http_response, create) -{ - zval *zserver = NULL; - zend_long fd; - swServer *serv; - - if (ZEND_NUM_ARGS() == 1) - { - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_LONG(fd) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - serv = sw_server(); - } - 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); - } - - if (serv == nullptr || !serv->gs->start) - { - 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 = (int) 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(); - } - - object_init_ex(return_value, swoole_http_response_ce); - php_swoole_http_response_set_context(return_value, ctx); - ctx->response.zobject = return_value; - sw_copy_to_stack(ctx->response.zobject, ctx->response._zobject); - - zend_update_property_long(swoole_http_response_ce, return_value, ZEND_STRL("fd"), fd); -} - -static PHP_METHOD(swoole_http_response, redirect) -{ - zval *zurl; - zval *zhttp_code = NULL; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_ZVAL(zurl) - Z_PARAM_OPTIONAL - 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); - if (UNEXPECTED(!ctx)) - { - RETURN_FALSE; - } - - // status - if (zhttp_code) - { - ctx->response.status = zval_get_long(zhttp_code); - } - else - { - ctx->response.status = 302; - } - - zval zkey; - ZVAL_STRINGL(&zkey, "Location", 8); - sw_zend_call_method_with_2_params(ZEND_THIS, NULL, NULL, "header", return_value, &zkey, zurl); - zval_ptr_dtor(&zkey); - if (!Z_BVAL_P(return_value)) - { - return; - } - swoole_http_response_end(ctx, nullptr, return_value); -} - -static PHP_METHOD(swoole_http_response, __destruct) { } diff --git a/swoole_http_server.cc b/swoole_http_server.cc deleted file mode 100644 index 5f1318aeb94..00000000000 --- a/swoole_http_server.cc +++ /dev/null @@ -1,353 +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 "swoole_http_server.h" -#include "websocket.h" - -#include "main/rfc1867.h" - -#ifdef SW_USE_HTTP2 -#include "http2.h" -#endif - -using namespace swoole; -using swoole::coroutine::Socket; - -swString *swoole_http_buffer; -#ifdef SW_HAVE_COMPRESSION -/* not only be used by zlib but also be used by br */ -swString *swoole_zlib_buffer; -#endif -swString *swoole_http_form_data_buffer; - -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); - -int php_swoole_http_onReceive(swServer *serv, swEventData *req) -{ - int fd = req->info.fd; - int server_fd = req->info.server_fd; - - swConnection *conn = swServer_connection_verify_no_ssl(serv, fd); - if (!conn) - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_NOT_EXIST, "connection[%d] is closed", fd); - return SW_ERR; - } - - swListenPort *port = (swListenPort *) serv->connection_list[server_fd].object; - //other server port - if (!port->open_http_protocol) - { - return php_swoole_onReceive(serv, req); - } - //websocket client - 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); - } -#endif - - http_context *ctx = swoole_http_context_new(fd); - swoole_http_server_init_context(serv, ctx); - - zval *zdata = &ctx->request.zdata; - php_swoole_get_recv_data(serv, zdata, req); - - swTraceLog(SW_TRACE_SERVER, "http request from %d with %d bytes: <request.zobject; - args[1] = *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)); - 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); - goto _dtor_and_return; - } - - do { - zval *zserver = ctx->request.zserver; - swConnection *serv_sock = swServer_connection_get(serv, conn->server_fd); - if (serv_sock) - { - add_assoc_long(zserver, "server_port", swSocket_get_port(serv_sock->socket_type, &serv_sock->info)); - } - add_assoc_long(zserver, "remote_port", swSocket_get_port(conn->socket_type, &conn->info)); - add_assoc_string(zserver, "remote_addr", (char *) swSocket_get_ip(conn->socket_type, &conn->info)); - add_assoc_long(zserver, "master_time", conn->last_time); - } while (0); - - // begin to check and call registerd callback - do { - zend_fcall_info_cache *fci_cache = NULL; - - 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 == NULL) - { - swoole_websocket_onHandshake(serv, port, ctx); - goto _dtor_and_return; - } - else - { - 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 == NULL) - { - swoole_websocket_onRequest(ctx); - goto _dtor_and_return; - } - } - - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, NULL, SwooleG.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); - } - } while (0); - - _dtor_and_return: - zval_ptr_dtor(zrequest_object); - zval_ptr_dtor(zresponse_object); - - return SW_OK; -} - -void php_swoole_http_onClose(swServer *serv, swDataHead *ev) -{ - swConnection *conn = swWorker_get_connection(serv, 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_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY_EX(swoole_http_server, "Swoole\\Http\\Server", "swoole_http_server", NULL, NULL, swoole_server); - SW_SET_CLASS_SERIALIZABLE(swoole_http_server, zend_class_serialize_deny, zend_class_unserialize_deny); - 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); - - zend_declare_property_null(swoole_http_server_ce, ZEND_STRL("onRequest"), ZEND_ACC_PRIVATE); -} - -http_context* swoole_http_context_new(int fd) -{ - http_context *ctx = (http_context *) ecalloc(1, sizeof(http_context)); - - zval *zrequest_object = &ctx->request._zobject; - ctx->request.zobject = zrequest_object; - object_init_ex(zrequest_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); - php_swoole_http_response_set_context(zresponse_object, ctx); - - zend_update_property_long(swoole_http_request_ce, zrequest_object, ZEND_STRL("fd"), fd); - zend_update_property_long(swoole_http_response_ce, zresponse_object, ZEND_STRL("fd"), fd); - -#if PHP_MEMORY_DEBUG - php_vmstat.new_http_request ++; -#endif - - 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")); - ctx->fd = fd; - - return ctx; -} - -void swoole_http_server_init_context(swServer *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; -#ifdef SW_HAVE_COMPRESSION - ctx->enable_compression = serv->http_compression; - ctx->compression_level = serv->http_compression_level; -#endif - ctx->private_data = serv; - ctx->upload_tmp_dir = serv->upload_tmp_dir; - ctx->send = http_context_send_data; - ctx->sendfile = http_context_sendfile; - ctx->close = http_context_disconnect; -} - -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; -#ifdef SW_HAVE_COMPRESSION - dst->enable_compression = src->enable_compression; - dst->compression_level = src->compression_level; -#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; -} - -void swoole_http_context_free(http_context *ctx) -{ - /* http context can only be free'd after request and response were free'd */ - if (ctx->request.zobject || ctx->response.zobject) - { - return; - } -#ifdef SW_USE_HTTP2 - if (ctx->stream) - { - return; - } -#endif - - http_request *req = &ctx->request; - http_response *res = &ctx->response; - if (req->path) - { - efree(req->path); - } - if (Z_TYPE(req->zdata) == IS_STRING) - { - zend_string_release(Z_STR(req->zdata)); - } - if (req->chunked_body) - { - swString_free(req->chunked_body); - } -#ifdef SW_USE_HTTP2 - if (req->h2_data_buffer) - { - swString_free(req->h2_data_buffer); - } -#endif - if (res->reason) - { - efree(res->reason); - } - efree(ctx); -} - -void php_swoole_http_server_init_global_variant() -{ - swoole_http_buffer = swString_new(SW_HTTP_RESPONSE_INIT_SIZE); - if (!swoole_http_buffer) - { - php_swoole_fatal_error(E_ERROR, "[swoole_http_buffer] swString_new(%d) failed", SW_HTTP_RESPONSE_INIT_SIZE); - return; - } - - swoole_http_form_data_buffer = swString_new(SW_HTTP_RESPONSE_INIT_SIZE); - if (!swoole_http_form_data_buffer) - { - php_swoole_fatal_error(E_ERROR, "[swoole_http_form_data_buffer] swString_new(%d) failed", SW_HTTP_RESPONSE_INIT_SIZE); - return; - } - - //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, NULL, NULL, 0); - } -} - -http_context* php_swoole_http_request_get_and_check_context(zval *zobject) -{ - http_context *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)"); - } - 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)"); - return NULL; - } - return ctx; -} - -bool http_context_send_data(http_context* ctx, const char *data, size_t length) -{ - swServer *serv = (swServer *) ctx->private_data; - ssize_t ret = serv->send(serv, ctx->fd, (void*) data, length); - if (ret < 0 && SwooleG.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); - ret = Z_BVAL_P(&return_value) ? SW_OK : SW_ERR; - } - return ret == SW_OK; -} - -static bool http_context_sendfile(http_context* ctx, const char *file, uint32_t l_file, off_t offset, size_t length) -{ - swServer *serv = (swServer *) ctx->private_data; - return serv->sendfile(serv, ctx->fd, file, l_file, offset, length) == SW_OK; -} - -static bool http_context_disconnect(http_context* ctx) -{ - swServer *serv = (swServer *) ctx->private_data; - return serv->close(serv, ctx->fd, 0) == SW_OK; -} diff --git a/swoole_http_server.h b/swoole_http_server.h deleted file mode 100644 index 26a75e100c3..00000000000 --- a/swoole_http_server.h +++ /dev/null @@ -1,40 +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: Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#pragma once - -#include "swoole_server.h" -#include "swoole_http.h" - -int swoole_websocket_onMessage(swServer *serv, swEventData *req); -int swoole_websocket_onHandshake(swServer *serv, swListenPort *port, http_context *ctx); -void swoole_websocket_onOpen(http_context *ctx); -void swoole_websocket_onRequest(http_context *ctx); -bool swoole_websocket_handshake(http_context *ctx); - -void swoole_http_server_init_context(swServer *serv, http_context *ctx); - -#ifdef SW_USE_HTTP2 - -int swoole_http2_server_onFrame(swServer *serv, swConnection *conn, swEventData *req); -int swoole_http2_server_parse(http2_session *client, const char *buf); -void swoole_http2_server_session_free(swConnection *conn); -int swoole_http2_server_ping(http_context *ctx); - -#endif - diff --git a/swoole_http_server_coro.cc b/swoole_http_server_coro.cc deleted file mode 100644 index a5e086bc336..00000000000 --- a/swoole_http_server_coro.cc +++ /dev/null @@ -1,765 +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 "swoole_http_server.h" - -#include "http.h" -#ifdef SW_USE_HTTP2 -#include "http2.h" -#endif - -#include -#include -#include - -using namespace std; -using namespace swoole; -using swoole::coroutine::Socket; -using swoole::coroutine::System; - -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 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); - -#ifdef SW_USE_HTTP2 -static void http2_server_onRequest(http2_session *session, http2_stream *stream); -#endif - -class http_server -{ -public: - Socket *socket; - zend_fcall_info_cache *default_handler; - map handlers; - zval zcallbacks; - bool running; - std::list clients; - - /* options */ - bool http_parse_cookie :1; - bool http_parse_post :1; - bool http_parse_files :1; -#ifdef SW_HAVE_COMPRESSION - bool http_compression :1; -#endif -#ifdef SW_HAVE_ZLIB - bool websocket_compression :1; -#endif - char *upload_tmp_dir; -#ifdef SW_HAVE_COMPRESSION - uint8_t http_compression_level; -#endif - - http_server(enum swSocket_type type) - { - socket = new Socket(type); - default_handler = nullptr; - array_init(&zcallbacks); - running = true; - - http_parse_cookie = true; - http_parse_post = true; - http_parse_files = false; -#ifdef SW_HAVE_COMPRESSION - http_compression = true; - http_compression_level = SW_Z_BEST_SPEED; -#endif -#ifdef SW_HAVE_ZLIB - websocket_compression = false; -#endif - upload_tmp_dir = sw_strdup("/tmp"); - } - - ~http_server() - { - sw_free(upload_tmp_dir); - } - - void set_handler(string pattern, zval *zcallback, const zend_fcall_info_cache *fci_cache) - { - handlers[pattern] = *fci_cache; - if (pattern == "/") - { - default_handler = &handlers[pattern]; - } - Z_ADDREF_P(zcallback); - add_assoc_zval_ex(&zcallbacks, pattern.c_str(), pattern.length(), zcallback); - } - - zend_fcall_info_cache* get_handler(http_context *ctx) - { - for (auto i = handlers.begin(); i != handlers.end(); i++) - { - 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; - } - } - 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; -#ifdef SW_HAVE_COMPRESSION - ctx->enable_compression = http_compression; - ctx->compression_level = http_compression_level; -#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; - - swoole_http_parser *parser = &ctx->parser; - parser->data = ctx; - swoole_http_parser_init(parser, PHP_HTTP_REQUEST); - - zend_update_property(swoole_http_response_ce, ctx->response.zobject, ZEND_STRL("socket"), zconn); - - return ctx; - } - -#ifdef SW_USE_HTTP2 - void recv_http2_frame(http_context *ctx) - { - Socket *sock = (Socket *) ctx->private_data; - swHttp2_send_setting_frame(&sock->protocol, sock->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; - - http2_session session(ctx->fd); - session.default_ctx = ctx; - session.handle = http2_server_onRequest; - session.private_data = this; - - while (true) - { - auto buffer = sock->get_read_buffer(); - ssize_t retval = sock->recv_packet(); - if (sw_unlikely(retval <= 0)) - { - break; - } - swoole_http2_server_parse(&session, buffer->str); - } - - /* default_ctx does not blong to session object */ - session.default_ctx = nullptr; - - ctx->detached = 1; - zval_dtor(ctx->request.zobject); - zval_dtor(ctx->response.zobject); - } -#endif -}; - -typedef struct -{ - http_server *server; - zend_object std; -} http_server_coro_t; - -static PHP_METHOD(swoole_http_server_coro, __construct); -static PHP_METHOD(swoole_http_server_coro, set); -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); - -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_PUBLIC) - 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_FE_END -}; - -static zend_object *php_swoole_http_server_coro_create_object(zend_class_entry *ce) -{ - http_server_coro_t *hsc = (http_server_coro_t *) ecalloc(1, sizeof(http_server_coro_t) + zend_object_properties_size(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 http_server* http_server_get_object(zend_object *obj) -{ - return php_swoole_http_server_coro_fetch_object(obj)->server; -} - -static inline void http_server_set_error(zval *zobject, Socket *sock) -{ - zend_update_property_long(swoole_http_server_coro_ce, zobject, ZEND_STRL("errCode"), sock->errCode); - zend_update_property_string(swoole_http_server_coro_ce, zobject, ZEND_STRL("errMsg"), sock->errMsg); -} - -static bool http_context_send_data(http_context* 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) -{ - Socket *sock = (Socket *) ctx->private_data; - return sock->sendfile(file, offset, length); -} - -static bool http_context_disconnect(http_context* 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); - if (hsc->server) - { - http_server *hs = hsc->server; - zval_ptr_dtor(&hs->zcallbacks); - delete hs; - } - zend_object_std_dtor(&hsc->std); -} - -void php_swoole_http_server_coro_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY(swoole_http_server_coro, "Swoole\\Coroutine\\Http\\Server", NULL, "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_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, 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); - zend_declare_property_long(swoole_http_server_coro_ce, ZEND_STRL("port"), -1, ZEND_ACC_PUBLIC); - zend_declare_property_bool(swoole_http_server_coro_ce, ZEND_STRL("ssl"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_http_server_coro_ce, ZEND_STRL("settings"), ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_http_server_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_string(swoole_http_server_coro_ce, ZEND_STRL("errMsg"), "", ZEND_ACC_PUBLIC); -} - -static PHP_METHOD(swoole_http_server_coro, __construct) -{ - char *host; - size_t l_host; - zend_long port = 0; - zend_bool ssl = 0; - zend_bool reuse_port = 0; - - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 4) - Z_PARAM_STRING(host, l_host) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(port) - Z_PARAM_BOOL(ssl) - Z_PARAM_BOOL(reuse_port) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - zend_update_property_stringl(swoole_http_server_coro_ce, ZEND_THIS, ZEND_STRL("host"), host, l_host); - zend_update_property_bool(swoole_http_server_coro_ce, ZEND_THIS, ZEND_STRL("ssl"), ssl); - - // check host - if (l_host == 0) - { - zend_throw_exception_ex(swoole_exception_ce, EINVAL, "host is empty"); - RETURN_FALSE; - } - - http_server_coro_t *hsc = php_swoole_http_server_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); - string host_str(host, l_host); - hsc->server = new http_server(Socket::convert_to_type(host_str)); - Socket *sock = hsc->server->socket; - -#ifdef SO_REUSEPORT - if (reuse_port) - { - sock->set_option(SOL_SOCKET, SO_REUSEPORT, 1); - } -#endif - if (!sock->bind(host_str, port)) - { - http_server_set_error(ZEND_THIS, sock); - zend_throw_exception_ex(swoole_exception_ce, sock->errCode, "bind(%s:%d) failed", host, (int) port); - RETURN_FALSE; - } - //check ssl - if (ssl) - { -#ifndef SW_USE_OPENSSL - zend_throw_exception_ex( - swoole_exception_ce, - EPROTONOSUPPORT, "you must configure with `--enable-openssl` to support ssl connection when compiling Swoole" - ); - RETURN_FALSE; -#else - /* we have to call ssl_check_context after user setProtocols */ - zval *zsettings = sw_zend_read_and_convert_property_array(swoole_http_server_coro_ce, ZEND_THIS, ZEND_STRL("settings"), 0); - add_assoc_bool(zsettings, "open_ssl", 1); -#endif - } - if (!sock->listen()) - { - http_server_set_error(ZEND_THIS, sock); - zend_throw_exception_ex(swoole_exception_ce, sock->errCode, "listen() failed"); - RETURN_FALSE; - } - - zend_update_property_long(swoole_http_server_coro_ce, ZEND_THIS, ZEND_STRL("fd"), sock->get_fd()); - zend_update_property_long(swoole_http_server_coro_ce, ZEND_THIS, ZEND_STRL("port"), sock->get_bind_port()); -} - -static PHP_METHOD(swoole_http_server_coro, handle) -{ - char *pattern; - size_t pattern_len; - - http_server *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); - zend_fcall_info fci; - zend_fcall_info_cache fci_cache; - - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STRING(pattern, pattern_len) - Z_PARAM_FUNC(fci, fci_cache) - ZEND_PARSE_PARAMETERS_END(); - - string key(pattern, pattern_len); - hs->set_handler(key, ZEND_CALL_ARG(execute_data, 2), &fci_cache); -} - -static PHP_METHOD(swoole_http_server_coro, set) -{ - zval *zset; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY(zset) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (php_swoole_array_length(zset) == 0) - { - RETURN_FALSE; - } - else - { - zval *zsettings = sw_zend_read_and_convert_property_array(swoole_http_server_coro_ce, ZEND_THIS, ZEND_STRL("settings"), 0); - php_array_merge(Z_ARRVAL_P(zsettings), Z_ARRVAL_P(zset)); - RETURN_TRUE; - } -} - -static PHP_METHOD(swoole_http_server_coro, start) -{ - http_server *hs = http_server_get_object(Z_OBJ_P(ZEND_THIS)); - Socket *sock = hs->socket; - - /* get callback fci cache */ - char *func_name = NULL; - zend_fcall_info_cache fci_cache; - zval zcallback; - ZVAL_STRING(&zcallback, "onAccept"); - if (!sw_zend_is_callable_ex(&zcallback, ZEND_THIS, 0, &func_name, NULL, &fci_cache, NULL)) - { - php_swoole_fatal_error(E_CORE_ERROR, "function '%s' is not callable", func_name); - return; - } - efree(func_name); - - /* check settings */ - zval *zsettings = sw_zend_read_and_convert_property_array(swoole_http_server_coro_ce, ZEND_THIS, ZEND_STRL("settings"), 0); - php_swoole_socket_set_protocol(hs->socket, zsettings); - HashTable *vht = Z_ARRVAL_P(zsettings); - zval *ztmp; - //parse cookie header - if (php_swoole_array_get_value(vht, "http_parse_cookie", ztmp)) - { - hs->http_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); - } - //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); - } -#ifdef SW_HAVE_COMPRESSION - //http content compression - if (php_swoole_array_get_value(vht, "http_compression", ztmp)) - { - hs->http_compression = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "http_compression_level", ztmp) || php_swoole_array_get_value(vht, "http_gzip_level", ztmp)) - { - zend_long level = zval_get_long(ztmp); - if (level > UINT8_MAX) - { - level = UINT8_MAX; - } - else if (level < 0) - { - level = 0; - } - hs->http_compression_level = level; - } -#endif -#ifdef SW_HAVE_ZLIB - if (php_swoole_array_get_value(vht, "websocket_compression", ztmp)) - { - hs->websocket_compression = zval_is_true(ztmp); - } -#endif - //temporary directory for HTTP uploaded file. - if (php_swoole_array_get_value(vht, "upload_tmp_dir", ztmp)) - { - zend::string str_v(ztmp); - if (php_swoole_create_dir(str_v.val(), str_v.len()) < 0) - { - php_swoole_fatal_error(E_ERROR, "Unable to create upload_tmp_dir[%s]", str_v.val()); - return; - } - if (hs->upload_tmp_dir) - { - sw_free(hs->upload_tmp_dir); - } - hs->upload_tmp_dir = str_v.dup(); - } - - php_swoole_http_server_init_global_variant(); - - 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); - zval_dtor(&zsocket); - if (cid < 0) - { - goto _wait_1s; - } - } - else - { - /* - * Too many connection, wait 1s - */ - if (sock->errCode == EMFILE || sock->errCode == ENFILE) - { - _wait_1s: System::sleep(SW_ACCEPT_RETRY_TIME); - } - else if (sock->errCode == ETIMEDOUT || sock->errCode == SW_ERROR_SSL_BAD_CLIENT) - { - continue; - } - else if (sock->errCode == ECANCELED) - { - http_server_set_error(ZEND_THIS, sock); - break; - } - else - { - http_server_set_error(ZEND_THIS, sock); - php_swoole_fatal_error(E_WARNING, "accept failed, Error: %s[%d]", sock->errMsg, sock->errCode); - break; - } - } - } - - 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)); - zval *zconn; - - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1) - Z_PARAM_OBJECT(zconn) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - Socket *sock = php_swoole_get_socket(zconn); - swString* buffer = sock->get_read_buffer(); - size_t total_bytes = 0; - http_context *ctx = nullptr; - - hs->clients.push_front(sock); - auto client_iterator = hs->clients.begin(); - -#ifdef SW_USE_OPENSSL - if (sock->open_ssl) - { - if (!sock->ssl_handshake()) - { - goto _handshake_failed; - } - } -#endif - - while (true) - { - ssize_t retval; - if (ctx != nullptr || total_bytes == 0) - { - retval = sock->recv(buffer->str + total_bytes, buffer->size - total_bytes); - - if (sw_unlikely(retval <= 0)) - { - break; - } - - if (!ctx) - { - ctx = hs->create_context(sock, zconn); - } - - if (total_bytes + retval > sock->protocol.package_max_length) - { - ctx->response.status = SW_HTTP_REQUEST_ENTITY_TOO_LARGE; - break; - } - } - else - { - /* redundant data from previous packet */ - retval = total_bytes; - total_bytes = 0; - - if (!ctx) - { - ctx = hs->create_context(sock, zconn); - } - } - - size_t parsed_n = swoole_http_requset_parse(ctx, buffer->str + total_bytes, retval); - size_t total_parsed_n = total_bytes + parsed_n; - total_bytes += retval; - - swTraceLog(SW_TRACE_CO_HTTP_SERVER, "parsed_n=%ld, retval=%ld, total_bytes=%ld, completed=%d", parsed_n, retval, total_bytes, ctx->completed); - - if (!ctx->completed) - { - if (ctx->parser.state == s_dead) - { - ctx->response.status = SW_HTTP_BAD_REQUEST; - break; - } - if (total_bytes == buffer->size) - { - if (swString_extend(buffer, buffer->size * 2) != SW_OK) - { - ctx->response.status = SW_HTTP_SERVICE_UNAVAILABLE; - break; - } - } - continue; - } - -#ifdef SW_USE_HTTP2 - if (ctx->parser.method == PHP_HTTP_NOT_IMPLEMENTED - && memcmp(buffer->str, SW_HTTP2_PRI_STRING, sizeof(SW_HTTP2_PRI_STRING) - 1) == 0) - { - buffer->length = total_bytes - (sizeof(SW_HTTP2_PRI_STRING) - 1); - buffer->offset = buffer->length == 0 ? 0 : (sizeof(SW_HTTP2_PRI_STRING) - 1); - hs->recv_http2_frame(ctx); - /* ownership of ctx has been transferred */ - ctx = nullptr; - break; - } -#endif - - ZVAL_STRINGL(&ctx->request.zdata, buffer->str, total_parsed_n); - - /* handle more packages */ - if ((size_t) retval > parsed_n) - { - total_bytes = retval - parsed_n; - memmove(buffer->str, buffer->str + total_parsed_n, total_bytes); - if (ctx->websocket) - { - /* for recv_packet */ - buffer->length = total_bytes; - } - } - else - { - total_bytes = 0; - } - - 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); - zval args[2] = {*ctx->request.zobject, *ctx->response.zobject}; - bool keep_alive = swoole_http_should_keep_alive(&ctx->parser) && !ctx->websocket; - - if (fci_cache) - { - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, NULL, 0))) - { - php_swoole_error(E_WARNING, "handler error"); - } - } - else - { - ctx->response.status = SW_HTTP_NOT_FOUND; - } - - zval_dtor(&args[0]); - zval_dtor(&args[1]); - ctx = nullptr; - - if (!hs->running || !keep_alive) - { - break; - } - } - - if (ctx) - { - 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); - } -} - -static PHP_METHOD(swoole_http_server_coro, shutdown) -{ - http_server *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(); - } - hs->clients.clear(); -} - -#ifdef SW_USE_HTTP2 -static void http2_server_onRequest(http2_session *session, http2_stream *stream) -{ - http_context *ctx = stream->ctx; - http_server *hs = (http_server*) session->private_data; - Socket *sock = (Socket *) ctx->private_data; - zval *zserver = ctx->request.zserver; - - add_assoc_long(zserver, "request_time", time(NULL)); - add_assoc_double(zserver, "request_time_float", swoole_microtime()); - add_assoc_long(zserver, "server_port", hs->socket->get_bind_port()); - add_assoc_long(zserver, "remote_port", sock->get_port()); - 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); - zval args[2] = {*ctx->request.zobject, *ctx->response.zobject}; - - if (fci_cache) - { - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, NULL, SwooleG.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)); - } - } - else - { - ctx->response.status = SW_HTTP_NOT_FOUND; - } - - zval_ptr_dtor(&args[0]); - zval_ptr_dtor(&args[1]); -} -#endif diff --git a/swoole_lock.cc b/swoole_lock.cc deleted file mode 100644 index b90570070d1..00000000000 --- a/swoole_lock.cc +++ /dev/null @@ -1,273 +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.h" - -static zend_class_entry *swoole_lock_ce; -static zend_object_handlers swoole_lock_handlers; - -typedef struct -{ - swLock *ptr; - zend_object std; -} lock_t; - -static sw_inline lock_t* php_swoole_lock_fetch_object(zend_object *obj) -{ - return (lock_t *) ((char *) obj - swoole_lock_handlers.offset); -} - -static swLock * php_swoole_lock_get_ptr(zval *zobject) -{ - return php_swoole_lock_fetch_object(Z_OBJ_P(zobject))->ptr; -} - -static swLock * php_swoole_lock_get_and_check_ptr(zval *zobject) -{ - swLock *lock = php_swoole_lock_get_ptr(zobject); - if (!lock) - { - php_swoole_fatal_error(E_ERROR, "you must call Lock constructor first"); - } - return lock; -} - -void php_swoole_lock_set_ptr(zval *zobject, swLock *ptr) -{ - php_swoole_lock_fetch_object(Z_OBJ_P(zobject))->ptr = ptr; -} - -static void php_swoole_lock_free_object(zend_object *object) -{ - zend_object_std_dtor(object); -} - -static zend_object *php_swoole_lock_create_object(zend_class_entry *ce) -{ - lock_t *lock = (lock_t *) ecalloc(1, sizeof(lock_t) + zend_object_properties_size(ce)); - zend_object_std_init(&lock->std, ce); - object_properties_init(&lock->std, ce); - lock->std.handlers = &swoole_lock_handlers; - return &lock->std; -} - -static PHP_METHOD(swoole_lock, __construct); -static PHP_METHOD(swoole_lock, __destruct); -static PHP_METHOD(swoole_lock, lock); -static PHP_METHOD(swoole_lock, lockwait); -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); - -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_FE_END -}; - -void php_swoole_lock_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY(swoole_lock, "Swoole\\Lock", "swoole_lock", NULL, swoole_lock_methods); - SW_SET_CLASS_SERIALIZABLE(swoole_lock, zend_class_serialize_deny, zend_class_unserialize_deny); - 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, lock_t, std); - - zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL("FILELOCK"), SW_FILELOCK); - zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL("MUTEX"), SW_MUTEX); - zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL("SEM"), SW_SEM); -#ifdef HAVE_RWLOCK - zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL("RWLOCK"), SW_RWLOCK); -#endif -#ifdef HAVE_SPINLOCK - zend_declare_class_constant_long(swoole_lock_ce, ZEND_STRL("SPINLOCK"), SW_SPINLOCK); -#endif - zend_declare_property_long(swoole_lock_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); - - SW_REGISTER_LONG_CONSTANT("SWOOLE_FILELOCK", SW_FILELOCK); - SW_REGISTER_LONG_CONSTANT("SWOOLE_MUTEX", SW_MUTEX); - SW_REGISTER_LONG_CONSTANT("SWOOLE_SEM", SW_SEM); -#ifdef HAVE_RWLOCK - SW_REGISTER_LONG_CONSTANT("SWOOLE_RWLOCK", SW_RWLOCK); -#endif -#ifdef HAVE_SPINLOCK - SW_REGISTER_LONG_CONSTANT("SWOOLE_SPINLOCK", SW_SPINLOCK); -#endif -} - -static PHP_METHOD(swoole_lock, __construct) -{ - swLock *lock = php_swoole_lock_get_ptr(ZEND_THIS); - - if (lock != NULL) - { - php_swoole_fatal_error(E_ERROR, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - } - - lock = (swLock *) SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(swLock)); - if (lock == NULL) - { - zend_throw_exception(swoole_exception_ce, "global memory allocation failure", SW_ERROR_MALLOC_FAIL); - RETURN_FALSE; - } - - zend_long type = SW_MUTEX; - char *filelock; - size_t filelock_len = 0; - int ret; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ls", &type, &filelock, &filelock_len) == FAILURE) - { - RETURN_FALSE; - } - - switch(type) - { -#ifdef HAVE_RWLOCK - case SW_RWLOCK: - ret = swRWLock_create(lock, 1); - break; -#endif - case SW_FILELOCK: - if (filelock_len == 0) - { - zend_throw_exception(swoole_exception_ce, "filelock requires file name of the lock", SW_ERROR_INVALID_PARAMS); - RETURN_FALSE; - } - int fd; - if ((fd = open(filelock, O_RDWR | O_CREAT, 0666)) < 0) - { - zend_throw_exception_ex(swoole_exception_ce, errno, "open file[%s] failed. Error: %s [%d]", filelock, strerror(errno), errno); - RETURN_FALSE; - } - ret = swFileLock_create(lock, fd); - break; -#ifdef SEM_UNDO - case SW_SEM: - ret = swSem_create(lock, IPC_PRIVATE); - break; -#endif -#ifdef HAVE_SPINLOCK - case SW_SPINLOCK: - ret = swSpinLock_create(lock, 1); - break; -#endif - case SW_MUTEX: - default: - ret = swMutex_create(lock, 1); - break; - } - if (ret < 0) - { - zend_throw_exception(swoole_exception_ce, "failed to create lock", errno); - RETURN_FALSE; - } - - php_swoole_lock_set_ptr(ZEND_THIS, lock); - RETURN_TRUE; -} - -static PHP_METHOD(swoole_lock, __destruct) { } - -static PHP_METHOD(swoole_lock, lock) -{ - swLock *lock = php_swoole_lock_get_and_check_ptr(ZEND_THIS); - SW_LOCK_CHECK_RETURN(lock->lock(lock)); -} - -static PHP_METHOD(swoole_lock, lockwait) -{ - double timeout = 1.0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &timeout) == FAILURE) - { - RETURN_FALSE; - } - swLock *lock = php_swoole_lock_get_and_check_ptr(ZEND_THIS); - if (lock->type != SW_MUTEX) - { - zend_throw_exception(swoole_exception_ce, "only mutex supports lockwait", -2); - RETURN_FALSE; - } - SW_LOCK_CHECK_RETURN(swMutex_lockwait(lock, (int)timeout * 1000)); -} - -static PHP_METHOD(swoole_lock, unlock) -{ - swLock *lock = php_swoole_lock_get_and_check_ptr(ZEND_THIS); - SW_LOCK_CHECK_RETURN(lock->unlock(lock)); -} - -static PHP_METHOD(swoole_lock, trylock) -{ - swLock *lock = php_swoole_lock_get_and_check_ptr(ZEND_THIS); - if (lock->trylock == NULL) - { - php_swoole_error(E_WARNING, "lock[type=%d] can't use trylock", lock->type); - RETURN_FALSE; - } - SW_LOCK_CHECK_RETURN(lock->trylock(lock)); -} - -static PHP_METHOD(swoole_lock, trylock_read) -{ - swLock *lock = php_swoole_lock_get_and_check_ptr(ZEND_THIS); - if (lock->trylock_rd == NULL) - { - php_swoole_error(E_WARNING, "lock[type=%d] can't use trylock_read", lock->type); - RETURN_FALSE; - } - SW_LOCK_CHECK_RETURN(lock->trylock_rd(lock)); -} - -static PHP_METHOD(swoole_lock, lock_read) -{ - swLock *lock = php_swoole_lock_get_and_check_ptr(ZEND_THIS); - if (lock->lock_rd == NULL) - { - php_swoole_error(E_WARNING, "lock[type=%d] can't use lock_read", lock->type); - RETURN_FALSE; - } - SW_LOCK_CHECK_RETURN(lock->lock_rd(lock)); -} - -static PHP_METHOD(swoole_lock, destroy) -{ - swLock *lock = php_swoole_lock_get_and_check_ptr(ZEND_THIS); - lock->free(lock); -} diff --git a/swoole_mysql_coro.cc b/swoole_mysql_coro.cc deleted file mode 100644 index 666e45355d3..00000000000 --- a/swoole_mysql_coro.cc +++ /dev/null @@ -1,2519 +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 "swoole_mysql_proto.h" - -// see mysqlnd 'L64' macro redefined -#undef L64 - -extern "C" { -#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 -} - -#include - -using namespace swoole; -using swoole::coroutine::Socket; - -/* 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 - -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 = 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 = cpp_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 = cpp_string::format( - "SQLSTATE[" MYSQLND_UNKNOWN_SQLSTATE "] [%d] %s", - code, cpp_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 = cpp_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 swTimeout_type 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 swTimeout_type 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_reuqest() - { - 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); - swString_clear(buffer); - 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(SW_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_reuqest() - { - if (sw_unlikely(!is_available())) - { - return false; - } - if (sw_unlikely(!client->is_available_for_new_reuqest())) - { - return false; - } - return true; - } - - inline void add_timeout_controller(double timeout, const enum swTimeout_type 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; -}; -} - -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; - -typedef struct { - mysql_client *client; - zend_object std; -} mysql_coro_t; - -typedef struct { - mysql_statement *statement; - zend_object *zclient; - zend_object std; -} mysql_coro_statement_t; - -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); - -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 -}; - -void 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; - } -#ifdef SW_USE_OPENSSL - socket->open_ssl = ssl; -#endif - socket->set_timeout(connect_timeout, SW_TIMEOUT_CONNECT); - add_timeout_controller(connect_timeout, SW_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); - swString_clear(buffer); - offset = 0; - } - while (read_n < need_length) - { - if (sw_unlikely(has_timedout(SW_TIMEOUT_READ))) - { - io_error(); - return nullptr; - } - if (sw_unlikely(buffer->length == buffer->size)) - { - /* offset + need_length = new size (min) */ - if (sw_unlikely(swString_extend(buffer, SW_MEM_ALIGNED_SIZE_EX(offset + need_length, SwooleG.pagesize)) != SW_OK)) - { - 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_reuqest())) - { - 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_reuqest())) - { - 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_reuqest())) - { - 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 = SwooleTG.buffer_stack; - 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 (swString_append_ptr(buffer, stack_buffer, lcb_size) < 0) - { - RETURN_FALSE; - } - if (swString_append_ptr(buffer, 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 *) ecalloc(1, sizeof(mysql_coro_t) + zend_object_properties_size(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 *) ecalloc(1, sizeof(mysql_coro_statement_t) + zend_object_properties_size(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, &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), zobject, ZEND_STRL("errno"), error_code); - zend_update_property_string(Z_OBJCE_P(zobject), zobject, ZEND_STRL("error"), error_msg); - if (!connected) - { - zend_update_property_bool(Z_OBJCE_P(zobject), 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), zobject, ZEND_STRL("affected_rows"), ok_packet->affected_rows); - zend_update_property_long(Z_OBJCE_P(zobject), 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), zobject, ZEND_STRL("affected_rows"), ok_packet->affected_rows); - zend_update_property_long(Z_OBJCE_P(zobject), 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), &zclient, ZEND_STRL("affected_rows"), ok_packet->affected_rows); - zend_update_property_long(Z_OBJCE_P(&zclient), &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", NULL, "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", NULL, "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", NULL, "Co\\MySQL\\Exception", NULL, 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, ZEND_THIS, ZEND_STRL("connect_errno"), mc->get_error_code()); - zend_update_property_string(swoole_mysql_coro_ce, 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, ZEND_THIS, ZEND_STRL("sock"), mc->get_fd()); - zend_update_property_bool(swoole_mysql_coro_ce, 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, SW_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, SW_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, SW_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, SW_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, SW_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, SW_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, SW_TIMEOUT_RDWR); - mc->query(return_value, command, command_length); - mc->del_timeout_controller(); -} - -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, 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, SW_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, SW_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, SW_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, SW_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, SW_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/swoole_mysql_proto.cc b/swoole_mysql_proto.cc deleted file mode 100644 index 9f8ded99835..00000000000 --- a/swoole_mysql_proto.cc +++ /dev/null @@ -1,756 +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 "swoole_mysql_proto.h" - -using namespace swoole::mysql; - -namespace swoole -{ -namespace mysql -{ -typedef struct -{ - unsigned int nr; - const char *name; - const char *collation; -} charset_t; - -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, NULL, NULL}, - }; - const charset_t *c = charsets; - while (c[0].nr) - { - if (!strcasecmp(c->name, name)) - { - return c->nr; - } - ++c; - } - return -1; -} - -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]; -} - -static uint32_t sha1_password_with_nonce(char* buf, const char* nonce, const char* password) -{ - char hash_0[20] = {}; - swoole_sha1(password, strlen(password), (uchar *) hash_0); - - char hash_1[20] = {}; - 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]; - 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]; - swoole_sha256(password, strlen(password), (unsigned char *) hashed); - 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]; - 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 = NULL; - RSA *public_rsa = NULL; - if (sw_unlikely((bio = BIO_new_mem_buf((void *)rsa_public_key, -1)) == NULL)) - { - 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, NULL, NULL, NULL)) == NULL)) - { - 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 - ); -} -} -} diff --git a/swoole_mysql_proto.h b/swoole_mysql_proto.h deleted file mode 100644 index bc555959ab0..00000000000 --- a/swoole_mysql_proto.h +++ /dev/null @@ -1,998 +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 "swoole_cxx.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 - -#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) - -#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 >= SwooleG.log_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::cpp_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::cpp_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::cpp_string::format( - "%s%02u:%02u:%02u.%0*u", - (neg ? "-" : ""), h, m, s, decimals, (uint32_t) (sp / ::pow(10, (double) (6 - decimals))) - ); - } else { - return swoole::cpp_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::cpp_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/swoole_process.cc b/swoole_process.cc deleted file mode 100644 index ce2ef1cd6dd..00000000000 --- a/swoole_process.cc +++ /dev/null @@ -1,1314 +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 "php_streams.h" -#include "php_network.h" - -#include "server.h" - -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] = {}; - -typedef struct -{ - swWorker *worker; - zend_object std; -} process_t; - -static sw_inline process_t* php_swoole_process_fetch_object(zend_object *obj) -{ - return (process_t *) ((char *) obj - swoole_process_handlers.offset); -} - -static sw_inline swWorker* php_swoole_process_get_worker(zval *zobject) -{ - return php_swoole_process_fetch_object(Z_OBJ_P(zobject))->worker; -} - -swWorker* php_swoole_process_get_and_check_worker(zval *zobject) -{ - swWorker *worker = php_swoole_process_get_worker(zobject); - if (!worker) - { - php_swoole_fatal_error(E_ERROR, "you must call Process constructor first"); - } - return worker; -} - -void php_swoole_process_set_worker(zval *zobject, swWorker *worker) -{ - php_swoole_process_fetch_object(Z_OBJ_P(zobject))->worker = worker; -} - -static void php_swoole_process_free_object(zend_object *object) -{ - process_t *process = php_swoole_process_fetch_object(object); - swWorker *worker = process->worker; - - if (worker) - { - swPipe *_pipe = worker->pipe_object; - if (_pipe) - { - _pipe->close(_pipe); - efree(_pipe); - } - - if (worker->queue) - { - efree(worker->queue); - } - - zend::process *proc = (zend::process *) worker->ptr2; - if (proc) - { - delete proc; - } - efree(worker); - } - - zend_object_std_dtor(object); -} - -static zend_object *php_swoole_process_create_object(zend_class_entry *ce) -{ - process_t *process = (process_t *) ecalloc(1, sizeof(process_t) + zend_object_properties_size(ce)); - zend_object_std_init(&process->std, ce); - object_properties_init(&process->std, ce); - process->std.handlers = &swoole_process_handlers; - return &process->std; -} - -static PHP_METHOD(swoole_process, __construct); -static PHP_METHOD(swoole_process, __destruct); -static PHP_METHOD(swoole_process, useQueue); -static PHP_METHOD(swoole_process, statQueue); -static PHP_METHOD(swoole_process, freeQueue); -static PHP_METHOD(swoole_process, pop); -static PHP_METHOD(swoole_process, push); -static PHP_METHOD(swoole_process, kill); -static PHP_METHOD(swoole_process, signal); -static PHP_METHOD(swoole_process, alarm); -static PHP_METHOD(swoole_process, wait); -static PHP_METHOD(swoole_process, daemon); -#ifdef HAVE_CPU_AFFINITY -static PHP_METHOD(swoole_process, setaffinity); -#endif -static PHP_METHOD(swoole_process, set); -static PHP_METHOD(swoole_process, setTimeout); -static PHP_METHOD(swoole_process, setBlocking); -static PHP_METHOD(swoole_process, start); -static PHP_METHOD(swoole_process, write); -static PHP_METHOD(swoole_process, read); -static PHP_METHOD(swoole_process, close); -static PHP_METHOD(swoole_process, exit); -static PHP_METHOD(swoole_process, exec); -static PHP_METHOD(swoole_process, exportSocket); - -static void php_swoole_onSignal(int signo); - -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_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) -#ifdef HAVE_CPU_AFFINITY - PHP_ME(swoole_process, setaffinity, arginfo_swoole_process_setaffinity, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) -#endif - 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_FE_END -}; - -void php_swoole_process_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY(swoole_process, "Swoole\\Process", "swoole_process", NULL, swoole_process_methods); - SW_SET_CLASS_SERIALIZABLE(swoole_process, zend_class_serialize_deny, zend_class_unserialize_deny); - 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(swoole_process, php_swoole_process_create_object, php_swoole_process_free_object, process_t, std); - - zend_declare_class_constant_long(swoole_process_ce, ZEND_STRL("IPC_NOWAIT"), MSGQUEUE_NOWAIT); - zend_declare_class_constant_long(swoole_process_ce, ZEND_STRL("PIPE_MASTER"), SW_PIPE_CLOSE_MASTER); - zend_declare_class_constant_long(swoole_process_ce, ZEND_STRL("PIPE_WORKER"), SW_PIPE_CLOSE_WORKER); - zend_declare_class_constant_long(swoole_process_ce, ZEND_STRL("PIPE_READ"), SW_PIPE_CLOSE_READ); - zend_declare_class_constant_long(swoole_process_ce, ZEND_STRL("PIPE_WRITE"), SW_PIPE_CLOSE_WRITE); - - zend_declare_property_null(swoole_process_ce, ZEND_STRL("pipe"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_process_ce, ZEND_STRL("msgQueueId"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_process_ce, ZEND_STRL("msgQueueKey"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_process_ce, ZEND_STRL("pid"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_process_ce, ZEND_STRL("id"), ZEND_ACC_PUBLIC); - - zend_declare_property_null(swoole_process_ce, ZEND_STRL("callback"), ZEND_ACC_PRIVATE); - - /** - * 31 signal constants - */ - if (!zend_hash_str_find(&module_registry, ZEND_STRL("pcntl"))) - { - REGISTER_LONG_CONSTANT("SIGHUP", (zend_long) SIGHUP, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGINT", (zend_long) SIGINT, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGQUIT", (zend_long) SIGQUIT, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGILL", (zend_long) SIGILL, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGTRAP", (zend_long) SIGTRAP, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGABRT", (zend_long) SIGABRT, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGBUS", (zend_long) SIGBUS, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGFPE", (zend_long) SIGFPE, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGKILL", (zend_long) SIGKILL, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGUSR1", (zend_long) SIGUSR1, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGSEGV", (zend_long) SIGSEGV, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGUSR2", (zend_long) SIGUSR2, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGPIPE", (zend_long) SIGPIPE, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGALRM", (zend_long) SIGALRM, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGTERM", (zend_long) SIGTERM, CONST_CS | CONST_PERSISTENT); -#ifdef SIGSTKFLT - REGISTER_LONG_CONSTANT("SIGSTKFLT", (zend_long) SIGSTKFLT, CONST_CS | CONST_PERSISTENT); -#endif - REGISTER_LONG_CONSTANT("SIGCHLD", (zend_long) SIGCHLD, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGCONT", (zend_long) SIGCONT, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGSTOP", (zend_long) SIGSTOP, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGTSTP", (zend_long) SIGTSTP, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGTTIN", (zend_long) SIGTTIN, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGTTOU", (zend_long) SIGTTOU, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGURG", (zend_long) SIGURG, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGXCPU", (zend_long) SIGXCPU, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGXFSZ", (zend_long) SIGXFSZ, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGVTALRM", (zend_long) SIGVTALRM, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGPROF", (zend_long) SIGPROF, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGWINCH", (zend_long) SIGWINCH, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SIGIO", (zend_long) SIGIO, CONST_CS | CONST_PERSISTENT); -#ifdef SIGPWR - REGISTER_LONG_CONSTANT("SIGPWR", (zend_long) SIGPWR, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef SIGSYS - REGISTER_LONG_CONSTANT("SIGSYS", (zend_long) SIGSYS, CONST_CS | CONST_PERSISTENT); -#endif - REGISTER_LONG_CONSTANT("SIG_IGN", (zend_long) SIG_IGN, CONST_CS | CONST_PERSISTENT); - } -} - -static PHP_METHOD(swoole_process, __construct) -{ - swWorker *process = php_swoole_process_get_worker(ZEND_THIS); - - if (process) - { - php_swoole_fatal_error(E_ERROR, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - } - - //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)); - RETURN_FALSE; - } - - if (sw_server() && sw_server()->gs->start == 1 && swIsMaster()) - { - php_swoole_fatal_error(E_ERROR, "%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)); - RETURN_FALSE; - } - - php_swoole_fci func; - zend_bool redirect_stdin_and_stdout = 0; - zend_long pipe_type = 2; - zend_bool enable_coroutine = SW_FALSE; - - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 4) - Z_PARAM_FUNC(func.fci, func.fci_cache); - Z_PARAM_OPTIONAL - Z_PARAM_BOOL(redirect_stdin_and_stdout) - Z_PARAM_LONG(pipe_type) - Z_PARAM_BOOL(enable_coroutine) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - process = (swWorker *) ecalloc(1, sizeof(swWorker)); - - uint32_t base = 1; - if (sw_server() && sw_server()->gs->start) - { - 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 = 1; - process->redirect_stdout = 1; - process->redirect_stderr = 1; - /** - * Forced to use stream pipe - */ - pipe_type = 1; - } - - if (pipe_type > 0) - { - swPipe *_pipe = (swPipe *) emalloc(sizeof(swPipe)); - int socket_type = pipe_type == zend::PIPE_TYPE_STREAM ? SOCK_STREAM : SOCK_DGRAM; - if (swPipeUnsock_create(_pipe, 1, socket_type) < 0) - { - zend_throw_exception(swoole_exception_ce, "swPipeUnsock_create failed", errno); - efree(_pipe); - efree(process); - RETURN_FALSE; - } - - process->pipe_master = _pipe->getSocket(_pipe, SW_PIPE_MASTER); - process->pipe_worker = _pipe->getSocket(_pipe, SW_PIPE_WORKER); - - process->pipe_object = _pipe; - process->pipe_current = process->pipe_master; - - zend_update_property_long(swoole_process_ce, ZEND_THIS, ZEND_STRL("pipe"), process->pipe_master->fd); - } - - zend::process *proc = new zend::process((enum zend::process_pipe_type) pipe_type, enable_coroutine); - process->ptr2 = proc; - - zend_update_property(swoole_process_ce, ZEND_THIS, ZEND_STRL("callback"), ZEND_CALL_ARG(execute_data, 1)); - php_swoole_process_set_worker(ZEND_THIS, process); -} - -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) - { - 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)); - } - else - { - RETURN_FALSE; - } -} - -static PHP_METHOD(swoole_process, useQueue) -{ - long msgkey = 0; - long mode = 2; - long capacity = -1; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|lll", &msgkey, &mode, &capacity) == FAILURE) - { - RETURN_FALSE; - } - - swWorker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); - - if (msgkey <= 0) - { - msgkey = ftok(zend_get_executed_filename(), 1); - } - - swMsgQueue *queue = (swMsgQueue *) emalloc(sizeof(swMsgQueue)); - if (swMsgQueue_create(queue, 1, msgkey, 0) < 0) - { - RETURN_FALSE; - } - if (mode & MSGQUEUE_NOWAIT) - { - swMsgQueue_set_blocking(queue, 0); - mode = mode & (~MSGQUEUE_NOWAIT); - } - if (capacity > 0) - { - swMsgQueue_set_capacity(queue, capacity); - } - process->queue = queue; - process->ipc_mode = mode; - zend_update_property_long(swoole_process_ce, ZEND_THIS, ZEND_STRL("msgQueueId"), queue->msg_id); - zend_update_property_long(swoole_process_ce, ZEND_THIS, ZEND_STRL("msgQueueKey"), msgkey); - RETURN_TRUE; -} - -static PHP_METHOD(swoole_process, statQueue) -{ - swWorker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); - if (!process->queue) - { - php_swoole_fatal_error(E_WARNING, "no queue, can't get stats of the queue"); - RETURN_FALSE; - } - - int queue_num = -1; - int queue_bytes = -1; - if (swMsgQueue_stat(process->queue, &queue_num, &queue_bytes) == 0) - { - array_init(return_value); - add_assoc_long_ex(return_value, ZEND_STRL("queue_num"), queue_num); - add_assoc_long_ex(return_value, ZEND_STRL("queue_bytes"), queue_bytes); - } - else - { - RETURN_FALSE; - } -} - -static PHP_METHOD(swoole_process, freeQueue) -{ - swWorker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); - if (process->queue && swMsgQueue_free(process->queue) == SW_OK) - { - efree(process->queue); - process->queue = NULL; - RETURN_TRUE; - } - else - { - RETURN_FALSE; - } -} - -static PHP_METHOD(swoole_process, kill) -{ - zend_long pid; - zend_long sig = SIGTERM; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &pid, &sig) == FAILURE) - { - RETURN_FALSE; - } - - 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); - } - RETURN_FALSE; - } - RETURN_TRUE; -} - -static PHP_METHOD(swoole_process, signal) -{ - zend_long signo = 0; - zval *zcallback = NULL; - zend_fcall_info_cache *fci_cache = NULL; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_LONG(signo) - Z_PARAM_OPTIONAL - Z_PARAM_ZVAL_EX(zcallback, 1, 0) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (!SWOOLE_G(cli)) - { - php_swoole_fatal_error(E_ERROR, "%s::signal can only be used in CLI mode", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - RETURN_FALSE; - } - - if (signo < 0 || signo >= SW_SIGNO_MAX) - { - php_swoole_fatal_error(E_WARNING, "invalid signal number [" ZEND_LONG_FMT "]", signo); - RETURN_FALSE; - } - - php_swoole_check_reactor(); - - swSignalHandler handler = swSignal_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); - RETURN_FALSE; - } - - if (zcallback == NULL) - { - fci_cache = signal_fci_caches[signo]; - if (fci_cache) - { - swSignal_add(signo, NULL); - signal_fci_caches[signo] = NULL; - swoole_event_defer(sw_zend_fci_cache_free, fci_cache); - SwooleTG.reactor->signal_listener_num--; - RETURN_TRUE; - } - else - { - php_swoole_error(E_WARNING, "unable to find the callback of signal [" ZEND_LONG_FMT "]", signo); - RETURN_FALSE; - } - } - else if (Z_TYPE_P(zcallback) == IS_LONG && Z_LVAL_P(zcallback) == (zend_long) SIG_IGN) - { - handler = NULL; - } - else - { - char *func_name; - fci_cache = (zend_fcall_info_cache *) ecalloc(1, sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(zcallback, NULL, 0, &func_name, 0, fci_cache, NULL)) - { - php_swoole_error(E_WARNING, "function '%s' is not callable", func_name); - efree(func_name); - efree(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 = 1; - - if (signal_fci_caches[signo]) - { - // free the old fci_cache - swoole_event_defer(sw_zend_fci_cache_free, signal_fci_caches[signo]); - } - else - { - SwooleTG.reactor->signal_listener_num++; - } - signal_fci_caches[signo] = fci_cache; - - // use user settings - SwooleG.use_signalfd = SwooleG.enable_signalfd; - - swSignal_add(signo, handler); - - RETURN_TRUE; -} - -static PHP_METHOD(swoole_process, alarm) -{ - zend_long usec; - zend_long type = ITIMER_REAL; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &usec, &type) == FAILURE) - { - RETURN_FALSE; - } - - if (!SWOOLE_G(cli)) - { - php_swoole_fatal_error(E_ERROR, "cannot use %s::alarm here", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - RETURN_FALSE; - } - - if (SwooleTG.timer) - { - php_swoole_fatal_error(E_WARNING, "cannot use both 'timer' and 'alarm' at the same time"); - RETURN_FALSE; - } - - struct itimerval timer_set = {}; - - if (usec > 0) - { - long _sec = usec / 1000000; - long _usec = usec - (_sec * 1000000); - - timer_set.it_interval.tv_sec = _sec; - timer_set.it_interval.tv_usec = _usec; - - timer_set.it_value.tv_sec = _sec; - timer_set.it_value.tv_usec = _usec; - - if (timer_set.it_value.tv_usec > 1e6) - { - timer_set.it_value.tv_usec = timer_set.it_value.tv_usec - 1e6; - timer_set.it_value.tv_sec += 1; - } - } - - if (setitimer(type, &timer_set, NULL) < 0) - { - php_swoole_sys_error(E_WARNING, "setitimer() failed"); - RETURN_FALSE; - } - - RETURN_TRUE; -} - -/** - * safe signal - */ -static void php_swoole_onSignal(int signo) -{ - zend_fcall_info_cache *fci_cache = signal_fci_caches[signo]; - - if (fci_cache) - { - zval zsigno; - - ZVAL_LONG(&zsigno, signo); - - if (UNEXPECTED(sw_zend_call_function_ex2(NULL, fci_cache, 1, &zsigno, NULL) != SUCCESS)) - { - php_swoole_fatal_error(E_WARNING, "%s: signal [%d] handler error", ZSTR_VAL(swoole_process_ce->name), signo); - } - } -} - -zend_bool php_swoole_signal_isset_handler(int signo) -{ - if (signo < 0 || signo >= SW_SIGNO_MAX) - { - php_swoole_fatal_error(E_WARNING, "invalid signal number [%d]", signo); - return 0; - } - return signal_fci_caches[signo] != NULL; -} - -void php_swoole_process_clean() -{ - for (int i = 0; i < SW_SIGNO_MAX; i++) - { - zend_fcall_info_cache *fci_cache = signal_fci_caches[i]; - if (fci_cache) - { - sw_zend_fci_cache_discard(fci_cache); - efree(fci_cache); - signal_fci_caches[i] = NULL; - } - } - - if (SwooleG.process_type != SW_PROCESS_USERWORKER) - { - SwooleG.process_type = 0; - } -} - -int php_swoole_process_start(swWorker *process, zval *zobject) -{ - zval *zcallback = sw_zend_read_property(swoole_process_ce, zobject, ZEND_STRL("callback"), 0); - zend_fcall_info_cache fci_cache; - - if (!sw_zend_is_callable_ex(zcallback, NULL, 0, NULL, 0, &fci_cache, NULL)) - { - php_swoole_fatal_error(E_ERROR, "Illegal callback function of %s", SW_Z_OBJCE_NAME_VAL_P(zobject)); - return SW_ERR; - } - - zend::process *proc = (zend::process *) process->ptr2; - - process->pipe_current = process->pipe_worker; - process->pid = getpid(); - - if (process->redirect_stdin) - { - if (dup2(process->pipe_current->fd, STDIN_FILENO) < 0) - { - php_swoole_sys_error(E_WARNING, "dup2() failed"); - } - } - - if (process->redirect_stdout) - { - if (dup2(process->pipe_current->fd, STDOUT_FILENO) < 0) - { - php_swoole_sys_error(E_WARNING, "dup2() failed"); - } - } - - if (process->redirect_stderr) - { - if (dup2(process->pipe_current->fd, STDERR_FILENO) < 0) - { - php_swoole_sys_error(E_WARNING, "dup2() failed"); - } - } - - php_swoole_process_clean(); - SwooleWG.id = process->id; - SwooleWG.worker = process; - - zend_update_property_long(swoole_process_ce, zobject, ZEND_STRL("pid"), process->pid); - if (process->pipe_current) - { - zend_update_property_long(swoole_process_ce, zobject, ZEND_STRL("pipe"), process->pipe_current->fd); - } - //eventloop create - if (proc->enable_coroutine && php_swoole_reactor_init() < 0) - { - return SW_ERR; - } - //main function - if (UNEXPECTED(!zend::function::call(&fci_cache, 1, zobject, NULL, 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(); - } - // equivalent to exit - sw_zend_bailout(); - - return SW_OK; -} - -static PHP_METHOD(swoole_process, start) -{ - swWorker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); - - if (process->pid && swoole_kill(process->pid, 0) == 0) - { - php_swoole_fatal_error(E_WARNING, "process has already been started"); - RETURN_FALSE; - } - - pid_t pid = swoole_fork(0); - if (pid < 0) - { - php_swoole_sys_error(E_WARNING, "fork() failed"); - RETURN_FALSE; - } - else if (pid > 0) - { - process->pid = pid; - process->child_process = 0; - zend_update_property_long(swoole_server_ce, ZEND_THIS, ZEND_STRL("pid"), process->pid); - RETURN_LONG(pid); - } - else - { - process->child_process = 1; - SW_CHECK_RETURN(php_swoole_process_start(process, ZEND_THIS)); - } - RETURN_TRUE; -} - -static PHP_METHOD(swoole_process, read) -{ - long buf_size = 8192; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &buf_size) == FAILURE) - { - RETURN_FALSE; - } - - if (buf_size > 65536) - { - buf_size = 65536; - } - - swWorker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); - - if (process->pipe_current == nullptr) - { - php_swoole_fatal_error(E_WARNING, "no pipe, cannot read from pipe"); - RETURN_FALSE; - } - - zend_string *buf = zend_string_alloc(buf_size, 0); - ssize_t ret = read(process->pipe_current->fd, buf->val, buf_size);; - if (ret < 0) - { - efree(buf); - if (errno != EINTR) - { - php_swoole_sys_error(E_WARNING, "read() failed"); - } - RETURN_FALSE; - } - buf->val[ret] = 0; - buf->len = ret; - RETURN_STR(buf); -} - -static PHP_METHOD(swoole_process, write) -{ - char *data = NULL; - size_t data_len = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &data, &data_len) == FAILURE) - { - RETURN_FALSE; - } - - if (data_len < 1) - { - php_swoole_fatal_error(E_WARNING, "the data to send is empty"); - RETURN_FALSE; - } - - swWorker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); - if (process->pipe_current == nullptr) - { - php_swoole_fatal_error(E_WARNING, "no pipe, cannot write into pipe"); - RETURN_FALSE; - } - - int ret; - - //async write - if (SwooleTG.reactor) - { - if (process->pipe_current->nonblock) - { - ret = swoole_event_write(process->pipe_current, data, (size_t) data_len); - } - else - { - goto _blocking_read; - } - } - else - { - _blocking_read: - ret = swSocket_write_blocking(process->pipe_current, data, data_len); - } - - if (ret < 0) - { - php_swoole_sys_error(E_WARNING, "write() failed"); - RETURN_FALSE; - } - ZVAL_LONG(return_value, ret); -} - -/** - * export Swoole\Coroutine\Socket object - */ -static PHP_METHOD(swoole_process, exportSocket) -{ - swWorker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); - if (process->pipe_current == nullptr) - { - php_swoole_fatal_error(E_WARNING, "no pipe, cannot export stream"); - RETURN_FALSE; - } - zend::process *proc = (zend::process *) process->ptr2; - if (!proc->zsocket) - { - proc->zsocket = php_swoole_dup_socket(process->pipe_current->fd, proc->pipe_type == zend::PIPE_TYPE_STREAM ? SW_SOCK_UNIX_STREAM : SW_SOCK_UNIX_DGRAM); - if (!proc->zsocket) - { - RETURN_FALSE; - } - } - GC_ADDREF(proc->zsocket); - RETURN_OBJ(proc->zsocket); -} - -static PHP_METHOD(swoole_process, push) -{ - char *data; - size_t length; - - struct - { - long type; - char data[SW_MSGMAX]; - } message; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &data, &length) == FAILURE) - { - RETURN_FALSE; - } - - if (length <= 0) - { - php_swoole_fatal_error(E_WARNING, "the data to push is empty"); - RETURN_FALSE; - } - else if (length >= sizeof(message.data)) - { - php_swoole_fatal_error(E_WARNING, "the data to push is too big"); - RETURN_FALSE; - } - - swWorker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); - - if (!process->queue) - { - php_swoole_fatal_error(E_WARNING, "no msgqueue, cannot use push()"); - RETURN_FALSE; - } - - message.type = process->id + 1; - memcpy(message.data, data, length); - - if (swMsgQueue_push(process->queue, (swQueue_data *)&message, length) < 0) - { - RETURN_FALSE; - } - RETURN_TRUE; -} - -static PHP_METHOD(swoole_process, pop) -{ - long maxsize = SW_MSGMAX; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &maxsize) == FAILURE) - { - RETURN_FALSE; - } - - if (maxsize > SW_MSGMAX || maxsize <= 0) - { - maxsize = SW_MSGMAX; - } - - swWorker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); - if (!process->queue) - { - php_swoole_fatal_error(E_WARNING, "no msgqueue, cannot use pop()"); - RETURN_FALSE; - } - - struct - { - long type; - char data[SW_MSGMAX]; - } message; - - if (process->ipc_mode == 2) - { - message.type = 0; - } - else - { - message.type = process->id + 1; - } - - int n = swMsgQueue_pop(process->queue, (swQueue_data *) &message, maxsize); - if (n < 0) - { - RETURN_FALSE; - } - RETURN_STRINGL(message.data, n); -} - -static PHP_METHOD(swoole_process, exec) -{ - char *execfile = NULL; - size_t execfile_len = 0; - zval *args; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sa", &execfile, &execfile_len, &args) == FAILURE) - { - RETURN_FALSE; - } - - if (execfile_len < 1) - { - php_swoole_fatal_error(E_WARNING, "exec file name is empty"); - RETURN_FALSE; - } - - int exec_argc = php_swoole_array_length(args); - char **exec_args = (char **) emalloc(sizeof(char*) * (exec_argc + 2)); - - zval *value = NULL; - exec_args[0] = sw_strdup(execfile); - int i = 1; - - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(args), value) - convert_to_string(value); - Z_TRY_ADDREF_P(value); - exec_args[i] = Z_STRVAL_P(value); - i++; - SW_HASHTABLE_FOREACH_END(); - exec_args[i] = NULL; - - if (execv(execfile, exec_args) < 0) - { - php_swoole_sys_error(E_WARNING, "execv(%s) failed", execfile); - RETURN_FALSE; - } - else - { - RETURN_TRUE; - } -} - -static PHP_METHOD(swoole_process, daemon) -{ - zend_bool nochdir = 1; - zend_bool noclose = 1; - zval *zpipes = NULL; - - ZEND_PARSE_PARAMETERS_START(0, 3) - Z_PARAM_OPTIONAL - Z_PARAM_BOOL(nochdir) - Z_PARAM_BOOL(noclose) - Z_PARAM_ARRAY(zpipes) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - zval *elem; - int fd = 0; - - if (zpipes) - { - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zpipes), elem) - { - if (!ZVAL_IS_NULL(elem)) - { - int new_fd = swoole_convert_to_fd(elem); - if (new_fd >= 0) - { - if (dup2(new_fd, fd) < 0) - { - swSysWarn("dup2(%d, %d) failed", new_fd, fd); - } - } - } - if (fd++ == 2) - { - break; - } - } - ZEND_HASH_FOREACH_END(); - } - - RETURN_BOOL(swoole_daemon(nochdir, noclose) == 0); -} - -#ifdef HAVE_CPU_AFFINITY -static PHP_METHOD(swoole_process, setaffinity) -{ - zval *array; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &array) == FAILURE) - { - RETURN_FALSE; - } - if (php_swoole_array_length(array) == 0) - { - 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; - } - - zval *value = NULL; - cpu_set_t 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; - } - CPU_SET(Z_LVAL_P(value), &cpu_set); - SW_HASHTABLE_FOREACH_END(); - -#ifdef __FreeBSD__ - if (cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, - sizeof(cpu_set), &cpu_set) < 0) -#else - if (sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set) < 0) -#endif - { - php_swoole_sys_error(E_WARNING, "sched_setaffinity() failed"); - RETURN_FALSE; - } - RETURN_TRUE; -} -#endif - -static PHP_METHOD(swoole_process, exit) -{ - long ret_code = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &ret_code) == FAILURE) - { - RETURN_FALSE; - } - - swWorker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); - - if (getpid() != process->pid) - { - php_swoole_fatal_error(E_WARNING, "not current process"); - RETURN_FALSE; - } - - if (ret_code < 0 || ret_code > 255) - { - php_swoole_fatal_error(E_WARNING, "exit ret_code range is [>0 and <255] "); - ret_code = 1; - } - - close(process->pipe_current->fd); - process->pipe_current->fd = -1; - - SwooleG.running = 0; - - if (ret_code == 0) - { - sw_zend_bailout(); - } - else - { - exit(ret_code); - } -} - -static PHP_METHOD(swoole_process, close) -{ - long which = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &which) == FAILURE) - { - RETURN_FALSE; - } - - swWorker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); - if (process->pipe_current == nullptr) - { - php_swoole_fatal_error(E_WARNING, "no pipe, cannot close the pipe"); - RETURN_FALSE; - } - - if (process->pipe_object == nullptr) - { - php_swoole_fatal_error(E_WARNING, "cannot close the pipe"); - RETURN_FALSE; - } - - int ret; - if (which == SW_PIPE_CLOSE_READ) - { - ret = shutdown(process->pipe_current->fd, SHUT_RD); - } - else if (which == SW_PIPE_CLOSE_WRITE) - { - ret = shutdown(process->pipe_current->fd, SHUT_WR); - } - else - { - ret = swPipeUnsock_close_ext(process->pipe_object, which); - } - if (ret < 0) - { - php_swoole_sys_error(E_WARNING, "close() failed"); - RETURN_FALSE; - } - if (which == 0) - { - process->pipe_current = nullptr; - efree(process->pipe_object); - process->pipe_object = nullptr; - } - RETURN_TRUE; -} - -static PHP_METHOD(swoole_process, set) -{ - zval *zset = NULL; - HashTable *vht = NULL; - zval *ztmp; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY(zset) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - vht = Z_ARRVAL_P(zset); - - swWorker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); - zend::process *proc = (zend::process *) process->ptr2; - - if (php_swoole_array_get_value(vht, "enable_coroutine", ztmp)) - { - proc->enable_coroutine = zval_is_true(ztmp); - } -} - -static PHP_METHOD(swoole_process, setTimeout) -{ - double seconds; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &seconds) == FAILURE) - { - RETURN_FALSE; - } - - swWorker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); - if (process->pipe_current == nullptr) - { - php_swoole_fatal_error(E_WARNING, "no pipe, cannot setTimeout the pipe"); - RETURN_FALSE; - } - SW_CHECK_RETURN(swSocket_set_timeout(process->pipe_current, seconds)); -} - -static PHP_METHOD(swoole_process, setBlocking) -{ - zend_bool blocking; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "b", &blocking) == FAILURE) - { - RETURN_FALSE; - } - - swWorker *process = php_swoole_process_get_and_check_worker(ZEND_THIS); - if (process->pipe_current == nullptr) - { - php_swoole_fatal_error(E_WARNING, "no pipe, cannot setBlocking the pipe"); - RETURN_FALSE; - } - if (blocking) - { - swSocket_set_block(process->pipe_current); - } - else - { - swSocket_set_nonblock(process->pipe_current); - } -} diff --git a/swoole_process_pool.cc b/swoole_process_pool.cc deleted file mode 100644 index f972aa2ddc5..00000000000 --- a/swoole_process_pool.cc +++ /dev/null @@ -1,690 +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" - -using namespace swoole; - -typedef struct -{ - zend_fcall_info_cache *onStart; - zend_fcall_info_cache *onWorkerStart; - zend_fcall_info_cache *onWorkerStop; - zend_fcall_info_cache *onMessage; - bool enable_coroutine; -} process_pool_property; - -static zend_class_entry *swoole_process_pool_ce; -static zend_object_handlers swoole_process_pool_handlers; -static swProcessPool *current_pool; - -typedef struct -{ - swProcessPool *pool; - process_pool_property *pp; - zend_object std; -} process_pool_t; - -static sw_inline process_pool_t* php_swoole_process_pool_fetch_object(zend_object *obj) -{ - return (process_pool_t *) ((char *) obj - swoole_process_pool_handlers.offset); -} - -static sw_inline swProcessPool * php_swoole_process_pool_get_pool(zval *zobject) -{ - return php_swoole_process_pool_fetch_object(Z_OBJ_P(zobject))->pool; -} - -static sw_inline swProcessPool * php_swoole_process_pool_get_and_check_pool(zval *zobject) -{ - swProcessPool *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 void php_swoole_process_pool_set_pool(zval *zobject, swProcessPool *pool) -{ - php_swoole_process_pool_fetch_object(Z_OBJ_P(zobject))->pool = pool; -} - -static sw_inline process_pool_property * php_swoole_process_pool_get_pp(zval *zobject) -{ - return php_swoole_process_pool_fetch_object(Z_OBJ_P(zobject))->pp; -} - -static sw_inline process_pool_property * php_swoole_process_pool_get_and_check_pp(zval *zobject) -{ - process_pool_property *pp = php_swoole_process_pool_get_pp(zobject); - if (!pp) - { - php_swoole_fatal_error(E_ERROR, "you must call Process\\Pool constructor first"); - } - return pp; -} - -static sw_inline void php_swoole_process_pool_set_pp(zval *zobject, process_pool_property *pp) -{ - php_swoole_process_pool_fetch_object(Z_OBJ_P(zobject))->pp = pp; -} - -static void php_swoole_process_pool_free_object(zend_object *object) -{ - process_pool_t *process_pool = php_swoole_process_pool_fetch_object(object); - - swProcessPool *pool = process_pool->pool; - if (pool) - { - efree(pool->ptr); - efree(pool); - } - - process_pool_property *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); - } - - zend_object_std_dtor(object); -} - -static zend_object *php_swoole_process_pool_create_object(zend_class_entry *ce) -{ - process_pool_t *process_pool = (process_pool_t *) ecalloc(1, sizeof(process_pool_t) + zend_object_properties_size(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; -} - -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 PHP_METHOD(swoole_process_pool, __construct); -static PHP_METHOD(swoole_process_pool, __destruct); -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, getProcess); -static PHP_METHOD(swoole_process_pool, start); -static PHP_METHOD(swoole_process_pool, shutdown); - -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_FE_END -}; - -void php_swoole_process_pool_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY(swoole_process_pool, "Swoole\\Process\\Pool", "swoole_process_pool", NULL, swoole_process_pool_methods); - SW_SET_CLASS_SERIALIZABLE(swoole_process_pool, zend_class_serialize_deny, zend_class_unserialize_deny); - 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, process_pool_t, std); - - zend_declare_property_long(swoole_process_pool_ce, ZEND_STRL("master_pid"), -1, ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_process_pool_ce, ZEND_STRL("workers"), ZEND_ACC_PUBLIC); -} - -static void pool_onWorkerStart(swProcessPool *pool, int worker_id) -{ - zval *zobject = (zval *) pool->ptr; - process_pool_property *pp = php_swoole_process_pool_get_and_check_pp(zobject); - - php_swoole_process_clean(); - SwooleWG.id = worker_id; - current_pool = pool; - - //main function - if (!pp->onWorkerStart) - { - return; - } - //eventloop create - if (pp->enable_coroutine && php_swoole_reactor_init() < 0) - { - return; - } - zval args[2]; - args[0] = *zobject; - ZVAL_LONG(&args[1], worker_id); - if (UNEXPECTED(!zend::function::call(pp->onWorkerStart, 2, args, NULL, pp->enable_coroutine))) - { - php_swoole_error(E_WARNING, "%s->onWorkerStart handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); - } - //eventloop start - if (pp->enable_coroutine) - { - php_swoole_event_wait(); - } -} - -static void pool_onMessage(swProcessPool *pool, char *data, uint32_t length) -{ - zval *zobject = (zval *) pool->ptr; - process_pool_property *pp = php_swoole_process_pool_get_and_check_pp(zobject); - zval args[2]; - - args[0] = *zobject; - ZVAL_STRINGL(&args[1], data, length); - - if (UNEXPECTED(!zend::function::call(pp->onMessage, 2, args, NULL, false))) - { - php_swoole_error(E_WARNING, "%s->onMessage handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); - } - - zval_ptr_dtor(&args[1]); -} - -static void pool_onWorkerStop(swProcessPool *pool, int worker_id) -{ - zval *zobject = (zval *) pool->ptr; - process_pool_property *pp = php_swoole_process_pool_get_and_check_pp(zobject); - zval args[2]; - - if (pp->onWorkerStop == NULL) - { - return; - } - - args[0] = *zobject; - ZVAL_LONG(&args[1], worker_id); - - if (UNEXPECTED(!zend::function::call(pp->onWorkerStop, 2, args, NULL, false))) - { - php_swoole_error(E_WARNING, "%s->onWorkerStop handler error", SW_Z_OBJCE_NAME_VAL_P(zobject)); - } -} - -static void pool_signal_handler(int sig) -{ - switch (sig) - { - case SIGTERM: - SwooleG.running = 0; - break; - case SIGUSR1: - case SIGUSR2: - current_pool->reloading = 1; - current_pool->reload_init = 0; - break; - default: - break; - } -} - -static PHP_METHOD(swoole_process_pool, __construct) -{ - zval *zobject = ZEND_THIS; - zend_long worker_num; - zend_long ipc_type = SW_IPC_NONE; - zend_long msgq_key = 0; - zend_bool enable_coroutine = 0; - - //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)); - RETURN_FALSE; - } - - if (SwooleG.serv) - { - php_swoole_fatal_error(E_ERROR, "%s cannot use in server process", SW_Z_OBJCE_NAME_VAL_P(zobject)); - RETURN_FALSE; - } - - 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"); - 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) - ); - } - - swProcessPool *pool = (swProcessPool *) emalloc(sizeof(swProcessPool)); - if (swProcessPool_create(pool, worker_num, (key_t) msgq_key, 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); - - if (enable_coroutine) - { - pool->main_loop = nullptr; - } - else - { - if (ipc_type > 0) - { - if (swProcessPool_set_protocol(pool, 0, SW_INPUT_BUFFER_SIZE) < 0) - { - zend_throw_exception_ex(swoole_exception_ce, errno, "failed to create process pool"); - RETURN_FALSE; - } - } - } - - process_pool_property *pp = (process_pool_property *) ecalloc(1, sizeof(process_pool_property)); - pp->enable_coroutine = enable_coroutine; - php_swoole_process_pool_set_pp(zobject, pp); - php_swoole_process_pool_set_pool(zobject, pool); -} - -static PHP_METHOD(swoole_process_pool, set) -{ - zval *zset = NULL; - HashTable *vht = NULL; - zval *ztmp; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY(zset) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - vht = Z_ARRVAL_P(zset); - - process_pool_property *pp = php_swoole_process_pool_get_and_check_pp(ZEND_THIS); - - if (php_swoole_array_get_value(vht, "enable_coroutine", ztmp)) - { - pp->enable_coroutine = zval_is_true(ztmp); - } -} - -static PHP_METHOD(swoole_process_pool, on) -{ - char *name; - size_t l_name; - - zend_fcall_info fci; - zend_fcall_info_cache fci_cache; - - swProcessPool *pool = php_swoole_process_pool_get_and_check_pool(ZEND_THIS); - - if (pool->started > 0) - { - php_swoole_fatal_error(E_WARNING, "process pool is started. unable to register event callback function"); - RETURN_FALSE; - } - - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 2, 2) - Z_PARAM_STRING(name, l_name) - Z_PARAM_FUNC(fci, fci_cache); - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - process_pool_property *pp = php_swoole_process_pool_get_and_check_pp(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)); - } - *pp->onWorkerStart = fci_cache; - sw_zend_fci_cache_persist(pp->onWorkerStart); - RETURN_TRUE; - } - 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"); - 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)); - } - *pp->onMessage = fci_cache; - sw_zend_fci_cache_persist(pp->onMessage); - RETURN_TRUE; - } - 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)); - } - *pp->onWorkerStop = fci_cache; - sw_zend_fci_cache_persist(pp->onWorkerStop); - RETURN_TRUE; - } - 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)); - } - *pp->onStart = fci_cache; - sw_zend_fci_cache_persist(pp->onStart); - RETURN_TRUE; - } - else - { - php_swoole_error(E_WARNING, "unknown event type[%s]", name); - RETURN_FALSE; - } -} - -static PHP_METHOD(swoole_process_pool, listen) -{ - char *host; - size_t l_host; - zend_long port = 0; - zend_long backlog = 2048; - - swProcessPool *pool = php_swoole_process_pool_get_and_check_pool(ZEND_THIS); - - if (pool->started > 0) - { - php_swoole_fatal_error(E_WARNING, "process pool is started. unable to listen"); - RETURN_FALSE; - } - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ll", &host, &l_host, &port, &backlog) == FAILURE) - { - RETURN_FALSE; - } - - if (pool->ipc_mode != SW_IPC_SOCKET) - { - php_swoole_fatal_error(E_WARNING, "unsupported ipc type[%d]", pool->ipc_mode); - RETURN_FALSE; - } - - int ret; - //unix socket - if (SW_STRCASECT(host, l_host, "unix:/")) - { - ret = swProcessPool_create_unix_socket(pool, host + 5, backlog); - } - else - { - ret = swProcessPool_create_tcp_socket(pool, host, port, backlog); - } - - swoole_fcntl_set_option(pool->stream->socket->fd, 0, 1); - pool->stream->socket->nonblock = 0; - pool->stream->socket->cloexec = 1; - - SW_CHECK_RETURN(ret); -} - -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; - } - - swProcessPool *pool = php_swoole_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; - } - if (length == 0) - { - RETURN_FALSE; - } - SW_CHECK_RETURN(swProcessPool_response(pool, data, length)); -} - -static PHP_METHOD(swoole_process_pool, start) -{ - swProcessPool *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"); - RETURN_FALSE; - } - - swoole_event_free(); - - process_pool_property *pp = php_swoole_process_pool_get_and_check_pp(ZEND_THIS); - - SwooleG.use_signalfd = 0; - - swSignal_add(SIGTERM, pool_signal_handler); - swSignal_add(SIGUSR1, pool_signal_handler); - swSignal_add(SIGUSR2, pool_signal_handler); - - if (pool->ipc_mode == SW_IPC_NONE || pp->enable_coroutine) - { - if (pp->onWorkerStart == NULL) - { - php_swoole_fatal_error(E_ERROR, "require onWorkerStart callback"); - RETURN_FALSE; - } - } - else - { - if (pp->onMessage == NULL) - { - 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, ZEND_THIS, ZEND_STRL("master_pid"), getpid()); - - if (swProcessPool_start(pool) < 0) - { - RETURN_FALSE; - } - - current_pool = pool; - - if (pp->onStart) - { - zval args[1]; - args[0] = *ZEND_THIS; - if (UNEXPECTED(!zend::function::call(pp->onStart, 1, args, NULL, 0))) - { - php_swoole_error(E_WARNING, "%s->onStart handler error", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - } - } - - swProcessPool_wait(pool); - swProcessPool_shutdown(pool); -} - -extern void php_swoole_process_set_worker(zval *zobject, swWorker *worker); - -static PHP_METHOD(swoole_process_pool, getProcess) -{ - long worker_id = -1; - - if (current_pool == NULL) - { - RETURN_FALSE; - } - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &worker_id) == FAILURE) - { - RETURN_FALSE; - } - - if (worker_id >= current_pool->worker_num) - { - php_swoole_error(E_WARNING, "invalid worker_id[%ld]", worker_id); - RETURN_FALSE; - } - else if (worker_id < 0) - { - worker_id = SwooleWG.id; - } - - zval *zworkers = sw_zend_read_and_convert_property_array(swoole_process_pool_ce, ZEND_THIS, ZEND_STRL("workers"), 0); - zval *zprocess = zend_hash_index_find(Z_ARRVAL_P(zworkers), worker_id); - zval zobject; - - if (zprocess == nullptr || ZVAL_IS_NULL(zprocess)) - { - zprocess = &zobject; - /** - * Separation from shared memory - */ - swWorker *worker = (swWorker *) emalloc(sizeof(swWorker)); - *worker = current_pool->workers[worker_id]; - - object_init_ex(zprocess, swoole_process_ce); - zend_update_property_long(swoole_process_ce, zprocess, ZEND_STRL("id"), SwooleWG.id); - zend_update_property_long(swoole_process_ce, zprocess, ZEND_STRL("pid"), worker->pid); - if (current_pool->ipc_mode == SW_IPC_UNIXSOCK) - { - //current process - if (worker->id == SwooleWG.id) - { - worker->pipe_current = worker->pipe_worker; - } - else - { - worker->pipe_current = worker->pipe_master; - } - /** - * Forbidden to close pipe in the php layer - */ - worker->pipe_object = nullptr; - zend_update_property_long(swoole_process_ce, zprocess, ZEND_STRL("pipe"), worker->pipe_current->fd); - } - php_swoole_process_set_worker(zprocess, worker); - process_pool_property *pp = php_swoole_process_pool_get_and_check_pp(ZEND_THIS); - zend::process *proc = new zend::process(zend::PIPE_TYPE_STREAM, pp->enable_coroutine); - worker->ptr2 = proc; - (void) add_index_zval(zworkers, worker_id, zprocess); - } - - RETURN_ZVAL(zprocess, 1, 0); -} - -static PHP_METHOD(swoole_process_pool, shutdown) -{ - zval *retval = sw_zend_read_property(swoole_process_pool_ce, ZEND_THIS, ZEND_STRL("master_pid"), 0); - long pid = zval_get_long(retval); - RETURN_BOOL(swoole_kill(pid, SIGTERM) == 0); -} - -static PHP_METHOD(swoole_process_pool, __destruct) { } diff --git a/swoole_redis_coro.cc b/swoole_redis_coro.cc deleted file mode 100644 index 1419cff03c7..00000000000 --- a/swoole_redis_coro.cc +++ /dev/null @@ -1,4892 +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 "thirdparty/hiredis/async.h" - -#include "ext/standard/php_var.h" - -using namespace swoole; -using swoole::coroutine::Socket; - -#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 */ -#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() - -#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, redis->zobject, ZEND_STRL("connected"), 0); - if (!(socket && socket->has_bound())) - { - redisFreeKeepFd(redis->context); - redis->context = NULL; - 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 *) ecalloc(1, sizeof(swRedisClient) + zend_object_properties_size(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(swoole_redis_coro_ce, zobject, ZEND_STRL("host"), 0); - zval *zport = sw_zend_read_property(swoole_redis_coro_ce, zobject, ZEND_STRL("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, zobject, ZEND_STRL("errType"), SW_REDIS_ERR_ALLOC); - zend_update_property_long(swoole_redis_coro_ce, zobject, ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_ALLOC)); - zend_update_property_string(swoole_redis_coro_ce, zobject, ZEND_STRL("errMsg"), "cannot allocate redis context"); - return false; - } - if (context->err) - { - zend_update_property_long(swoole_redis_coro_ce, zobject, ZEND_STRL("errType"), context->err); - zend_update_property_long(swoole_redis_coro_ce, zobject, ZEND_STRL("errCode"), sw_redis_convert_err(context->err)); - zend_update_property_string(swoole_redis_coro_ce, 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, zobject, ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, zobject, ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, zobject, ZEND_STRL("errMsg"), "Can not found the connection"); - swoole_redis_coro_close(redis); - return false; - } - - socket->set_timeout(redis->timeout, SW_TIMEOUT_RDWR); - redis->reconnected_count = 0; - zend_update_property_bool(swoole_redis_coro_ce, zobject, ZEND_STRL("connected"), 1); - zend_update_property_long(swoole_redis_coro_ce, 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, redis->zobject, ZEND_STRL("errType"), SW_REDIS_ERR_CLOSED); - zend_update_property_long(swoole_redis_coro_ce, redis->zobject, ZEND_STRL("errCode"), socket->errCode); - zend_update_property_string(swoole_redis_coro_ce, 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, redis->zobject, ZEND_STRL("errType"), SW_REDIS_ERR_CLOSED); - // Notice: do not update errCode - zend_update_property_string(swoole_redis_coro_ce, 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, redis->zobject, ZEND_STRL("errType"), 0); - zend_update_property_long(swoole_redis_coro_ce, redis->zobject, ZEND_STRL("errCode"), 0); - zend_update_property_string(swoole_redis_coro_ce, 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, redis->zobject, ZEND_STRL("errType"), redis->context->err); - zend_update_property_long(swoole_redis_coro_ce, redis->zobject, ZEND_STRL("errCode"), sw_redis_convert_err(redis->context->err)); - zend_update_property_string(swoole_redis_coro_ce, 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, redis->zobject, ZEND_STRL("host"), p1); - zend_update_property_long(swoole_redis_coro_ce, 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, redis->zobject, ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, redis->zobject, ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, redis->zobject, ZEND_STRL("errMsg"), "Timeout value must be a LONG"); - efree(z_args); - RETURN_FALSE; - } - int j, tail; - tail = has_timeout ? argc - 2 : argc - 1; - for (j = 0; j < tail; ++j) - { - zend_string *convert_str = zval_get_string(&z_args[j]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - } - if(has_timeout) { - buf_len = sw_snprintf(buf, sizeof(buf), ZEND_LONG_FMT, SW_REDIS_COMMAND_ARGS_LVAL(z_args[tail])); - SW_REDIS_COMMAND_ARGV_FILL((char*)buf, buf_len); - } - } - efree(z_args); - - redis_request(redis, argc, argv, argvlen, return_value); -} - -static inline void sw_redis_command_key(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) -{ - char *key; - size_t key_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &key_len) == FAILURE) - { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[2]; - char *argv[2]; - int argc = 2; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - redis_request(redis, argc, argv, argvlen, return_value); - - if (redis->compatibility_mode) - { - if (ZVAL_IS_ARRAY(return_value) && sw_mem_equal(ZEND_STRL("HGETALL"), cmd, cmd_len)) - { - swoole_redis_handle_assoc_array_result(return_value, false); - } - else if (ZVAL_IS_NULL(return_value) && sw_mem_equal(ZEND_STRL("GET"), cmd, cmd_len)) - { - RETURN_FALSE; - } - } -} - -static sw_inline void sw_redis_command_key_var_val(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) -{ - int argc = ZEND_NUM_ARGS(); - // We at least need a key and one value - if (argc < 2) - { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - // Make sure we at least have a key, and we can get other args - SW_REDIS_COMMAND_ALLOC_ARGS_ARR - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) - { - efree(z_args); - RETURN_FALSE; - } - - int i = 0, j; - argc++; - SW_REDIS_COMMAND_ALLOC_ARGV - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - zend_string *convert_str = zval_get_string(&z_args[0]); - SW_REDIS_COMMAND_ARGV_FILL(ZSTR_VAL(convert_str), ZSTR_LEN(convert_str)) - zend_string_release(convert_str); - for (j = 1; j < argc - 1; ++j) - { - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(SW_REDIS_COMMAND_ARGS_REF(z_args[j])) - } - efree(z_args); - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_long_val(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) -{ - char *key; - size_t key_len; - long l_val; - zval *z_value; - if(zend_parse_parameters(ZEND_NUM_ARGS(), "slz", &key, &key_len, &l_val, &z_value) == FAILURE) - { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[4]; - char *argv[4]; - int argc = 4; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%ld", l_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_value) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_long_str(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) -{ - char *key, *val; - size_t key_len, val_len; - long l_val; - if(zend_parse_parameters(ZEND_NUM_ARGS(), "sls", &key, &key_len, &l_val, &val, &val_len)==FAILURE) - { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[4]; - char *argv[4]; - int argc = 4; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%ld", l_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - SW_REDIS_COMMAND_ARGV_FILL(val, val_len) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_long(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) -{ - char *key; - size_t key_len; - long l_val; - if(zend_parse_parameters(ZEND_NUM_ARGS(), "sl", &key, &key_len, &l_val)==FAILURE) - { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[3]; - char *argv[3]; - int argc = 3; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%ld", l_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_long_long(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) -{ - char *key; - size_t key_len; - long l1_val, l2_val; - if(zend_parse_parameters(ZEND_NUM_ARGS(), "sll", &key, &key_len, &l1_val, &l2_val)==FAILURE) - { - return; - } - SW_REDIS_COMMAND_CHECK - int i = 0; - size_t argvlen[4]; - char *argv[4]; - int argc = 4; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%ld", l1_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - sprintf(str, "%ld", l2_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_dbl(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) -{ - char *key; - size_t key_len; - double d_val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sd", &key, &key_len, &d_val) == FAILURE) - { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i =0; - size_t argvlen[3]; - char *argv[3]; - int argc = 3; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - char str[32]; - sprintf(str, "%f", d_val); - SW_REDIS_COMMAND_ARGV_FILL(str, strlen(str)) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_key(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) -{ - char *key1, *key2; - size_t key1_len, key2_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &key1, &key1_len, &key2, &key2_len) == FAILURE) - { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i =0; - size_t argvlen[3]; - char *argv[3]; - int argc = 3; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key1, key1_len) - SW_REDIS_COMMAND_ARGV_FILL(key2, key2_len) - redis_request(redis, argc, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_val(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) -{ - char *key; - size_t key_len; - zval *z_value; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &key, &key_len, &z_value) == FAILURE) - { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i =0; - size_t argvlen[3]; - char *argv[3]; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL_WITH_SERIALIZE(z_value) - redis_request(redis, 3, argv, argvlen, return_value); - - if (redis->compatibility_mode && ZVAL_IS_NULL(return_value) && strncmp("ZRANK", cmd, cmd_len) == 0) - { - RETURN_FALSE; - } -} - -static sw_inline void sw_redis_command_key_str(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) -{ - char *key, *val; - size_t key_len, val_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &key, &key_len, &val, &val_len) == FAILURE) - { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i =0; - size_t argvlen[3]; - char *argv[3]; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(val, val_len) - redis_request(redis, 3, argv, argvlen, return_value); -} - -static sw_inline void sw_redis_command_key_str_str(INTERNAL_FUNCTION_PARAMETERS, const char *cmd, int cmd_len) -{ - char *key, *val1, *val2; - size_t key_len, val1_len, val2_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss", &key, &key_len, &val1, &val1_len, &val2, &val2_len) == FAILURE) - { - RETURN_FALSE; - } - SW_REDIS_COMMAND_CHECK - int i =0; - size_t argvlen[4]; - char *argv[4]; - SW_REDIS_COMMAND_ARGV_FILL(cmd, cmd_len) - SW_REDIS_COMMAND_ARGV_FILL(key, key_len) - SW_REDIS_COMMAND_ARGV_FILL(val1, val1_len) - SW_REDIS_COMMAND_ARGV_FILL(val2, val2_len) - redis_request(redis, 4, argv, argvlen, return_value); -} - -static 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); -/*---------------------Redis Command End------------------------*/ - -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 -}; - -void php_swoole_redis_coro_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY(swoole_redis_coro, "Swoole\\Coroutine\\Redis", NULL, "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, SW_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 = NULL; - - 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 = Socket::default_connect_timeout; - redis->timeout = 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, zobject, ZEND_STRL("host"), host); - zend_update_property_long(swoole_redis_coro_ce, 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, zobject, ZEND_STRL("errType"), 0); - zend_update_property_long(swoole_redis_coro_ce, zobject, ZEND_STRL("errCode"), 0); - zend_update_property_string(swoole_redis_coro_ce, 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, redis->zobject, ZEND_STRL("errType"), redis->context->err); - zend_update_property_long(swoole_redis_coro_ce, redis->zobject, ZEND_STRL("errCode"), sw_redis_convert_err(redis->context->err)); - zend_update_property_string(swoole_redis_coro_ce, 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 = NULL, *set_type = NULL; - size_t key_len, argc = 3; - zval *z_value, *z_opts = NULL; - 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, ZEND_THIS, ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, 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 == NULL) - { - 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 == NULL) - { - 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 == NULL) { - 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), "%ld", 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), "%ld", start); - SW_REDIS_COMMAND_ARGV_FILL((char* )buf, buf_len) - buf_len = sw_snprintf(buf, sizeof(buf), "%ld", 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), "%ld", start); - SW_REDIS_COMMAND_ARGV_FILL((char* )buf, buf_len) - buf_len = sw_snprintf(buf, sizeof(buf), "%ld", 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=NULL; - HashTable *ht_keys, *ht_weights=NULL; - 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 != NULL) - { - ht_weights = Z_ARRVAL_P(z_weights); - if (zend_hash_num_elements(ht_weights) != keys_count) - { - zend_update_property_long(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, 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, ZEND_THIS, ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, 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 != NULL) { - 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, redis->zobject, ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, redis->zobject, ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, 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=NULL; - HashTable *ht_keys, *ht_weights=NULL; - 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 != NULL) { - ht_weights = Z_ARRVAL_P(z_weights); - if(zend_hash_num_elements(ht_weights) != keys_count) { - zend_update_property_long(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, 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, ZEND_THIS, ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, 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 != NULL) { - 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, ZEND_THIS, ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, 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, ZEND_THIS, ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, 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, ZEND_THIS, ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, 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, ZEND_THIS, ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, 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, ZEND_THIS, ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, 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=NULL, *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=NULL, *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), "%ld", 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), "%ld", 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, ZEND_THIS, ZEND_STRL("errType"), SW_REDIS_ERR_OTHER); - zend_update_property_long(swoole_redis_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), sw_redis_convert_err(SW_REDIS_ERR_OTHER)); - zend_update_property_string(swoole_redis_coro_ce, 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 = NULL; - 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 = NULL; - 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 = NULL; - 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 = NULL; - 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 = NULL; - 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, redis->zobject, ZEND_STRL("errType"), redis->context->err); - zend_update_property_long(swoole_redis_coro_ce, redis->zobject, ZEND_STRL("errCode"), sw_redis_convert_err(redis->context->err)); - zend_update_property_string(swoole_redis_coro_ce, 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, redis->zobject, ZEND_STRL("errType"), redis->context->err); - zend_update_property_long(swoole_redis_coro_ce, redis->zobject, ZEND_STRL("errCode"), sw_redis_convert_err(redis->context->err)); - zend_update_property_string(swoole_redis_coro_ce, 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/swoole_redis_server.cc b/swoole_redis_server.cc deleted file mode 100644 index 925ebc6a7f8..00000000000 --- a/swoole_redis_server.cc +++ /dev/null @@ -1,492 +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 "swoole_server.h" -#include "redis.h" - -#include -#include - -BEGIN_EXTERN_C() -#include "ext/standard/php_string.h" -END_EXTERN_C() - -using namespace swoole; -using namespace std; - -static zend_class_entry *swoole_redis_server_ce; -static zend_object_handlers swoole_redis_server_handlers; - -static swString *format_buffer; -static unordered_map redis_handlers; - -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); - -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_FE_END -}; - -void php_swoole_redis_server_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY_EX(swoole_redis_server, "Swoole\\Redis\\Server", "swoole_redis_server", NULL, swoole_redis_server_methods, swoole_server); - SW_SET_CLASS_SERIALIZABLE(swoole_redis_server, zend_class_serialize_deny, zend_class_unserialize_deny); - 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); -} - -void php_swoole_redis_server_rshutdown() -{ - for (auto i = redis_handlers.begin(); i != redis_handlers.end(); i++) - { - sw_zend_fci_cache_discard(&i->second); - } - redis_handlers.clear(); -} - -static int redis_onReceive(swServer *serv, swEventData *req) -{ - int fd = req->info.fd; - swConnection *conn = swWorker_get_connection(serv, fd); - if (!conn) - { - swWarn("connection[%d] is closed", fd); - return SW_ERR; - } - - swListenPort *port = swServer_get_port(serv, conn->fd); - //other server port - if (!port->open_redis_protocol) - { - return php_swoole_onReceive(serv, req); - } - - zval zdata; - php_swoole_get_recv_data(serv, &zdata, req); - char *p = Z_STRVAL(zdata); - char *pe = p + Z_STRLEN(zdata); - int ret; - int length = 0; - - zval zparams; - array_init(&zparams); - - int state = SW_REDIS_RECEIVE_TOTAL_LINE; - int add_param = 0; - char *command = NULL; - 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; - break; - } - /* no break */ - - case SW_REDIS_RECEIVE_LENGTH: - if (*p == '$' && (p = swRedis_get_number(p, &ret))) - { - if (ret == -1) - { - add_next_index_null(&zparams); - break; - } - length = ret; - state = SW_REDIS_RECEIVE_STRING; - break; - } - //integer - else if (*p == ':' && (p = swRedis_get_number(p, &ret))) - { - add_next_index_long(&zparams, ret); - break; - } - /* no break */ - - case SW_REDIS_RECEIVE_STRING: - if (add_param == 0) - { - command = p; - command_len = length; - add_param = 1; - } - else - { - add_next_index_stringl(&zparams, p, length); - } - p += length + SW_CRLF_LEN; - state = SW_REDIS_RECEIVE_LENGTH; - break; - - default: - break; - } - } while(p < pe); - - if (command_len >= SW_REDIS_MAX_COMMAND_SIZE) - { - php_swoole_error(E_WARNING, "command [%.8s...](length=%d) is too long", command, command_len); - serv->close(serv, fd, 0); - return SW_OK; - } - - char _command[SW_REDIS_MAX_COMMAND_SIZE]; - command[command_len] = 0; - size_t _command_len = sw_snprintf(_command, sizeof(_command), "_handler_%.*s", command_len, command); - php_strtolower(_command, _command_len); - - auto i = redis_handlers.find(string(_command, _command_len)); - if (i == redis_handlers.end()) - { - char err_msg[256]; - length = sw_snprintf(err_msg, sizeof(err_msg), "-ERR unknown command '%.*s'\r\n", command_len, command); - serv->send(serv, fd, err_msg, length); - return SW_OK; // TODO: return SW_ERR? - } - - zend_fcall_info_cache *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, SwooleG.enable_coroutine))) - { - php_swoole_error(E_WARNING, "%s->onRequest with command '%.*s' handler error", ZSTR_VAL(swoole_redis_server_ce->name), command_len, command); - } - - if (Z_TYPE_P(&retval) == IS_STRING) - { - serv->send(serv, fd, Z_STRVAL_P(&retval), Z_STRLEN_P(&retval)); - } - zval_ptr_dtor(&retval); - zval_ptr_dtor(&zdata); - zval_ptr_dtor(&zparams); - - 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->gs->start > 0) - { - 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; - - format_buffer = swString_new(SW_BUFFER_SIZE_STD); - if (!format_buffer) - { - php_swoole_fatal_error(E_ERROR, "[1] swString_new(%d) failed", SW_BUFFER_SIZE_STD); - RETURN_FALSE; - } - - 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->listen_list->front(); - - 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 (swServer_start(serv) < 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; - } - - 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, NULL, 0, &func_name, NULL, fci_cache, NULL)) - { - php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name); - return; - } - efree(func_name); - - char _command[SW_REDIS_MAX_COMMAND_SIZE]; - size_t _command_len = sw_snprintf(_command, sizeof(_command), "_handler_%s", command); - php_strtolower(_command, _command_len); - - zend_update_property(swoole_redis_server_ce, ZEND_THIS, _command, _command_len, zcallback); - - string key(_command, _command_len); - auto i = redis_handlers.find(key); - if (i != redis_handlers.end()) - { - sw_zend_fci_cache_discard(&i->second); - } - - sw_zend_fci_cache_persist(fci_cache); - redis_handlers[key] = *fci_cache; - - RETURN_TRUE; -} - -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; - } - - char _command[SW_REDIS_MAX_COMMAND_SIZE]; - size_t _command_len = sw_snprintf(_command, sizeof(_command), "_handler_%s", command); - php_strtolower(_command, _command_len); - - zval rv; - zval *handler = zend_read_property(swoole_redis_server_ce, ZEND_THIS, _command, _command_len, 1, &rv); - RETURN_ZVAL(handler, 1, 0); -} - -static PHP_METHOD(swoole_redis_server, format) -{ - zend_long type; - zval *value = NULL; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|z", &type, &value) == FAILURE) - { - RETURN_FALSE; - } - - char message[256]; - int length; - zval *item; - - 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) - { - convert_to_string(value); - length = sw_snprintf(message, sizeof(message), "+%.*s\r\n", (int)Z_STRLEN_P(value), Z_STRVAL_P(value)); - } - else - { - length = sw_snprintf(message, sizeof(message), "+%s\r\n", "OK"); - } - RETURN_STRINGL(message, length); - } - else if (type == SW_REDIS_REPLY_ERROR) - { - if (value) - { - convert_to_string(value); - length = sw_snprintf(message, sizeof(message), "-%.*s\r\n", (int)Z_STRLEN_P(value), Z_STRVAL_P(value)); - } - else - { - length = sw_snprintf(message, sizeof(message), "-%s\r\n", "ERR"); - } - RETURN_STRINGL(message, length); - } - else if (type == SW_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) - { - if (!value) - { - _no_value: - php_swoole_fatal_error(E_WARNING, "require more parameters"); - RETURN_FALSE; - } - convert_to_string(value); - if (Z_STRLEN_P(value) > SW_REDIS_MAX_STRING_SIZE || Z_STRLEN_P(value) < 1) - { - php_swoole_fatal_error(E_WARNING, "invalid string size"); - RETURN_FALSE; - } - swString_clear(format_buffer); - length = sw_snprintf(message, sizeof(message), "$%zu\r\n", Z_STRLEN_P(value)); - swString_append_ptr(format_buffer, message, length); - swString_append_ptr(format_buffer, Z_STRVAL_P(value), Z_STRLEN_P(value)); - swString_append_ptr(format_buffer, SW_CRLF, SW_CRLF_LEN); - RETURN_STRINGL(format_buffer->str, format_buffer->length); - } - else if (type == SW_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"); - } - swString_clear(format_buffer); - length = sw_snprintf(message, sizeof(message), "*%d\r\n", zend_hash_num_elements(Z_ARRVAL_P(value))); - swString_append_ptr(format_buffer, message, length); - - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(value), item) - zval _copy; - if (Z_TYPE_P(item) != IS_STRING) - { - _copy = *item; - zval_copy_ctor(&_copy); - item = &_copy; - } - convert_to_string(item); - length = sw_snprintf(message, sizeof(message), "$%zu\r\n", Z_STRLEN_P(item)); - swString_append_ptr(format_buffer, message, length); - swString_append_ptr(format_buffer, Z_STRVAL_P(item), Z_STRLEN_P(item)); - swString_append_ptr(format_buffer, SW_CRLF, SW_CRLF_LEN); - if (item == &_copy) - { - zval_dtor(item); - } - SW_HASHTABLE_FOREACH_END(); - - RETURN_STRINGL(format_buffer->str, format_buffer->length); - } - else if (type == SW_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"); - } - swString_clear(format_buffer); - length = sw_snprintf(message, sizeof(message), "*%d\r\n", 2 * zend_hash_num_elements(Z_ARRVAL_P(value))); - swString_append_ptr(format_buffer, message, length); - - char *key; - uint32_t keylen; - int keytype; - - SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(value), key, keylen, keytype, item) - if (key == NULL || keylen == 0) - { - continue; - } - zval _copy; - if (Z_TYPE_P(item) != IS_STRING) - { - _copy = *item; - zval_copy_ctor(&_copy); - item = &_copy; - } - convert_to_string(item); - length = sw_snprintf(message, sizeof(message), "$%d\r\n%s\r\n$%zu\r\n", keylen, key, Z_STRLEN_P(item)); - swString_append_ptr(format_buffer, message, length); - swString_append_ptr(format_buffer, Z_STRVAL_P(item), Z_STRLEN_P(item)); - swString_append_ptr(format_buffer, SW_CRLF, SW_CRLF_LEN); - - if (item == &_copy) - { - zval_dtor(item); - } - (void) keytype; - SW_HASHTABLE_FOREACH_END(); - - RETURN_STRINGL(format_buffer->str, format_buffer->length); - } - else - { - php_swoole_error(E_WARNING, "Unknown type[" ZEND_LONG_FMT "]", type); - RETURN_FALSE; - } -} diff --git a/swoole_runtime.cc b/swoole_runtime.cc deleted file mode 100644 index 07e5186e1d1..00000000000 --- a/swoole_runtime.cc +++ /dev/null @@ -1,1838 +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/php/standard/proc_open.h" -#include -#include - -/* openssl */ -#ifndef OPENSSL_NO_TLS1_METHOD -#define HAVE_TLS1 1 -#endif -#ifndef OPENSSL_NO_TLS1_1_METHOD -#define HAVE_TLS11 1 -#endif -#ifndef OPENSSL_NO_TLS1_2_METHOD -#define HAVE_TLS12 1 -#endif -#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(OPENSSL_NO_TLS1_3) -#define HAVE_TLS13 1 -#endif -#ifndef OPENSSL_NO_ECDH -#define HAVE_ECDH 1 -#endif -#ifndef OPENSSL_NO_TLSEXT -#define HAVE_TLS_SNI 1 -#if OPENSSL_VERSION_NUMBER >= 0x10002000L -#define HAVE_TLS_ALPN 1 -#endif -#endif -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) -#define HAVE_SEC_LEVEL 1 -#endif - -using namespace swoole; -using namespace std; -using swoole::coroutine::System; -using swoole::coroutine::Socket; - -extern "C" -{ -static PHP_METHOD(swoole_runtime, enableCoroutine); -static PHP_METHOD(swoole_runtime, getHookFlags); -static PHP_METHOD(swoole_runtime, setHookFlags); -static PHP_FUNCTION(swoole_sleep); -static PHP_FUNCTION(swoole_usleep); -static PHP_FUNCTION(swoole_time_nanosleep); -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); -} - -static int socket_set_option(php_stream *stream, int option, int value, void *ptrparam); -#if PHP_VERSION_ID < 70400 -static size_t socket_read(php_stream *stream, char *buf, size_t count); -static size_t socket_write(php_stream *stream, const char *buf, size_t count); -#else -static ssize_t socket_read(php_stream *stream, char *buf, size_t count); -static ssize_t socket_write(php_stream *stream, const char *buf, size_t count); -#endif -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); - -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 -{ - socket_write, - socket_read, - socket_close, - socket_flush, - "tcp_socket/coroutine", - NULL, /* seek */ - socket_cast, - socket_stat, - socket_set_option, -}; - -struct php_swoole_netstream_data_t -{ - php_netstream_data_t stream; - Socket *socket; -}; - -static bool hook_init = false; -static int hook_flags = 0; - -static struct -{ - php_stream_transport_factory tcp; - php_stream_transport_factory udp; - php_stream_transport_factory _unix; - php_stream_transport_factory udg; - php_stream_transport_factory ssl; - php_stream_transport_factory tls; -} ori_factory = { - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, -}; - -static php_stream_wrapper ori_php_plain_files_wrapper; - -#if PHP_VERSION_ID < 70200 -typedef void (*zif_handler)(INTERNAL_FUNCTION_PARAMETERS); -#endif - -#define SW_HOOK_FUNC(f) hook_func(ZEND_STRL(#f), PHP_FN(swoole_##f)) -#define SW_UNHOOK_FUNC(f) unhook_func(ZEND_STRL(#f)) - -static void hook_func(const char *name, size_t l_name, zif_handler handler = nullptr); -static void unhook_func(const char *name, size_t l_name); - -static zend_array *function_table = nullptr; - -extern "C" -{ -#include "ext/standard/file.h" -#include "thirdparty/php/streams/plain_wrapper.c" -} - -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) - PHP_FE_END -}; - -void php_swoole_runtime_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY_BASE(swoole_runtime, "Swoole\\Runtime", "swoole_runtime", NULL, swoole_runtime_methods, NULL); - SW_SET_CLASS_CREATE(swoole_runtime, sw_zend_create_object_deny); - - SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_TCP", SW_HOOK_TCP); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_UDP", SW_HOOK_UDP); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_UNIX", SW_HOOK_UNIX); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_UDG", SW_HOOK_UDG); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_SSL", SW_HOOK_SSL); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_TLS", SW_HOOK_TLS); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_STREAM_FUNCTION", SW_HOOK_STREAM_FUNCTION); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_STREAM_SELECT", SW_HOOK_STREAM_FUNCTION); // backward compatibility - SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_FILE", SW_HOOK_FILE); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_SLEEP", SW_HOOK_SLEEP); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_PROC", SW_HOOK_PROC); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_CURL", SW_HOOK_CURL); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_BLOCKING_FUNCTION", SW_HOOK_BLOCKING_FUNCTION); - SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_ALL", SW_HOOK_ALL); - - swoole_proc_open_init(module_number); -} - -struct real_func -{ - zend_function *function; - zif_handler ori_handler; - zend_fcall_info_cache *fci_cache; - zval name; -}; - -void php_swoole_runtime_rshutdown() -{ - if (!hook_init) - { - return; - } - - hook_init = false; - - void *ptr; - ZEND_HASH_FOREACH_PTR(function_table, ptr) - { - real_func *rf = static_cast(ptr); - /** - * php library function - */ - if (rf->fci_cache) - { - zval_dtor(&rf->name); - efree(rf->fci_cache); - } - rf->function->internal_function.handler = rf->ori_handler; - efree(rf); - } - ZEND_HASH_FOREACH_END(); - zend_hash_destroy(function_table); - efree(function_table); - function_table = nullptr; -} - -static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *portno, int get_err, zend_string **err) -{ - char *colon; - char *host = NULL; - char *p; - - if (*(str) == '[' && str_len > 1) - { - /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */ - p = (char*) memchr(str + 1, ']', str_len - 2); - if (!p || *(p + 1) != ':') - { - if (get_err) - { - *err = strpprintf(0, "Failed to parse IPv6 address \"%s\"", str); - } - return NULL; - } - *portno = atoi(p + 2); - return estrndup(str + 1, p - str - 1); - } - if (str_len) - { - colon = (char*) memchr(str, ':', str_len - 1); - } - else - { - colon = NULL; - } - if (colon) - { - *portno = atoi(colon + 1); - host = estrndup(str, colon - str); - } - else - { - if (get_err) - { - *err = strpprintf(0, "Failed to parse address \"%s\"", str); - } - return NULL; - } - - return host; -} - -#if PHP_VERSION_ID < 70400 -static size_t socket_write(php_stream *stream, const char *buf, size_t count) -#else -static ssize_t socket_write(php_stream *stream, const char *buf, size_t count) -#endif -{ - php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t *) stream->abstract; - if (UNEXPECTED(!abstract)) - { - return 0; - } - Socket *sock = (Socket*) abstract->socket; - ssize_t didwrite; - if (UNEXPECTED(!sock)) - { - return 0; - } - didwrite = sock->send_all(buf, count); - if (didwrite > 0) - { - php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), didwrite, 0); - } -#if PHP_VERSION_ID < 70400 - if (didwrite < 0) - { - didwrite = 0; - } -#endif - - return didwrite; -} - -#if PHP_VERSION_ID < 70400 -static size_t socket_read(php_stream *stream, char *buf, size_t count) -#else -static ssize_t socket_read(php_stream *stream, char *buf, size_t count) -#endif -{ - php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t *) stream->abstract; - if (UNEXPECTED(!abstract)) - { - return 0; - } - Socket *sock = (Socket*) abstract->socket; - ssize_t nr_bytes = 0; - if (UNEXPECTED(!sock)) - { - return 0; - } - 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 && swSocket_error(sock->errCode) == SW_CLOSE)); - if (nr_bytes > 0) - { - php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), nr_bytes, 0); - } - -#if PHP_VERSION_ID < 70400 - if (nr_bytes < 0) - { - nr_bytes = 0; - } -#endif - - return nr_bytes; -} - -static int socket_flush(php_stream *stream) -{ - return 0; -} - -static int socket_close(php_stream *stream, int close_handle) -{ - php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t *) stream->abstract; - if (UNEXPECTED(!abstract)) - { - return FAILURE; - } - /** set it null immediately */ - stream->abstract = NULL; - 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); - return SUCCESS; -} - -enum -{ - STREAM_XPORT_OP_BIND, - STREAM_XPORT_OP_CONNECT, - STREAM_XPORT_OP_LISTEN, - STREAM_XPORT_OP_ACCEPT, - STREAM_XPORT_OP_CONNECT_ASYNC, - STREAM_XPORT_OP_GET_NAME, - STREAM_XPORT_OP_GET_PEER_NAME, - STREAM_XPORT_OP_RECV, - STREAM_XPORT_OP_SEND, - STREAM_XPORT_OP_SHUTDOWN, -}; - -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)) - { - return FAILURE; - } - - switch (castas) - { - case PHP_STREAM_AS_STDIO: - if (ret) - { - *(FILE**) ret = fdopen(sock->get_fd(), stream->mode); - if (*ret) - { - return SUCCESS; - } - return FAILURE; - } - return SUCCESS; - case PHP_STREAM_AS_FD_FOR_SELECT: - case PHP_STREAM_AS_FD: - case PHP_STREAM_AS_SOCKETD: - if (ret) - *(php_socket_t *) ret = sock->get_fd(); - return SUCCESS; - default: - return FAILURE; - } -} - -static int socket_stat(php_stream *stream, php_stream_statbuf *ssb) -{ - 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)) - { - return FAILURE; - } - return zend_fstat(sock->get_fd(), &ssb->sb); -} - -static inline int socket_connect(php_stream *stream, Socket *sock, php_stream_xport_param *xparam) -{ - char *host = NULL; - int portno = 0; - int ret = 0; - char *ip_address = NULL; - - if (UNEXPECTED(sock->get_fd() < 0)) - { - return FAILURE; - } - - if (sock->get_type() == SW_SOCK_TCP || sock->get_type() == SW_SOCK_TCP6 || sock->get_type() == SW_SOCK_UDP || sock->get_type() == SW_SOCK_UDP6) - { - ip_address = parse_ip_address_ex(xparam->inputs.name, xparam->inputs.namelen, &portno, xparam->want_errortext, - &xparam->outputs.error_text); - host = ip_address; - if (sock->get_sock_type() == SOCK_STREAM) - { - int sockoptval = 1; - setsockopt(sock->get_fd(), IPPROTO_TCP, TCP_NODELAY, (char*) &sockoptval, sizeof(sockoptval)); - } - } - else - { - host = xparam->inputs.name; - } - if (host == NULL) - { - return FAILURE; - } - if (xparam->inputs.timeout) - { - sock->set_timeout(xparam->inputs.timeout, SW_TIMEOUT_CONNECT); - } - if (sock->connect(host, portno) == false) - { - xparam->outputs.error_code = sock->errCode; - if (sock->errMsg) - { - xparam->outputs.error_text = zend_string_init(sock->errMsg, strlen(sock->errMsg), 0); - } - ret = -1; - } - if (ip_address) - { - efree(ip_address); - } - return ret; -} - -static inline int socket_bind(php_stream *stream, Socket *sock, php_stream_xport_param *xparam STREAMS_DC) -{ - char *host = NULL; - int portno = 0; - char *ip_address = NULL; - - if (sock->get_type() == SW_SOCK_TCP || sock->get_type() == SW_SOCK_TCP6 || sock->get_type() == SW_SOCK_UDP || sock->get_type() == SW_SOCK_UDP6) - { - ip_address = parse_ip_address_ex(xparam->inputs.name, xparam->inputs.namelen, &portno, xparam->want_errortext, &xparam->outputs.error_text); - host = ip_address; - } - else - { - host = xparam->inputs.name; - } - int ret = sock->bind(host, portno) ? 0 : -1; - if (ip_address) - { - efree(ip_address); - } - return ret; -} - -static inline int socket_accept(php_stream *stream, Socket *sock, php_stream_xport_param *xparam STREAMS_DC) -{ - int tcp_nodelay = 0; - zval *tmpzval = NULL; - - xparam->outputs.client = NULL; - - if ((NULL != PHP_STREAM_CONTEXT(stream)) - && (tmpzval = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "socket", "tcp_nodelay")) != NULL - && zval_is_true(tmpzval)) - { - tcp_nodelay = 1; - } - - zend_string **textaddr = xparam->want_textaddr ? &xparam->outputs.textaddr : NULL; - struct sockaddr **addr = xparam->want_addr ? &xparam->outputs.addr : NULL; - socklen_t *addrlen = xparam->want_addr ? &xparam->outputs.addrlen : NULL; - - struct timeval *timeout = xparam->inputs.timeout; - zend_string **error_string = xparam->want_errortext ? &xparam->outputs.error_text : NULL; - int *error_code = &xparam->outputs.error_code; - - int error = 0; - php_sockaddr_storage sa; - socklen_t sl = sizeof(sa); - - if (timeout) - { - sock->set_timeout(timeout, SW_TIMEOUT_READ); - } - - Socket *clisock = sock->accept(); - -#ifdef SW_USE_OPENSSL - if (clisock != nullptr && clisock->open_ssl) - { - if (!clisock->ssl_handshake()) - { - sock->errCode = clisock->errCode; - delete clisock; - clisock = nullptr; - } - } -#endif - - if (clisock == nullptr) - { - error = sock->errCode; - if (error_code) - { - *error_code = error; - } - if (error_string) - { - *error_string = php_socket_error_str(error); - } - return FAILURE; - } - else - { - php_network_populate_name_from_sockaddr((struct sockaddr*) &sa, sl, textaddr, addr, addrlen); -#ifdef TCP_NODELAY - if (tcp_nodelay) - { - setsockopt(clisock->get_fd(), IPPROTO_TCP, TCP_NODELAY, (char*) &tcp_nodelay, sizeof(tcp_nodelay)); - } -#endif - php_swoole_netstream_data_t *abstract = (php_swoole_netstream_data_t*) emalloc(sizeof(*abstract)); - memset(abstract, 0, sizeof(*abstract)); - - abstract->socket = clisock; - - xparam->outputs.client = php_stream_alloc_rel(stream->ops, (void* )abstract, NULL, "r+"); - if (xparam->outputs.client) - { - xparam->outputs.client->ctx = stream->ctx; - if (stream->ctx) - { - GC_ADDREF(stream->ctx); - } - } - return 0; - } -} - -static inline int socket_recvfrom(Socket *sock, char *buf, size_t buflen, zend_string **textaddr, struct sockaddr **addr, - socklen_t *addrlen) -{ - int ret; - int want_addr = textaddr || addr; - - if (want_addr) - { - php_sockaddr_storage sa; - socklen_t sl = sizeof(sa); - ret = sock->recvfrom(buf, buflen, (struct sockaddr*) &sa, &sl); - if (sl) - { - php_network_populate_name_from_sockaddr((struct sockaddr*) &sa, sl, textaddr, addr, addrlen); - } - else - { - if (textaddr) - { - *textaddr = ZSTR_EMPTY_ALLOC(); - } - if (addr) - { - *addr = NULL; - *addrlen = 0; - } - } - } - else - { - ret = sock->recv(buf, buflen); - } - - return ret; -} - -static inline int socket_sendto(Socket *sock, const char *buf, size_t buflen, struct sockaddr *addr, socklen_t addrlen) -{ - if (addr) - { - return sendto(sock->get_fd(), buf, buflen, 0, addr, addrlen); - } - else - { - return sock->send(buf, buflen); - } -} - -#ifdef SW_USE_OPENSSL - -#define GET_VER_OPT(name) (PHP_STREAM_CONTEXT(stream) && (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), "ssl", name)) != NULL) -#define GET_VER_OPT_STRING(name, str) if (GET_VER_OPT(name)) { convert_to_string_ex(val); str = Z_STRVAL_P(val); } -#define GET_VER_OPT_LONG(name, num) if (GET_VER_OPT(name)) { convert_to_long_ex(val); num = Z_LVAL_P(val); } - -static int socket_setup_crypto(php_stream *stream, Socket *sock, php_stream_xport_crypto_param *cparam STREAMS_DC) -{ - return 0; -} - -static int socket_enable_crypto(php_stream *stream, Socket *sock, php_stream_xport_crypto_param *cparam STREAMS_DC) -{ - if (cparam->inputs.activate && !sock->is_ssl_enable()) - { - return sock->ssl_handshake() ? 0 : -1; - } - else if (!cparam->inputs.activate && sock->is_ssl_enable()) - { - return sock->ssl_shutdown() ? 0 : -1; - } - return -1; -} -#endif - -static inline int socket_xport_api(php_stream *stream, Socket *sock, php_stream_xport_param *xparam STREAMS_DC) -{ - static const int shutdown_how[] = { SHUT_RD, SHUT_WR, SHUT_RDWR }; - - switch (xparam->op) - { - case STREAM_XPORT_OP_LISTEN: - { - xparam->outputs.returncode = sock->listen(xparam->inputs.backlog) ? 0 : -1; - break; - } - case STREAM_XPORT_OP_CONNECT: - case STREAM_XPORT_OP_CONNECT_ASYNC: - xparam->outputs.returncode = socket_connect(stream, sock, xparam); - break; - case STREAM_XPORT_OP_BIND: - { - if (sock->get_sock_domain() != AF_UNIX) - { - zval *tmpzval = NULL; - int sockoptval = 1; - php_stream_context *ctx = PHP_STREAM_CONTEXT(stream); - if (!ctx) - { - break; - } - -#ifdef SO_REUSEADDR - setsockopt(sock->get_fd(), SOL_SOCKET, SO_REUSEADDR, (char*) &sockoptval, sizeof(sockoptval)); -#endif - -#ifdef SO_REUSEPORT - if ((tmpzval = php_stream_context_get_option(ctx, "socket", "so_reuseport")) != NULL - && zval_is_true(tmpzval)) - { - setsockopt(sock->get_fd(), SOL_SOCKET, SO_REUSEPORT, (char*) &sockoptval, sizeof(sockoptval)); - } -#endif - -#ifdef SO_BROADCAST - if ((tmpzval = php_stream_context_get_option(ctx, "socket", "so_broadcast")) != NULL - && zval_is_true(tmpzval)) - { - setsockopt(sock->get_fd(), SOL_SOCKET, SO_BROADCAST, (char*) &sockoptval, sizeof(sockoptval)); - } -#endif - } - xparam->outputs.returncode = socket_bind(stream, sock, xparam STREAMS_CC); - break; - } - case STREAM_XPORT_OP_ACCEPT: - xparam->outputs.returncode = socket_accept(stream, sock, xparam STREAMS_CC); - break; - case STREAM_XPORT_OP_GET_NAME: - xparam->outputs.returncode = php_network_get_sock_name(sock->get_fd(), - xparam->want_textaddr ? &xparam->outputs.textaddr : NULL, - xparam->want_addr ? &xparam->outputs.addr : NULL, xparam->want_addr ? &xparam->outputs.addrlen : NULL - ); - break; - case STREAM_XPORT_OP_GET_PEER_NAME: - xparam->outputs.returncode = php_network_get_peer_name(sock->get_fd(), - xparam->want_textaddr ? &xparam->outputs.textaddr : NULL, - xparam->want_addr ? &xparam->outputs.addr : NULL, xparam->want_addr ? &xparam->outputs.addrlen : NULL - ); - break; - - case STREAM_XPORT_OP_SEND: - if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) - { - php_swoole_error(E_WARNING, "STREAM_OOB flags is not supports"); - xparam->outputs.returncode = -1; - break; - } - xparam->outputs.returncode = socket_sendto(sock, xparam->inputs.buf, xparam->inputs.buflen, xparam->inputs.addr, - xparam->inputs.addrlen); - if (xparam->outputs.returncode == -1) - { - char *err = php_socket_strerror(php_socket_errno(), NULL, 0); - php_error_docref(NULL, E_WARNING, "%s\n", err); - efree(err); - } - break; - - case STREAM_XPORT_OP_RECV: - if ((xparam->inputs.flags & STREAM_OOB) == STREAM_OOB) - { - php_swoole_error(E_WARNING, "STREAM_OOB flags is not supports"); - xparam->outputs.returncode = -1; - break; - } - if ((xparam->inputs.flags & STREAM_PEEK) == STREAM_PEEK) - { - xparam->outputs.returncode = sock->peek(xparam->inputs.buf, xparam->inputs.buflen); - } - else - { - xparam->outputs.returncode = socket_recvfrom(sock, xparam->inputs.buf, xparam->inputs.buflen, - xparam->want_textaddr ? &xparam->outputs.textaddr : NULL, - xparam->want_addr ? &xparam->outputs.addr : NULL, - xparam->want_addr ? &xparam->outputs.addrlen : NULL - ); - } - break; - case STREAM_XPORT_OP_SHUTDOWN: - xparam->outputs.returncode = sock->shutdown(shutdown_how[xparam->how]); - break; - default: -#ifdef SW_DEBUG - php_swoole_fatal_error(E_WARNING, "socket_xport_api: unsupported option %d", xparam->op); -#endif - break; - } - return PHP_STREAM_OPTION_RETURN_OK; -} - -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; - if (UNEXPECTED(!abstract || !abstract->socket)) - { - return PHP_STREAM_OPTION_RETURN_ERR; - } - Socket *sock = (Socket*) abstract->socket; - switch (option) - { - case PHP_STREAM_OPTION_BLOCKING: - // The coroutine socket always consistent with the sync blocking socket - return value ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR; - case PHP_STREAM_OPTION_XPORT_API: - { - return socket_xport_api(stream, sock, (php_stream_xport_param *) ptrparam STREAMS_CC); - } - case PHP_STREAM_OPTION_META_DATA_API: - { -#ifdef SW_USE_OPENSSL - SSL *ssl = sock->socket ? sock->socket->ssl : nullptr; - if (ssl) - { - zval tmp; - const char *proto_str; - const SSL_CIPHER *cipher; - - array_init(&tmp); - switch (SSL_version(ssl)) - { -#ifdef HAVE_TLS13 - case TLS1_3_VERSION: - proto_str = "TLSv1.3"; - break; -#endif -#ifdef HAVE_TLS12 - case TLS1_2_VERSION: - proto_str = "TLSv1.2"; - break; -#endif -#ifdef HAVE_TLS11 - case TLS1_1_VERSION: - proto_str = "TLSv1.1"; - break; -#endif - case TLS1_VERSION: - proto_str = "TLSv1"; - break; -#ifdef HAVE_SSL3 - case SSL3_VERSION: - proto_str = "SSLv3"; - break; -#endif - default: - proto_str = "UNKNOWN"; - break; - } - - cipher = SSL_get_current_cipher(ssl); - add_assoc_string(&tmp, "protocol", (char* )proto_str); - add_assoc_string(&tmp, "cipher_name", (char * ) SSL_CIPHER_get_name(cipher)); - add_assoc_long(&tmp, "cipher_bits", SSL_CIPHER_get_bits(cipher, NULL)); - add_assoc_string(&tmp, "cipher_version", (char *) SSL_CIPHER_get_version(cipher)); - add_assoc_zval((zval *)ptrparam, "crypto", &tmp); - } -#endif - add_assoc_bool((zval *)ptrparam, "timed_out", sock->errCode == ETIMEDOUT); - add_assoc_bool((zval *)ptrparam, "eof", stream->eof); - add_assoc_bool((zval *)ptrparam, "blocked", 1); - break; - } - case PHP_STREAM_OPTION_READ_TIMEOUT: - { - abstract->socket->set_timeout((struct timeval*) ptrparam, SW_TIMEOUT_READ); - break; - } -#ifdef SW_USE_OPENSSL - case PHP_STREAM_OPTION_CRYPTO_API: - { - php_stream_xport_crypto_param *cparam = (php_stream_xport_crypto_param *) ptrparam; - switch (cparam->op) - { - case STREAM_XPORT_CRYPTO_OP_SETUP: - cparam->outputs.returncode = socket_setup_crypto(stream, sock, cparam STREAMS_CC); - return PHP_STREAM_OPTION_RETURN_OK; - case STREAM_XPORT_CRYPTO_OP_ENABLE: - cparam->outputs.returncode = socket_enable_crypto(stream, sock, cparam STREAMS_CC); - return PHP_STREAM_OPTION_RETURN_OK; - default: - /* never here */ - SW_ASSERT(0); - break; - } - break; - } -#endif - case PHP_STREAM_OPTION_CHECK_LIVENESS: - { - return sock->check_liveness() ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR; - } - case PHP_STREAM_OPTION_READ_BUFFER: - case PHP_STREAM_OPTION_WRITE_BUFFER: - { - // TODO: read/write buffer - break; - } - default: -#ifdef SW_DEBUG - php_swoole_fatal_error(E_WARNING, "socket_set_option: unsupported option %d with value %d", option, value); -#endif - break; - } - return PHP_STREAM_OPTION_RETURN_OK; -} - -static php_stream *socket_create( - 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 *stream = NULL; - php_swoole_netstream_data_t *abstract = NULL; - Socket *sock; - - Coroutine::get_current_safe(); - - 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 - sock = new Socket(resourcename[0] == '[' ? SW_SOCK_TCP6 : SW_SOCK_TCP); - sock->open_ssl = true; -#else - php_swoole_error(E_WARNING, "you must configure with `--enable-openssl` to support ssl connection when compiling Swoole"); - return NULL; -#endif - } - else if (SW_STREQ(proto, protolen, "unix")) - { - sock = new Socket(SW_SOCK_UNIX_STREAM); - } - else if (SW_STREQ(proto, protolen, "udp")) - { - sock = new Socket(SW_SOCK_UDP); - } - else if (SW_STREQ(proto, protolen, "udg")) - { - sock = new Socket(SW_SOCK_UNIX_DGRAM); - } - else - { - /* abort? */ - goto _tcp; - } - - if (UNEXPECTED(sock->get_fd() < 0)) - { - _failed: - if (!stream) - { - delete sock; - } - else - { - php_stream_close(stream); - } - return NULL; - } - - abstract = (php_swoole_netstream_data_t*) ecalloc(1, sizeof(*abstract)); - abstract->socket = sock; - abstract->stream.socket = sock->get_fd(); - - if (timeout) - { - sock->set_timeout(timeout); - abstract->stream.timeout = *timeout; - } - else if (FG(default_socket_timeout) > 0) - { - sock->set_timeout((double) FG(default_socket_timeout)); - abstract->stream.timeout.tv_sec = FG(default_socket_timeout); - } - else - { - sock->set_timeout(-1); - abstract->stream.timeout.tv_sec = -1; - } - - 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 == NULL) - { - goto _failed; - } - - if (context && ZVAL_IS_ARRAY(&context->options)) - { -#ifdef SW_USE_OPENSSL - zval *ztmp; - if (sock->open_ssl && php_swoole_array_get_value(Z_ARRVAL_P(&context->options), "ssl", ztmp) && ZVAL_IS_ARRAY(ztmp)) - { - [](Socket *sock, HashTable *options) - { - zval zalias, *ztmp; - array_init(&zalias); -#define SSL_OPTION_ALIAS(name, alias) do { \ - if (php_swoole_array_get_value(options, name, ztmp)) \ - { \ - add_assoc_zval_ex(&zalias, ZEND_STRL(alias), ztmp); \ - } \ -} while (0); - SSL_OPTION_ALIAS("peer_name", "ssl_host_name"); - SSL_OPTION_ALIAS("verify_peer", "ssl_verify_peer"); - SSL_OPTION_ALIAS("allow_self_signed", "ssl_allow_self_signed"); - SSL_OPTION_ALIAS("cafile", "ssl_cafile"); - SSL_OPTION_ALIAS("capath", "ssl_capath"); - SSL_OPTION_ALIAS("local_cert", "ssl_cert_file"); - SSL_OPTION_ALIAS("local_pk", "ssl_key_file"); - SSL_OPTION_ALIAS("passphrase", "ssl_passphrase"); - SSL_OPTION_ALIAS("verify_depth", "ssl_verify_depth"); - SSL_OPTION_ALIAS("disable_compression", "ssl_disable_compression"); -#undef SSL_OPTION_ALIAS - php_swoole_socket_set_ssl(sock, &zalias); - zend_array_destroy(Z_ARRVAL(zalias)); - } (sock, Z_ARRVAL_P(ztmp)); - } -#endif - } - - return stream; -} - -bool PHPCoroutine::enable_hook(int flags) -{ - if (!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)); - - function_table = (zend_array*) emalloc(sizeof(zend_array)); - zend_hash_init(function_table, 8, NULL, NULL, 0); - - hook_init = true; - } - // php_stream - if (flags & SW_HOOK_TCP) - { - if (!(hook_flags & SW_HOOK_TCP)) - { - if (php_stream_xport_register("tcp", socket_create) != SUCCESS) - { - flags ^= SW_HOOK_TCP; - } - } - } - else - { - if (hook_flags & SW_HOOK_TCP) - { - php_stream_xport_register("tcp", ori_factory.tcp); - } - } - if (flags & SW_HOOK_UDP) - { - if (!(hook_flags & SW_HOOK_UDP)) - { - if (php_stream_xport_register("udp", socket_create) != SUCCESS) - { - flags ^= SW_HOOK_UDP; - } - } - } - else - { - if (hook_flags & SW_HOOK_UDP) - { - php_stream_xport_register("udp", ori_factory.udp); - } - } - if (flags & SW_HOOK_UNIX) - { - if (!(hook_flags & SW_HOOK_UNIX)) - { - if (php_stream_xport_register("unix", socket_create) != SUCCESS) - { - flags ^= SW_HOOK_UNIX; - } - } - } - else - { - if (hook_flags & SW_HOOK_UNIX) - { - php_stream_xport_register("unix", ori_factory._unix); - } - } - if (flags & SW_HOOK_UDG) - { - if (!(hook_flags & SW_HOOK_UDG)) - { - if (php_stream_xport_register("udg", socket_create) != SUCCESS) - { - flags ^= SW_HOOK_UDG; - } - } - } - else - { - if (hook_flags & SW_HOOK_UDG) - { - php_stream_xport_register("udg", ori_factory.udg); - } - } - if (flags & SW_HOOK_SSL) - { - if (!(hook_flags & SW_HOOK_SSL)) - { - if (php_stream_xport_register("ssl", socket_create) != SUCCESS) - { - flags ^= SW_HOOK_SSL; - } - } - } - else - { - if (hook_flags & SW_HOOK_SSL) - { - if (ori_factory.ssl != nullptr) { - php_stream_xport_register("ssl", ori_factory.ssl); - } else { - php_stream_xport_unregister("ssl"); - } - } - } - if (flags & SW_HOOK_TLS) - { - if (!(hook_flags & SW_HOOK_TLS)) - { - if (php_stream_xport_register("tls", socket_create) != SUCCESS) - { - flags ^= SW_HOOK_TLS; - } - } - } - else - { - if (hook_flags & SW_HOOK_TLS) - { - if (ori_factory.tls != nullptr) { - php_stream_xport_register("tls", ori_factory.tls); - } else { - php_stream_xport_unregister("tls"); - } - } - } - if (flags & SW_HOOK_STREAM_FUNCTION) - { - if (!(hook_flags & SW_HOOK_STREAM_FUNCTION)) - { - SW_HOOK_FUNC(stream_select); - SW_HOOK_FUNC(stream_socket_pair); - } - } - else - { - if (hook_flags & SW_HOOK_STREAM_FUNCTION) - { - SW_UNHOOK_FUNC(stream_select); - SW_UNHOOK_FUNC(stream_socket_pair); - } - } - // file - if (flags & SW_HOOK_FILE) - { - if (!(hook_flags & SW_HOOK_FILE)) - { - memcpy((void*) &php_plain_files_wrapper, &sw_php_plain_files_wrapper, sizeof(php_plain_files_wrapper)); - } - } - else - { - if (hook_flags & SW_HOOK_FILE) - { - memcpy((void*) &php_plain_files_wrapper, &ori_php_plain_files_wrapper, sizeof(php_plain_files_wrapper)); - } - } - // sleep - if (flags & SW_HOOK_SLEEP) - { - if (!(hook_flags & SW_HOOK_SLEEP)) - { - SW_HOOK_FUNC(sleep); - SW_HOOK_FUNC(usleep); - SW_HOOK_FUNC(time_nanosleep); - SW_HOOK_FUNC(time_sleep_until); - } - } - else - { - if (hook_flags & SW_HOOK_SLEEP) - { - SW_UNHOOK_FUNC(sleep); - SW_UNHOOK_FUNC(usleep); - SW_UNHOOK_FUNC(time_nanosleep); - SW_UNHOOK_FUNC(time_sleep_until); - } - } - // proc_open - if (flags & SW_HOOK_PROC) - { - if (!(hook_flags & SW_HOOK_PROC)) - { - SW_HOOK_FUNC(proc_open); - SW_HOOK_FUNC(proc_close); - SW_HOOK_FUNC(proc_get_status); - SW_HOOK_FUNC(proc_terminate); - } - } - else - { - if (hook_flags & SW_HOOK_PROC) - { - SW_UNHOOK_FUNC(proc_open); - SW_UNHOOK_FUNC(proc_close); - SW_UNHOOK_FUNC(proc_get_status); - SW_UNHOOK_FUNC(proc_terminate); - } - } - // blocking function - if (flags & SW_HOOK_BLOCKING_FUNCTION) - { - if (!(hook_flags & SW_HOOK_BLOCKING_FUNCTION)) - { - hook_func(ZEND_STRL("gethostbyname"), PHP_FN(swoole_coroutine_gethostbyname)); - hook_func(ZEND_STRL("exec")); - hook_func(ZEND_STRL("shell_exec")); - } - } - else - { - if (hook_flags & SW_HOOK_BLOCKING_FUNCTION) - { - SW_UNHOOK_FUNC(gethostbyname); - unhook_func(ZEND_STRL("exec")); - unhook_func(ZEND_STRL("shell_exec")); - } - } - - if (flags & SW_HOOK_CURL) - { - if (!(hook_flags & SW_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")); - } - } - else - { - if (hook_flags & SW_HOOK_CURL) - { - unhook_func(ZEND_STRL("curl_init")); - unhook_func(ZEND_STRL("curl_setopt")); - unhook_func(ZEND_STRL("curl_setopt_array")); - unhook_func(ZEND_STRL("curl_exec")); - unhook_func(ZEND_STRL("curl_getinfo")); - unhook_func(ZEND_STRL("curl_errno")); - unhook_func(ZEND_STRL("curl_error")); - unhook_func(ZEND_STRL("curl_reset")); - unhook_func(ZEND_STRL("curl_close")); - unhook_func(ZEND_STRL("curl_multi_getcontent")); - } - } - - hook_flags = flags; - return true; -} - -bool PHPCoroutine::disable_hook() -{ - return enable_hook(0); -} - -static PHP_METHOD(swoole_runtime, enableCoroutine) -{ - zval *zflags = nullptr; - /*TODO: enable SW_HOOK_CURL by default after curl handler completed */ - zend_long flags = SW_HOOK_ALL; - - ZEND_PARSE_PARAMETERS_START(0, 2) - 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)); - } - } - - RETURN_BOOL(PHPCoroutine::enable_hook(flags)); -} - -static PHP_METHOD(swoole_runtime, getHookFlags) -{ - RETURN_LONG(hook_flags); -} - -static PHP_METHOD(swoole_runtime, setHookFlags) -{ - zend_long flags = SW_HOOK_ALL; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_LONG(flags) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - RETURN_BOOL(PHPCoroutine::enable_hook(flags)); -} - -static PHP_FUNCTION(swoole_sleep) -{ - zend_long num; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &num) == FAILURE) - { - RETURN_FALSE; - } - if (num < 0) - { - php_error_docref(NULL, E_WARNING, "Number of seconds must be greater than or equal to 0"); - RETURN_FALSE; - } - - if (num >= SW_TIMER_MIN_SEC && Coroutine::get_current()) - { - RETURN_LONG(System::sleep((double ) num) < 0 ? num : 0); - } - else - { - RETURN_LONG(php_sleep(num)); - } -} - -static PHP_FUNCTION(swoole_usleep) -{ - zend_long num; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &num) == FAILURE) - { - RETURN_FALSE; - } - if (num < 0) - { - php_error_docref(NULL, E_WARNING, "Number of seconds must be greater than or equal to 0"); - RETURN_FALSE; - } - double sec = (double) num / 1000000; - if (sec >= SW_TIMER_MIN_SEC && Coroutine::get_current()) - { - System::sleep(sec); - } - else - { - usleep((unsigned int)num); - } -} - -static PHP_FUNCTION(swoole_time_nanosleep) -{ - zend_long tv_sec, tv_nsec; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &tv_sec, &tv_nsec) == FAILURE) - { - RETURN_FALSE; - } - - if (tv_sec < 0) - { - php_error_docref(NULL, E_WARNING, "The seconds value must be greater than 0"); - RETURN_FALSE; - } - if (tv_nsec < 0) - { - php_error_docref(NULL, E_WARNING, "The nanoseconds value must be greater than 0"); - RETURN_FALSE; - } - double _time = (double) tv_sec + (double) tv_nsec / 1000000000.00; - if (_time >= SW_TIMER_MIN_SEC && Coroutine::get_current()) - { - System::sleep(_time); - } - else - { - struct timespec php_req, php_rem; - php_req.tv_sec = (time_t) tv_sec; - php_req.tv_nsec = (long) tv_nsec; - - if (nanosleep(&php_req, &php_rem) == 0) - { - RETURN_TRUE; - } - else if (errno == EINTR) - { - array_init(return_value); - add_assoc_long_ex(return_value, "seconds", sizeof("seconds") - 1, php_rem.tv_sec); - add_assoc_long_ex(return_value, "nanoseconds", sizeof("nanoseconds") - 1, php_rem.tv_nsec); - } - else if (errno == EINVAL) - { - php_swoole_error(E_WARNING, "nanoseconds was not in the range 0 to 999 999 999 or seconds was negative"); - } - } -} - -static PHP_FUNCTION(swoole_time_sleep_until) -{ - double d_ts, c_ts; - struct timeval tm; - struct timespec php_req, php_rem; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "d", &d_ts) == FAILURE) - { - RETURN_FALSE; - } - - if (gettimeofday((struct timeval *) &tm, NULL) != 0) - { - RETURN_FALSE; - } - - c_ts = (double) (d_ts - tm.tv_sec - tm.tv_usec / 1000000.00); - if (c_ts < 0) - { - php_error_docref(NULL, E_WARNING, "Sleep until to time is less than current time"); - RETURN_FALSE; - } - - php_req.tv_sec = (time_t) c_ts; - if (php_req.tv_sec > c_ts) - { - php_req.tv_sec--; - } - php_req.tv_nsec = (long) ((c_ts - php_req.tv_sec) * 1000000000.00); - - double _time = (double) php_req.tv_sec + (double) php_req.tv_nsec / 1000000000.00; - if (_time >= SW_TIMER_MIN_SEC && Coroutine::get_current()) - { - System::sleep(_time); - } - else - { - while (nanosleep(&php_req, &php_rem)) - { - if (errno == EINTR) - { - php_req.tv_sec = php_rem.tv_sec; - php_req.tv_nsec = php_rem.tv_nsec; - } - else - { - RETURN_FALSE; - } - } - } - RETURN_TRUE; -} - -static void stream_array_to_fd_set(zval *stream_array, std::unordered_map &fds, int event) -{ - zval *elem; - zend_ulong index; - zend_string *key; - php_socket_t sock; - - if (!ZVAL_IS_ARRAY(stream_array)) - { - return; - } - - ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(stream_array), index, key, elem) - { - ZVAL_DEREF(elem); - sock = swoole_convert_to_fd(elem); - if (sock < 0) - { - continue; - } - auto i = fds.find(sock); - if (i == fds.end()) - { - fds.emplace(make_pair(sock, socket_poll_fd(event, new zend::key_value(index, key, elem)))); - } - else - { - i->second.events |= event; - } - } - ZEND_HASH_FOREACH_END(); -} - -static int stream_array_emulate_read_fd_set(zval *stream_array) -{ - zval *elem, *dest_elem, new_array; - HashTable *ht; - php_stream *stream; - int ret = 0; - zend_ulong num_ind; - zend_string *key; - - if (!ZVAL_IS_ARRAY(stream_array)) - { - return 0; - } - - ZVAL_NEW_ARR(&new_array); - ht = Z_ARRVAL(new_array); - zend_hash_init(ht, zend_hash_num_elements(Z_ARRVAL_P(stream_array)), NULL, ZVAL_PTR_DTOR, 0); - - ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(stream_array), num_ind, key, elem) - { - ZVAL_DEREF(elem); - php_stream_from_zval_no_verify(stream, elem); - if (stream == NULL) - { - continue; - } - if ((stream->writepos - stream->readpos) > 0) - { - /* allow readable non-descriptor based streams to participate in stream_select. - * Non-descriptor streams will only "work" if they have previously buffered the - * data. Not ideal, but better than nothing. - * This branch of code also allows blocking streams with buffered data to - * operate correctly in stream_select. - * */ - dest_elem = !key ? zend_hash_index_update(ht, num_ind, elem) : zend_hash_update(ht, key, elem); - zval_add_ref(dest_elem); - ret++; - continue; - } - } ZEND_HASH_FOREACH_END(); - - if (ret > 0) - { - /* destroy old array and add new one */ - zend_array_destroy(Z_ARR_P(stream_array)); - ZVAL_ARR(stream_array, ht); - } - else - { - zend_array_destroy(ht); - } - - return ret; -} - -static PHP_FUNCTION(swoole_stream_select) -{ - Coroutine::get_current_safe(); - - zval *r_array, *w_array, *e_array; - zend_long sec, usec = 0; - zend_bool secnull; - 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_OPTIONAL - Z_PARAM_LONG(usec) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - std::unordered_map fds; - - if (r_array != NULL) - { - stream_array_to_fd_set(r_array, fds, SW_EVENT_READ); - } - - if (w_array != NULL) - { - stream_array_to_fd_set(w_array, fds, SW_EVENT_WRITE); - } - - if (e_array != NULL) - { - stream_array_to_fd_set(e_array, fds, SW_EVENT_ERROR); - } - - if (fds.size() == 0) - { - php_error_docref(NULL, E_WARNING, "No stream arrays were passed"); - RETURN_FALSE; - } - - double timeout = -1; - if (!secnull) - { - if (sec < 0) - { - php_error_docref(NULL, E_WARNING, "The seconds parameter must be greater than 0"); - RETURN_FALSE; - } - else if (usec < 0) - { - php_error_docref(NULL, E_WARNING, "The microseconds parameter must be greater than 0"); - RETURN_FALSE; - } - 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 - * that we selected, but return only the readable sockets */ - if (r_array != NULL) - { - retval = stream_array_emulate_read_fd_set(r_array); - if (retval > 0) - { - if (w_array != NULL) - { - zend_hash_clean(Z_ARRVAL_P(w_array)); - } - if (e_array != NULL) - { - zend_hash_clean(Z_ARRVAL_P(e_array)); - } - RETURN_LONG(retval); - } - } - - /** - * timeout or add failed - */ - if (!System::socket_poll(fds, timeout)) - { - RETURN_LONG(0); - } - - if (r_array != NULL) - { - zend_hash_clean(Z_ARRVAL_P(r_array)); - } - if (w_array != NULL) - { - zend_hash_clean(Z_ARRVAL_P(w_array)); - } - if (e_array != NULL) - { - zend_hash_clean(Z_ARRVAL_P(e_array)); - } - - for (auto &i : fds) - { - zend::key_value *kv = (zend::key_value *) i.second.ptr; - int revents = i.second.revents; - SW_ASSERT((revents & (~(SW_EVENT_READ |SW_EVENT_WRITE | SW_EVENT_ERROR))) == 0); - if (revents > 0) - { - if ((revents & SW_EVENT_READ) && r_array) - { - kv->add_to(r_array); - } - if ((revents & SW_EVENT_WRITE) && w_array) - { - kv->add_to(w_array); - } - if ((revents & SW_EVENT_ERROR) && e_array) - { - kv->add_to(e_array); - } - retval++; - } - delete kv; - } - - RETURN_LONG(retval); -} - -static void hook_func(const char *name, size_t l_name, zif_handler handler) -{ - real_func *rf = (real_func *) zend_hash_str_find_ptr(function_table, name, l_name); - bool use_php_func = false; - /** - * use php library function - */ - if (handler == nullptr) - { - handler = PHP_FN(swoole_user_func_handler); - use_php_func = true; - } - if (rf) - { - rf->function->internal_function.handler = handler; - return; - } - - zend_function *zf = (zend_function *) zend_hash_str_find_ptr(EG(function_table), name, l_name); - if (zf == nullptr) - { - return; - } - - rf = (real_func *) emalloc(sizeof(real_func)); - bzero(rf, sizeof(real_func)); - rf->function = zf; - rf->ori_handler = zf->internal_function.handler; - zf->internal_function.handler = handler; - - if (use_php_func) - { - char func[128]; - memcpy(func, ZEND_STRL("swoole_")); - memcpy(func + 7, zf->common.function_name->val, zf->common.function_name->len); - - ZVAL_STRINGL(&rf->name, func, zf->common.function_name->len + 7); - - 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, NULL, 0, &func_name, NULL, func_cache, NULL)) - { - php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name); - return; - } - efree(func_name); - rf->fci_cache = func_cache; - } - - zend_hash_add_ptr(function_table, zf->common.function_name, rf); -} - -static void unhook_func(const char *name, size_t l_name) -{ - real_func *rf = (real_func *) zend_hash_str_find_ptr(function_table, name, l_name); - if (rf == nullptr) - { - return; - } - rf->function->internal_function.handler = rf->ori_handler; -} - -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); - - if (FG(default_socket_timeout) > 0) - { - sock->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(); - - php_stream *stream = php_stream_alloc_rel(&socket_ops, abstract, nullptr, "r+"); - - if (stream == NULL) - { - delete sock; - } - else - { - stream->flags |= PHP_STREAM_FLAG_AVOID_BLOCKING; - } - - return stream; -} - -static PHP_FUNCTION(swoole_stream_socket_pair) -{ - zend_long domain, type, protocol; - php_stream *s1, *s2; - php_socket_t pair[2]; - - ZEND_PARSE_PARAMETERS_START(3, 3) - Z_PARAM_LONG(domain) - Z_PARAM_LONG(type) - Z_PARAM_LONG(protocol) - 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)); - RETURN_FALSE; - } - - array_init(return_value); - - php_swoole_check_reactor(); - - s1 = php_swoole_create_stream_from_socket(pair[0], domain, type, protocol STREAMS_CC); - s2 = php_swoole_create_stream_from_socket(pair[1], domain, type, protocol STREAMS_CC); - - /* set the __exposed flag. - * php_stream_to_zval() does, add_next_index_resource() does not */ - php_stream_auto_cleanup(s1); - php_stream_auto_cleanup(s2); - - add_next_index_resource(return_value, s1->res); - add_next_index_resource(return_value, s2->res); -} - -static PHP_FUNCTION(swoole_user_func_handler) -{ - zend_fcall_info fci; - fci.size = sizeof(fci); - fci.object = NULL; - fci.function_name = {}; - fci.retval = return_value; - fci.param_count = ZEND_NUM_ARGS(); - fci.params = ZEND_CALL_ARG(execute_data, 1); - fci.no_separation = 1; - - real_func *rf = (real_func *) zend_hash_find_ptr(function_table, execute_data->func->common.function_name); - zend_call_function(&fci, rf->fci_cache); -} diff --git a/swoole_server.cc b/swoole_server.cc deleted file mode 100644 index eb96bc06b04..00000000000 --- a/swoole_server.cc +++ /dev/null @@ -1,4580 +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 "swoole_server.h" -#include "websocket.h" -#include "ext/standard/php_var.h" -#include "zend_smart_str.h" - -#ifdef SW_HAVE_ZLIB -#include -#endif - -#include -#include -#include - -using namespace std; -using namespace swoole; - -typedef struct -{ - int current_fd; - uint32_t session_id; - swServer *serv; - swListenPort *port; - int index; -} swConnectionIterator; - -static int php_swoole_task_id = 0; -static int dgram_server_socket; - -struct -{ - zval *zobjects[SW_MAX_LISTEN_PORT]; - uint8_t num; - php_swoole_server_port_property *primary_port; -} server_port_list; - -typedef struct -{ - php_coro_context context; - int *list; - uint32_t count; - zval *result; - swTimer_node *timer; -} swTaskCo; - -static zend_fcall_info_cache *server_callbacks[PHP_SWOOLE_SERVER_CALLBACK_NUM]; - -static unordered_map task_callbacks; -static unordered_map task_coroutine_map; -static unordered_map *> send_coroutine_map; -static vector serv_user_process; - -struct server_event { - enum php_swoole_server_callback_type type; - std::string name; - server_event(enum php_swoole_server_callback_type type, std::string &&name) : type(type) , name(name) { } -}; - -static unordered_map server_event_map({ - { "start", server_event(SW_SERVER_CB_onStart, "Start") }, - { "shutdown", server_event(SW_SERVER_CB_onShutdown, "Shutdown") }, - { "workerstart", server_event(SW_SERVER_CB_onWorkerStart, "WorkerStart") }, - { "workerstop", server_event(SW_SERVER_CB_onWorkerStop, "WorkerStop") }, - { "beforereload", server_event(SW_SERVER_CB_onBeforeReload, "BeforeReload") }, - { "afterreload", server_event(SW_SERVER_CB_onAfterReload, "AfterReload") }, - { "task", server_event(SW_SERVER_CB_onTask, "Task") }, - { "finish", server_event(SW_SERVER_CB_onFinish, "Finish") }, - { "workerexit", server_event(SW_SERVER_CB_onWorkerExit, "WorkerExit") }, - { "workererror", server_event(SW_SERVER_CB_onWorkerError, "WorkerError") }, - { "managerstart", server_event(SW_SERVER_CB_onManagerStart, "ManagerStart") }, - { "managerstop", server_event(SW_SERVER_CB_onManagerStop, "ManagerStop") }, - { "pipemessage", server_event(SW_SERVER_CB_onPipeMessage, "PipeMessage") }, -}); - -static int php_swoole_task_finish(swServer *serv, zval *zdata, swEventData *current_task); -static void php_swoole_onPipeMessage(swServer *serv, swEventData *req); -static void php_swoole_onStart(swServer *); -static void php_swoole_onShutdown(swServer *); -static void php_swoole_onWorkerStart(swServer *, int worker_id); -static void php_swoole_onBeforeReload(swServer *serv); -static void php_swoole_onAfterReload(swServer *serv); -static void php_swoole_onWorkerStop(swServer *, int worker_id); -static void php_swoole_onWorkerExit(swServer *serv, int worker_id); -static void php_swoole_onUserWorkerStart(swServer *serv, swWorker *worker); -static int php_swoole_onTask(swServer *, swEventData *task); -static int php_swoole_onFinish(swServer *, swEventData *task); -static void php_swoole_onWorkerError(swServer *serv, int worker_id, pid_t worker_pid, int exit_code, int signo); -static void php_swoole_onManagerStart(swServer *serv); -static void php_swoole_onManagerStop(swServer *serv); - -static void php_swoole_onSendTimeout(swTimer *timer, swTimer_node *tnode); -static enum swReturn_code php_swoole_server_send_resume(swServer *serv, php_coro_context *context, int fd); -static void php_swoole_task_onTimeout(swTimer *timer, swTimer_node *tnode); -static int php_swoole_server_dispatch_func(swServer *serv, swConnection *conn, swSendData *data); -static zval* php_swoole_server_add_port(swServer *serv, swListenPort *port); - -/** - * Worker Buffer - */ -static void** php_swoole_server_worker_create_buffers(swServer *serv, uint buffer_num); -static void* php_swoole_server_worker_get_buffer(swServer *serv, swDataHead *info); -static size_t php_swoole_server_worker_get_buffer_len(swServer *serv, swDataHead *info); -static void php_swoole_server_worker_add_buffer_len(swServer *serv, swDataHead *info, size_t len); -static void php_swoole_server_worker_move_buffer(swServer *serv, swPipeBuffer *buffer); - -static size_t php_swoole_server_worker_get_packet(swServer *serv, swEventData *req, char **data_ptr); - -static inline zend_bool php_swoole_server_isset_callback(swListenPort *port, int event_type) -{ - php_swoole_server_port_property *property = (php_swoole_server_port_property *) port->ptr; - if (property->callbacks[event_type] || server_port_list.primary_port->callbacks[event_type]) - { - return SW_TRUE; - } - else - { - return SW_FALSE; - } -} - -static sw_inline zend_bool is_enable_coroutine(swServer *serv) -{ - if (swIsTaskWorker()) - { - return serv->task_enable_coroutine; - } - else - { - return serv->enable_coroutine; - } -} - -void php_swoole_server_rshutdown() -{ - if (!sw_server()) - { - return; - } - - swServer *serv = sw_server(); - - swWorker_clean_pipe_buffer(serv); - - if (serv->gs->start > 0 && !swIsUserWorker()) - { - 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: - swoole_error_log(SW_LOG_ERROR, SW_ERROR_PHP_FATAL_ERROR, "Fatal error: %s in %s on line %d", PG(last_error_message), - PG(last_error_file)?PG(last_error_file):"-", PG(last_error_lineno)); - break; - default: - break; - } - } - else - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SERVER_WORKER_TERMINATED, "worker process is terminated by exit()/die()"); - } - } -} - -zend_class_entry *swoole_server_ce; -zend_object_handlers swoole_server_handlers; - -zend_class_entry *swoole_connection_iterator_ce; -static zend_object_handlers swoole_connection_iterator_handlers; - -static zend_class_entry *swoole_server_task_ce; -static zend_object_handlers swoole_server_task_handlers; - -typedef struct -{ - swServer *serv; - zend_object std; -} server_t; - -static sw_inline server_t* php_swoole_server_fetch_object(zend_object *obj) -{ - return (server_t *) ((char *) obj - swoole_server_handlers.offset); -} - -static sw_inline swServer* php_swoole_server_get_server(zval *zobject) -{ - return php_swoole_server_fetch_object(Z_OBJ_P(zobject))->serv; -} - -swServer* php_swoole_server_get_and_check_server(zval *zobject) -{ - swServer* serv = php_swoole_server_get_server(zobject); - if (UNEXPECTED(!serv)) - { - php_swoole_fatal_error(E_ERROR, "Invaild instance of %s", SW_Z_OBJCE_NAME_VAL_P(zobject)); - } - return serv; -} - -static sw_inline void php_swoole_server_set_server(zval *zobject, swServer *serv) -{ - php_swoole_server_fetch_object(Z_OBJ_P(zobject))->serv = serv; -} - -static void php_swoole_server_free_object(zend_object *object) -{ - server_t *server = php_swoole_server_fetch_object(object); - swServer *serv = server->serv; - - 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->ptr2) - { - efree(serv->ptr2); - } - for (int i = 0; i < PHP_SWOOLE_SERVER_CALLBACK_NUM; i++) - { - zend_fcall_info_cache *fci_cache = server_callbacks[i]; - if (fci_cache) - { - efree(fci_cache); - server_callbacks[i] = NULL; - } - } - for (auto i = serv_user_process.begin(); i != serv_user_process.end(); i++) - { - sw_zval_free(*i); - } - for (int i = 0; i < server_port_list.num; i++) - { - sw_zval_free(server_port_list.zobjects[i]); - server_port_list.zobjects[i] = NULL; - } - } - - zend_object_std_dtor(object); -} - -static zend_object *php_swoole_server_create_object(zend_class_entry *ce) -{ - server_t *server = (server_t *) ecalloc(1, sizeof(server_t) + zend_object_properties_size(ce)); - zend_object_std_init(&server->std, ce); - object_properties_init(&server->std, ce); - server->std.handlers = &swoole_server_handlers; - return &server->std; -} - -typedef struct -{ - swConnectionIterator iterator; - zend_object std; -} connection_iterator_t; - -static sw_inline connection_iterator_t* php_swoole_connection_iterator_fetch_object(zend_object *obj) -{ - return (connection_iterator_t *) ((char *) obj - swoole_connection_iterator_handlers.offset); -} - -static sw_inline swConnectionIterator* php_swoole_connection_iterator_get_ptr(zval *zobject) -{ - return &php_swoole_connection_iterator_fetch_object(Z_OBJ_P(zobject))->iterator; -} - -swConnectionIterator* php_swoole_connection_iterator_get_and_check_ptr(zval *zobject) -{ - swConnectionIterator* 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)); - } - return iterator; -} - -static void php_swoole_connection_iterator_free_object(zend_object *object) -{ - zend_object_std_dtor(object); -} - -static zend_object *php_swoole_connection_iterator_create_object(zend_class_entry *ce) -{ - connection_iterator_t *connection = (connection_iterator_t *) ecalloc(1, sizeof(connection_iterator_t) + zend_object_properties_size(ce)); - zend_object_std_init(&connection->std, ce); - object_properties_init(&connection->std, ce); - connection->std.handlers = &swoole_connection_iterator_handlers; - return &connection->std; -} - -typedef struct -{ - swServer *serv; - swDataHead info; - zend_object std; -} server_task_t; - -static sw_inline server_task_t* php_swoole_server_task_fetch_object(zend_object *obj) -{ - return (server_task_t *) ((char *) obj - swoole_server_task_handlers.offset); -} - -static sw_inline swServer* php_swoole_server_task_get_server(zval *zobject) -{ - swServer* 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)); - } - return serv; -} - -static sw_inline void php_swoole_server_task_set_server(zval *zobject, swServer *serv) -{ - php_swoole_server_task_fetch_object(Z_OBJ_P(zobject))->serv = serv; -} - -static sw_inline swDataHead* php_swoole_server_task_get_info(zval *zobject) -{ - server_task_t *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)); - } - return &task->info; -} - -static sw_inline void php_swoole_server_task_set_info(zval *zobject, swDataHead *info) -{ - php_swoole_server_task_fetch_object(Z_OBJ_P(zobject))->info = *info; -} - -static void php_swoole_server_task_free_object(zend_object *object) -{ - zend_object_std_dtor(object); -} - -static zend_object *php_swoole_server_task_create_object(zend_class_entry *ce) -{ - server_task_t *server_task = (server_task_t *) ecalloc(1, sizeof(server_task_t) + zend_object_properties_size(ce)); - zend_object_std_init(&server_task->std, ce); - object_properties_init(&server_task->std, ce); - server_task->std.handlers = &swoole_server_task_handlers; - return &server_task->std; -} - -// arginfo server -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() - -//for object style -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 - -static PHP_METHOD(swoole_server, __construct); -static PHP_METHOD(swoole_server, __destruct); -static PHP_METHOD(swoole_server, set); -static PHP_METHOD(swoole_server, on); -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, start); -static PHP_METHOD(swoole_server, stop); -static PHP_METHOD(swoole_server, send); -static PHP_METHOD(swoole_server, sendfile); -static PHP_METHOD(swoole_server, stats); -static PHP_METHOD(swoole_server, bind); -static PHP_METHOD(swoole_server, sendto); -static PHP_METHOD(swoole_server, sendwait); -static PHP_METHOD(swoole_server, exists); -static PHP_METHOD(swoole_server, protect); -static PHP_METHOD(swoole_server, close); -static PHP_METHOD(swoole_server, pause); -static PHP_METHOD(swoole_server, resume); -static PHP_METHOD(swoole_server, task); -static PHP_METHOD(swoole_server, taskwait); -static PHP_METHOD(swoole_server, taskWaitMulti); -static PHP_METHOD(swoole_server, taskCo); -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, getClientList); -static PHP_METHOD(swoole_server, getClientInfo); -static PHP_METHOD(swoole_server, getWorkerId); -static PHP_METHOD(swoole_server, getWorkerPid); -static PHP_METHOD(swoole_server, getWorkerStatus); -static PHP_METHOD(swoole_server, getManagerPid); -static PHP_METHOD(swoole_server, getMasterPid); -#ifdef SW_BUFFER_RECV_TIME -static PHP_METHOD(swoole_server, getReceivedTime); -#endif -#ifdef SWOOLE_SOCKETS_SUPPORT -static PHP_METHOD(swoole_server, getSocket); -#endif - -/** - * Server\Connection - */ -static PHP_METHOD(swoole_connection_iterator, count); -static PHP_METHOD(swoole_connection_iterator, rewind); -static PHP_METHOD(swoole_connection_iterator, next); -static PHP_METHOD(swoole_connection_iterator, current); -static PHP_METHOD(swoole_connection_iterator, key); -static PHP_METHOD(swoole_connection_iterator, valid); -static PHP_METHOD(swoole_connection_iterator, offsetExists); -static PHP_METHOD(swoole_connection_iterator, offsetGet); -static PHP_METHOD(swoole_connection_iterator, offsetSet); -static PHP_METHOD(swoole_connection_iterator, offsetUnset); -static PHP_METHOD(swoole_connection_iterator, __construct); -static PHP_METHOD(swoole_connection_iterator, __destruct); - -/** - * Server\Task - */ -static PHP_METHOD(swoole_server_task, finish); -static PHP_METHOD(swoole_server_task, pack); - -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) - //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) - //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) -#ifdef SWOOLE_SOCKETS_SUPPORT - PHP_ME(swoole_server, getSocket, arginfo_swoole_server_getSocket, ZEND_ACC_PUBLIC) -#endif -#ifdef SW_BUFFER_RECV_TIME - PHP_ME(swoole_server, getReceivedTime, arginfo_swoole_void, ZEND_ACC_PUBLIC) -#endif - PHP_ME(swoole_server, bind, arginfo_swoole_server_bind, ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} -}; - -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_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_FE_END -}; - -void php_swoole_server_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY(swoole_server, "Swoole\\Server", "swoole_server", NULL, swoole_server_methods); - SW_SET_CLASS_SERIALIZABLE(swoole_server, zend_class_serialize_deny, zend_class_unserialize_deny); - 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, php_swoole_server_create_object, php_swoole_server_free_object, server_t, 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"); - - SW_INIT_CLASS_ENTRY(swoole_server_task, "Swoole\\Server\\Task", "swoole_server_task", NULL, 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_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, php_swoole_server_task_create_object, php_swoole_server_task_free_object, server_task_t, std); - - SW_INIT_CLASS_ENTRY(swoole_connection_iterator, "Swoole\\Connection\\Iterator", "swoole_connection_iterator", NULL, swoole_connection_iterator_methods); - SW_SET_CLASS_SERIALIZABLE(swoole_connection_iterator, zend_class_serialize_deny, zend_class_unserialize_deny); - 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, php_swoole_connection_iterator_create_object, php_swoole_connection_iterator_free_object, connection_iterator_t, std); - zend_class_implements(swoole_connection_iterator_ce, 2, zend_ce_iterator, zend_ce_arrayaccess); -#ifdef SW_HAVE_COUNTABLE - zend_class_implements(swoole_connection_iterator_ce, 1, zend_ce_countable); -#endif - - zend_declare_property_null(swoole_server_ce, ZEND_STRL("onStart"), 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); - zend_declare_property_null(swoole_server_ce, ZEND_STRL("onBeforeReload"), ZEND_ACC_PRIVATE); - zend_declare_property_null(swoole_server_ce, ZEND_STRL("onAfterReload"), ZEND_ACC_PRIVATE); - zend_declare_property_null(swoole_server_ce, ZEND_STRL("onWorkerExit"), ZEND_ACC_PRIVATE); - zend_declare_property_null(swoole_server_ce, ZEND_STRL("onWorkerError"), ZEND_ACC_PRIVATE); - zend_declare_property_null(swoole_server_ce, ZEND_STRL("onTask"), ZEND_ACC_PRIVATE); - zend_declare_property_null(swoole_server_ce, ZEND_STRL("onFinish"), ZEND_ACC_PRIVATE); - zend_declare_property_null(swoole_server_ce, ZEND_STRL("onManagerStart"), ZEND_ACC_PRIVATE); - zend_declare_property_null(swoole_server_ce, ZEND_STRL("onManagerStop"), ZEND_ACC_PRIVATE); - zend_declare_property_null(swoole_server_ce, ZEND_STRL("onPipeMessage"), ZEND_ACC_PRIVATE); - - zend_declare_property_null(swoole_server_ce, ZEND_STRL("setting"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_server_ce, ZEND_STRL("connections"), ZEND_ACC_PUBLIC); - 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_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); - zend_declare_property_long(swoole_server_ce, ZEND_STRL("manager_pid"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_server_ce, ZEND_STRL("worker_id"), -1, ZEND_ACC_PUBLIC); - 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_task_ce, ZEND_STRL("data"), ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_server_task_ce, ZEND_STRL("id"), -1, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_server_task_ce, ZEND_STRL("worker_id"), -1, ZEND_ACC_PUBLIC); - zend_declare_property_long(swoole_server_task_ce, ZEND_STRL("flags"), 0, ZEND_ACC_PUBLIC); - - 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); - - SW_REGISTER_LONG_CONSTANT("SWOOLE_TASK_TMPFILE", SW_TASK_TMPFILE); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TASK_SERIALIZE", SW_TASK_SERIALIZE); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TASK_NONBLOCK", SW_TASK_NONBLOCK); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TASK_CALLBACK", SW_TASK_CALLBACK); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TASK_WAITALL", SW_TASK_WAITALL); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TASK_COROUTINE", SW_TASK_COROUTINE); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TASK_PEEK", SW_TASK_PEEK); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TASK_NOREPLY", SW_TASK_NOREPLY); - - SW_REGISTER_LONG_CONSTANT("SWOOLE_WORKER_BUSY", SW_WORKER_BUSY); - SW_REGISTER_LONG_CONSTANT("SWOOLE_WORKER_IDLE", SW_WORKER_IDLE); -} - -zend_fcall_info_cache* php_swoole_server_get_fci_cache(swServer *serv, int server_fd, int event_type) -{ - swListenPort *port = (swListenPort *) serv->connection_list[server_fd].object; - php_swoole_server_port_property *property; - zend_fcall_info_cache* fci_cache; - - if (sw_unlikely(!port)) - { - return NULL; - } - if ((property = (php_swoole_server_port_property *) port->ptr) && (fci_cache = property->caches[event_type])) - { - return fci_cache; - } - else - { - return server_port_list.primary_port->caches[event_type]; - } -} - -int php_swoole_create_dir(const char* path, size_t length) -{ - if (access(path, F_OK) == 0) - { - return 0; - } -#if 1 - return php_stream_mkdir(path, 0777, PHP_STREAM_MKDIR_RECURSIVE | REPORT_ERRORS, NULL) ? 0 : -1; -#else - int startpath; - int endpath; - int i = 0; - int pathlen = length; - char curpath[128] = {}; - if ('/' != path[0]) - { - if (getcwd(curpath, sizeof(curpath)) == NULL) - { - php_swoole_sys_error(E_WARNING, "getcwd() failed"); - return -1; - } - strcat(curpath, "/"); - startpath = strlen(curpath); - strcat(curpath, path); - if (path[pathlen] != '/') - { - strcat(curpath, "/"); - } - endpath = strlen(curpath); - } - else - { - strcpy(curpath, path); - if (path[pathlen] != '/') - { - strcat(curpath, "/"); - } - startpath = 1; - endpath = strlen(curpath); - } - for (i = startpath; i < endpath ; i++ ) - { - if ('/' == curpath[i]) - { - curpath[i] = '\0'; - if (access(curpath, F_OK) != 0) - { - if (mkdir(curpath, 0755) == -1) - { - php_swoole_sys_error(E_WARNING, "mkdir(%s, 0755)", path); - return -1; - } - } - curpath[i] = '/'; - } - } - return 0; -#endif -} - -int php_swoole_task_pack(swEventData *task, zval *zdata) -{ - smart_str serialized_data = {}; - php_serialize_data_t var_hash; - - task->info.type = SW_SERVER_EVENT_TASK; - //field fd save task_id - task->info.fd = php_swoole_task_id++; - if (sw_unlikely(php_swoole_task_id >= INT_MAX)) - { - php_swoole_task_id = 0; - } - //field reactor_id save the worker_id - task->info.reactor_id = SwooleWG.id; - swTask_type(task) = 0; - - char *task_data_str; - int task_data_len = 0; - //need serialize - if (Z_TYPE_P(zdata) != IS_STRING) - { - //serialize - swTask_type(task) |= SW_TASK_SERIALIZE; - - PHP_VAR_SERIALIZE_INIT(var_hash); - php_var_serialize(&serialized_data, zdata, &var_hash); - PHP_VAR_SERIALIZE_DESTROY(var_hash); - - if (!serialized_data.s) - { - return -1; - } - task_data_str = ZSTR_VAL(serialized_data.s); - task_data_len = ZSTR_LEN(serialized_data.s); - } - else - { - task_data_str = Z_STRVAL_P(zdata); - task_data_len = Z_STRLEN_P(zdata); - } - - if (task_data_len >= (int)(SW_IPC_MAX_SIZE - sizeof(task->info))) - { - if (swTaskWorker_large_pack(task, task_data_str, task_data_len) < 0) - { - php_swoole_fatal_error(E_WARNING, "large task pack failed"); - task->info.fd = SW_ERR; - task->info.len = 0; - } - } - else - { - memcpy(task->data, task_data_str, task_data_len); - task->info.len = task_data_len; - } - smart_str_free(&serialized_data); - return task->info.fd; -} - -void php_swoole_get_recv_data(swServer *serv, zval *zdata, swEventData *req) -{ - char *data = NULL; - zend_string *worker_buffer; - - size_t length = serv->get_packet(serv, req, &data); - if (length == 0) - { - ZVAL_EMPTY_STRING(zdata); - } - else - { - if (req->info.flags & SW_EVENT_DATA_OBJ_PTR) - { - worker_buffer = (zend_string *) (data - XtOffsetOf(zend_string, val)); - ZVAL_STR(zdata, worker_buffer); - } - else - { - ZVAL_STRINGL(zdata, data, length); - } - } -} - -static sw_inline int php_swoole_check_task_param(swServer *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)) - { - php_swoole_fatal_error(E_WARNING, "worker_id must be less than task_worker_num[%u]", serv->task_worker_num); - return SW_ERR; - } - if (UNEXPECTED(swIsTaskWorker())) - { - php_swoole_fatal_error(E_WARNING, "Server->task() cannot use in the task-worker"); - return SW_ERR; - } - return SW_OK; -} - -zval* php_swoole_task_unpack(swEventData *task_result) -{ - zval *result_data, *result_unserialized_data; - char *result_data_str; - int result_data_len = 0; - php_unserialize_data_t var_hash; - swString *large_packet; - - /** - * Large result package - */ - if (swTask_type(task_result) & SW_TASK_TMPFILE) - { - large_packet = swTaskWorker_large_unpack(task_result); - /** - * unpack failed - */ - if (large_packet == NULL) - { - return NULL; - } - result_data_str = large_packet->str; - result_data_len = large_packet->length; - } - else - { - result_data_str = task_result->data; - result_data_len = task_result->info.len; - } - - if (swTask_type(task_result) & SW_TASK_SERIALIZE) - { - result_unserialized_data = sw_malloc_zval(); - - 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); - } - 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(swServer *serv, swEventData *req, double timeout, int dst_worker_id, INTERNAL_FUNCTION_PARAMETERS) -{ - swTask_type(req) |= (SW_TASK_NONBLOCK | SW_TASK_COROUTINE); - - swTaskCo *task_co = (swTaskCo *) emalloc(sizeof(swTaskCo)); - bzero(task_co, sizeof(swTaskCo)); - task_co->count = 1; - Z_LVAL(task_co->context.coro_params) = req->info.fd; - - sw_atomic_fetch_add(&serv->stats->tasking_num, 1); - if (swProcessPool_dispatch(&serv->gs->task_workers, req, &dst_worker_id) < 0) - { - sw_atomic_fetch_sub(&serv->stats->tasking_num, 1); - RETURN_FALSE; - } - else - { - task_coroutine_map[req->info.fd] = task_co; - } - - long ms = (long) (timeout * 1000); - swTimer_node *timer = swoole_timer_add(ms, SW_FALSE, php_swoole_task_onTimeout, task_co); - if (timer) - { - task_co->timer = timer; - } - PHPCoroutine::yield_m(return_value, &task_co->context); -} - -static void php_swoole_task_onTimeout(swTimer *timer, swTimer_node *tnode) -{ - swTaskCo *task_co = (swTaskCo *) tnode->data; - php_coro_context *context = &task_co->context; - zval *retval = NULL; - - //Server->taskwait, single task - if (task_co->list == NULL) - { - zval result; - ZVAL_FALSE(&result); - int ret = PHPCoroutine::resume_m(context, &result, retval); - if (ret == SW_CORO_ERR_END && retval) - { - zval_ptr_dtor(retval); - } - 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_coroutine_map.erase(task_co->list[i]); - } - } - - int ret = PHPCoroutine::resume_m(context, result, retval); - if (ret == SW_CORO_ERR_END && retval) - { - zval_ptr_dtor(retval); - } - sw_zval_free(result); - efree(task_co); -} - -extern swListenPort* php_swoole_server_port_get_and_check_ptr(zval *zobject); -extern void php_swoole_server_port_set_ptr(zval *zobject, swListenPort *port); -extern php_swoole_server_port_property* php_swoole_server_port_get_property(zval *zobject); - -static zval* php_swoole_server_add_port(swServer *serv, swListenPort *port) -{ - /* port */ - zval *zport; - zport = sw_malloc_zval(); - object_init_ex(zport, swoole_server_port_ce); - server_port_list.zobjects[server_port_list.num++] = zport; - - /* port ptr */ - php_swoole_server_port_set_ptr(zport, port); - - /* port property */ - php_swoole_server_port_property *property = php_swoole_server_port_get_property(zport); - property->serv = serv; - property->port = port; - - /* linked */ - port->ptr = property; - - zend_update_property_string(swoole_server_port_ce, zport, ZEND_STRL("host"), port->host); - zend_update_property_long(swoole_server_port_ce, zport, ZEND_STRL("port"), port->port); - zend_update_property_long(swoole_server_port_ce, zport, ZEND_STRL("type"), port->type); - zend_update_property_long(swoole_server_port_ce, zport, ZEND_STRL("sock"), port->socket->fd); - - do - { - zval *zserv = (zval *) serv->ptr2; - 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); - Z_ADDREF_P(zport); - } while (0); - - /* iterator */ - do - { - zval connection_iterator; - object_init_ex(&connection_iterator, swoole_connection_iterator_ce); - - swConnectionIterator *iterator = php_swoole_connection_iterator_get_ptr(&connection_iterator); - iterator->serv = serv; - iterator->port = port; - - zend_update_property(swoole_server_port_ce, zport, ZEND_STRL("connections"), &connection_iterator); - zval_ptr_dtor(&connection_iterator); - } while (0); - - return zport; -} - -void php_swoole_server_before_start(swServer *serv, zval *zobject) -{ - /** - * create swoole server - */ - if (swServer_create(serv) < 0) - { - php_swoole_fatal_error(E_ERROR, "failed to create the server. Error: %s", sw_error); - return; - } - -#ifdef SW_LOG_TRACE_OPEN - auto primary_port = serv->listen_list->front(); - swTraceLog(SW_TRACE_SERVER, "Create Server: host=%s, port=%d, mode=%d, type=%d", primary_port->host, (int) primary_port->port, serv->factory_mode, (int) primary_port->type); -#endif - - if (serv->enable_coroutine) - { - serv->reload_async = 1; - } - - if (serv->send_yield) - { - if (serv->onClose == NULL && swServer_support_unsafe_events(serv)) - { - serv->onClose = php_swoole_onClose; - } - } - - /** - * init method - */ - serv->create_buffers = php_swoole_server_worker_create_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; - - /** - * Master Process ID - */ - zend_update_property_long(swoole_server_ce, zobject, ZEND_STRL("master_pid"), getpid()); - - zval *zsetting = sw_zend_read_and_convert_property_array(swoole_server_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); - } - if (!zend_hash_str_exists(Z_ARRVAL_P(zsetting), ZEND_STRL("task_worker_num"))) - { - add_assoc_long(zsetting, "task_worker_num", serv->task_worker_num); - } - if (!zend_hash_str_exists(Z_ARRVAL_P(zsetting), ZEND_STRL("output_buffer_size"))) - { - add_assoc_long(zsetting, "output_buffer_size", serv->output_buffer_size); - } - if (!zend_hash_str_exists(Z_ARRVAL_P(zsetting), ZEND_STRL("max_connection"))) - { - add_assoc_long(zsetting, "max_connection", serv->max_connection); - } - - int i; - zval *zport; - zval *zport_setting; - swListenPort *port; - bool find_http_port = false; - - for (i = 1; i < server_port_list.num; i++) - { - zport = server_port_list.zobjects[i]; - zport_setting = sw_zend_read_property(swoole_server_port_ce, zport, ZEND_STRL("setting"), 0); - //use swoole_server->setting - if (zport_setting == NULL || ZVAL_IS_NULL(zport_setting)) - { - Z_TRY_ADDREF_P(zport); - sw_zend_call_method_with_1_params(zport, swoole_server_port_ce, NULL, "set", NULL, zsetting); - } - } - - for (i = 0; i < server_port_list.num; i++) - { - zport = server_port_list.zobjects[i]; - port = php_swoole_server_port_get_and_check_ptr(zport); - - if (swServer_if_require_packet_callback(serv, port, php_swoole_server_isset_callback(port, SW_SERVER_CB_onPacket))) - { - php_swoole_fatal_error(E_ERROR, "require onPacket callback"); - return; - } - -#ifdef SW_USE_OPENSSL - if (port->ssl_option.verify_peer && !port->ssl_option.client_cert_file) - { - php_swoole_fatal_error(E_ERROR, "server open verify peer require client_cert_file config"); - return; - } -#endif - if (port->open_http2_protocol && !swServer_dispatch_mode_is_mod(serv)) - { - 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); - return; - } - - if (!port->open_http_protocol) - { - port->open_http_protocol = port->open_websocket_protocol || port->open_http2_protocol; - } - if (port->open_http_protocol) - { - find_http_port = true; - if (port->open_websocket_protocol) - { - if (!php_swoole_server_isset_callback(port, SW_SERVER_CB_onMessage)) - { - php_swoole_fatal_error(E_ERROR, "require onMessage callback"); - return; - } - } - else if (port->open_http_protocol && !php_swoole_server_isset_callback(port, SW_SERVER_CB_onRequest)) - { - php_swoole_fatal_error(E_ERROR, "require onRequest callback"); - return; - } - } - else if (!port->open_redis_protocol) - { - if (swSocket_is_stream(port->type) && !php_swoole_server_isset_callback(port, SW_SERVER_CB_onReceive)) - { - php_swoole_fatal_error(E_ERROR, "require onReceive callback"); - return; - } - } - } - - if (find_http_port) - { - serv->onReceive = php_swoole_http_onReceive; - if (swServer_support_unsafe_events(serv)) - { - serv->onClose = php_swoole_http_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(swServer *serv) -{ - /* - * optional callback - */ - if (server_callbacks[SW_SERVER_CB_onStart] != NULL) - { - serv->onStart = php_swoole_onStart; - } - serv->onShutdown = php_swoole_onShutdown; - /** - * require callback, set the master/manager/worker PID - */ - serv->onWorkerStart = php_swoole_onWorkerStart; - - if (server_callbacks[SW_SERVER_CB_onBeforeReload] != NULL) - { - serv->onBeforeReload = php_swoole_onBeforeReload; - } - - if (server_callbacks[SW_SERVER_CB_onAfterReload] != NULL) - { - serv->onAfterReload = php_swoole_onAfterReload; - } - - if (server_callbacks[SW_SERVER_CB_onWorkerStop] != NULL) - { - serv->onWorkerStop = php_swoole_onWorkerStop; - } - if (server_callbacks[SW_SERVER_CB_onWorkerExit] != NULL) - { - serv->onWorkerExit = php_swoole_onWorkerExit; - } - /** - * Task Worker - */ - if (server_callbacks[SW_SERVER_CB_onTask] != NULL) - { - serv->onTask = php_swoole_onTask; - serv->onFinish = php_swoole_onFinish; - } - if (server_callbacks[SW_SERVER_CB_onWorkerError] != NULL) - { - serv->onWorkerError = php_swoole_onWorkerError; - } - if (server_callbacks[SW_SERVER_CB_onManagerStart] != NULL) - { - serv->onManagerStart = php_swoole_onManagerStart; - } - if (server_callbacks[SW_SERVER_CB_onManagerStop] != NULL) - { - serv->onManagerStop = php_swoole_onManagerStop; - } - if (server_callbacks[SW_SERVER_CB_onPipeMessage] != NULL) - { - serv->onPipeMessage = php_swoole_onPipeMessage; - } - if (serv->send_yield && swServer_support_unsafe_events(serv)) - { - serv->onBufferEmpty = php_swoole_onBufferEmpty; - } -} - -static int php_swoole_task_finish(swServer *serv, zval *zdata, swEventData *current_task) -{ - int flags = 0; - smart_str serialized_data = {}; - php_serialize_data_t var_hash; - char *data_str; - int data_len = 0; - int ret; - - //need serialize - if (Z_TYPE_P(zdata) != IS_STRING) - { - //serialize - flags |= SW_TASK_SERIALIZE; - - PHP_VAR_SERIALIZE_INIT(var_hash); - php_var_serialize(&serialized_data, zdata, &var_hash); - PHP_VAR_SERIALIZE_DESTROY(var_hash); - data_str = ZSTR_VAL(serialized_data.s); - data_len = ZSTR_LEN(serialized_data.s); - - } - else - { - data_str = Z_STRVAL_P(zdata); - data_len = Z_STRLEN_P(zdata); - } - - ret = swTaskWorker_finish(serv, data_str, data_len, flags, current_task); - smart_str_free(&serialized_data); - return ret; -} - -static void php_swoole_onPipeMessage(swServer *serv, swEventData *req) -{ - zend_fcall_info_cache *fci_cache = server_callbacks[SW_SERVER_CB_onPipeMessage]; - zval *zserv = (zval *) serv->ptr2; - zval *zdata = php_swoole_task_unpack(req); - zval args[3]; - - if (UNEXPECTED(zdata == NULL)) - { - return; - } - swTraceLog(SW_TRACE_SERVER, "PipeMessage: fd=%d|len=%d|from_id=%d|data=%.*s\n", req->info.fd, req->info.len, req->info.reactor_id, req->info.len, req->data); - args[0] = *zserv; - ZVAL_LONG(&args[1], (zend_long) req->info.reactor_id); - args[2] = *zdata; - - if (UNEXPECTED(!zend::function::call(fci_cache, 3, args, NULL, is_enable_coroutine(serv)))) - { - php_swoole_error(E_WARNING, "%s->onPipeMessage handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } - - sw_zval_free(zdata); -} - -int php_swoole_onReceive(swServer *serv, swEventData *req) -{ - zend_fcall_info_cache *fci_cache = php_swoole_server_get_fci_cache(serv, req->info.server_fd, SW_SERVER_CB_onReceive); - - if (fci_cache) - { - zval *zserv = (zval *) serv->ptr2; - zval args[4]; - - args[0] = *zserv; - ZVAL_LONG(&args[1], (zend_long) req->info.fd); - ZVAL_LONG(&args[2], (zend_long) req->info.reactor_id); - php_swoole_get_recv_data(serv, &args[3], req); - - if (UNEXPECTED(!zend::function::call(fci_cache, 4, args, NULL, SwooleG.enable_coroutine))) - { - php_swoole_error(E_WARNING, "%s->onReceive handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - serv->close(serv, req->info.fd, 0); - } - zval_ptr_dtor(&args[3]); - } - - return SW_OK; -} - -int php_swoole_onPacket(swServer *serv, swEventData *req) -{ - zval *zserv = (zval *) serv->ptr2; - zval zaddr; - - char *buffer; - serv->get_packet(serv, req, &buffer); - - array_init(&zaddr); - - swDgramPacket *packet = (swDgramPacket*) buffer; - - add_assoc_long(&zaddr, "server_socket", req->info.server_fd); - swConnection *from_sock = swServer_connection_get(serv, req->info.server_fd); - if (from_sock) - { - add_assoc_long(&zaddr, "server_port", swSocket_get_port(from_sock->socket_type, &from_sock->info)); - } - - char address[INET6_ADDRSTRLEN]; - - dgram_server_socket = req->info.server_fd; - - if (packet->socket_type == SW_SOCK_UDP) - { - inet_ntop(AF_INET, &packet->socket_addr.addr.inet_v4.sin_addr, address, sizeof(address)); - add_assoc_string(&zaddr, "address", address); - add_assoc_long(&zaddr, "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)); - add_assoc_string(&zaddr, "address", address); - add_assoc_long(&zaddr, "port", packet->socket_addr.addr.inet_v6.sin6_port); - } - else if (packet->socket_type == SW_SOCK_UNIX_DGRAM) - { - add_assoc_string(&zaddr, "address", packet->socket_addr.addr.un.sun_path); - } - - zend_fcall_info_cache *fci_cache = php_swoole_server_get_fci_cache(serv, req->info.server_fd, SW_SERVER_CB_onPacket); - zval args[3]; - args[0] = *zserv; - ZVAL_STRINGL(&args[1], packet->data, packet->length); - args[2] = zaddr; - - if (UNEXPECTED(!zend::function::call(fci_cache, 3, args, NULL, SwooleG.enable_coroutine))) - { - php_swoole_error(E_WARNING, "%s->onPipeMessage handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } - - zval_ptr_dtor(&zaddr); - zval_ptr_dtor(&args[1]); - - return SW_OK; -} - -static sw_inline void php_swoole_create_task_object(zval *ztask, swServer *serv, swEventData *req, zval *zdata) -{ - object_init_ex(ztask, swoole_server_task_ce); - 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, ztask, ZEND_STRL("worker_id"), (zend_long) req->info.reactor_id); - zend_update_property_long(swoole_server_task_ce, ztask, ZEND_STRL("id"), (zend_long) req->info.fd); - zend_update_property(swoole_server_task_ce, ztask, ZEND_STRL("data"), zdata); - zend_update_property_long(swoole_server_task_ce, ztask, ZEND_STRL("flags"), (zend_long) swTask_type(req)); -} - -static int php_swoole_onTask(swServer *serv, swEventData *req) -{ - sw_atomic_fetch_sub(&serv->stats->tasking_num, 1); - - zval *zserv = (zval *) serv->ptr2; - zval *zdata = php_swoole_task_unpack(req); - - if (zdata == NULL) - { - return SW_ERR; - } - - zval retval; - uint32_t argc; - zval argv[4]; - - if (serv->task_enable_coroutine || serv->task_use_object) - { - argc = 2; - argv[0] = *zserv; - php_swoole_create_task_object(&argv[1], serv, req, zdata); - } - 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; - } - - if (UNEXPECTED(!zend::function::call(server_callbacks[SW_SERVER_CB_onTask], argc, argv, &retval, serv->task_enable_coroutine))) - { - php_swoole_error(E_WARNING, "%s->onTask handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } - - if (argc == 2) - { - zval_ptr_dtor(&argv[1]); - } - sw_zval_free(zdata); - - if (!ZVAL_IS_NULL(&retval)) - { - php_swoole_task_finish(serv, &retval, req); - zval_ptr_dtor(&retval); - } - - return SW_OK; -} - -static int php_swoole_onFinish(swServer *serv, swEventData *req) -{ - zval *zserv = (zval *) serv->ptr2; - zval args[3]; - - zval *zdata = php_swoole_task_unpack(req); - if (zdata == NULL) - { - return SW_ERR; - } - - if (swTask_type(req) & SW_TASK_COROUTINE) - { - int task_id = req->info.fd; - auto task_co_iterator = task_coroutine_map.find(task_id); - - if (task_co_iterator == task_coroutine_map.end()) - { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_TASK_TIMEOUT, "task[%d] has expired", task_id); - _fail: - sw_zval_free(zdata); - return SW_OK; - } - swTaskCo *task_co = task_co_iterator->second; - //Server->taskwait - if (task_co->list == NULL) - { - zval *retval = NULL; - if (task_co->timer) - { - swoole_timer_del(task_co->timer); - } - php_coro_context *context = &task_co->context; - int ret = PHPCoroutine::resume_m(context, zdata, retval); - if (ret == SW_CORO_ERR_END && retval) - { - zval_ptr_dtor(retval); - } - efree(task_co); - sw_zval_free(zdata); - task_coroutine_map.erase(task_id); - 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++) - { - if (task_co->list[i] == task_id) - { - task_index = i; - break; - } - } - if (task_index < 0) - { - php_swoole_fatal_error(E_WARNING, "task[%d] is invalid", task_id); - goto _fail; - } - (void) add_index_zval(result, task_index, zdata); - efree(zdata); - task_coroutine_map.erase(task_id); - - if (php_swoole_array_length(result) == task_co->count) - { - zval *retval = NULL; - if (task_co->timer) - { - swoole_timer_del(task_co->timer); - task_co->timer = NULL; - } - php_coro_context *context = &task_co->context; - int ret = PHPCoroutine::resume_m(context, result, retval); - if (ret == SW_CORO_ERR_END && retval) - { - zval_ptr_dtor(retval); - } - sw_zval_free(result); - efree(task_co); - } - return SW_OK; - } - - args[0] = *zserv; - ZVAL_LONG(&args[1], (zend_long) req->info.fd); - args[2] = *zdata; - - zend_fcall_info_cache *fci_cache = NULL; - if (swTask_type(req) & SW_TASK_CALLBACK) - { - auto callback_iterator = task_callbacks.find(req->info.fd); - if (callback_iterator == task_callbacks.end()) - { - swTask_type(req) = swTask_type(req) & (~SW_TASK_CALLBACK); - } - else - { - fci_cache = &callback_iterator->second; - } - } - else - { - fci_cache = server_callbacks[SW_SERVER_CB_onFinish]; - } - if (UNEXPECTED(fci_cache == NULL)) - { - sw_zval_free(zdata); - php_swoole_fatal_error(E_WARNING, "require onFinish callback"); - return SW_ERR; - } - if (UNEXPECTED(!zend::function::call(fci_cache, 3, args, NULL, SwooleG.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); - task_callbacks.erase(req->info.fd); - } - sw_zval_free(zdata); - - return SW_OK; -} - -static void php_swoole_onStart(swServer *serv) -{ - swServer_lock(serv); - zval *zserv = (zval *) serv->ptr2; - zend_update_property_long(swoole_server_ce, zserv, ZEND_STRL("master_pid"), serv->gs->master_pid); - zend_update_property_long(swoole_server_ce, zserv, ZEND_STRL("manager_pid"), serv->gs->manager_pid); - if (UNEXPECTED(!zend::function::call(server_callbacks[SW_SERVER_CB_onStart], 1, zserv, NULL, false))) - { - php_swoole_error(E_WARNING, "%s->onStart handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } - swServer_unlock(serv); -} - -static void php_swoole_onManagerStart(swServer *serv) -{ - zval *zserv = (zval *) serv->ptr2; - zend_update_property_long(swoole_server_ce, zserv, ZEND_STRL("master_pid"), serv->gs->master_pid); - zend_update_property_long(swoole_server_ce, zserv, ZEND_STRL("manager_pid"), serv->gs->manager_pid); - if (UNEXPECTED(!zend::function::call(server_callbacks[SW_SERVER_CB_onManagerStart], 1, zserv, NULL, false))) - { - php_swoole_error(E_WARNING, "%s->onManagerStart handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } -} - -static void php_swoole_onManagerStop(swServer *serv) -{ - zval *zserv = (zval *) serv->ptr2; - if (UNEXPECTED(!zend::function::call(server_callbacks[SW_SERVER_CB_onManagerStop], 1, zserv, NULL, false))) - { - php_swoole_error(E_WARNING, "%s->onManagerStop handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } -} - -static void php_swoole_onShutdown(swServer *serv) -{ - swServer_lock(serv); - zval *zserv = (zval *) serv->ptr2; - if (server_callbacks[SW_SERVER_CB_onShutdown] != NULL) - { - if (UNEXPECTED(!zend::function::call(server_callbacks[SW_SERVER_CB_onShutdown], 1, zserv, NULL, false))) - { - php_swoole_error(E_WARNING, "%s->onShutdown handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } - } - swServer_unlock(serv); -} - -static void php_swoole_onWorkerStart(swServer *serv, int worker_id) -{ - zend_fcall_info_cache *fci_cache = server_callbacks[SW_SERVER_CB_onWorkerStart]; - zval *zserv = (zval *) serv->ptr2; - - zend_update_property_long(swoole_server_ce, zserv, ZEND_STRL("master_pid"), serv->gs->master_pid); - zend_update_property_long(swoole_server_ce, zserv, ZEND_STRL("manager_pid"), serv->gs->manager_pid); - zend_update_property_long(swoole_server_ce, zserv, ZEND_STRL("worker_id"), worker_id); - zend_update_property_bool(swoole_server_ce, zserv, ZEND_STRL("taskworker"), swIsTaskWorker()); - zend_update_property_long(swoole_server_ce, zserv, ZEND_STRL("worker_pid"), getpid()); - - if (!is_enable_coroutine(serv)) - { - SwooleG.enable_coroutine = 0; - PHPCoroutine::disable_hook(); - } - - if (fci_cache) - { - zval args[2]; - args[0] = *zserv; - ZVAL_LONG(&args[1], worker_id); - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, NULL, is_enable_coroutine(serv)))) - { - php_swoole_error(E_WARNING, "%s->onWorkerStart handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } - } -} - -static void php_swoole_onBeforeReload(swServer *serv) -{ - zend_fcall_info_cache *fci_cache = server_callbacks[SW_SERVER_CB_onBeforeReload]; - zval *zserv = (zval *) serv->ptr2; - - if (fci_cache) - { - zval args[1]; - args[0] = *zserv; - if (UNEXPECTED(!zend::function::call(fci_cache, 1, args, NULL, false))) - { - php_swoole_error(E_WARNING, "%s->onBeforeReload handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } - } -} - -static void php_swoole_onAfterReload(swServer *serv) -{ - zend_fcall_info_cache *fci_cache = server_callbacks[SW_SERVER_CB_onAfterReload]; - zval *zserv = (zval *) serv->ptr2; - - if (fci_cache) - { - zval args[1]; - args[0] = *zserv; - if (UNEXPECTED(!zend::function::call(fci_cache, 1, args, NULL, false))) - { - php_swoole_error(E_WARNING, "%s->onAfterReload handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } - } -} - -static void php_swoole_onWorkerStop(swServer *serv, int worker_id) -{ - if (SwooleWG.shutdown) - { - return; - } - SwooleWG.shutdown = 1; - - zval *zserv = (zval *) serv->ptr2; - zval args[2]; - args[0] = *zserv; - ZVAL_LONG(&args[1], worker_id); - if (UNEXPECTED(!zend::function::call(server_callbacks[SW_SERVER_CB_onWorkerStop], 2, args, NULL, false))) - { - php_swoole_error(E_WARNING, "%s->onWorkerStop handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } -} - -static void php_swoole_onWorkerExit(swServer *serv, int worker_id) -{ - zval *zserv = (zval *) serv->ptr2; - zval args[2]; - args[0] = *zserv; - ZVAL_LONG(&args[1], worker_id); - if (UNEXPECTED(!zend::function::call(server_callbacks[SW_SERVER_CB_onWorkerExit], 2, args, NULL, false))) - { - php_swoole_error(E_WARNING, "%s->onWorkerExit handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } -} - -static void php_swoole_onUserWorkerStart(swServer *serv, swWorker *worker) -{ - if (serv->enable_coroutine) - { - SwooleG.enable_coroutine = 1; - } - zval *object = (zval *) worker->ptr; - zend_update_property_long(swoole_process_ce, object, ZEND_STRL("id"), SwooleWG.id); - - zval *zserv = (zval *) serv->ptr2; - zend_update_property_long(swoole_server_ce, zserv, ZEND_STRL("master_pid"), serv->gs->master_pid); - zend_update_property_long(swoole_server_ce, zserv, ZEND_STRL("manager_pid"), serv->gs->manager_pid); - - php_swoole_process_start(worker, object); -} - -static void php_swoole_onWorkerError(swServer *serv, int worker_id, pid_t worker_pid, int exit_code, int signo) -{ - zval *zserv = (zval *) serv->ptr2; - zval args[5]; - - args[0] = *zserv; - ZVAL_LONG(&args[1], worker_id); - ZVAL_LONG(&args[2], worker_pid); - ZVAL_LONG(&args[3], exit_code); - ZVAL_LONG(&args[4], signo); - - if (UNEXPECTED(!zend::function::call(server_callbacks[SW_SERVER_CB_onWorkerError], 5, args, NULL, false))) - { - php_swoole_error(E_WARNING, "%s->onWorkerError handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } -} - -void php_swoole_onConnect(swServer *serv, swDataHead *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->ptr2; - zval args[3]; - args[0] = *zserv; - ZVAL_LONG(&args[1], info->fd); - ZVAL_LONG(&args[2], info->reactor_id); - if (UNEXPECTED(!zend::function::call(fci_cache, 3, args, NULL, SwooleG.enable_coroutine))) - { - php_swoole_error(E_WARNING, "%s->onConnect handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } - } -} - -void php_swoole_onClose(swServer *serv, swDataHead *info) -{ - zval *zserv = (zval *) serv->ptr2; - - if (SwooleG.enable_coroutine && serv->send_yield) - { - unordered_map *>::iterator _i_coros_list = send_coroutine_map.find(info->fd); - if (_i_coros_list != send_coroutine_map.end()) - { - list *coros_list = _i_coros_list->second; - if (coros_list->empty()) - { - php_swoole_fatal_error(E_WARNING, "send_yield[onClose]: nothing can be resumed"); - } - else - { - do - { - php_coro_context *context = coros_list->front(); - coros_list->pop_front(); - SwooleG.error = ECONNRESET; - zval_ptr_dtor(&context->coro_params); - ZVAL_NULL(&context->coro_params); - php_swoole_server_send_resume(serv, context, info->fd); - } while (!coros_list->empty()); - delete coros_list; - send_coroutine_map.erase(info->fd); - } - } - } - - zend_fcall_info_cache *fci_cache = php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onClose); - if (fci_cache) - { - zval args[3]; - args[0] = *zserv; - ZVAL_LONG(&args[1], info->fd); - ZVAL_LONG(&args[2], info->reactor_id); - if (UNEXPECTED(!zend::function::call(fci_cache, 3, args, NULL, SwooleG.enable_coroutine))) - { - php_swoole_error(E_WARNING, "%s->onClose handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } - } -} - -void php_swoole_onBufferFull(swServer *serv, swDataHead *info) -{ - zval *zserv = (zval *) serv->ptr2; - zend_fcall_info_cache *fci_cache = php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onBufferFull); - - if (fci_cache) - { - zval args[2]; - - args[0] = *zserv; - ZVAL_LONG(&args[1], info->fd); - - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, NULL, false))) - { - php_swoole_error(E_WARNING, "%s->onBufferFull handler error", SW_Z_OBJCE_NAME_VAL_P(zserv)); - } - } -} - -static void php_swoole_onSendTimeout(swTimer *timer, swTimer_node *tnode) -{ - php_coro_context *context = (php_coro_context *) tnode->data; - zval *zdata = &context->coro_params; - zval result; - zval *retval = NULL; - - SwooleG.error = ETIMEDOUT; - ZVAL_FALSE(&result); - - int fd = (int) (long) context->private_data; - - unordered_map *>::iterator _i_coros_list = send_coroutine_map.find(fd); - if (_i_coros_list != send_coroutine_map.end()) - { - list *coros_list = _i_coros_list->second; - coros_list->remove(context); - //free memory - if (coros_list->size() == 0) - { - delete coros_list; - send_coroutine_map.erase(fd); - } - } - else - { - swWarn("send coroutine[fd=%d] not exists", fd); - return; - } - - context->private_data = NULL; - - int ret = PHPCoroutine::resume_m(context, &result, retval); - if (ret == SW_CORO_ERR_END && retval) - { - zval_ptr_dtor(retval); - } - zval_ptr_dtor(zdata); - efree(context); -} - -static enum swReturn_code php_swoole_server_send_resume(swServer *serv, php_coro_context *context, int fd) -{ - char *data; - zval *zdata = &context->coro_params; - zval result; - zval *retval = NULL; - - 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; - } - int ret = serv->send(serv, fd, data, length); - if (ret < 0 && SwooleG.error == SW_ERROR_OUTPUT_SEND_YIELD && serv->send_yield) - { - return SW_CONTINUE; - } - ZVAL_BOOL(&result, ret == SW_OK); - } - - if (context->timer) - { - swoole_timer_del((swTimer_node *) context->timer); - context->timer = NULL; - } - - int ret = PHPCoroutine::resume_m(context, &result, retval); - if (ret == SW_CORO_ERR_END && retval) - { - zval_ptr_dtor(retval); - } - zval_ptr_dtor(zdata); - efree(context); - return SW_READY; -} - -void php_swoole_server_send_yield(swServer *serv, int fd, zval *zdata, zval *return_value) -{ - list *coros_list; - auto coroutine_iterator = send_coroutine_map.find(fd); - - if (coroutine_iterator == send_coroutine_map.end()) - { - coros_list = new list; - send_coroutine_map[fd] = coros_list; - } - else - { - coros_list = coroutine_iterator->second; - } - - php_coro_context *context = (php_coro_context *) emalloc(sizeof(php_coro_context)); - coros_list->push_back(context); - if (serv->send_timeout > 0) - { - context->private_data = (void*) (long) fd; - context->timer = swoole_timer_add((long) (serv->send_timeout * 1000), SW_FALSE, php_swoole_onSendTimeout, context); - } - else - { - context->timer = NULL; - } - context->coro_params = *zdata; - PHPCoroutine::yield_m(return_value, context); -} - -static int php_swoole_server_dispatch_func(swServer *serv, swConnection *conn, swSendData *data) -{ - swServer_lock(serv); - - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache*) serv->private_data_3; - zval args[4]; - zval *zserv = &args[0], *zfd = &args[1], *ztype = &args[2], *zdata = NULL; - zval retval; - zend_long worker_id = -1; - - *zserv = *((zval *) serv->ptr2); - ZVAL_LONG(zfd, (zend_long) (conn ? conn->session_id : data->info.fd)); - ZVAL_LONG(ztype, (zend_long) data->info.type); - if (sw_zend_function_max_num_args(fci_cache->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(NULL, fci_cache, zdata ? 4 : 3, args, &retval) != 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); - if (worker_id >= (zend_long) serv->worker_num) - { - php_swoole_fatal_error(E_WARNING, "invalid target worker-id[" ZEND_LONG_FMT "]", worker_id); - worker_id = -1; - } - zval_ptr_dtor(&retval); - } - if (zdata) - { - zval_ptr_dtor(zdata); - } - - swServer_unlock(serv); - - /* the exception should only be thrown after unlocked */ - if (UNEXPECTED(EG(exception))) - { - zend_exception_error(EG(exception), E_ERROR); - } - - return worker_id; -} - - -void php_swoole_onBufferEmpty(swServer *serv, swDataHead *info) -{ - zval *zserv = (zval *) serv->ptr2; - zend_fcall_info_cache *fci_cache; - - if (serv->send_yield) - { - unordered_map *>::iterator _i_coros_list = send_coroutine_map.find(info->fd); - if (_i_coros_list != send_coroutine_map.end()) - { - list *coros_list = _i_coros_list->second; - if (coros_list->empty()) - { - php_swoole_fatal_error(E_WARNING, "send_yield: nothing can be resumed"); - } - else - { - do - { - php_coro_context *context = coros_list->front(); - if (php_swoole_server_send_resume(serv, context, info->fd) == SW_CONTINUE) - { - return; - } - else - { - coros_list->pop_front(); - } - } while (!coros_list->empty()); - delete coros_list; - send_coroutine_map.erase(info->fd); - } - } - } - - fci_cache = php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onBufferEmpty); - if (fci_cache) - { - zval args[2]; - - args[0] = *zserv; - ZVAL_LONG(&args[1], info->fd); - - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, NULL, 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(swServer *serv, uint buffer_num) -{ - zend_string **buffers = (zend_string **) sw_calloc(buffer_num, sizeof(zend_string *)); - if (buffers == NULL) - { - swError("malloc for worker input_buffers failed"); - } - return (void **) buffers; -} - -static sw_inline zend_string* php_swoole_server_worker_get_input_buffer(swServer *serv, int reactor_id) -{ - zend_string **buffers = (zend_string **) SwooleWG.input_buffers; - if (serv->factory_mode == SW_MODE_BASE) - { - return buffers[0]; - } - else - { - return buffers[reactor_id]; - } -} - -static sw_inline void php_swoole_server_worker_set_buffer(swServer *serv, swDataHead *info, zend_string *addr) -{ - zend_string **buffers = (zend_string **) SwooleWG.input_buffers; - buffers[info->reactor_id] = addr; -} - -static void* php_swoole_server_worker_get_buffer(swServer *serv, swDataHead *info) -{ - zend_string *worker_buffer = php_swoole_server_worker_get_input_buffer(serv, info->reactor_id); - - if (worker_buffer == NULL) - { - worker_buffer = zend_string_alloc(info->len, 0); - worker_buffer->len = 0; - php_swoole_server_worker_set_buffer(serv, info, worker_buffer); - } - - return worker_buffer->val + worker_buffer->len; -} - -static size_t php_swoole_server_worker_get_buffer_len(swServer *serv, swDataHead *info) -{ - zend_string *worker_buffer = php_swoole_server_worker_get_input_buffer(serv, info->reactor_id); - - return worker_buffer == NULL ? 0 : worker_buffer->len; -} - -static void php_swoole_server_worker_add_buffer_len(swServer *serv, swDataHead *info, size_t len) -{ - zend_string *worker_buffer = php_swoole_server_worker_get_input_buffer(serv, info->reactor_id); - worker_buffer->len += len; -} - -static void php_swoole_server_worker_move_buffer(swServer *serv, swPipeBuffer *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, NULL); -} - -static size_t php_swoole_server_worker_get_packet(swServer *serv, swEventData *req, char **data_ptr) -{ - size_t length; - if (req->info.flags & SW_EVENT_DATA_PTR) - { - swPacket_ptr *task = (swPacket_ptr *) 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; - } - - return length; -} - -static PHP_METHOD(swoole_server, __construct) -{ - swServer *serv = php_swoole_server_get_server(ZEND_THIS); - if (serv) - { - php_swoole_fatal_error(E_ERROR, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - } - - zval *zserv = ZEND_THIS; - char *host; - size_t host_len = 0; - zend_long sock_type = SW_SOCK_TCP; - zend_long serv_port = 0; - zend_long serv_mode = SW_MODE_PROCESS; - - //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 (SwooleTG.reactor) - { - zend_throw_exception_ex(swoole_exception_ce, -2, "eventLoop has already been created. unable to create %s", SW_Z_OBJCE_NAME_VAL_P(zserv)); - RETURN_FALSE; - } - - if (sw_server() != NULL) - { - 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 - Z_PARAM_LONG(serv_port) - Z_PARAM_LONG(serv_mode) - Z_PARAM_LONG(sock_type) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (serv_mode != SW_MODE_BASE && serv_mode != SW_MODE_PROCESS) - { - php_swoole_fatal_error(E_ERROR, "invalid $mode parameters %d", (int) serv_mode); - RETURN_FALSE; - } - - serv = (swServer *) sw_malloc(sizeof(swServer)); - if (!serv) - { - zend_throw_exception_ex(swoole_exception_ce, errno, "malloc(%ld) failed", sizeof(swServer)); - RETURN_FALSE; - } - - swServer_init(serv); - serv->ptr2 = sw_zval_dup(zserv); - php_swoole_server_set_server(zserv, serv); - - if (serv_mode == SW_MODE_BASE) - { - serv->reactor_num = 1; - serv->worker_num = 1; - } - serv->factory_mode = serv_mode; - - /* primary port */ - do - { - if (serv_port == 0 && strcasecmp(host, "SYSTEMD") == 0) - { - if (swServer_add_systemd_socket(serv) <= 0) - { - php_swoole_fatal_error(E_ERROR, "failed to add systemd socket"); - RETURN_FALSE; - } - } - else - { - swListenPort *port = swServer_add_port(serv, (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; - } - } - - for (auto ls : *serv->listen_list) - { - php_swoole_server_add_port(serv, ls); - } - - server_port_list.primary_port = (php_swoole_server_port_property *) serv->listen_list->front()->ptr; - } while (0); - - /* iterator */ - do - { - zval connection_iterator; - object_init_ex(&connection_iterator, swoole_connection_iterator_ce); - - swConnectionIterator *iterator = php_swoole_connection_iterator_get_ptr(&connection_iterator); - iterator->serv = serv; - - zend_update_property(swoole_server_ce, zserv, ZEND_STRL("connections"), &connection_iterator); - zval_ptr_dtor(&connection_iterator); - } while (0); - - /* info */ - zend_update_property_stringl(swoole_server_ce, zserv, ZEND_STRL("host"), host, host_len); - zend_update_property_long(swoole_server_ce, zserv, ZEND_STRL("port"), (zend_long) serv->listen_list->front()->port); - zend_update_property_long(swoole_server_ce, zserv, ZEND_STRL("mode"), serv->factory_mode); - zend_update_property_long(swoole_server_ce, zserv, ZEND_STRL("type"), sock_type); -} - -static PHP_METHOD(swoole_server, __destruct) { } - -static PHP_METHOD(swoole_server, set) -{ - zval *zserv = ZEND_THIS; - swServer *serv = php_swoole_server_get_and_check_server(zserv); - if (serv->gs->start > 0) - { - php_swoole_fatal_error(E_WARNING, "server is running, unable to execute %s->set", SW_Z_OBJCE_NAME_VAL_P(zserv)); - RETURN_FALSE; - } - - zval *zset = NULL, *ztmp; - HashTable *vht; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY(zset) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - vht = Z_ARRVAL_P(zset); - - if (php_swoole_array_get_value(vht, "chroot", ztmp)) - { - if (SwooleG.chroot) - { - sw_free(SwooleG.chroot); - } - SwooleG.chroot = zend::string(ztmp).dup(); - } - if (php_swoole_array_get_value(vht, "user", ztmp)) - { - if (SwooleG.user) - { - sw_free(SwooleG.user); - } - SwooleG.user = zend::string(ztmp).dup(); - } - if (php_swoole_array_get_value(vht, "group", ztmp)) - { - if (SwooleG.group) - { - sw_free(SwooleG.group); - } - SwooleG.group = zend::string(ztmp).dup(); - } - if (php_swoole_array_get_value(vht, "daemonize", ztmp)) - { - serv->daemonize = zval_is_true(ztmp); - } -#ifdef SW_DEBUG - //debug - if (php_swoole_array_get_value(vht, "debug_mode", ztmp)) - { - if (zval_is_true(ztmp)) - { - SwooleG.log_level = 0; - } - } -#endif - if (php_swoole_array_get_value(vht, "trace_flags", ztmp)) - { - SwooleG.trace_flags = (uint32_t) SW_MAX(0, zval_get_long(ztmp)); - } - //pid file - if (php_swoole_array_get_value(vht, "pid_file", ztmp)) - { - if (serv->pid_file) - { - sw_free(serv->pid_file); - } - serv->pid_file = zend::string(ztmp).dup(); - } - if (php_swoole_array_get_value(vht, "reactor_num", ztmp)) - { - zend_long v = zval_get_long(ztmp); - serv->reactor_num = SW_MAX(0, SW_MIN(v, UINT16_MAX)); - if (serv->reactor_num == 0) - { - serv->reactor_num = SW_CPU_NUM; - } - } - if (php_swoole_array_get_value(vht, "single_thread", ztmp)) - { - serv->single_thread = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "worker_num", ztmp)) - { - zend_long v = zval_get_long(ztmp); - serv->worker_num = SW_MAX(0, SW_MIN(v, UINT32_MAX)); - if (serv->worker_num == 0) - { - serv->worker_num = SW_CPU_NUM; - } - } - if (php_swoole_array_get_value(vht, "max_wait_time", ztmp)) - { - zend_long v = zval_get_long(ztmp); - 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); - serv->max_queued_bytes = SW_MAX(0, SW_MIN(v, UINT32_MAX)); - } - if (php_swoole_array_get_value(vht, "enable_coroutine", ztmp)) - { - serv->enable_coroutine = SwooleG.enable_coroutine = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "max_coro_num", ztmp) || php_swoole_array_get_value(vht, "max_coroutine", ztmp)) - { - zend_long max_num; - max_num = zval_get_long(ztmp); - PHPCoroutine::set_max_num(max_num <= 0 ? SW_DEFAULT_MAX_CORO_NUM : max_num); - } - if (php_swoole_array_get_value(vht, "hook_flags", ztmp)) - { - PHPCoroutine::config.hook_flags = zval_get_long(ztmp); - } - if (php_swoole_array_get_value(vht, "send_timeout", ztmp)) - { - serv->send_timeout = zval_get_double(ztmp); - } - if (php_swoole_array_get_value(vht, "dispatch_mode", ztmp)) - { - zend_long v = zval_get_long(ztmp); - serv->dispatch_mode = SW_MAX(0, SW_MIN(v, UINT8_MAX)); - } - 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)) - { - php_swoole_error(E_WARNING, "'send_yield' option can only be set when using dispatch_mode=2/4"); - serv->send_yield = 0; - } - } - if (php_swoole_array_get_value(vht, "dispatch_func", ztmp)) - { - swServer_dispatch_function c_dispatch_func = NULL; - while(1) - { - if (Z_TYPE_P(ztmp) == IS_STRING) - { - c_dispatch_func = (swServer_dispatch_function) swoole_get_function(Z_STRVAL_P(ztmp), Z_STRLEN_P(ztmp)); - if (c_dispatch_func) - { - break; - } - } -#ifdef ZTS - if (serv->factory_mode == SW_MODE_PROCESS && !serv->single_thread) - { - php_swoole_fatal_error(E_ERROR, "option [dispatch_func] does not support with ZTS"); - } -#endif - char *func_name = NULL; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(ztmp, NULL, 0, &func_name, NULL, fci_cache, NULL)) - { - php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name); - return; - } - efree(func_name); - sw_zend_fci_cache_persist(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); - } - serv->private_data_3 = (void *) fci_cache; - c_dispatch_func = php_swoole_server_dispatch_func; - break; - } - serv->dispatch_func = c_dispatch_func; - } - if (php_swoole_array_get_value(vht, "log_file", ztmp)) - { - if (SwooleG.log_file) - { - sw_free(SwooleG.log_file); - } - SwooleG.log_file = zend::string(ztmp).dup(); - } - if (php_swoole_array_get_value(vht, "log_level", ztmp)) - { - zend_long level; - level = zval_get_long(ztmp); - SwooleG.log_level = (uint32_t) (level < 0 ? UINT32_MAX : level); - } - /** - * for dispatch_mode = 1/3 - */ - if (php_swoole_array_get_value(vht, "discard_timeout_request", ztmp)) - { - serv->discard_timeout_request = zval_is_true(ztmp); - } - //onConnect/onClose event - if (php_swoole_array_get_value(vht, "enable_unsafe_event", ztmp)) - { - serv->enable_unsafe_event = zval_is_true(ztmp); - } - //delay receive - if (php_swoole_array_get_value(vht, "enable_delay_receive", ztmp)) - { - serv->enable_delay_receive = zval_is_true(ztmp); - } -#if defined(__linux__) and defined(HAVE_REUSEPORT) - if (php_swoole_array_get_value(vht, "enable_reuse_port", ztmp)) - { - serv->enable_reuse_port = zval_is_true(ztmp); - } -#endif - //task use object - if (php_swoole_array_get_value(vht, "task_use_object", ztmp)) - { - serv->task_use_object = zval_is_true(ztmp); - } - //task coroutine - if (php_swoole_array_get_value(vht, "task_enable_coroutine", ztmp)) - { - serv->task_enable_coroutine = zval_is_true(ztmp); - } - //task_worker_num - if (php_swoole_array_get_value(vht, "task_worker_num", ztmp)) - { - zend_long v = zval_get_long(ztmp); - serv->task_worker_num = SW_MAX(0, SW_MIN(v, UINT32_MAX)); - } - //task ipc mode, 1,2,3 - if (php_swoole_array_get_value(vht, "task_ipc_mode", ztmp)) - { - zend_long v = zval_get_long(ztmp); - serv->task_ipc_mode = SW_MAX(0, SW_MIN(v, UINT8_MAX)); - } - /** - * Temporary file directory for task_worker - */ - if (php_swoole_array_get_value(vht, "task_tmpdir", ztmp)) - { - zend::string str_v(ztmp); - if (php_swoole_create_dir(str_v.val(), str_v.len()) < 0) - { - php_swoole_fatal_error(E_ERROR, "Unable to create task_tmpdir[%s]", str_v.val()); - return; - } - if (SwooleG.task_tmpdir) - { - sw_free(SwooleG.task_tmpdir); - } - SwooleG.task_tmpdir = (char*) sw_malloc(str_v.len() + sizeof(SW_TASK_TMP_FILE) + 1); - if (!SwooleG.task_tmpdir) - { - php_swoole_fatal_error(E_ERROR, "malloc() failed"); - RETURN_FALSE; - } - SwooleG.task_tmpdir_len = sw_snprintf(SwooleG.task_tmpdir, SW_TASK_TMPDIR_SIZE, "%s/swoole.task.XXXXXX", str_v.val()) + 1; - } - //task_max_request - if (php_swoole_array_get_value(vht, "task_max_request", ztmp)) - { - zend_long v = zval_get_long(ztmp); - serv->task_max_request = SW_MAX(0, SW_MIN(v, UINT32_MAX)); - //task_max_request_grace - if (php_swoole_array_get_value(vht, "task_max_request_grace", ztmp)) - { - zend_long v = zval_get_long(ztmp); - serv->task_max_request_grace = SW_MAX(0, SW_MIN(v, UINT32_MAX)); - } - else if (serv->task_max_request > SW_WORKER_MIN_REQUEST) - { - serv->task_max_request_grace = serv->task_max_request / 2; - } - } - //max_connection - if (php_swoole_array_get_value(vht, "max_connection", ztmp) || php_swoole_array_get_value(vht, "max_conn", ztmp)) - { - zend_long v = zval_get_long(ztmp); - serv->max_connection = SW_MAX(0, SW_MIN(v, UINT32_MAX)); - } - //heartbeat_check_interval - 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)) - { - 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; - } - //max_request - if (php_swoole_array_get_value(vht, "max_request", ztmp)) - { - zend_long v = zval_get_long(ztmp); - serv->max_request = SW_MAX(0, SW_MIN(v, UINT32_MAX)); - //max_request_grace - if (php_swoole_array_get_value(vht, "max_request_grace", ztmp)) - { - zend_long v = zval_get_long(ztmp); - serv->max_request_grace = SW_MAX(0, SW_MIN(v, UINT32_MAX)); - } - else if (serv->max_request > SW_WORKER_MIN_REQUEST) - { - serv->max_request_grace = serv->max_request / 2; - } - } - //reload async - if (php_swoole_array_get_value(vht, "reload_async", ztmp)) - { - serv->reload_async = zval_is_true(ztmp); - } - //cpu affinity - if (php_swoole_array_get_value(vht, "open_cpu_affinity", ztmp)) - { - serv->open_cpu_affinity = zval_is_true(ztmp); - } - //cpu affinity set - if (php_swoole_array_get_value(vht, "cpu_affinity_ignore", ztmp)) - { - int ignore_num = zend_hash_num_elements(Z_ARRVAL_P(ztmp)); - if (ignore_num >= SW_CPU_NUM) - { - php_swoole_fatal_error(E_ERROR, "cpu_affinity_ignore num must be less than cpu num (%d)", SW_CPU_NUM); - RETURN_FALSE; - } - int available_num = SW_CPU_NUM - ignore_num; - int *available_cpu = (int *) sw_malloc(sizeof(int) * available_num); - if (!available_cpu) - { - php_swoole_fatal_error(E_WARNING, "malloc() failed"); - RETURN_FALSE; - } - int flag, i, available_i = 0; - - zval *zval_core = NULL; - for (i = 0; i < SW_CPU_NUM; i++) - { - flag = 1; - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmp), zval_core) - if (i == zval_get_long(zval_core)) - { - flag = 0; - break; - } - SW_HASHTABLE_FOREACH_END(); - if (flag) - { - available_cpu[available_i] = i; - available_i++; - } - } - serv->cpu_affinity_available_num = available_num; - if (serv->cpu_affinity_available) - { - sw_free(serv->cpu_affinity_available); - } - serv->cpu_affinity_available = available_cpu; - } - //parse cookie header - if (php_swoole_array_get_value(vht, "http_parse_cookie", ztmp)) - { - serv->http_parse_cookie = zval_is_true(ztmp); - } - //parse x-www-form-urlencoded form data - if (php_swoole_array_get_value(vht, "http_parse_post", ztmp)) - { - serv->http_parse_post = zval_is_true(ztmp); - } - //parse multipart/form-data file uploads - if (php_swoole_array_get_value(vht, "http_parse_files", ztmp)) - { - serv->http_parse_files = zval_is_true(ztmp); - } -#ifdef SW_HAVE_COMPRESSION - //http content compression - if (php_swoole_array_get_value(vht, "http_compression", ztmp)) - { - serv->http_compression = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "http_compression_level", ztmp) || php_swoole_array_get_value(vht, "http_gzip_level", ztmp)) - { - zend_long level = zval_get_long(ztmp); - if (level > UINT8_MAX) - { - level = UINT8_MAX; - } - else if (level < 0) - { - level = 0; - } - serv->http_compression_level = level; - } -#endif - -#ifdef SW_HAVE_ZLIB - if (php_swoole_array_get_value(vht, "websocket_compression", ztmp)) - { - serv->websocket_compression = zval_is_true(ztmp); - } -#endif - - //temporary directory for HTTP uploaded file. - if (php_swoole_array_get_value(vht, "upload_tmp_dir", ztmp)) - { - zend::string str_v(ztmp); - if (php_swoole_create_dir(str_v.val(), str_v.len()) < 0) - { - php_swoole_fatal_error(E_ERROR, "Unable to create upload_tmp_dir[%s]", str_v.val()); - return; - } - if (serv->upload_tmp_dir) - { - sw_free(serv->upload_tmp_dir); - } - serv->upload_tmp_dir = str_v.dup(); - } - /** - * http static file handler - */ - if (php_swoole_array_get_value(vht, "enable_static_handler", ztmp)) - { - serv->enable_static_handler = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "document_root", ztmp)) - { - zend::string str_v(ztmp); - if (str_v.len() >= PATH_MAX) - { - php_swoole_fatal_error(E_ERROR, "The length of document_root must be less than %d", PATH_MAX); - return; - } - if (serv->document_root) - { - sw_free(serv->document_root); - } - serv->document_root = (char *) sw_malloc(PATH_MAX); - if (!serv->document_root) - { - php_swoole_fatal_error(E_ERROR, "malloc() failed"); - RETURN_FALSE; - } - if (!realpath(str_v.val(), serv->document_root)) - { - php_swoole_fatal_error(E_ERROR, "document_root[%s] does not exist", serv->document_root); - sw_free(serv->document_root); - serv->document_root = nullptr; - RETURN_FALSE; - } - serv->document_root_len = strlen(serv->document_root); - } - if (php_swoole_array_get_value(vht, "http_autoindex", ztmp)) - { - serv->http_autoindex = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "http_index_files", ztmp)) - { - if (ZVAL_IS_ARRAY(ztmp)) - { - zval *_http_index_files; - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmp), _http_index_files) - zend::string __http_index_files(_http_index_files); - if (__http_index_files.len() > 0) - { - swServer_http_static_handler_add_http_index_files(serv, __http_index_files.val(), __http_index_files.len()); - } - SW_HASHTABLE_FOREACH_END(); - } - else - { - php_swoole_fatal_error(E_ERROR, "http_index_files must be array"); - RETURN_FALSE; - } - } - /** - * [static_handler] locations - */ - if (php_swoole_array_get_value(vht, "static_handler_locations", ztmp)) - { - if (ZVAL_IS_ARRAY(ztmp)) - { - zval *_location; - SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmp), _location) - zend::string __location(_location); - if (__location.len() > 0 && __location.val()[0] == '/') - { - swServer_http_static_handler_add_location(serv, __location.val(), __location.len()); - } - SW_HASHTABLE_FOREACH_END(); - } - else - { - php_swoole_fatal_error(E_ERROR, "static_handler_locations num must be array"); - RETURN_FALSE; - } - } - /** - * buffer input size - */ - 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); - serv->input_buffer_size = SW_MAX(0, SW_MIN(v, UINT32_MAX)); - } - /** - * buffer output size - */ - 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); - serv->output_buffer_size = SW_MAX(0, SW_MIN(v, UINT32_MAX)); - } - //message queue key - if (php_swoole_array_get_value(vht, "message_queue_key", ztmp)) - { - zend_long v = zval_get_long(ztmp); - serv->message_queue_key = SW_MAX(0, SW_MIN(v, INT64_MAX)); - } - - if (serv->task_enable_coroutine - && (serv->task_ipc_mode == SW_TASK_IPC_MSGQUEUE || serv->task_ipc_mode == SW_TASK_IPC_PREEMPTIVE)) - { - php_swoole_fatal_error(E_ERROR, "cannot use msgqueue when task_enable_coroutine is enable"); - RETURN_FALSE; - } - - sw_zend_call_method_with_1_params(server_port_list.zobjects[0], swoole_server_port_ce, NULL, "set", NULL, zset); - - zval *zsetting = sw_zend_read_and_convert_property_array(swoole_server_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - php_array_merge(Z_ARRVAL_P(zsetting), Z_ARRVAL_P(zset)); - - RETURN_TRUE; -} - -static PHP_METHOD(swoole_server, on) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (serv->gs->start > 0) - { - php_swoole_fatal_error(E_WARNING, "server is running, unable to register event callback function"); - RETURN_FALSE; - } - - zval *name; - zval *cb; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &name, &cb) == FAILURE) - { - RETURN_FALSE; - } - - char *func_name = NULL; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(cb, NULL, 0, &func_name, NULL, fci_cache, NULL)) - { - php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name); - return; - } - efree(func_name); - - zend::string _event_name_ori(name); - zend::string _event_name_tolower(zend_string_tolower(_event_name_ori.get())); - - auto i = server_event_map.find(_event_name_tolower.to_std_string()); - if (i == server_event_map.end()) - { - zval *port_object = server_port_list.zobjects[0]; - zval retval; - efree(fci_cache); - sw_zend_call_method_with_2_params(port_object, swoole_server_port_ce, NULL, "on", &retval, name, cb); - RETURN_BOOL(Z_BVAL_P(&retval)); - } - else - { - int event_type = i->second.type; - string property_name = "on" + i->second.name; - - zend_update_property(swoole_server_ce, ZEND_THIS, property_name.c_str(), property_name.length(), cb); - - if (server_callbacks[event_type]) - { - efree(server_callbacks[event_type]); - } - server_callbacks[event_type] = fci_cache; - - RETURN_TRUE; - } -} - -static PHP_METHOD(swoole_server, getCallback) -{ - zval *name; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ZVAL(name) - 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())); - auto i = server_event_map.find(_event_name_tolower.to_std_string()); - if (i != server_event_map.end()) - { - string property_name = "on" + i->second.name; - // Notice: we should use Z_OBJCE_P instead of swoole_server_ce, because we need to consider the subclasses. - zval rv, *property = zend_read_property(Z_OBJCE_P(ZEND_THIS), ZEND_THIS, property_name.c_str(), property_name.length(), 1, &rv); - if (!ZVAL_IS_NULL(property)) - { - RETURN_ZVAL(property, 1, 0); - } - } - sw_zend_call_method_with_1_params(server_port_list.zobjects[0], swoole_server_port_ce, NULL, "getcallback", return_value, name); -} - -static PHP_METHOD(swoole_server, listen) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (serv->gs->start > 0) - { - php_swoole_fatal_error(E_WARNING, "server is running, can't add listener"); - RETURN_FALSE; - } - - char *host; - size_t host_len; - long sock_type; - long port; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll", &host, &host_len, &port, &sock_type) == FAILURE) - { - RETURN_FALSE; - } - - swListenPort *ls = swServer_add_port(serv, (enum swSocket_type) sock_type, host, (int) port); - if (!ls) - { - RETURN_FALSE; - } - - zval *port_object = php_swoole_server_add_port(serv, ls); - RETURN_ZVAL(port_object, 1, NULL); -} - -extern swWorker* php_swoole_process_get_and_check_worker(zval *zobject); - -static PHP_METHOD(swoole_server, addProcess) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (serv->gs->start > 0) - { - php_swoole_fatal_error(E_WARNING, "server is running, can't add process"); - RETURN_FALSE; - } - - zval *process = NULL; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &process) == FAILURE) - { - RETURN_FALSE; - } - - if (ZVAL_IS_NULL(process)) - { - php_swoole_fatal_error(E_WARNING, "the first parameter can't be empty"); - RETURN_FALSE; - } - - if (!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 == NULL) - { - serv->onUserWorkerStart = php_swoole_onUserWorkerStart; - } - - zval *tmp_process = (zval *) emalloc(sizeof(zval)); - memcpy(tmp_process, process, sizeof(zval)); - process = tmp_process; - - serv_user_process.push_back(process); - - Z_TRY_ADDREF_P(process); - - swWorker *worker = php_swoole_process_get_and_check_worker(process); - worker->ptr = process; - - int id = swServer_add_worker(serv, worker); - if (id < 0) - { - php_swoole_fatal_error(E_WARNING, "swServer_add_worker failed"); - RETURN_FALSE; - } - zend_update_property_long(swoole_process_ce, 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); -} - -static inline zend_bool is_http_server(zval *zobject) -{ - return instanceof_function(Z_OBJCE_P(zobject), swoole_http_server_ce); -} - -static PHP_METHOD(swoole_server, start) -{ - zval *zserv = ZEND_THIS; - swServer *serv = php_swoole_server_get_and_check_server(zserv); - if (serv->gs->start > 0) - { - php_swoole_fatal_error(E_WARNING, "server is running, unable to execute %s->start", SW_Z_OBJCE_NAME_VAL_P(zserv)); - RETURN_FALSE; - } - if (serv->gs->shutdown > 0) - { - php_swoole_fatal_error(E_WARNING, "server have been shutdown, unable to execute %s->start", SW_Z_OBJCE_NAME_VAL_P(zserv)); - RETURN_FALSE; - } - - php_swoole_server_register_callbacks(serv); - - serv->onReceive = php_swoole_onReceive; - - 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); - - enum protocol_flags - { - SW_HTTP2_PROTOCOL = 1u << 1, - SW_WEBSOCKET_PROTOCOL = 1u << 2 - }; - uint8_t protocol_flag = 0; - auto primary_port = serv->listen_list->front(); - 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(zserv)) - { - add_assoc_bool(zsetting, "open_websocket_protocol", 1); - protocol_flag |= SW_WEBSOCKET_PROTOCOL; - } - swPort_clear_protocol(primary_port); - 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); - } - - php_swoole_server_before_start(serv, zserv); - - if (swServer_start(serv) < 0) - { - php_swoole_fatal_error(E_ERROR, "failed to start server. Error: %s", sw_error); - } - - RETURN_TRUE; -} - -static PHP_METHOD(swoole_server, send) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - int ret; - zend_long fd; - zval *zfd; - zval *zdata; - zend_long server_socket = -1; - - ZEND_PARSE_PARAMETERS_START(2, 3) - Z_PARAM_ZVAL(zfd) - Z_PARAM_ZVAL(zdata) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(server_socket) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (UNEXPECTED(ZVAL_IS_NULL(zfd))) - { - php_swoole_fatal_error(E_WARNING, "fd can not be null"); - 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"); - RETURN_FALSE; - } - - //UNIX DGRAM SOCKET - if (serv->have_dgram_sock && Z_TYPE_P(zfd) == IS_STRING && Z_STRVAL_P(zfd)[0] == '/') - { - struct sockaddr_un addr_un; - memcpy(addr_un.sun_path, Z_STRVAL_P(zfd), Z_STRLEN_P(zfd)); - addr_un.sun_family = AF_UNIX; - addr_un.sun_path[Z_STRLEN_P(zfd)] = 0; - ret = swSocket_sendto_blocking( - server_socket == -1 ? dgram_server_socket : server_socket, - data, length, 0, - (struct sockaddr *) &addr_un, sizeof(addr_un) - ); - SW_CHECK_RETURN(ret); - } - - fd = zval_get_long(zfd); - if (UNEXPECTED((int) fd <= 0)) - { - php_swoole_fatal_error(E_WARNING, "invalid fd[" ZEND_LONG_FMT "]", fd); - RETURN_FALSE; - } - ret = serv->send(serv, fd, data, length); - if (ret < 0 && SwooleG.error == SW_ERROR_OUTPUT_SEND_YIELD) - { - zval_add_ref(zdata); - php_swoole_server_send_yield(serv, fd, zdata, return_value); - } - else - { - SW_CHECK_RETURN(ret); - } -} - -static PHP_METHOD(swoole_server, sendto) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - char *ip; - size_t ip_len; - zend_long port; - char *data; - size_t len; - zend_long server_socket = -1; - - zend_bool ipv6 = 0; - - ZEND_PARSE_PARAMETERS_START(3, 4) - Z_PARAM_STRING(ip, ip_len) - Z_PARAM_LONG(port) - Z_PARAM_STRING(data, len) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(server_socket) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (len == 0) - { - php_swoole_fatal_error(E_WARNING, "data is empty"); - RETURN_FALSE; - } - - if (strchr(ip, ':')) - { - ipv6 = 1; - } - - if (ipv6 == 0 && serv->udp_socket_ipv4 <= 0) - { - php_swoole_fatal_error(E_WARNING, "UDP listener has to be added before executing sendto"); - RETURN_FALSE; - } - else if (ipv6 == 1 && serv->udp_socket_ipv6 <= 0) - { - php_swoole_fatal_error(E_WARNING, "UDP6 listener has to be added before executing sendto"); - RETURN_FALSE; - } - - if (server_socket < 0) - { - server_socket = ipv6 ? serv->udp_socket_ipv6 : serv->udp_socket_ipv4; - } - - int ret; - if (ipv6) - { - ret = swSocket_udp_sendto6(server_socket, ip, port, data, len); - } - else - { - ret = swSocket_udp_sendto(server_socket, ip, port, data, len); - } - SW_CHECK_RETURN(ret); -} - -static PHP_METHOD(swoole_server, sendfile) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - zend_long fd; - char *filename; - size_t len; - 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; - } - - if (swIsMaster()) - { - php_swoole_fatal_error(E_WARNING, "can't sendfile[%s] to the connections in master process", filename); - RETURN_FALSE; - } - - SW_CHECK_RETURN(serv->sendfile(serv, (int) fd, filename, len, offset, length)); -} - -static PHP_METHOD(swoole_server, close) -{ - if (swIsMaster()) - { - php_swoole_fatal_error(E_WARNING, "can't close the connections in master process"); - RETURN_FALSE; - } - - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - zend_long fd; - zend_bool reset = SW_FALSE; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_LONG(fd) - Z_PARAM_OPTIONAL - Z_PARAM_BOOL(reset) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - SW_CHECK_RETURN(serv->close(serv, (int )fd, (int )reset)); -} - -static PHP_METHOD(swoole_server, pause) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - zend_long fd; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &fd) == FAILURE) - { - RETURN_FALSE; - } - - SW_CHECK_RETURN(serv->feedback(serv, fd, SW_SERVER_EVENT_PAUSE_RECV)); -} - -static PHP_METHOD(swoole_server, resume) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - zend_long fd; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &fd) == FAILURE) - { - RETURN_FALSE; - } - - SW_CHECK_RETURN(serv->feedback(serv, fd, SW_SERVER_EVENT_RESUME_RECV)); -} - -static PHP_METHOD(swoole_server, stats) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - uint32_t i; - - array_init(return_value); - add_assoc_long_ex(return_value, ZEND_STRL("start_time"), serv->stats->start_time); - add_assoc_long_ex(return_value, ZEND_STRL("connection_num"), serv->stats->connection_num); - add_assoc_long_ex(return_value, ZEND_STRL("accept_count"), serv->stats->accept_count); - add_assoc_long_ex(return_value, ZEND_STRL("close_count"), serv->stats->close_count); - /** - * reset - */ - int tasking_num = serv->stats->tasking_num; - if (tasking_num < 0) - { - tasking_num = serv->stats->tasking_num = 0; - } - - uint32_t worker_num = serv->worker_num; - uint32_t idle_worker_num = 0; - add_assoc_long_ex(return_value, ZEND_STRL("worker_num"), worker_num); - for (i = 0; i < worker_num; i++) - { - swWorker *worker = swServer_get_worker(serv, i); - if (worker->status == SW_WORKER_IDLE) - { - 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("tasking_num"), tasking_num); - add_assoc_long_ex(return_value, ZEND_STRL("request_count"), serv->stats->request_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) - { - int queue_num = -1; - int queue_bytes = -1; - if (swMsgQueue_stat(serv->gs->task_workers.queue, &queue_num, &queue_bytes) == 0) - { - add_assoc_long_ex(return_value, ZEND_STRL("task_queue_num"), queue_num); - add_assoc_long_ex(return_value, ZEND_STRL("task_queue_bytes"), queue_bytes); - } - } - - if (serv->task_worker_num > 0) - { - idle_worker_num = 0; - for (i = worker_num; i < (worker_num + serv->task_worker_num); i++) - { - swWorker *worker = swServer_get_worker(serv, i); - if (worker->status == SW_WORKER_IDLE) - { - idle_worker_num++; - } - } - add_assoc_long_ex(return_value, ZEND_STRL("task_idle_worker_num"), idle_worker_num); - } - - add_assoc_long_ex(return_value, ZEND_STRL("coroutine_num"), Coroutine::count()); -} - -static PHP_METHOD(swoole_server, reload) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - zend_bool only_reload_taskworker = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &only_reload_taskworker) == FAILURE) - { - 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; -} - -static PHP_METHOD(swoole_server, heartbeat) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - zend_bool close_connection = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &close_connection) == FAILURE) - { - RETURN_FALSE; - } - - if (serv->heartbeat_idle_time < 1) - { - RETURN_FALSE; - } - - int serv_max_fd = swServer_get_maxfd(serv); - int serv_min_fd = swServer_get_minfd(serv); - - array_init(return_value); - - int fd; - int checktime = (int) time(NULL) - serv->heartbeat_idle_time; - - for (fd = serv_min_fd; fd <= serv_max_fd; fd++) - { - swTrace("heartbeat check fd=%d", fd); - swConnection *conn = swServer_connection_get(serv, fd); - if (swServer_connection_valid(serv, conn)) - { - if (conn->protect || conn->last_time > checktime) - { - continue; - } - if (close_connection) - { - conn->close_force = 1; - serv->factory.end(&serv->factory, fd); - } - add_next_index_long(return_value, conn->session_id); - } - } -} - -static PHP_METHOD(swoole_server, taskwait) -{ - if (!swIsWorker()) - { - php_swoole_fatal_error(E_WARNING, "taskwait method can only be used in the worker process"); - RETURN_FALSE; - } - - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - swEventData 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; - } - - if (php_swoole_check_task_param(serv, dst_worker_id) < 0) - { - RETURN_FALSE; - } - - if (php_swoole_task_pack(&buf, zdata) < 0) - { - RETURN_FALSE; - } - - int _dst_worker_id = (int) dst_worker_id; - - //coroutine - if (PHPCoroutine::get_cid() >= 0) - { - php_swoole_task_wait_co(serv, &buf, timeout, _dst_worker_id, INTERNAL_FUNCTION_PARAM_PASSTHRU); - return; - } - - int task_id = buf.info.fd; - - uint64_t notify; - swEventData *task_result = &(serv->task_result[SwooleWG.id]); - bzero(task_result, sizeof(swEventData)); - swPipe *task_notify_pipe = &serv->task_notify[SwooleWG.id]; - swSocket *task_notify_socket = task_notify_pipe->getSocket(task_notify_pipe, SW_PIPE_WORKER); - - //clear history task - while (read(task_notify_socket->fd, ¬ify, sizeof(notify)) > 0) {} - - sw_atomic_fetch_add(&serv->stats->tasking_num, 1); - - if (swProcessPool_dispatch_blocking(&serv->gs->task_workers, &buf, &_dst_worker_id) >= 0) - { - task_notify_pipe->timeout = timeout; - while(1) - { - if (task_notify_pipe->read(task_notify_pipe, ¬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 == NULL) - { - 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; - } - } - } - else - { - sw_atomic_fetch_sub(&serv->stats->tasking_num, 1); - } - RETURN_FALSE; -} - -static PHP_METHOD(swoole_server, taskWaitMulti) -{ - if (!swIsWorker()) - { - php_swoole_fatal_error(E_WARNING, "taskWaitMulti method can only be used in the worker process"); - RETURN_FALSE; - } - - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - swEventData 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; - } - - array_init(return_value); - - int dst_worker_id; - int task_id; - int i = 0; - int n_task = php_swoole_array_length(ztasks); - - if (n_task >= SW_MAX_CONCURRENT_TASK) - { - php_swoole_fatal_error(E_WARNING, "too many concurrent tasks"); - RETURN_FALSE; - } - - int list_of_id[SW_MAX_CONCURRENT_TASK] = {}; - - uint64_t notify; - swEventData *task_result = &(serv->task_result[SwooleWG.id]); - bzero(task_result, sizeof(swEventData)); - swPipe *task_notify_pipe = &serv->task_notify[SwooleWG.id]; - swWorker *worker = swServer_get_worker(serv, SwooleWG.id); - - char _tmpfile[sizeof(SW_TASK_TMP_FILE)] = SW_TASK_TMP_FILE; - int _tmpfile_fd = swoole_tmpfile(_tmpfile); - if (_tmpfile_fd < 0) - { - RETURN_FALSE; - } - close(_tmpfile_fd); - int *finish_count = (int *) task_result->data; - - worker->lock.lock(&worker->lock); - *finish_count = 0; - memcpy(task_result->data + 4, _tmpfile, sizeof(_tmpfile)); - worker->lock.unlock(&worker->lock); - - //clear history task - swSocket *task_notify_socket = task_notify_pipe->getSocket(task_notify_pipe, SW_PIPE_WORKER); - while (read(task_notify_socket->fd, ¬ify, sizeof(notify)) > 0); - - 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, "task pack failed"); - goto _fail; - } - swTask_type(&buf) |= SW_TASK_WAITALL; - dst_worker_id = -1; - sw_atomic_fetch_add(&serv->stats->tasking_num, 1); - if (swProcessPool_dispatch_blocking(&serv->gs->task_workers, &buf, &dst_worker_id) < 0) - { - php_swoole_sys_error(E_WARNING, "taskwait failed"); - task_id = -1; - _fail: - add_index_bool(return_value, i, 0); - n_task --; - } - else - { - sw_atomic_fetch_sub(&serv->stats->tasking_num, 1); - } - list_of_id[i] = task_id; - i++; - SW_HASHTABLE_FOREACH_END(); - - if (n_task == 0) - { - SwooleG.error = SW_ERROR_TASK_DISPATCH_FAIL; - RETURN_FALSE; - } - - double _now = swoole_microtime(); - while (n_task > 0) - { - task_notify_pipe->timeout = timeout; - int ret = task_notify_pipe->read(task_notify_pipe, ¬ify, sizeof(notify)); - if (ret > 0 && *finish_count < n_task) - { - if (swoole_microtime() - _now < timeout) - { - continue; - } - } - break; - } - - worker->lock.lock(&worker->lock); - swString *content = swoole_file_get_contents(_tmpfile); - worker->lock.unlock(&worker->lock); - - if (content == NULL) - { - RETURN_FALSE; - } - - swEventData *result; - zval *zdata; - uint32_t j; - - do - { - result = (swEventData *) (content->str + content->offset); - task_id = result->info.fd; - zdata = php_swoole_task_unpack(result); - if (zdata == NULL) - { - goto _next; - } - 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); - _next: - content->offset += sizeof(swDataHead) + result->info.len; - } while (content->offset < 0 || (size_t) content->offset < content->length); - //free memory - swString_free(content); - //delete tmp file - unlink(_tmpfile); -} - -static PHP_METHOD(swoole_server, taskCo) -{ - if (!swIsWorker()) - { - php_swoole_fatal_error(E_WARNING, "taskCo method can only be used in the worker process"); - RETURN_FALSE; - } - - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - zval *ztasks; - zval *ztask; - double timeout = SW_TASKWAIT_TIMEOUT; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|d", &ztasks, &timeout) == FAILURE) - { - RETURN_FALSE; - } - - int dst_worker_id = -1; - int task_id; - int i = 0; - uint32_t n_task = php_swoole_array_length(ztasks); - - swEventData 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) - { - RETURN_FALSE; - } - - int *list = (int *) ecalloc(n_task, sizeof(int)); - if (list == NULL) - { - RETURN_FALSE; - } - - swTaskCo *task_co = (swTaskCo *) emalloc(sizeof(swTaskCo)); - if (task_co == NULL) - { - efree(list); - RETURN_FALSE; - } - - 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->stats->tasking_num, 1); - if (swProcessPool_dispatch(&serv->gs->task_workers, &buf, &dst_worker_id) < 0) - { - task_id = -1; - _fail: - add_index_bool(result, i, 0); - n_task --; - sw_atomic_fetch_sub(&serv->stats->tasking_num, 1); - } - else - { - task_coroutine_map[buf.info.fd] = task_co; - } - list[i] = task_id; - i++; - SW_HASHTABLE_FOREACH_END(); - - if (n_task == 0) - { - SwooleG.error = SW_ERROR_TASK_DISPATCH_FAIL; - RETURN_FALSE; - } - - long ms = (long) (timeout * 1000); - - task_co->result = result; - task_co->list = list; - task_co->count = n_task; - - swTimer_node *timer = swoole_timer_add(ms, SW_FALSE, php_swoole_task_onTimeout, task_co); - if (timer) - { - task_co->timer = timer; - } - PHPCoroutine::yield_m(return_value, &task_co->context); -} - -static PHP_METHOD(swoole_server, task) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - 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; - - 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) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (php_swoole_check_task_param(serv, dst_worker_id) < 0) - { - RETURN_FALSE; - } - - swEventData buf; - memset(&buf.info, 0, sizeof(buf.info)); - - if (php_swoole_task_pack(&buf, zdata) < 0) - { - RETURN_FALSE; - } - - if (!swIsWorker()) - { - swTask_type(&buf) |= SW_TASK_NOREPLY; - } - else if (fci.size) - { - swTask_type(&buf) |= SW_TASK_CALLBACK; - sw_zend_fci_cache_persist(&fci_cache); - task_callbacks[buf.info.fd] = fci_cache; - } - - swTask_type(&buf) |= SW_TASK_NONBLOCK; - - int _dst_worker_id = (int) dst_worker_id; - sw_atomic_fetch_add(&serv->stats->tasking_num, 1); - - if (swProcessPool_dispatch(&serv->gs->task_workers, &buf, &_dst_worker_id) >= 0) - { - RETURN_LONG(buf.info.fd); - } - - sw_atomic_fetch_sub(&serv->stats->tasking_num, 1); - RETURN_FALSE; -} - -static PHP_METHOD(swoole_server, sendMessage) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - if (!serv->onPipeMessage) - { - php_swoole_fatal_error(E_WARNING, "onPipeMessage is null, can't use sendMessage"); - RETURN_FALSE; - } - - zval *zmessage; - zend_long worker_id = -1; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "zl", &zmessage, &worker_id) == FAILURE) - { - RETURN_FALSE; - } - - if (worker_id == SwooleWG.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) - { - php_swoole_fatal_error(E_WARNING, "worker_id[%d] is invalid", (int) worker_id); - RETURN_FALSE; - } - - swEventData buf; - memset(&buf.info, 0, sizeof(buf.info)); - - if (php_swoole_task_pack(&buf, zmessage) < 0) - { - RETURN_FALSE; - } - - buf.info.type = SW_SERVER_EVENT_PIPE_MESSAGE; - buf.info.reactor_id = SwooleWG.id; - - swWorker *to_worker = swServer_get_worker(serv, worker_id); - SW_CHECK_RETURN(swWorker_send2worker(to_worker, &buf, sizeof(buf.info) + buf.info.len, SW_PIPE_MASTER | SW_PIPE_NONBLOCK)); -} - -static PHP_METHOD(swoole_server, finish) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - if (sw_unlikely(serv->task_enable_coroutine)) - { - php_swoole_fatal_error(E_ERROR, "please use %s->finish instead when task_enable_coroutine is enable", ZSTR_VAL(swoole_server_task_ce->name)); - RETURN_FALSE; - } - - zval *zdata; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ZVAL(zdata) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - SW_CHECK_RETURN(php_swoole_task_finish(serv, zdata, NULL)); -} - -static PHP_METHOD(swoole_server_task, finish) -{ - swServer *serv = php_swoole_server_task_get_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - zval *zdata; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ZVAL(zdata) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - swDataHead *info = php_swoole_server_task_get_info(ZEND_THIS); - SW_CHECK_RETURN(php_swoole_task_finish(serv, zdata, (swEventData* )info)); -} - -static PHP_METHOD(swoole_server_task, pack) -{ - swEventData 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) - { - RETURN_FALSE; - } - swTask_type(&buf) |= (SW_TASK_NONBLOCK | SW_TASK_NOREPLY); - - RETURN_STRINGL((char* )&buf, sizeof(buf.info) + buf.info.len); -} - -static PHP_METHOD(swoole_server, bind) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - zend_long fd = 0; - zend_long uid = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &fd, &uid) == FAILURE) - { - RETURN_FALSE; - } - - if (uid > UINT32_MAX) - { - php_swoole_fatal_error(E_WARNING, "uid can not be greater than %u", UINT32_MAX); - RETURN_FALSE; - } - - swConnection *conn = swWorker_get_connection(serv, fd); - if (conn == NULL || conn->active == 0) - { - RETURN_FALSE; - } - - sw_spinlock(&conn->lock); - if (conn->uid != 0) - { - RETVAL_FALSE; - } - else - { - conn->uid = (uint32_t) uid; - RETVAL_TRUE; - } - sw_spinlock_release(&conn->lock); -} - -#ifdef SWOOLE_SOCKETS_SUPPORT -static PHP_METHOD(swoole_server, getSocket) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - - zend_long port = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &port) == FAILURE) - { - RETURN_FALSE; - } - - int sock = swServer_get_socket(serv, port); - php_socket *socket_object = swoole_convert_to_socket(sock); - - if (!socket_object) - { - RETURN_FALSE; - } - SW_ZEND_REGISTER_RESOURCE(return_value, (void *) socket_object, php_sockets_le_socket()); - zval *zsocket = sw_zval_dup(return_value); - Z_TRY_ADDREF_P(zsocket); -} -#endif - -static PHP_METHOD(swoole_server, getClientInfo) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - zend_long fd; - 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; - } - - swConnection *conn = swServer_connection_verify(serv, fd); - if (!conn) - { - RETURN_FALSE; - } - //connection is closed - if (conn->active == 0 && !dont_check_connection) - { - RETURN_FALSE; - } - else - { - array_init(return_value); - - if (conn->uid > 0 || serv->dispatch_mode == SW_DISPATCH_UIDMOD) - { - add_assoc_long(return_value, "uid", conn->uid); - } - - swListenPort *port = swServer_get_port(serv, conn->fd); - if (port && port->open_websocket_protocol) - { - add_assoc_long(return_value, "websocket_status", conn->websocket_status); - } - -#ifdef SW_USE_OPENSSL - if (conn->ssl_client_cert && conn->ssl_client_cert_pid == SwooleG.pid) - { - add_assoc_stringl(return_value, "ssl_client_cert", conn->ssl_client_cert->str, conn->ssl_client_cert->length); - } -#endif - //server socket - swConnection *from_sock = swServer_connection_get(serv, conn->server_fd); - if (from_sock) - { - add_assoc_long(return_value, "server_port", swSocket_get_port(from_sock->socket_type, &from_sock->info)); - } - 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", swSocket_get_port(conn->socket_type, &conn->info)); - add_assoc_string(return_value, "remote_ip", (char *) swSocket_get_ip(conn->socket_type, &conn->info)); - 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", conn->last_time); - add_assoc_long(return_value, "close_errno", conn->close_errno); - } -} - -static PHP_METHOD(swoole_server, getClientList) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - zend_long start_fd = 0; - zend_long find_count = 10; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ll", &start_fd, &find_count) == FAILURE) - { - RETURN_FALSE; - } - - // exceeded the maximum number of searches - if (find_count > SW_MAX_FIND_COUNT) - { - php_swoole_fatal_error(E_WARNING, "swoole connection list max_find_count=%d", SW_MAX_FIND_COUNT); - RETURN_FALSE; - } - - // copy it out to avoid being overwritten by other processes - int serv_max_fd = swServer_get_maxfd(serv); - - if (start_fd == 0) - { - start_fd = swServer_get_minfd(serv); - } - else - { - swConnection *conn = swWorker_get_connection(serv, start_fd); - if (!conn) - { - RETURN_FALSE; - } - start_fd = conn->fd; - } - - if ((int) start_fd >= serv_max_fd) - { - RETURN_FALSE; - } - - array_init(return_value); - int fd = start_fd + 1; - swConnection *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->connection_list[fd]; - - if (conn->active && !conn->closed) - { -#ifdef SW_USE_OPENSSL - if (conn->ssl && !conn->ssl_ready) - { - continue; - } -#endif - add_next_index_long(return_value, conn->session_id); - find_count--; - } - //finish fetch - if (find_count <= 0) - { - break; - } - } -} - -static PHP_METHOD(swoole_server, sendwait) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - zend_long fd; - zval *zdata; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz", &fd, &zdata) == FAILURE) - { - 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"); - RETURN_FALSE; - } - - if (serv->factory_mode != SW_MODE_BASE || swIsTaskWorker()) - { - php_swoole_fatal_error(E_WARNING, "can't sendwait"); - RETURN_FALSE; - } - - SW_CHECK_RETURN(serv->sendwait(serv, fd, data, length)); -} - -static PHP_METHOD(swoole_server, exists) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - zend_long fd; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_LONG(fd) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - swConnection *conn = swWorker_get_connection(serv, fd); - if (!conn) - { - RETURN_FALSE; - } - //connection is closed - if (conn->active == 0 || conn->closed) - { - RETURN_FALSE; - } - else - { - RETURN_TRUE; - } -} - -static PHP_METHOD(swoole_server, protect) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - zend_long fd; - zend_bool value = 1; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|b", &fd, &value) == FAILURE) - { - RETURN_FALSE; - } - - swConnection *conn = swWorker_get_connection(serv, fd); - if (!conn) - { - RETURN_FALSE; - } - //connection is closed - if (conn->active == 0 || conn->closed) - { - RETURN_FALSE; - } - else - { - conn->protect = value; - RETURN_TRUE; - } -} - -#ifdef SW_BUFFER_RECV_TIME -static PHP_METHOD(swoole_server, getReceivedTime) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - if (serv->last_receive_usec > 0) - { - RETURN_DOUBLE(serv->last_receive_usec); - } - else - { - RETURN_FALSE; - } -} -#endif - -static PHP_METHOD(swoole_server, getWorkerId) -{ - if (!swIsWorker()) - { - RETURN_FALSE; - } - else - { - RETURN_LONG(SwooleWG.id); - } -} - -static PHP_METHOD(swoole_server, getWorkerStatus) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - zend_long worker_id = SwooleWG.id; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &worker_id) == FAILURE) - { - RETURN_FALSE; - } - - swWorker *worker = swServer_get_worker(serv, worker_id); - if (!worker) - { - RETURN_FALSE; - } - else - { - RETURN_LONG(worker->status); - } -} - -static PHP_METHOD(swoole_server, getWorkerPid) -{ - if (!swIsWorker()) - { - RETURN_FALSE; - } - else - { - RETURN_LONG(SwooleG.pid); - } -} - -static PHP_METHOD(swoole_server, getManagerPid) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - RETURN_LONG(serv->gs->manager_pid); -} - -static PHP_METHOD(swoole_server, getMasterPid) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - RETURN_LONG(serv->gs->master_pid); -} - -static PHP_METHOD(swoole_server, shutdown) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - 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; - } -} - -static PHP_METHOD(swoole_server, stop) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - zend_bool wait_reactor = 0; - long worker_id = SwooleWG.id; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|lb", &worker_id, &wait_reactor) == FAILURE) - { - RETURN_FALSE; - } - - if (worker_id == SwooleWG.id && wait_reactor == 0) - { - if (SwooleTG.reactor != NULL) - { - SwooleTG.reactor->running = 0; - } - SwooleG.running = 0; - } - else - { - swWorker *worker = swServer_get_worker(serv, worker_id); - if (worker == NULL) - { - 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; -} - -// swoole_connection_iterator - -static PHP_METHOD(swoole_connection_iterator, __construct) -{ - php_swoole_fatal_error(E_ERROR, "please use the Swoole\\Server->connections"); - return; -} - -static PHP_METHOD(swoole_connection_iterator, rewind) -{ - swConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); - iterator->index = 0; - iterator->current_fd = swServer_get_minfd(iterator->serv); -} - -static PHP_METHOD(swoole_connection_iterator, valid) -{ - swConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); - int fd = iterator->current_fd; - swConnection *conn; - - int max_fd = swServer_get_maxfd(iterator->serv); - for (; fd <= max_fd; fd++) - { - conn = &iterator->serv->connection_list[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 != (uint32_t) iterator->port->socket_fd)) - { - continue; - } - iterator->session_id = conn->session_id; - iterator->current_fd = fd; - iterator->index++; - RETURN_TRUE; - } - } - - RETURN_FALSE; -} - -static PHP_METHOD(swoole_connection_iterator, current) -{ - swConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); - RETURN_LONG(iterator->session_id); -} - -static PHP_METHOD(swoole_connection_iterator, next) -{ - swConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); - iterator->current_fd++; -} - -static PHP_METHOD(swoole_connection_iterator, key) -{ - swConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); - RETURN_LONG(iterator->index); -} - -static PHP_METHOD(swoole_connection_iterator, count) -{ - swConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); - if (iterator->port) - { - RETURN_LONG(iterator->port->connection_num); - } - else - { - RETURN_LONG(iterator->serv->stats->connection_num); - } -} - -static PHP_METHOD(swoole_connection_iterator, offsetExists) -{ - swConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); - zval *zserv = (zval *) iterator->serv->ptr2; - zval *zfd; - zval retval; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zfd) == FAILURE) - { - RETURN_FALSE; - } - sw_zend_call_method_with_1_params(zserv, swoole_server_ce, NULL, "exists", &retval, zfd); - RETVAL_BOOL(Z_BVAL_P(&retval)); -} - -static PHP_METHOD(swoole_connection_iterator, offsetGet) -{ - swConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); - zval *zserv = (zval *) iterator->serv->ptr2; - zval *zfd; - zval retval; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zfd) == FAILURE) - { - RETURN_FALSE; - } - sw_zend_call_method_with_1_params(zserv, swoole_server_ce, NULL, "getClientInfo", &retval, zfd); - RETVAL_ZVAL(&retval, 0, 0); -} - -static PHP_METHOD(swoole_connection_iterator, offsetSet) { } -static PHP_METHOD(swoole_connection_iterator, offsetUnset) { } -static PHP_METHOD(swoole_connection_iterator, __destruct) { } diff --git a/swoole_server.h b/swoole_server.h deleted file mode 100644 index df69630160b..00000000000 --- a/swoole_server.h +++ /dev/null @@ -1,85 +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: Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#pragma once - -#include "php_swoole_cxx.h" -#include "server.h" - -//-------------------------------------------------------- -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) -}; -//-------------------------------------------------------- -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) -}; - -#define PHP_SWOOLE_SERVER_CALLBACK_NUM (SW_SERVER_CB_onPipeMessage + 1) -#define PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM (SW_SERVER_CB_onBufferEmpty + 1) - -typedef struct -{ - 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; - zval *zsetting; -} php_swoole_server_port_property; - -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); -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(); -void php_swoole_server_send_yield(swServer *serv, int fd, zval *zdata, zval *return_value); -void php_swoole_get_recv_data(swServer *serv, zval *zdata, swEventData *req); -void php_swoole_onConnect(swServer *, swDataHead *); -int php_swoole_onReceive(swServer *, swEventData *); -int php_swoole_http_onReceive(swServer *, swEventData *); -void php_swoole_http_onClose(swServer *, swDataHead *); -int php_swoole_onPacket(swServer *, swEventData *); -void php_swoole_onClose(swServer *, swDataHead *); -void php_swoole_onBufferFull(swServer *, swDataHead *); -void php_swoole_onBufferEmpty(swServer *, swDataHead *); - -swServer* php_swoole_server_get_and_check_server(zval *zobject); diff --git a/swoole_server_port.cc b/swoole_server_port.cc deleted file mode 100644 index f52c43fcd92..00000000000 --- a/swoole_server_port.cc +++ /dev/null @@ -1,793 +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 "swoole_server.h" - -using namespace std; -using namespace swoole; - -struct server_port_event { - 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) { } -}; -static 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") }, -}); - -zend_class_entry *swoole_server_port_ce; -static zend_object_handlers swoole_server_port_handlers; - -typedef struct -{ - swListenPort *port; - php_swoole_server_port_property property; - zend_object std; -} server_port_t; - -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 swListenPort* php_swoole_server_port_get_ptr(zval *zobject) -{ - return php_swoole_server_port_fetch_object(Z_OBJ_P(zobject))->port; -} - -swListenPort* php_swoole_server_port_get_and_check_ptr(zval *zobject) -{ - swListenPort* 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)); - } - return port; -} - -void php_swoole_server_port_set_ptr(zval *zobject, swListenPort *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) -{ - 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); - if (UNEXPECTED(!property->serv)) - { - php_swoole_fatal_error(E_ERROR, "Invaild instance of %s", SW_Z_OBJCE_NAME_VAL_P(zobject)); - } - return property; -} - -static void php_swoole_server_port_free_object(zend_object *object) -{ - server_port_t *server_port = php_swoole_server_port_fetch_object(object); - - php_swoole_server_port_property *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] = NULL; - } - } - } - - swListenPort *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; - } - } - - zend_object_std_dtor(object); -} - -static zend_object *php_swoole_server_port_create_object(zend_class_entry *ce) -{ - server_port_t *server_port = (server_port_t *) ecalloc(1, sizeof(server_port_t) + zend_object_properties_size(ce)); - zend_object_std_init(&server_port->std, ce); - object_properties_init(&server_port->std, ce); - server_port->std.handlers = &swoole_server_port_handlers; - return &server_port->std; -} - -static PHP_METHOD(swoole_server_port, __construct); -static PHP_METHOD(swoole_server_port, __destruct); -static PHP_METHOD(swoole_server_port, on); -static PHP_METHOD(swoole_server_port, set); -static PHP_METHOD(swoole_server_port, getCallback); - -#ifdef SWOOLE_SOCKETS_SUPPORT -static PHP_METHOD(swoole_server_port, getSocket); -#endif - -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) -#ifdef SWOOLE_SOCKETS_SUPPORT - PHP_ME(swoole_server_port, getSocket, arginfo_swoole_void, ZEND_ACC_PUBLIC) -#endif - PHP_FE_END -}; - -void php_swoole_server_port_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY(swoole_server_port, "Swoole\\Server\\Port", "swoole_server_port", NULL, swoole_server_port_methods); - SW_SET_CLASS_SERIALIZABLE(swoole_server_port, zend_class_serialize_deny, zend_class_unserialize_deny); - 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, std); - - zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onConnect"), ZEND_ACC_PRIVATE); - zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onReceive"), ZEND_ACC_PRIVATE); - zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onClose"), ZEND_ACC_PRIVATE); - zend_declare_property_null(swoole_server_port_ce, ZEND_STRL("onPacket"), ZEND_ACC_PRIVATE); - 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("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("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_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] - */ -static ssize_t php_swoole_server_length_func(swProtocol *protocol, swSocket *conn, char *data, uint32_t length) -{ - swServer *serv = (swServer *) protocol->private_data_2; - swServer_lock(serv); - - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) protocol->private_data; - zval zdata; - zval retval; - ssize_t ret = -1; - - // TODO: reduce memory copy - ZVAL_STRINGL(&zdata, data, length); - if (UNEXPECTED(sw_zend_call_function_ex(NULL, fci_cache, 1, &zdata, &retval) != SUCCESS)) - { - php_swoole_fatal_error(E_WARNING, "length function handler error"); - } - else - { - ret = zval_get_long(&retval); - zval_ptr_dtor(&retval); - } - zval_ptr_dtor(&zdata); - - swServer_unlock(serv); - - /* the exception should only be thrown after unlocked */ - if (UNEXPECTED(EG(exception))) - { - zend_exception_error(EG(exception), E_ERROR); - } - - return ret; -} - -static PHP_METHOD(swoole_server_port, __construct) -{ - php_swoole_fatal_error(E_ERROR, "please use the Swoole\\Server->listen method"); - return; -} - -static PHP_METHOD(swoole_server_port, __destruct) { } - -static PHP_METHOD(swoole_server_port, set) -{ - zval *zset = NULL; - HashTable *vht; - zval *ztmp; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY(zset) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - vht = Z_ARRVAL_P(zset); - - swListenPort *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); - - if (port == NULL || property == NULL) - { - php_swoole_fatal_error(E_ERROR, "please use the swoole_server->listen method"); - return; - } - - //backlog - if (php_swoole_array_get_value(vht, "backlog", ztmp)) - { - zend_long v = zval_get_long(ztmp); - 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); - 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; - } - } - /** - * !!! 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); - 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; - } - } - /** - * !!! 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); - 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)) - { - zend_long v = zval_get_long(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); - port->buffer_low_watermark = SW_MAX(0, SW_MIN(v, UINT32_MAX)); - } - //server: tcp_nodelay - if (php_swoole_array_get_value(vht, "open_tcp_nodelay", ztmp)) - { - port->open_tcp_nodelay = zval_is_true(ztmp); - } - else - { - port->open_tcp_nodelay = 1; - } - //tcp_defer_accept - if (php_swoole_array_get_value(vht, "tcp_defer_accept", ztmp)) - { - zend_long v = zval_get_long(ztmp); - port->tcp_defer_accept = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX)); - } - //tcp_keepalive - if (php_swoole_array_get_value(vht, "open_tcp_keepalive", ztmp)) - { - port->open_tcp_keepalive = zval_is_true(ztmp); - } - //buffer: eof check - if (php_swoole_array_get_value(vht, "open_eof_check", ztmp)) - { - port->open_eof_check = zval_is_true(ztmp); - } - //buffer: split package with eof - if (php_swoole_array_get_value(vht, "open_eof_split", ztmp)) - { - port->protocol.split_by_eof = zval_is_true(ztmp); - if (port->protocol.split_by_eof) - { - port->open_eof_check = 1; - } - } - //package eof - if (php_swoole_array_get_value(vht, "package_eof", ztmp)) - { - zend::string str_v(ztmp); - port->protocol.package_eof_len = str_v.len(); - if (port->protocol.package_eof_len == 0) - { - php_swoole_fatal_error(E_ERROR, "package_eof cannot be an empty string"); - RETURN_FALSE; - } - else if (port->protocol.package_eof_len > SW_DATA_EOF_MAXLEN) - { - php_swoole_fatal_error(E_ERROR, "package_eof max length is %d", SW_DATA_EOF_MAXLEN); - RETURN_FALSE; - } - memcpy(port->protocol.package_eof, str_v.val(), str_v.len()); - } - //http_protocol - if (php_swoole_array_get_value(vht, "open_http_protocol", ztmp)) - { - port->open_http_protocol = zval_is_true(ztmp); - } - //websocket protocol - if (php_swoole_array_get_value(vht, "open_websocket_protocol", ztmp)) - { - port->open_websocket_protocol = zval_is_true(ztmp); - if (port->open_websocket_protocol) - { - port->open_http_protocol = 1; - } - } - if (php_swoole_array_get_value(vht, "websocket_subprotocol", ztmp)) - { - zend::string str_v(ztmp); - if (port->websocket_subprotocol) - { - sw_free(port->websocket_subprotocol); - } - port->websocket_subprotocol = str_v.dup(); - port->websocket_subprotocol_length = str_v.len(); - } - if (php_swoole_array_get_value(vht, "open_websocket_close_frame", ztmp)) - { - port->open_websocket_close_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); - if (port->open_http2_protocol) - { - 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); - } - //redis protocol - if (php_swoole_array_get_value(vht, "open_redis_protocol", ztmp)) - { - port->open_redis_protocol = zval_get_long(ztmp); - } - //tcp_keepidle - if (php_swoole_array_get_value(vht, "tcp_keepidle", ztmp)) - { - zend_long v = zval_get_long(ztmp); - port->tcp_keepidle = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX)); - } - //tcp_keepinterval - if (php_swoole_array_get_value(vht, "tcp_keepinterval", ztmp)) - { - zend_long v = zval_get_long(ztmp); - port->tcp_keepinterval = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX)); - } - //tcp_keepcount - if (php_swoole_array_get_value(vht, "tcp_keepcount", ztmp)) - { - zend_long v = zval_get_long(ztmp); - port->tcp_keepcount = SW_MAX(INT_MIN, SW_MIN(v, INT_MAX)); - } - //tcp_fastopen - if (php_swoole_array_get_value(vht, "tcp_fastopen", ztmp)) - { - port->tcp_fastopen = zval_is_true(ztmp); - } - //open length check - if (php_swoole_array_get_value(vht, "open_length_check", ztmp)) - { - port->open_length_check = zval_is_true(ztmp); - } - //package length size - if (php_swoole_array_get_value(vht, "package_length_type", ztmp)) - { - zend::string str_v(ztmp); - 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"); - RETURN_FALSE; - } - } - //package length offset - if (php_swoole_array_get_value(vht, "package_length_offset", ztmp)) - { - zend_long v = zval_get_long(ztmp); - port->protocol.package_length_offset = SW_MAX(0, SW_MIN(v, UINT16_MAX)); - if (port->protocol.package_length_offset > SW_IPC_BUFFER_SIZE) - { - php_swoole_fatal_error(E_ERROR, "'package_length_offset' value is too large"); - } - } - //package body start - if (php_swoole_array_get_value(vht, "package_body_offset", ztmp) || php_swoole_array_get_value(vht, "package_body_start", ztmp)) - { - zend_long v = zval_get_long(ztmp); - port->protocol.package_body_offset = SW_MAX(0, SW_MIN(v, UINT16_MAX)); - if (port->protocol.package_body_offset > SW_IPC_BUFFER_SIZE) - { - php_swoole_fatal_error(E_ERROR, "'package_body_offset' value is too large"); - } - } - //length function - if (php_swoole_array_get_value(vht, "package_length_func", ztmp)) - { - while (1) - { - if (Z_TYPE_P(ztmp) == IS_STRING) - { - swProtocol_length_function func = (swProtocol_length_function) swoole_get_function(Z_STRVAL_P(ztmp), Z_STRLEN_P(ztmp)); - if (func != NULL) - { - port->protocol.get_package_length = func; - break; - } - } -#ifdef ZTS - swServer *serv = property->serv; - if (serv->factory_mode == SW_MODE_PROCESS && !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, NULL, 0, &func_name, NULL, fci_cache, NULL)) - { - php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name); - return; - } - efree(func_name); - 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); - } - sw_zend_fci_cache_persist(fci_cache); - port->protocol.private_data = fci_cache; - break; - } - - 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); - port->protocol.package_max_length = SW_MAX(0, SW_MIN(v, UINT32_MAX)); - } - -#ifdef SW_USE_OPENSSL - if (port->ssl) - { - if (php_swoole_array_get_value(vht, "ssl_cert_file", ztmp)) - { - zend::string str_v(ztmp); - if (access(str_v.val(), R_OK) < 0) - { - php_swoole_fatal_error(E_ERROR, "ssl cert file[%s] not found", str_v.val()); - return; - } - if (port->ssl_option.cert_file) - { - sw_free(port->ssl_option.cert_file); - } - port->ssl_option.cert_file = str_v.dup(); - port->open_ssl_encrypt = 1; - } - if (php_swoole_array_get_value(vht, "ssl_key_file", ztmp)) - { - zend::string str_v(ztmp); - if (access(str_v.val(), R_OK) < 0) - { - php_swoole_fatal_error(E_ERROR, "ssl key file[%s] not found", str_v.val()); - return; - } - if (port->ssl_option.key_file) - { - sw_free(port->ssl_option.key_file); - } - port->ssl_option.key_file = str_v.dup(); - } - if (php_swoole_array_get_value(vht, "ssl_method", ztmp)) - { - zend_long v = zval_get_long(ztmp); - port->ssl_option.method = SW_MAX(0, SW_MIN(v, UINT8_MAX)); - } - if (php_swoole_array_get_value(vht, "ssl_compress", ztmp)) - { - port->ssl_option.disable_compress = !zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "ssl_protocols", ztmp)) - { - zend_long v = zval_get_long(ztmp); - port->ssl_option.disable_protocols = (SW_SSL_SSLv2 | SW_SSL_SSLv3 | SW_SSL_TLSv1 | SW_SSL_TLSv1_1 - | SW_SSL_TLSv1_2) ^ v; - } - if (php_swoole_array_get_value(vht, "ssl_verify_peer", ztmp)) - { - port->ssl_option.verify_peer = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "ssl_allow_self_signed", ztmp)) - { - port->ssl_option.allow_self_signed = zval_is_true(ztmp); - } - //verify client cert - if (php_swoole_array_get_value(vht, "ssl_client_cert_file", ztmp)) - { - zend::string str_v(ztmp); - if (access(str_v.val(), R_OK) < 0) - { - php_swoole_fatal_error(E_ERROR, "ssl_client_cert_file[%s] not found", str_v.val()); - return; - } - if (port->ssl_option.client_cert_file) - { - sw_free(port->ssl_option.client_cert_file); - } - port->ssl_option.client_cert_file = str_v.dup(); - } - if (php_swoole_array_get_value(vht, "ssl_verify_depth", ztmp)) - { - zend_long v = zval_get_long(ztmp); - port->ssl_option.verify_depth = SW_MAX(0, SW_MIN(v, UINT8_MAX)); - } - if (php_swoole_array_get_value(vht, "ssl_prefer_server_ciphers", ztmp)) - { - port->ssl_config.prefer_server_ciphers = zval_is_true(ztmp); - } - // if ((v = zend_hash_str_find(vht, ZEND_STRL("ssl_session_tickets")))) - // { - // port->ssl_config.session_tickets = zval_is_true(v); - // } - // if ((v = zend_hash_str_find(vht, ZEND_STRL("ssl_stapling")))) - // { - // port->ssl_config.stapling = zval_is_true(v); - // } - // if ((v = zend_hash_str_find(vht, ZEND_STRL("ssl_stapling_verify")))) - // { - // port->ssl_config.stapling_verify = zval_is_true(v); - // } - if (php_swoole_array_get_value(vht, "ssl_ciphers", ztmp)) - { - if (port->ssl_config.ciphers) - { - sw_free(port->ssl_config.ciphers); - } - port->ssl_config.ciphers = zend::string(ztmp).dup(); - } - if (php_swoole_array_get_value(vht, "ssl_ecdh_curve", ztmp)) - { - if (port->ssl_config.ecdh_curve) - { - sw_free(port->ssl_config.ecdh_curve); - } - port->ssl_config.ecdh_curve = zend::string(ztmp).dup(); - } - if (php_swoole_array_get_value(vht, "ssl_dhparam", ztmp)) - { - if (port->ssl_config.dhparam) - { - sw_free(port->ssl_config.dhparam); - } - port->ssl_config.dhparam = zend::string(ztmp).dup(); - } - // if ((v = zend_hash_str_find(vht, ZEND_STRL("ssl_session_cache")))) - // { - // port->ssl_config.session_cache = zend::string_dup(v); - // } - if (swPort_enable_ssl_encrypt(port) < 0) - { - php_swoole_fatal_error(E_ERROR, "swPort_enable_ssl_encrypt() failed"); - RETURN_FALSE; - } - } -#endif - - zval *zsetting = sw_zend_read_and_convert_property_array(swoole_server_port_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - php_array_merge(Z_ARRVAL_P(zsetting), Z_ARRVAL_P(zset)); - property->zsetting = zsetting; -} - -static PHP_METHOD(swoole_server_port, on) -{ - char *name = NULL; - size_t len, i; - zval *cb; - - php_swoole_server_port_property *property = php_swoole_server_port_get_and_check_property(ZEND_THIS); - swServer *serv = property->serv; - if (serv->gs->start > 0) - { - 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; - } - - char *func_name = NULL; - zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache)); - if (!sw_zend_is_callable_ex(cb, NULL, 0, &func_name, NULL, fci_cache, NULL)) - { - 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]))) - { - 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, 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; - } - break; - } - - if (l_property_name == 0) - { - php_swoole_error(E_WARNING, "unknown event types[%s]", name); - efree(fci_cache); - RETURN_FALSE; - } - RETURN_TRUE; -} - -static PHP_METHOD(swoole_server_port, getCallback) -{ - zval *name; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ZVAL(name) - 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())); - auto i = server_port_event_map.find(_event_name_tolower.to_std_string()); - if (i != server_port_event_map.end()) - { - string property_name = "on" + i->second.name; - zval rv, *property = zend_read_property(swoole_server_port_ce, ZEND_THIS, property_name.c_str(), property_name.length(), 1, &rv); - if (!ZVAL_IS_NULL(property)) - { - RETURN_ZVAL(property, 1, 0); - } - } - RETURN_NULL(); -} - -#ifdef SWOOLE_SOCKETS_SUPPORT -static PHP_METHOD(swoole_server_port, getSocket) -{ - swListenPort *port = php_swoole_server_port_get_and_check_ptr(ZEND_THIS); - php_socket *socket_object = swoole_convert_to_socket(port->socket->fd); - if (!socket_object) - { - RETURN_FALSE; - } - SW_ZEND_REGISTER_RESOURCE(return_value, (void *) socket_object, php_sockets_le_socket()); - zval *zsocket = sw_zval_dup(return_value); - Z_TRY_ADDREF_P(zsocket); -} -#endif diff --git a/swoole_socket_coro.cc b/swoole_socket_coro.cc deleted file mode 100644 index b1f17139dce..00000000000 --- a/swoole_socket_coro.cc +++ /dev/null @@ -1,1775 +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: Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#include "php_swoole_cxx.h" -#include "mqtt.h" -#include "thirdparty/php/sockets/php_sockets_cxx.h" - -#include - -using namespace swoole; - -zend_class_entry *swoole_socket_coro_ce; -static zend_object_handlers swoole_socket_coro_handlers; - -static zend_class_entry *swoole_socket_coro_exception_ce; -static zend_object_handlers swoole_socket_coro_exception_handlers; - -typedef struct -{ - Socket *socket; - bool reference; - zend_object std; -} socket_coro; - -static PHP_METHOD(swoole_socket_coro, __construct); -static PHP_METHOD(swoole_socket_coro, bind); -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, peek); -static PHP_METHOD(swoole_socket_coro, recv); -static PHP_METHOD(swoole_socket_coro, send); -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, recvfrom); -static PHP_METHOD(swoole_socket_coro, sendto); -static PHP_METHOD(swoole_socket_coro, getOption); -static PHP_METHOD(swoole_socket_coro, setOption); -static PHP_METHOD(swoole_socket_coro, setProtocol); -#ifdef SW_USE_OPENSSL -static PHP_METHOD(swoole_socket_coro, sslHandshake); -#endif -static PHP_METHOD(swoole_socket_coro, shutdown); -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); - -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_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, 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) -#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_FE_END -}; - -#define SW_BAD_SOCKET ((Socket *)-1) -#define swoole_get_socket_coro(_sock, _zobject) \ - socket_coro* _sock = php_swoole_socket_coro_fetch_object(Z_OBJ_P(_zobject)); \ - if (UNEXPECTED(!sock->socket)) \ - { \ - php_swoole_fatal_error(E_ERROR, "you must call Socket constructor first"); \ - } \ - if (UNEXPECTED(_sock->socket == SW_BAD_SOCKET)) { \ - zend_update_property_long(swoole_socket_coro_ce, _zobject, ZEND_STRL("errCode"), EBADF); \ - zend_update_property_string(swoole_socket_coro_ce, _zobject, ZEND_STRL("errMsg"), strerror(EBADF)); \ - RETURN_FALSE; \ - } - -static sw_inline socket_coro* php_swoole_socket_coro_fetch_object(zend_object *obj) -{ - return (socket_coro *) ((char *) obj - swoole_socket_coro_handlers.offset); -} - -static void php_swoole_socket_coro_free_object(zend_object *object) -{ - socket_coro *sock = (socket_coro *) php_swoole_socket_coro_fetch_object(object); - if (!sock->reference && sock->socket && sock->socket != SW_BAD_SOCKET) - { - sock->socket->close(); - delete sock->socket; - } - zend_object_std_dtor(&sock->std); -} - -static zend_object* php_swoole_socket_coro_create_object(zend_class_entry *ce) -{ - socket_coro *sock = (socket_coro *) ecalloc(1, sizeof(socket_coro) + zend_object_properties_size(ce)); - zend_object_std_init(&sock->std, ce); - /* Even if you don't use properties yourself you should still call object_properties_init(), - * because extending classes may use properties. (Generally a lot of the stuff you will do is - * for the sake of not breaking extending classes). */ - object_properties_init(&sock->std, ce); - sock->std.handlers = &swoole_socket_coro_handlers; - return &sock->std; -} - -static void swoole_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); - REGISTER_LONG_CONSTANT("SOCK_STREAM", SOCK_STREAM, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SOCK_DGRAM", SOCK_DGRAM, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SOCK_RAW", SOCK_RAW, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SOCK_SEQPACKET", SOCK_SEQPACKET, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SOCK_RDM", SOCK_RDM, CONST_CS | CONST_PERSISTENT); - - REGISTER_LONG_CONSTANT("MSG_OOB", MSG_OOB, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("MSG_WAITALL", MSG_WAITALL, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("MSG_CTRUNC", MSG_CTRUNC, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("MSG_TRUNC", MSG_TRUNC, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("MSG_PEEK", MSG_PEEK, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("MSG_DONTROUTE", MSG_DONTROUTE, CONST_CS | CONST_PERSISTENT); -#ifdef MSG_EOR - REGISTER_LONG_CONSTANT("MSG_EOR", MSG_EOR, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef MSG_EOF - REGISTER_LONG_CONSTANT("MSG_EOF", MSG_EOF, CONST_CS | CONST_PERSISTENT); -#endif - -#ifdef MSG_CONFIRM - REGISTER_LONG_CONSTANT("MSG_CONFIRM", MSG_CONFIRM, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef MSG_ERRQUEUE - REGISTER_LONG_CONSTANT("MSG_ERRQUEUE", MSG_ERRQUEUE, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef MSG_NOSIGNAL - REGISTER_LONG_CONSTANT("MSG_NOSIGNAL", MSG_NOSIGNAL, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef MSG_DONTWAIT - REGISTER_LONG_CONSTANT("MSG_DONTWAIT", MSG_DONTWAIT, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef MSG_MORE - REGISTER_LONG_CONSTANT("MSG_MORE", MSG_MORE, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef MSG_WAITFORONE - REGISTER_LONG_CONSTANT("MSG_WAITFORONE",MSG_WAITFORONE, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef MSG_CMSG_CLOEXEC - REGISTER_LONG_CONSTANT("MSG_CMSG_CLOEXEC",MSG_CMSG_CLOEXEC,CONST_CS | CONST_PERSISTENT); -#endif - - REGISTER_LONG_CONSTANT("SO_DEBUG", SO_DEBUG, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SO_REUSEADDR", SO_REUSEADDR, CONST_CS | CONST_PERSISTENT); -#ifdef SO_REUSEPORT - REGISTER_LONG_CONSTANT("SO_REUSEPORT", SO_REUSEPORT, CONST_CS | CONST_PERSISTENT); -#endif - REGISTER_LONG_CONSTANT("SO_KEEPALIVE", SO_KEEPALIVE, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SO_DONTROUTE", SO_DONTROUTE, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SO_LINGER", SO_LINGER, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SO_BROADCAST", SO_BROADCAST, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SO_OOBINLINE", SO_OOBINLINE, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SO_SNDBUF", SO_SNDBUF, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SO_RCVBUF", SO_RCVBUF, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SO_SNDLOWAT", SO_SNDLOWAT, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SO_RCVLOWAT", SO_RCVLOWAT, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SO_SNDTIMEO", SO_SNDTIMEO, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SO_RCVTIMEO", SO_RCVTIMEO, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SO_TYPE", SO_TYPE, CONST_CS | CONST_PERSISTENT); -#ifdef SO_FAMILY - REGISTER_LONG_CONSTANT("SO_FAMILY", SO_FAMILY, CONST_CS | CONST_PERSISTENT); -#endif - REGISTER_LONG_CONSTANT("SO_ERROR", SO_ERROR, CONST_CS | CONST_PERSISTENT); -#ifdef SO_BINDTODEVICE - REGISTER_LONG_CONSTANT("SO_BINDTODEVICE", SO_BINDTODEVICE, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef SO_USER_COOKIE - REGISTER_LONG_CONSTANT("SO_LABEL", SO_LABEL, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SO_PEERLABEL", SO_PEERLABEL, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SO_LISTENQLIMIT", SO_LISTENQLIMIT, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SO_LISTENQLEN", SO_LISTENQLEN, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SO_USER_COOKIE", SO_USER_COOKIE, CONST_CS | CONST_PERSISTENT); -#endif - REGISTER_LONG_CONSTANT("SOL_SOCKET", SOL_SOCKET, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SOMAXCONN", SOMAXCONN, CONST_CS | CONST_PERSISTENT); -#ifdef TCP_NODELAY - REGISTER_LONG_CONSTANT("TCP_NODELAY", TCP_NODELAY, CONST_CS | CONST_PERSISTENT); -#endif - - REGISTER_LONG_CONSTANT("MCAST_JOIN_GROUP", PHP_MCAST_JOIN_GROUP, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("MCAST_LEAVE_GROUP", PHP_MCAST_LEAVE_GROUP, CONST_CS | CONST_PERSISTENT); -#ifdef HAS_MCAST_EXT - REGISTER_LONG_CONSTANT("MCAST_BLOCK_SOURCE", PHP_MCAST_BLOCK_SOURCE, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("MCAST_UNBLOCK_SOURCE", PHP_MCAST_UNBLOCK_SOURCE, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("MCAST_JOIN_SOURCE_GROUP", PHP_MCAST_JOIN_SOURCE_GROUP, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("MCAST_LEAVE_SOURCE_GROUP", PHP_MCAST_LEAVE_SOURCE_GROUP, CONST_CS | CONST_PERSISTENT); -#endif - - REGISTER_LONG_CONSTANT("IP_MULTICAST_IF", IP_MULTICAST_IF, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("IP_MULTICAST_TTL", IP_MULTICAST_TTL, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("IP_MULTICAST_LOOP", IP_MULTICAST_LOOP, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("IPV6_MULTICAST_IF", IPV6_MULTICAST_IF, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("IPV6_MULTICAST_HOPS", IPV6_MULTICAST_HOPS, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("IPV6_MULTICAST_LOOP", IPV6_MULTICAST_LOOP, CONST_CS | CONST_PERSISTENT); - -#ifdef IPV6_V6ONLY - REGISTER_LONG_CONSTANT("IPV6_V6ONLY", IPV6_V6ONLY, CONST_CS | CONST_PERSISTENT); -#endif - -#ifdef EPERM - /* Operation not permitted */ - REGISTER_LONG_CONSTANT("SOCKET_EPERM", EPERM, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENOENT - /* No such file or directory */ - REGISTER_LONG_CONSTANT("SOCKET_ENOENT", ENOENT, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EINTR - /* Interrupted system call */ - REGISTER_LONG_CONSTANT("SOCKET_EINTR", EINTR, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EIO - /* I/O error */ - REGISTER_LONG_CONSTANT("SOCKET_EIO", EIO, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENXIO - /* No such device or address */ - REGISTER_LONG_CONSTANT("SOCKET_ENXIO", ENXIO, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef E2BIG - /* Arg list too long */ - REGISTER_LONG_CONSTANT("SOCKET_E2BIG", E2BIG, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EBADF - /* Bad file number */ - REGISTER_LONG_CONSTANT("SOCKET_EBADF", EBADF, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EAGAIN - /* Try again */ - REGISTER_LONG_CONSTANT("SOCKET_EAGAIN", EAGAIN, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENOMEM - /* Out of memory */ - REGISTER_LONG_CONSTANT("SOCKET_ENOMEM", ENOMEM, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EACCES - /* Permission denied */ - REGISTER_LONG_CONSTANT("SOCKET_EACCES", EACCES, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EFAULT - /* Bad address */ - REGISTER_LONG_CONSTANT("SOCKET_EFAULT", EFAULT, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENOTBLK - /* Block device required */ - REGISTER_LONG_CONSTANT("SOCKET_ENOTBLK", ENOTBLK, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EBUSY - /* Device or resource busy */ - REGISTER_LONG_CONSTANT("SOCKET_EBUSY", EBUSY, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EEXIST - /* File exists */ - REGISTER_LONG_CONSTANT("SOCKET_EEXIST", EEXIST, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EXDEV - /* Cross-device link */ - REGISTER_LONG_CONSTANT("SOCKET_EXDEV", EXDEV, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENODEV - /* No such device */ - REGISTER_LONG_CONSTANT("SOCKET_ENODEV", ENODEV, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENOTDIR - /* Not a directory */ - REGISTER_LONG_CONSTANT("SOCKET_ENOTDIR", ENOTDIR, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EISDIR - /* Is a directory */ - REGISTER_LONG_CONSTANT("SOCKET_EISDIR", EISDIR, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EINVAL - /* Invalid argument */ - REGISTER_LONG_CONSTANT("SOCKET_EINVAL", EINVAL, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENFILE - /* File table overflow */ - REGISTER_LONG_CONSTANT("SOCKET_ENFILE", ENFILE, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EMFILE - /* Too many open files */ - REGISTER_LONG_CONSTANT("SOCKET_EMFILE", EMFILE, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENOTTY - /* Not a typewriter */ - REGISTER_LONG_CONSTANT("SOCKET_ENOTTY", ENOTTY, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENOSPC - /* No space left on device */ - REGISTER_LONG_CONSTANT("SOCKET_ENOSPC", ENOSPC, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ESPIPE - /* Illegal seek */ - REGISTER_LONG_CONSTANT("SOCKET_ESPIPE", ESPIPE, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EROFS - /* Read-only file system */ - REGISTER_LONG_CONSTANT("SOCKET_EROFS", EROFS, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EMLINK - /* Too many links */ - REGISTER_LONG_CONSTANT("SOCKET_EMLINK", EMLINK, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EPIPE - /* Broken pipe */ - REGISTER_LONG_CONSTANT("SOCKET_EPIPE", EPIPE, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENAMETOOLONG - /* File name too long */ - REGISTER_LONG_CONSTANT("SOCKET_ENAMETOOLONG", ENAMETOOLONG, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENOLCK - /* No record locks available */ - REGISTER_LONG_CONSTANT("SOCKET_ENOLCK", ENOLCK, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENOSYS - /* Function not implemented */ - REGISTER_LONG_CONSTANT("SOCKET_ENOSYS", ENOSYS, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENOTEMPTY - /* Directory not empty */ - REGISTER_LONG_CONSTANT("SOCKET_ENOTEMPTY", ENOTEMPTY, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ELOOP - /* Too many symbolic links encountered */ - REGISTER_LONG_CONSTANT("SOCKET_ELOOP", ELOOP, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EWOULDBLOCK - /* Operation would block */ - REGISTER_LONG_CONSTANT("SOCKET_EWOULDBLOCK", EWOULDBLOCK, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENOMSG - /* No message of desired type */ - REGISTER_LONG_CONSTANT("SOCKET_ENOMSG", ENOMSG, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EIDRM - /* Identifier removed */ - REGISTER_LONG_CONSTANT("SOCKET_EIDRM", EIDRM, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ECHRNG - /* Channel number out of range */ - REGISTER_LONG_CONSTANT("SOCKET_ECHRNG", ECHRNG, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EL2NSYNC - /* Level 2 not synchronized */ - REGISTER_LONG_CONSTANT("SOCKET_EL2NSYNC", EL2NSYNC, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EL3HLT - /* Level 3 halted */ - REGISTER_LONG_CONSTANT("SOCKET_EL3HLT", EL3HLT, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EL3RST - /* Level 3 reset */ - REGISTER_LONG_CONSTANT("SOCKET_EL3RST", EL3RST, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ELNRNG - /* Link number out of range */ - REGISTER_LONG_CONSTANT("SOCKET_ELNRNG", ELNRNG, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EUNATCH - /* Protocol driver not attached */ - REGISTER_LONG_CONSTANT("SOCKET_EUNATCH", EUNATCH, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENOCSI - /* No CSI structure available */ - REGISTER_LONG_CONSTANT("SOCKET_ENOCSI", ENOCSI, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EL2HLT - /* Level 2 halted */ - REGISTER_LONG_CONSTANT("SOCKET_EL2HLT", EL2HLT, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EBADE - /* Invalid exchange */ - REGISTER_LONG_CONSTANT("SOCKET_EBADE", EBADE, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EBADR - /* Invalid request descriptor */ - REGISTER_LONG_CONSTANT("SOCKET_EBADR", EBADR, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EXFULL - /* Exchange full */ - REGISTER_LONG_CONSTANT("SOCKET_EXFULL", EXFULL, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENOANO - /* No anode */ - REGISTER_LONG_CONSTANT("SOCKET_ENOANO", ENOANO, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EBADRQC - /* Invalid request code */ - REGISTER_LONG_CONSTANT("SOCKET_EBADRQC", EBADRQC, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EBADSLT - /* Invalid slot */ - REGISTER_LONG_CONSTANT("SOCKET_EBADSLT", EBADSLT, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENOSTR - /* Device not a stream */ - REGISTER_LONG_CONSTANT("SOCKET_ENOSTR", ENOSTR, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENODATA - /* No data available */ - REGISTER_LONG_CONSTANT("SOCKET_ENODATA", ENODATA, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ETIME - /* Timer expired */ - REGISTER_LONG_CONSTANT("SOCKET_ETIME", ETIME, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENOSR - /* Out of streams resources */ - REGISTER_LONG_CONSTANT("SOCKET_ENOSR", ENOSR, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENONET - /* Machine is not on the network */ - REGISTER_LONG_CONSTANT("SOCKET_ENONET", ENONET, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EREMOTE - /* Object is remote */ - REGISTER_LONG_CONSTANT("SOCKET_EREMOTE", EREMOTE, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENOLINK - /* Link has been severed */ - REGISTER_LONG_CONSTANT("SOCKET_ENOLINK", ENOLINK, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EADV - /* Advertise error */ - REGISTER_LONG_CONSTANT("SOCKET_EADV", EADV, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ESRMNT - /* Srmount error */ - REGISTER_LONG_CONSTANT("SOCKET_ESRMNT", ESRMNT, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ECOMM - /* Communication error on send */ - REGISTER_LONG_CONSTANT("SOCKET_ECOMM", ECOMM, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EPROTO - /* Protocol error */ - REGISTER_LONG_CONSTANT("SOCKET_EPROTO", EPROTO, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EMULTIHOP - /* Multihop attempted */ - REGISTER_LONG_CONSTANT("SOCKET_EMULTIHOP", EMULTIHOP, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EBADMSG - /* Not a data message */ - REGISTER_LONG_CONSTANT("SOCKET_EBADMSG", EBADMSG, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENOTUNIQ - /* Name not unique on network */ - REGISTER_LONG_CONSTANT("SOCKET_ENOTUNIQ", ENOTUNIQ, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EBADFD - /* File descriptor in bad state */ - REGISTER_LONG_CONSTANT("SOCKET_EBADFD", EBADFD, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EREMCHG - /* Remote address changed */ - REGISTER_LONG_CONSTANT("SOCKET_EREMCHG", EREMCHG, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ERESTART - /* Interrupted system call should be restarted */ - REGISTER_LONG_CONSTANT("SOCKET_ERESTART", ERESTART, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ESTRPIPE - /* Streams pipe error */ - REGISTER_LONG_CONSTANT("SOCKET_ESTRPIPE", ESTRPIPE, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EUSERS - /* Too many users */ - REGISTER_LONG_CONSTANT("SOCKET_EUSERS", EUSERS, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENOTSOCK - /* Socket operation on non-socket */ - REGISTER_LONG_CONSTANT("SOCKET_ENOTSOCK", ENOTSOCK, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EDESTADDRREQ - /* Destination address required */ - REGISTER_LONG_CONSTANT("SOCKET_EDESTADDRREQ", EDESTADDRREQ, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EMSGSIZE - /* Message too long */ - REGISTER_LONG_CONSTANT("SOCKET_EMSGSIZE", EMSGSIZE, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EPROTOTYPE - /* Protocol wrong type for socket */ - REGISTER_LONG_CONSTANT("SOCKET_EPROTOTYPE", EPROTOTYPE, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENOPROTOOPT - /* Protocol not available */ - REGISTER_LONG_CONSTANT("SOCKET_ENOPROTOOPT", ENOPROTOOPT, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EPROTONOSUPPORT - /* Protocol not supported */ - REGISTER_LONG_CONSTANT("SOCKET_EPROTONOSUPPORT", EPROTONOSUPPORT, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ESOCKTNOSUPPORT - /* Socket type not supported */ - REGISTER_LONG_CONSTANT("SOCKET_ESOCKTNOSUPPORT", ESOCKTNOSUPPORT, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EOPNOTSUPP - /* Operation not supported on transport endpoint */ - REGISTER_LONG_CONSTANT("SOCKET_EOPNOTSUPP", EOPNOTSUPP, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EPFNOSUPPORT - /* Protocol family not supported */ - REGISTER_LONG_CONSTANT("SOCKET_EPFNOSUPPORT", EPFNOSUPPORT, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EAFNOSUPPORT - /* Address family not supported by protocol */ - REGISTER_LONG_CONSTANT("SOCKET_EAFNOSUPPORT", EAFNOSUPPORT, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EADDRINUSE - /* Address already in use */ - REGISTER_LONG_CONSTANT("SOCKET_EADDRINUSE", EADDRINUSE, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EADDRNOTAVAIL - /* Cannot assign requested address */ - REGISTER_LONG_CONSTANT("SOCKET_EADDRNOTAVAIL", EADDRNOTAVAIL, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENETDOWN - /* Network is down */ - REGISTER_LONG_CONSTANT("SOCKET_ENETDOWN", ENETDOWN, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENETUNREACH - /* Network is unreachable */ - REGISTER_LONG_CONSTANT("SOCKET_ENETUNREACH", ENETUNREACH, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENETRESET - /* Network dropped connection because of reset */ - REGISTER_LONG_CONSTANT("SOCKET_ENETRESET", ENETRESET, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ECONNABORTED - /* Software caused connection abort */ - REGISTER_LONG_CONSTANT("SOCKET_ECONNABORTED", ECONNABORTED, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ECONNRESET - /* Connection reset by peer */ - REGISTER_LONG_CONSTANT("SOCKET_ECONNRESET", ECONNRESET, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENOBUFS - /* No buffer space available */ - REGISTER_LONG_CONSTANT("SOCKET_ENOBUFS", ENOBUFS, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EISCONN - /* Transport endpoint is already connected */ - REGISTER_LONG_CONSTANT("SOCKET_EISCONN", EISCONN, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENOTCONN - /* Transport endpoint is not connected */ - REGISTER_LONG_CONSTANT("SOCKET_ENOTCONN", ENOTCONN, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ESHUTDOWN - /* Cannot send after transport endpoint shutdown */ - REGISTER_LONG_CONSTANT("SOCKET_ESHUTDOWN", ESHUTDOWN, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ETOOMANYREFS - /* Too many references: cannot splice */ - REGISTER_LONG_CONSTANT("SOCKET_ETOOMANYREFS", ETOOMANYREFS, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ETIMEDOUT - /* Connection timed out */ - REGISTER_LONG_CONSTANT("SOCKET_ETIMEDOUT", ETIMEDOUT, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ECONNREFUSED - /* Connection refused */ - REGISTER_LONG_CONSTANT("SOCKET_ECONNREFUSED", ECONNREFUSED, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EHOSTDOWN - /* Host is down */ - REGISTER_LONG_CONSTANT("SOCKET_EHOSTDOWN", EHOSTDOWN, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EHOSTUNREACH - /* No route to host */ - REGISTER_LONG_CONSTANT("SOCKET_EHOSTUNREACH", EHOSTUNREACH, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EALREADY - /* Operation already in progress */ - REGISTER_LONG_CONSTANT("SOCKET_EALREADY", EALREADY, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EINPROGRESS - /* Operation now in progress */ - REGISTER_LONG_CONSTANT("SOCKET_EINPROGRESS", EINPROGRESS, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EISNAM - /* Is a named type file */ - REGISTER_LONG_CONSTANT("SOCKET_EISNAM", EISNAM, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EREMOTEIO - /* Remote I/O error */ - REGISTER_LONG_CONSTANT("SOCKET_EREMOTEIO", EREMOTEIO, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EDQUOT - /* Quota exceeded */ - REGISTER_LONG_CONSTANT("SOCKET_EDQUOT", EDQUOT, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef ENOMEDIUM - /* No medium found */ - REGISTER_LONG_CONSTANT("SOCKET_ENOMEDIUM", ENOMEDIUM, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef EMEDIUMTYPE - /* Wrong medium type */ - REGISTER_LONG_CONSTANT("SOCKET_EMEDIUMTYPE", EMEDIUMTYPE, CONST_CS | CONST_PERSISTENT); -#endif - - REGISTER_LONG_CONSTANT("IPPROTO_IP", IPPROTO_IP, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("IPPROTO_IPV6", IPPROTO_IPV6, CONST_CS | CONST_PERSISTENT); - - REGISTER_LONG_CONSTANT("SOL_TCP", IPPROTO_TCP, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("SOL_UDP", IPPROTO_UDP, CONST_CS | CONST_PERSISTENT); - - REGISTER_LONG_CONSTANT("IPV6_UNICAST_HOPS", IPV6_UNICAST_HOPS, CONST_CS | CONST_PERSISTENT); - - REGISTER_LONG_CONSTANT("AI_PASSIVE", AI_PASSIVE, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("AI_CANONNAME", AI_CANONNAME, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("AI_NUMERICHOST", AI_NUMERICHOST, CONST_CS | CONST_PERSISTENT); -#if HAVE_AI_V4MAPPED - REGISTER_LONG_CONSTANT("AI_V4MAPPED", AI_V4MAPPED, CONST_CS | CONST_PERSISTENT); -#endif -#if HAVE_AI_ALL - REGISTER_LONG_CONSTANT("AI_ALL", AI_ALL, CONST_CS | CONST_PERSISTENT); -#endif - REGISTER_LONG_CONSTANT("AI_ADDRCONFIG", AI_ADDRCONFIG, CONST_CS | CONST_PERSISTENT); -#if HAVE_AI_IDN - REGISTER_LONG_CONSTANT("AI_IDN", AI_IDN, CONST_CS | CONST_PERSISTENT); - REGISTER_LONG_CONSTANT("AI_CANONIDN", AI_CANONIDN, CONST_CS | CONST_PERSISTENT); - // REGISTER_LONG_CONSTANT("AI_IDN_ALLOW_UNASSIGNED", AI_IDN_ALLOW_UNASSIGNED, CONST_CS | CONST_PERSISTENT); - // REGISTER_LONG_CONSTANT("AI_IDN_USE_STD3_ASCII_RULES", AI_IDN_USE_STD3_ASCII_RULES, CONST_CS | CONST_PERSISTENT); -#endif -#ifdef AI_NUMERICSERV - REGISTER_LONG_CONSTANT("AI_NUMERICSERV", AI_NUMERICSERV, CONST_CS | CONST_PERSISTENT); -#endif -} - -void php_swoole_socket_coro_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY(swoole_socket_coro, "Swoole\\Coroutine\\Socket", NULL, "Co\\Socket", swoole_socket_coro_methods); - SW_SET_CLASS_SERIALIZABLE(swoole_socket_coro, zend_class_serialize_deny, zend_class_unserialize_deny); - 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, socket_coro, 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("errCode"), 0, ZEND_ACC_PUBLIC); - zend_declare_property_string(swoole_socket_coro_ce, ZEND_STRL("errMsg"), "", ZEND_ACC_PUBLIC); - - SW_INIT_CLASS_ENTRY_EX(swoole_socket_coro_exception, "Swoole\\Coroutine\\Socket\\Exception", NULL, "Co\\Socket\\Exception", NULL, swoole_exception); - - if (!zend_hash_str_find_ptr(&module_registry, ZEND_STRL("sockets"))) - { - swoole_socket_coro_register_constants(module_number); - } -#ifdef ECANCELED - SW_REGISTER_LONG_CONSTANT("SOCKET_ECANCELED", ECANCELED); -#endif -} - -static sw_inline void swoole_socket_coro_sync_properties(zval *zobject, socket_coro *sock) -{ - zend_update_property_long(swoole_socket_coro_ce, zobject, ZEND_STRL("errCode"), sock->socket->errCode); - zend_update_property_string(swoole_socket_coro_ce, zobject, ZEND_STRL("errMsg"), sock->socket->errMsg); -} - -static void sw_inline php_swoole_init_socket(zval *zobject, socket_coro *sock) -{ - zend_update_property_long(swoole_socket_coro_ce, zobject, ZEND_STRL("fd"), sock->socket->get_fd()); -} - -SW_API bool php_swoole_export_socket(zval *zobject, Socket *_socket) -{ - zend_object *object = php_swoole_socket_coro_create_object(swoole_socket_coro_ce); - if (!object) - { - return false; - } - - socket_coro *sock = (socket_coro *) php_swoole_socket_coro_fetch_object(object); - sock->reference = 1; - sock->socket = _socket; - - ZVAL_OBJ(zobject, object); - php_swoole_init_socket(zobject, sock); - return true; -} - -SW_API zend_object* php_swoole_dup_socket(int fd, enum swSocket_type type) -{ - zval zobject; - zend_object *object = php_swoole_socket_coro_create_object(swoole_socket_coro_ce); - socket_coro *sock = (socket_coro *) php_swoole_socket_coro_fetch_object(object); - - php_swoole_check_reactor(); - int new_fd = dup(fd); - if (new_fd < 0) - { - php_swoole_sys_error(E_WARNING, "dup(%d) failed", fd); - return nullptr; - } - sock->socket = new Socket(new_fd, 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); - zend_update_property_long(swoole_socket_coro_ce, &zobject, ZEND_STRL("fd"), sock->socket->get_fd()); - return object; -} - -SW_API Socket* php_swoole_get_socket(zval *zobject) -{ - SW_ASSERT(Z_OBJCE_P(zobject) == swoole_socket_coro_ce); - socket_coro *sock = (socket_coro *) php_swoole_socket_coro_fetch_object(Z_OBJ_P(zobject)); - return sock->socket; -} - -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); - socket_coro *sock = (socket_coro *) php_swoole_socket_coro_fetch_object(object); - sock->socket = socket; - ZVAL_OBJ(zobject, object); - php_swoole_init_socket(zobject, sock); -} - -SW_API bool php_swoole_socket_set_protocol(Socket *sock, zval *zset) -{ - HashTable *vht = Z_ARRVAL_P(zset); - zval *ztmp; - bool ret = true; - - /** - * ssl - */ -#ifdef SW_USE_OPENSSL - if (php_swoole_array_get_value(vht, "open_ssl", ztmp)) - { - sock->open_ssl = zval_is_true(ztmp); - } - if (sock->open_ssl) - { - if (!php_swoole_socket_set_ssl(sock, zset)) - { - ret = false; - } - if (!sock->ssl_check_context()) - { - ret = false; - } - } -#endif - /** - * protocol - */ - //buffer: eof check - if (php_swoole_array_get_value(vht, "open_eof_check", ztmp)) - { - sock->open_eof_check = zval_is_true(ztmp); - } - //buffer: split package with eof - if (php_swoole_array_get_value(vht, "open_eof_split", ztmp)) - { - sock->protocol.split_by_eof = zval_is_true(ztmp); - if (sock->protocol.split_by_eof) - { - sock->open_eof_check = 1; - } - } - //package eof - if (php_swoole_array_get_value(vht, "package_eof", ztmp)) - { - zend::string str_v(ztmp); - if (str_v.len() == 0) - { - php_swoole_fatal_error(E_ERROR, "package_eof cannot be an empty string"); - ret = false; - } - else if (str_v.len() > SW_DATA_EOF_MAXLEN) - { - php_swoole_fatal_error(E_ERROR, "package_eof max length is %d", SW_DATA_EOF_MAXLEN); - ret = false; - } - else - { - sock->protocol.package_eof_len = str_v.len(); - memcpy(sock->protocol.package_eof, str_v.val(), str_v.len()); - } - } - if (php_swoole_array_get_value(vht, "open_fastcgi_protocol", ztmp)) - { - #define FCGI_HEADER_LEN 8 - #define FCGI_MAX_LENGTH 0xffff - sock->open_length_check = zval_is_true(ztmp); - sock->protocol.package_length_size = FCGI_HEADER_LEN; - sock->protocol.package_length_offset = 0; - sock->protocol.package_body_offset = 0; - sock->protocol.get_package_length = [](swProtocol *protocol, swSocket *conn, char *data, uint32_t size) { - const uint8_t *p = (const uint8_t *) data; - ssize_t length = 0; - if (size >= FCGI_HEADER_LEN) - { - length = ((p[4] << 8) | p[5]) + p[6]; - if (length > FCGI_MAX_LENGTH) - { - length = -1; - } - else - { - length += FCGI_HEADER_LEN; - } - } - return length; - }; - } - // open mqtt protocol - if (php_swoole_array_get_value(vht, "open_mqtt_protocol", ztmp)) - { - sock->open_length_check = zval_is_true(ztmp); - sock->protocol.package_length_size = SW_MQTT_MIN_LENGTH; - sock->protocol.package_length_offset = 0; - sock->protocol.package_body_offset = 0; - sock->protocol.get_package_length = swMqtt_get_package_length; - } - // open length check - if (php_swoole_array_get_value(vht, "open_length_check", ztmp)) - { - sock->open_length_check = zval_is_true(ztmp); - sock->protocol.get_package_length = swProtocol_get_package_length; - } - // package length size - if (php_swoole_array_get_value(vht, "package_length_type", ztmp)) - { - zend::string str_v(ztmp); - sock->protocol.package_length_type = str_v.val()[0]; - sock->protocol.package_length_size = swoole_type_size(sock->protocol.package_length_type); - if (sock->protocol.package_length_size == 0) - { - php_swoole_fatal_error(E_WARNING, "Unknown package_length_type name '%c', see pack(). Link: http://php.net/pack", sock->protocol.package_length_type); - ret = false; - } - } - // package length offset - if (php_swoole_array_get_value(vht, "package_length_offset", ztmp)) - { - zend_long v = zval_get_long(ztmp); - sock->protocol.package_length_offset = SW_MAX(0, SW_MIN(v, UINT16_MAX)); - } - // package body start - if (php_swoole_array_get_value(vht, "package_body_offset", ztmp)) - { - zend_long v = zval_get_long(ztmp); - sock->protocol.package_body_offset = SW_MAX(0, SW_MIN(v, UINT16_MAX)); - } - // length function - if (php_swoole_array_get_value(vht, "package_length_func", ztmp)) - { - do - { - swProtocol_length_function func; - if (Z_TYPE_P(ztmp) == IS_STRING && (func = (swProtocol_length_function) swoole_get_function(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, NULL, 0, &func_name, NULL, fci_cache, NULL)) - { - 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; - } - 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); - sock->protocol.package_max_length = SW_MAX(0, SW_MIN(v, UINT32_MAX)); - } - else - { - sock->protocol.package_max_length = SW_INPUT_BUFFER_SIZE; - } - - return ret; -} - -static PHP_METHOD(swoole_socket_coro, __construct) -{ - zend_long domain, type, protocol = IPPROTO_IP; - - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 2, 3) - Z_PARAM_LONG(domain) - Z_PARAM_LONG(type) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(protocol) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - socket_coro *sock = (socket_coro *) php_swoole_socket_coro_fetch_object(Z_OBJ_P(ZEND_THIS)); - - 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); - } -} - -static PHP_METHOD(swoole_socket_coro, bind) -{ - char *address; - size_t l_address; - zend_long port = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STRING(address, l_address) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(port) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - 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); - RETURN_FALSE; - } - RETURN_TRUE; -} - -static PHP_METHOD(swoole_socket_coro, listen) -{ - zend_long backlog = SW_BACKLOG; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(backlog) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - swoole_get_socket_coro(sock, ZEND_THIS); - - if (!sock->socket->listen(backlog)) - { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); - RETURN_FALSE; - } - RETURN_TRUE; -} - -static PHP_METHOD(swoole_socket_coro, accept) -{ - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - swoole_get_socket_coro(sock, ZEND_THIS); - - Socket *conn = sock->socket->accept(timeout); - if (conn) - { - zend_object *client = php_swoole_socket_coro_create_object(swoole_socket_coro_ce); - socket_coro *client_sock = (socket_coro *) php_swoole_socket_coro_fetch_object(client); - client_sock->socket = conn; - ZVAL_OBJ(return_value, &client_sock->std); - php_swoole_init_socket(return_value, client_sock); - } - else - { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); - RETURN_FALSE; - } -} - -static PHP_METHOD(swoole_socket_coro, connect) -{ - char *host; - size_t l_host; - zend_long port = 0; - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(1, 3) - Z_PARAM_STRING(host, l_host) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(port) - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - swoole_get_socket_coro(sock, ZEND_THIS); - - if (sock->socket->get_sock_domain() == AF_INET6 || sock->socket->get_sock_domain() == AF_INET) - { - if (ZEND_NUM_ARGS() == 1) - { - php_swoole_error(E_WARNING, "Socket of type AF_INET/AF_INET6 requires port argument"); - RETURN_FALSE; - } - else if (port == 0 || port >= 65536) - { - php_swoole_error(E_WARNING, "Invalid port argument[" ZEND_LONG_FMT "]", port); - RETURN_FALSE; - } - } - Socket::timeout_setter ts(sock->socket, timeout, SW_TIMEOUT_CONNECT); - if (!sock->socket->connect(std::string(host, l_host), port)) - { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); - RETURN_FALSE; - } - RETURN_TRUE; -} - -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); - RETURN_BOOL(liveness); -} - -static PHP_METHOD(swoole_socket_coro, peek) -{ - zend_long length = SW_BUFFER_SIZE_BIG; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(length) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (UNEXPECTED(length <= 0)) - { - length = SW_BUFFER_SIZE_BIG; - } - - swoole_get_socket_coro(sock, ZEND_THIS); - - 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); - if (UNEXPECTED(bytes < 0)) - { - zend_string_free(buf); - RETURN_FALSE; - } - else if (UNEXPECTED(bytes == 0)) - { - zend_string_free(buf); - RETURN_EMPTY_STRING(); - } - else - { - RETURN_STR(sw_zend_string_recycle(buf, length, bytes)); - } -} - -static sw_inline void swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAMETERS, const bool all) -{ - zend_long length = SW_BUFFER_SIZE_BIG; - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 2) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(length) - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (UNEXPECTED(length <= 0)) - { - length = SW_BUFFER_SIZE_BIG; - } - - swoole_get_socket_coro(sock, ZEND_THIS); - - zend_string *buf = zend_string_alloc(length, 0); - Socket::timeout_setter ts(sock->socket, timeout, SW_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); - if (UNEXPECTED(bytes < 0)) - { - zend_string_free(buf); - RETURN_FALSE; - } - else if (UNEXPECTED(bytes == 0)) - { - zend_string_free(buf); - RETURN_EMPTY_STRING(); - } - else - { - RETURN_STR(sw_zend_string_recycle(buf, length, bytes)); - } -} - -static PHP_METHOD(swoole_socket_coro, recv) -{ - swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); -} - -static PHP_METHOD(swoole_socket_coro, recvAll) -{ - swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); -} - -static PHP_METHOD(swoole_socket_coro, recvPacket) -{ - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - swoole_get_socket_coro(sock, ZEND_THIS); - ssize_t retval = sock->socket->recv_packet(timeout); - swoole_socket_coro_sync_properties(ZEND_THIS, sock); - if (retval < 0) - { - RETURN_FALSE; - } - else if (retval == 0) - { - RETURN_EMPTY_STRING(); - } - else - { - swString *buf = sock->socket->get_read_buffer(); - RETURN_STRINGL(buf->str, retval); - } -} - -static sw_inline void swoole_socket_coro_send(INTERNAL_FUNCTION_PARAMETERS, const bool all) -{ - char *data; - size_t length; - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STRING(data, length) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - swoole_get_socket_coro(sock, ZEND_THIS); - - Socket::timeout_setter ts(sock->socket, timeout, SW_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); - if (UNEXPECTED(retval < 0)) - { - RETURN_FALSE; - } - else - { - RETURN_LONG(retval); - } -} - -static PHP_METHOD(swoole_socket_coro, send) -{ - swoole_socket_coro_send(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); -} - -static PHP_METHOD(swoole_socket_coro, sendFile) -{ - char *file; - size_t file_len; - 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; - } - if (file_len == 0) - { - php_swoole_fatal_error(E_WARNING, "file to send is empty"); - RETURN_FALSE; - } - - swoole_get_socket_coro(sock, ZEND_THIS); - if (!sock->socket->sendfile(file, offset, length)) - { - zend_update_property_long(swoole_socket_coro_ce, ZEND_THIS, ZEND_STRL("errCode"), sock->socket->errCode); - zend_update_property_string(swoole_socket_coro_ce, ZEND_THIS, ZEND_STRL("errMsg"), sock->socket->errMsg); - RETVAL_FALSE; - } - else - { - RETVAL_TRUE; - } -} - -static PHP_METHOD(swoole_socket_coro, sendAll) -{ - swoole_socket_coro_send(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); -} - -static PHP_METHOD(swoole_socket_coro, recvfrom) -{ - zval *peername; - double timeout = 0; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_ZVAL_EX(peername, 0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(timeout) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - swoole_get_socket_coro(sock, ZEND_THIS); - - zend_string *buf = zend_string_alloc(SW_BUFFER_SIZE_BIG, 0); - Socket::timeout_setter ts(sock->socket, timeout, SW_TIMEOUT_READ); - ssize_t bytes = sock->socket->recvfrom(ZSTR_VAL(buf), SW_BUFFER_SIZE_BIG); - swoole_socket_coro_sync_properties(ZEND_THIS, sock); - if (bytes < 0) - { - zend_string_free(buf); - RETURN_FALSE; - } - else if (bytes == 0) - { - zend_string_free(buf); - RETURN_EMPTY_STRING(); - } - else - { - zval_dtor(peername); - array_init(peername); - add_assoc_string(peername, "address", (char *) sock->socket->get_ip()); - add_assoc_long(peername, "port", sock->socket->get_port()); - - ZSTR_LEN(buf) = bytes; - ZSTR_VAL(buf)[bytes] = 0; - RETURN_STR(buf); - } -} - -static PHP_METHOD(swoole_socket_coro, sendto) -{ - char *data; - size_t l_data; - char *addr; - size_t l_addr; - zend_long port = 0; - - ZEND_PARSE_PARAMETERS_START(3, 3) - Z_PARAM_STRING(addr, l_addr) - Z_PARAM_LONG(port) - Z_PARAM_STRING(data, l_data) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - 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); - if (retval < 0) - { - RETURN_FALSE; - } - else - { - RETURN_LONG(retval); - } -} - -static PHP_METHOD(swoole_socket_coro, shutdown) -{ - zend_long how = SHUT_RDWR; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(how) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - swoole_get_socket_coro(sock, ZEND_THIS); - - if (!sock->socket->shutdown(how)) - { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); - RETURN_FALSE; - } - RETURN_TRUE; -} - -static PHP_METHOD(swoole_socket_coro, close) -{ - swoole_get_socket_coro(sock, ZEND_THIS); - if (sock->reference) - { - 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; - } - RETURN_TRUE; -} - -static PHP_METHOD(swoole_socket_coro, getsockname) -{ - swoole_get_socket_coro(sock, ZEND_THIS); - - swSocketAddress sa; - if (!sock->socket->getsockname(&sa)) - { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); - RETURN_FALSE; - } - - array_init(return_value); - add_assoc_string(return_value, "address", (char *) swSocket_get_ip(sock->socket->get_type(), &sa)); - add_assoc_long(return_value, "port", swSocket_get_port(sock->socket->get_type(), &sa)); -} - -static PHP_METHOD(swoole_socket_coro, getpeername) -{ - swoole_get_socket_coro(sock, ZEND_THIS); - - swSocketAddress sa; - if (!sock->socket->getpeername(&sa)) - { - swoole_socket_coro_sync_properties(ZEND_THIS, sock); - RETURN_FALSE; - } - - array_init(return_value); - add_assoc_string(return_value, "address", (char *) swSocket_get_ip(sock->socket->get_type(), &sa)); - add_assoc_long(return_value, "port", swSocket_get_port(sock->socket->get_type(), &sa)); -} - -static PHP_METHOD(swoole_socket_coro, getOption) -{ - struct linger linger_val; - struct timeval tv; - socklen_t optlen; - int other_val; - zend_long level, optname; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &level, &optname) == FAILURE) - { - return; - } - - swoole_get_socket_coro(sock, ZEND_THIS); - - if (level == IPPROTO_IP) - { - switch (optname) - { - case IP_MULTICAST_IF: - { - struct in_addr if_addr; - unsigned int if_index; - optlen = sizeof(if_addr); - if (getsockopt(sock->socket->get_fd(), level, optname, (char*) &if_addr, &optlen) != 0) - { - php_swoole_sys_error(E_WARNING, "getsockopt(%d, " ZEND_LONG_FMT ", " ZEND_LONG_FMT ")", sock->socket->get_fd(), level, optname); - RETURN_FALSE; - } - if (php_add4_to_if_index(&if_addr, sock->socket, &if_index) == SUCCESS) - { - RETURN_LONG((zend_long ) if_index); - } - else - { - RETURN_FALSE; - } - } - } - } - else if (level == IPPROTO_IPV6) - { - int ret = php_do_getsockopt_ipv6_rfc3542(sock->socket, level, optname, return_value); - if (ret == SUCCESS) - { - return; - } - else if (ret == FAILURE) - { - RETURN_FALSE; - } /* else continue */ - } - - /* sol_socket options and general case */ - switch (optname) - { - case SO_LINGER: - optlen = sizeof(linger_val); - - if (getsockopt(sock->socket->get_fd(), level, optname, (char*) &linger_val, &optlen) != 0) - { - php_swoole_sys_error(E_WARNING, "getsockopt(%d, " ZEND_LONG_FMT ", " ZEND_LONG_FMT ")", sock->socket->get_fd(), level, optname); - RETURN_FALSE; - } - - array_init(return_value); - add_assoc_long(return_value, "l_onoff", linger_val.l_onoff); - add_assoc_long(return_value, "l_linger", linger_val.l_linger); - break; - - case SO_RCVTIMEO: - case SO_SNDTIMEO: - optlen = sizeof(tv); - - if (getsockopt(sock->socket->get_fd(), level, optname, (char*) &tv, &optlen) != 0) - { - php_swoole_sys_error(E_WARNING, "getsockopt(%d, " ZEND_LONG_FMT ", " ZEND_LONG_FMT ")", sock->socket->get_fd(), level, optname); - RETURN_FALSE; - } - - array_init(return_value); - - add_assoc_long(return_value, "sec", tv.tv_sec); - add_assoc_long(return_value, "usec", tv.tv_usec); - break; - - default: - optlen = sizeof(other_val); - - if (getsockopt(sock->socket->get_fd(), level, optname, (char*) &other_val, &optlen) != 0) - { - php_swoole_sys_error(E_WARNING, "getsockopt(%d, " ZEND_LONG_FMT ", " ZEND_LONG_FMT ")", sock->socket->get_fd(), level, optname); - RETURN_FALSE; - } - if (optlen == 1) - { - other_val = *((unsigned char *) &other_val); - } - - RETURN_LONG(other_val); - break; - } -} - -static PHP_METHOD(swoole_socket_coro, setOption) -{ - zval *arg4; - struct linger lv; - int ov, optlen, retval; - struct timeval tv; - zend_long level, optname; - char *opt_ptr; - HashTable *opt_ht; - zval *l_onoff, *l_linger; - zval *sec, *usec; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "llz", &level, &optname, &arg4) == FAILURE) - { - return; - } - - swoole_get_socket_coro(sock, ZEND_THIS); - -#define HANDLE_SUBCALL(res) \ - do { \ - if (res == 1) { goto default_case; } \ - else if (res == SUCCESS) { RETURN_TRUE; } \ - else { RETURN_FALSE; } \ - } while (0) - - if (level == IPPROTO_IP) - { - int res = php_do_setsockopt_ip_mcast(sock->socket, level, optname, arg4); - HANDLE_SUBCALL(res); - } - else if (level == IPPROTO_IPV6) - { - int res = php_do_setsockopt_ipv6_mcast(sock->socket, level, optname, arg4); - if (res == 1) - { - res = php_do_setsockopt_ipv6_rfc3542(sock->socket, level, optname, arg4); - } - HANDLE_SUBCALL(res); - } - - switch (optname) { - case SO_LINGER: { - const char l_onoff_key[] = "l_onoff"; - const char l_linger_key[] = "l_linger"; - - convert_to_array_ex(arg4); - opt_ht = Z_ARRVAL_P(arg4); - - if ((l_onoff = zend_hash_str_find(opt_ht, l_onoff_key, sizeof(l_onoff_key) - 1)) == NULL) { - php_error_docref(NULL, E_WARNING, "no key \"%s\" passed in optval", l_onoff_key); - RETURN_FALSE; - } - if ((l_linger = zend_hash_str_find(opt_ht, l_linger_key, sizeof(l_linger_key) - 1)) == NULL) { - php_error_docref(NULL, E_WARNING, "no key \"%s\" passed in optval", l_linger_key); - RETURN_FALSE; - } - - convert_to_long_ex(l_onoff); - convert_to_long_ex(l_linger); - - lv.l_onoff = (unsigned short)Z_LVAL_P(l_onoff); - lv.l_linger = (unsigned short)Z_LVAL_P(l_linger); - - optlen = sizeof(lv); - opt_ptr = (char*) &lv; - break; - } - - case SO_RCVTIMEO: - case SO_SNDTIMEO: { - const char sec_key[] = "sec"; - const char usec_key[] = "usec"; - - convert_to_array_ex(arg4); - opt_ht = Z_ARRVAL_P(arg4); - - if ((sec = zend_hash_str_find(opt_ht, sec_key, sizeof(sec_key) - 1)) == NULL) { - php_error_docref(NULL, E_WARNING, "no key \"%s\" passed in optval", sec_key); - RETURN_FALSE; - } - if ((usec = zend_hash_str_find(opt_ht, usec_key, sizeof(usec_key) - 1)) == NULL) { - php_error_docref(NULL, E_WARNING, "no key \"%s\" passed in optval", usec_key); - RETURN_FALSE; - } - - convert_to_long_ex(sec); - convert_to_long_ex(usec); - tv.tv_sec = Z_LVAL_P(sec); - tv.tv_usec = Z_LVAL_P(usec); - sock->socket->set_timeout(&tv, optname == SO_RCVTIMEO ? SW_TIMEOUT_READ : SW_TIMEOUT_CONNECT | SW_TIMEOUT_WRITE); - RETURN_TRUE; - break; - } -#ifdef SO_BINDTODEVICE - case SO_BINDTODEVICE: { - if (Z_TYPE_P(arg4) == IS_STRING) { - opt_ptr = Z_STRVAL_P(arg4); - optlen = Z_STRLEN_P(arg4); - } else { - opt_ptr = (char *) ""; - optlen = 0; - } - break; - } -#endif - - default: - default_case: - convert_to_long_ex(arg4); - ov = Z_LVAL_P(arg4); - - optlen = sizeof(ov); - opt_ptr = (char*) &ov; - break; - } - - retval = setsockopt(sock->socket->get_fd(), level, optname, opt_ptr, optlen); - if (retval != 0) - { - php_swoole_sys_error(E_WARNING, "setsockopt(%d) failed", sock->socket->get_fd()); - RETURN_FALSE; - } - - RETURN_TRUE; -} - -static PHP_METHOD(swoole_socket_coro, cancel) -{ - swoole_get_socket_coro(sock, ZEND_THIS); - zend_long event = SW_EVENT_READ; - - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(event) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - RETURN_BOOL(sock->socket->cancel(event == SW_EVENT_READ ? SW_EVENT_READ : SW_EVENT_WRITE)); -} - -static PHP_METHOD(swoole_socket_coro, setProtocol) -{ - swoole_get_socket_coro(sock, ZEND_THIS); - zval *zset; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY(zset) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (php_swoole_array_length(zset) == 0) - { - RETURN_FALSE; - } - else - { - RETURN_BOOL(php_swoole_socket_set_protocol(sock->socket, zset)); - } -} - -#ifdef SW_USE_OPENSSL -static PHP_METHOD(swoole_socket_coro, sslHandshake) -{ - swoole_get_socket_coro(sock, ZEND_THIS); - - RETURN_BOOL(sock->socket->ssl_handshake()); -} -#endif diff --git a/swoole_table.cc b/swoole_table.cc deleted file mode 100644 index c3b055b358f..00000000000 --- a/swoole_table.cc +++ /dev/null @@ -1,976 +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.h" -#include "table.h" - -static inline void php_swoole_table_row2array(swTable *table, swTableRow *row, zval *return_value) -{ - array_init(return_value); - - swTable_string_length_t vlen = 0; - double dval = 0; - int64_t lval = 0; - - for (auto i = table->column_list->begin(); i != table->column_list->end(); i++) - { - swTableColumn *col = *i; - if (col->type == SW_TABLE_STRING) - { - memcpy(&vlen, row->data + col->index, sizeof(swTable_string_length_t)); - add_assoc_stringl_ex(return_value, col->name.c_str(), col->name.length(), row->data + col->index + sizeof(swTable_string_length_t), vlen); - } - else if (col->type == SW_TABLE_FLOAT) - { - memcpy(&dval, row->data + col->index, sizeof(dval)); - add_assoc_double_ex(return_value, col->name.c_str(), col->name.length(), dval); - } - else - { - switch (col->type) - { - case SW_TABLE_INT8: - memcpy(&lval, row->data + col->index, 1); - add_assoc_long_ex(return_value, col->name.c_str(), col->name.length(), (int8_t) lval); - break; - case SW_TABLE_INT16: - memcpy(&lval, row->data + col->index, 2); - add_assoc_long_ex(return_value, col->name.c_str(), col->name.length(), (int16_t) lval); - break; - case SW_TABLE_INT32: - memcpy(&lval, row->data + col->index, 4); - add_assoc_long_ex(return_value, col->name.c_str(), col->name.length(), (int32_t) lval); - break; - default: - memcpy(&lval, row->data + col->index, 8); - add_assoc_long_ex(return_value, col->name.c_str(), col->name.length(), lval); - break; - } - } - } -} - -static inline void php_swoole_table_get_field_value(swTable *table, swTableRow *row, zval *return_value, char *field, uint16_t field_len) -{ - swTable_string_length_t vlen = 0; - double dval = 0; - int64_t lval = 0; - - swTableColumn *col = swTableColumn_get(table, std::string(field, field_len)); - if (!col) - { - ZVAL_FALSE(return_value); - return; - } - if (col->type == SW_TABLE_STRING) - { - memcpy(&vlen, row->data + col->index, sizeof(swTable_string_length_t)); - ZVAL_STRINGL(return_value, row->data + col->index + sizeof(swTable_string_length_t), vlen); - } - else if (col->type == SW_TABLE_FLOAT) - { - memcpy(&dval, row->data + col->index, sizeof(dval)); - ZVAL_DOUBLE(return_value, dval); - } - else - { - switch (col->type) - { - case SW_TABLE_INT8: - memcpy(&lval, row->data + col->index, 1); - ZVAL_LONG(return_value, (int8_t) lval); - break; - case SW_TABLE_INT16: - memcpy(&lval, row->data + col->index, 2); - ZVAL_LONG(return_value, (int16_t) lval); - break; - case SW_TABLE_INT32: - memcpy(&lval, row->data + col->index, 4); - ZVAL_LONG(return_value, (int32_t) lval); - break; - default: - memcpy(&lval, row->data + col->index, 8); - ZVAL_LONG(return_value, lval); - break; - } - } -} - -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; - -typedef struct -{ - swTable *ptr; - zend_object std; -} table_t; - -static inline table_t* php_swoole_table_fetch_object(zend_object *obj) -{ - return (table_t *) ((char *) obj - swoole_table_handlers.offset); -} - -static inline swTable * php_swoole_table_get_ptr(zval *zobject) -{ - return php_swoole_table_fetch_object(Z_OBJ_P(zobject))->ptr; -} - -static inline swTable * php_swoole_table_get_and_check_ptr(zval *zobject) -{ - swTable *table = php_swoole_table_get_ptr(zobject); - if (!table) - { - php_swoole_fatal_error(E_ERROR, "you must call Table constructor first"); - } - return table; -} - -static inline swTable * php_swoole_table_get_and_check_ptr2(zval *zobject) -{ - swTable *table = php_swoole_table_get_and_check_ptr(zobject); - if (!table->memory) - { - php_swoole_fatal_error(E_ERROR, "the swoole table does not exist"); - } - return table; -} - -static void inline php_swoole_table_set_ptr(zval *zobject, swTable *ptr) -{ - php_swoole_table_fetch_object(Z_OBJ_P(zobject))->ptr = ptr; -} - -static inline void php_swoole_table_free_object(zend_object *object) -{ - zend_object_std_dtor(object); -} - -static inline zend_object *php_swoole_table_create_object(zend_class_entry *ce) -{ - table_t *table = (table_t *) ecalloc(1, sizeof(table_t) + zend_object_properties_size(ce)); - zend_object_std_init(&table->std, ce); - object_properties_init(&table->std, ce); - table->std.handlers = &swoole_table_handlers; - return &table->std; -} - -typedef struct -{ - swTable *ptr; - zend_object std; -} table_row_t; - -static inline table_row_t* php_swoole_table_row_fetch_object(zend_object *obj) -{ - return (table_row_t *) ((char *) obj - swoole_table_row_handlers.offset); -} - -static inline swTable * php_swoole_table_row_get_ptr(zval *zobject) -{ - return php_swoole_table_row_fetch_object(Z_OBJ_P(zobject))->ptr; -} - -static inline swTable * php_swoole_table_row_get_and_check_ptr(zval *zobject) -{ - swTable *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, swTable *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) -{ - table_row_t *table_row = (table_row_t *) ecalloc(1, sizeof(table_row_t) + zend_object_properties_size(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; -} - -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() - -static PHP_METHOD(swoole_table, __construct); -static PHP_METHOD(swoole_table, column); -static PHP_METHOD(swoole_table, create); -static PHP_METHOD(swoole_table, set); -static PHP_METHOD(swoole_table, get); -static PHP_METHOD(swoole_table, del); -static PHP_METHOD(swoole_table, exists); -static PHP_METHOD(swoole_table, incr); -static PHP_METHOD(swoole_table, decr); -static PHP_METHOD(swoole_table, count); -static PHP_METHOD(swoole_table, destroy); -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, rewind); -static PHP_METHOD(swoole_table, next); -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); - -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_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, getMemorySize, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) - 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, 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_FE_END -}; - -void php_swoole_table_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY(swoole_table, "Swoole\\Table", "swoole_table", NULL, swoole_table_methods); - SW_SET_CLASS_SERIALIZABLE(swoole_table, zend_class_serialize_deny, zend_class_unserialize_deny); - 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, table_t, std); - zend_class_implements(swoole_table_ce, 2, zend_ce_iterator, zend_ce_arrayaccess); -#ifdef SW_HAVE_COUNTABLE - zend_class_implements(swoole_table_ce, 1, zend_ce_countable); -#endif - - zend_declare_class_constant_long(swoole_table_ce, ZEND_STRL("TYPE_INT"), SW_TABLE_INT); - zend_declare_class_constant_long(swoole_table_ce, ZEND_STRL("TYPE_STRING"), SW_TABLE_STRING); - zend_declare_class_constant_long(swoole_table_ce, ZEND_STRL("TYPE_FLOAT"), SW_TABLE_FLOAT); - - SW_INIT_CLASS_ENTRY(swoole_table_row, "Swoole\\Table\\Row", "swoole_table_row", NULL, 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, table_row_t, 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) -{ - swTable *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_long table_size; - double conflict_proportion = SW_TABLE_CONFLICT_PROPORTION; - - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 2) - Z_PARAM_LONG(table_size) - Z_PARAM_OPTIONAL - Z_PARAM_DOUBLE(conflict_proportion) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - table = swTable_new(table_size, conflict_proportion); - if (table == NULL) - { - zend_throw_exception(swoole_exception_ce, "global memory allocation failure", SW_ERROR_MALLOC_FAIL); - RETURN_FALSE; - } - table->hash_func = [](const char *key, size_t len) { - zend_string *string = (zend_string *) (key - offsetof(zend_string, val)); - return zend_string_hash_val(string); - }; - php_swoole_table_set_ptr(ZEND_THIS, table); -} - -PHP_METHOD(swoole_table, column) -{ - swTable *table = php_swoole_table_get_and_check_ptr(ZEND_THIS); - char *name; - size_t len; - long type; - long size = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sl|l", &name, &len, &type, &size) == FAILURE) - { - RETURN_FALSE; - } - if (type == SW_TABLE_STRING) - { - if (size < 1) - { - php_swoole_fatal_error(E_WARNING, "the length of string type values has to be more than zero"); - RETURN_FALSE; - } - size = SW_MEM_ALIGNED_SIZE(size); - } - //default int32 - if (type == SW_TABLE_INT && size < 4) - { - size = 4; - } - if (table->memory) - { - php_swoole_fatal_error(E_WARNING, "unable to add column after table has been created"); - RETURN_FALSE; - } - RETURN_BOOL(swTableColumn_add(table, std::string(name, len), (enum swTableColumn_type )type, size)); -} - -static PHP_METHOD(swoole_table, create) -{ - swTable *table = php_swoole_table_get_and_check_ptr(ZEND_THIS); - - if (swTable_create(table) < 0) - { - php_swoole_fatal_error(E_ERROR, "unable to allocate memory"); - RETURN_FALSE; - } - zend_update_property_long(swoole_table_ce, ZEND_THIS, ZEND_STRL("size"), table->size); - zend_update_property_long(swoole_table_ce, ZEND_THIS, ZEND_STRL("memorySize"), table->memory_size); - RETURN_TRUE; -} - -static PHP_METHOD(swoole_table, destroy) -{ - swTable *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); - - swTable_free(table); - php_swoole_table_set_ptr(ZEND_THIS, nullptr); - RETURN_TRUE; -} - -static PHP_METHOD(swoole_table, set) -{ - swTable *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); - zval *array; - char *key; - size_t keylen; - - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 2, 2) - Z_PARAM_STRING(key, keylen) - Z_PARAM_ARRAY(array) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (!table->memory) - { - php_swoole_fatal_error(E_ERROR, "the table object does not exist"); - RETURN_FALSE; - } - - if (keylen >= SW_TABLE_KEY_SIZE) - { - php_swoole_fatal_error(E_WARNING, "key[%s] is too long", key); - } - - swTableRow *_rowlock = NULL; - swTableRow *row = swTableRow_set(table, key, keylen, &_rowlock); - if (!row) - { - swTableRow_unlock(_rowlock); - php_swoole_error(E_WARNING, "failed to set('%*s'), unable to allocate memory", (int )keylen, key); - RETURN_FALSE; - } - - HashTable *ht = Z_ARRVAL_P(array); - char *k; - uint32_t klen; - int ktype; - zval *zv; - swTableColumn *col; - - SW_HASHTABLE_FOREACH_START2(ht, k, klen, ktype, zv) - { - col = swTableColumn_get(table, std::string(k, klen)); - if (k == NULL || col == NULL) - { - continue; - } - else if (col->type == SW_TABLE_STRING) - { - zend_string *str = zval_get_string(zv); - swTableRow_set_value(row, col, ZSTR_VAL(str), ZSTR_LEN(str)); - zend_string_release(str); - } - else if (col->type == SW_TABLE_FLOAT) - { - double _value = zval_get_double(zv); - swTableRow_set_value(row, col, &_value, 0); - } - else - { - long _value = zval_get_long(zv); - swTableRow_set_value(row, col, &_value, 0); - } - } - (void) ktype; - SW_HASHTABLE_FOREACH_END(); - swTableRow_unlock(_rowlock); - RETURN_TRUE; -} - -static PHP_METHOD(swoole_table, offsetSet) -{ - ZEND_MN(swoole_table_set)(INTERNAL_FUNCTION_PARAM_PASSTHRU); -} - -static PHP_METHOD(swoole_table, incr) -{ - swTable *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); - char *key; - size_t key_len; - char *col; - size_t col_len; - zval* incrby = NULL; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|z", &key, &key_len, &col, &col_len, &incrby) == FAILURE) - { - RETURN_FALSE; - } - - swTableRow *_rowlock = nullptr; - swTableRow *row = swTableRow_set(table, key, key_len, &_rowlock); - if (!row) - { - swTableRow_unlock(_rowlock); - php_swoole_fatal_error(E_WARNING, "unable to allocate memory"); - RETURN_FALSE; - } - - swTableColumn *column = swTableColumn_get(table, std::string(col, col_len)); - if (column == nullptr) - { - swTableRow_unlock(_rowlock); - php_swoole_fatal_error(E_WARNING, "column[%s] does not exist", col); - RETURN_FALSE; - } - else if (column->type == SW_TABLE_STRING) - { - swTableRow_unlock(_rowlock); - php_swoole_fatal_error(E_WARNING, "can't execute 'incr' on a string type column"); - RETURN_FALSE; - } - else if (column->type == SW_TABLE_FLOAT) - { - double set_value = 0; - memcpy(&set_value, row->data + column->index, sizeof(set_value)); - if (incrby) - { - set_value += zval_get_double(incrby); - } - else - { - set_value += 1; - } - swTableRow_set_value(row, column, &set_value, 0); - RETVAL_DOUBLE(set_value); - } - else - { - int64_t set_value = 0; - memcpy(&set_value, row->data + column->index, column->size); - if (incrby) - { - set_value += zval_get_long(incrby); - } - else - { - set_value += 1; - } - swTableRow_set_value(row, column, &set_value, 0); - RETVAL_LONG(set_value); - } - swTableRow_unlock(_rowlock); -} - -static PHP_METHOD(swoole_table, decr) -{ - swTable *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); - char *key; - size_t key_len; - char *col; - size_t col_len; - zval *decrby = NULL; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|z", &key, &key_len, &col, &col_len, &decrby) == FAILURE) - { - RETURN_FALSE; - } - - swTableRow *_rowlock = NULL; - swTableRow *row = swTableRow_set(table, key, key_len, &_rowlock); - if (!row) - { - swTableRow_unlock(_rowlock); - php_swoole_fatal_error(E_WARNING, "unable to allocate memory"); - RETURN_FALSE; - } - - swTableColumn *column = swTableColumn_get(table, std::string(col, col_len)); - if (column == nullptr) - { - swTableRow_unlock(_rowlock); - php_swoole_fatal_error(E_WARNING, "column[%s] does not exist", col); - RETURN_FALSE; - } - else if (column->type == SW_TABLE_STRING) - { - swTableRow_unlock(_rowlock); - php_swoole_fatal_error(E_WARNING, "can't execute 'decr' on a string type column"); - RETURN_FALSE; - } - else if (column->type == SW_TABLE_FLOAT) - { - double set_value = 0; - memcpy(&set_value, row->data + column->index, sizeof(set_value)); - if (decrby) - { - set_value -= zval_get_double(decrby); - } - else - { - set_value -= 1; - } - swTableRow_set_value(row, column, &set_value, 0); - RETVAL_DOUBLE(set_value); - } - else - { - int64_t set_value = 0; - memcpy(&set_value, row->data + column->index, column->size); - if (decrby) - { - set_value -= zval_get_long(decrby); - } - else - { - set_value -= 1; - } - swTableRow_set_value(row, column, &set_value, 0); - RETVAL_LONG(set_value); - } - swTableRow_unlock(_rowlock); -} - -static PHP_METHOD(swoole_table, get) -{ - swTable *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); - char *key; - size_t keylen; - char *field = NULL; - size_t field_len = 0; - - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 2) - Z_PARAM_STRING(key, keylen) - Z_PARAM_OPTIONAL - Z_PARAM_STRING(field, field_len) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - swTableRow *_rowlock = NULL; - swTableRow *row = swTableRow_get(table, key, keylen, &_rowlock); - if (!row) - { - RETVAL_FALSE; - } - else if (field && field_len > 0) - { - php_swoole_table_get_field_value(table, row, return_value, field, (uint16_t) field_len); - } - else - { - php_swoole_table_row2array(table, row, return_value); - } - swTableRow_unlock(_rowlock); -} - -static PHP_METHOD(swoole_table, offsetGet) -{ - swTable *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); - char *key; - size_t keylen; - char *field = NULL; - size_t field_len = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &key, &keylen, &field, &field_len) == FAILURE) - { - RETURN_FALSE; - } - - zval value; - swTableRow *_rowlock = NULL; - swTableRow *row = swTableRow_get(table, key, keylen, &_rowlock); - if (!row) - { - array_init(&value); - } - else - { - php_swoole_table_row2array(table, row, &value); - } - swTableRow_unlock(_rowlock); - - object_init_ex(return_value, swoole_table_row_ce); - zend_update_property(swoole_table_row_ce, return_value, ZEND_STRL("value"), &value); - zend_update_property_stringl(swoole_table_row_ce, 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) -{ - swTable *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); - char *key; - size_t keylen; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &keylen) == FAILURE) - { - RETURN_FALSE; - } - - - swTableRow *_rowlock = NULL; - swTableRow *row = swTableRow_get(table, key, keylen, &_rowlock); - swTableRow_unlock(_rowlock); - if (!row) - { - RETURN_FALSE; - } - else - { - RETURN_TRUE; - } -} - -static PHP_METHOD(swoole_table, offsetExists) -{ - ZEND_MN(swoole_table_exists)(INTERNAL_FUNCTION_PARAM_PASSTHRU); -} - -static PHP_METHOD(swoole_table, del) -{ - swTable *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); - char *key; - size_t keylen; - - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1) - Z_PARAM_STRING(key, keylen) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - SW_CHECK_RETURN(swTableRow_del(table, 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 - - swTable *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; - } - - if (mode == COUNT_NORMAL) - { - RETURN_LONG(table->row_num); - } - else - { - RETURN_LONG(table->row_num * table->column_list->size()); - } -} - -static PHP_METHOD(swoole_table, getMemorySize) -{ - swTable *table = php_swoole_table_get_ptr(ZEND_THIS); - - if (!table) - { - RETURN_LONG(0); - } - else if (!table->memory) - { - RETURN_LONG(swTable_get_memory_size(table)); - } - else - { - RETURN_LONG(table->memory_size); - } -} - -static PHP_METHOD(swoole_table, rewind) -{ - swTable *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); - swTable_iterator_rewind(table); - swTable_iterator_forward(table); -} - -static PHP_METHOD(swoole_table, current) -{ - swTable *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); - swTableRow *row = swTable_iterator_current(table); - if (row) - { - swTableRow_lock(row); - php_swoole_table_row2array(table, row, return_value); - swTableRow_unlock(row); - } - else - { - RETURN_NULL(); - } -} - -static PHP_METHOD(swoole_table, key) -{ - swTable *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); - swTableRow *row = swTable_iterator_current(table); - if (row) - { - swTableRow_lock(row); - RETVAL_STRING(row->key); - swTableRow_unlock(row); - } - else - { - RETURN_NULL(); - } -} - -static PHP_METHOD(swoole_table, next) -{ - swTable *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); - swTable_iterator_forward(table); -} - -static PHP_METHOD(swoole_table, valid) -{ - swTable *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); - swTableRow *row = swTable_iterator_current(table); - RETURN_BOOL(row != NULL); -} - -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(swoole_table_row_ce, ZEND_THIS, ZEND_STRL("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(swoole_table_row_ce, ZEND_THIS, ZEND_STRL("value"), 0); - zval *retval = NULL; - 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) -{ - swTable *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(swoole_table_row_ce, ZEND_THIS, ZEND_STRL("key"), 0); - - swTableRow *_rowlock = NULL; - swTableRow *row = swTableRow_set(table, Z_STRVAL_P(zprop_key), Z_STRLEN_P(zprop_key), &_rowlock); - if (!row) - { - swTableRow_unlock(_rowlock); - php_swoole_error(E_WARNING, "Unable to allocate memory"); - RETURN_FALSE; - } - - swTableColumn *col = swTableColumn_get(table, std::string(key, keylen)); - if (col == NULL) - { - swTableRow_unlock(_rowlock); - php_swoole_fatal_error(E_WARNING, "column[%s] does not exist", key); - RETURN_FALSE; - } - if (col->type == SW_TABLE_STRING) - { - zend_string *str = zval_get_string(value); - swTableRow_set_value(row, col, ZSTR_VAL(str), ZSTR_LEN(str)); - zend_string_release(str); - } - else if (col->type == SW_TABLE_FLOAT) - { - double _value = zval_get_double(value); - swTableRow_set_value(row, col, &_value, 0); - } - else - { - long _value = zval_get_long(value); - swTableRow_set_value(row, col, &_value, 0); - } - swTableRow_unlock(_rowlock); - - zval *zprop_value = sw_zend_read_property(swoole_table_row_ce, ZEND_THIS, ZEND_STRL("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/swoole_timer.cc b/swoole_timer.cc deleted file mode 100644 index 1f6f966af90..00000000000 --- a/swoole_timer.cc +++ /dev/null @@ -1,391 +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: Tianfeng Han | - +----------------------------------------------------------------------+ - */ - -#include "php_swoole_cxx.h" - -#include "server.h" - -#include "ext/spl/spl_array.h" - -using namespace swoole; - -zend_class_entry *swoole_timer_ce; -static zend_object_handlers swoole_timer_handlers; - -static zend_class_entry *swoole_timer_iterator_ce; - -static struct { - bool enable_coroutine_isset; - bool enable_coroutine; -} settings; - -static PHP_FUNCTION(swoole_timer_set); -static PHP_FUNCTION(swoole_timer_after); -static PHP_FUNCTION(swoole_timer_tick); -static PHP_FUNCTION(swoole_timer_exists); -static PHP_FUNCTION(swoole_timer_info); -static PHP_FUNCTION(swoole_timer_stats); -static PHP_FUNCTION(swoole_timer_list); -static PHP_FUNCTION(swoole_timer_clear); -static PHP_FUNCTION(swoole_timer_clear_all); - -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_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) - PHP_FE_END -}; - -void php_swoole_timer_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY(swoole_timer, "Swoole\\Timer", "swoole_timer", NULL, 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", NULL, NULL, 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_REGISTER_LONG_CONSTANT("SWOOLE_TIMER_MIN_MS", SW_TIMER_MIN_MS); - SW_REGISTER_DOUBLE_CONSTANT("SWOOLE_TIMER_MIN_SEC", SW_TIMER_MIN_SEC); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TIMER_MAX_MS", SW_TIMER_MAX_MS); - SW_REGISTER_DOUBLE_CONSTANT("SWOOLE_TIMER_MAX_SEC", SW_TIMER_MAX_SEC); -} - -static void php_swoole_timer_dtor(swTimer_node *tnode) -{ - php_swoole_fci *fci = (php_swoole_fci *) tnode->data; - sw_zend_fci_params_discard(&fci->fci); - sw_zend_fci_cache_discard(&fci->fci_cache); - efree(fci); -} - -bool php_swoole_timer_clear(swTimer_node *tnode) -{ - return swoole_timer_del(tnode); -} - -bool php_swoole_timer_clear_all() -{ - if (UNEXPECTED(!SwooleTG.timer)) - { - return SW_FALSE; - } - - uint32_t num = swHashMap_count(SwooleTG.timer->map), index = 0; - swTimer_node **list = (swTimer_node **) emalloc(num * sizeof(swTimer_node*)); - swHashMap_rewind(SwooleTG.timer->map); - while (1) - { - uint64_t timer_id; - swTimer_node *tnode = (swTimer_node *) swHashMap_each_int(SwooleTG.timer->map, &timer_id); - if (UNEXPECTED(!tnode)) - { - break; - } - if (tnode->type == SW_TIMER_TYPE_PHP) - { - list[index++] = tnode; - } - } - - while (index--) - { - swoole_timer_del(list[index]); - } - - efree(list); - - return SW_TRUE; -} - -static void php_swoole_onTimeout(swTimer *timer, swTimer_node *tnode) -{ - php_swoole_fci *fci = (php_swoole_fci *) tnode->data; - bool enable_coroutine = settings.enable_coroutine_isset ? settings.enable_coroutine : SwooleG.enable_coroutine; - - if (UNEXPECTED(!zend::function::call(&fci->fci_cache, fci->fci.param_count, fci->fci.params, NULL, enable_coroutine))) - { - php_swoole_error(E_WARNING, "%s->onTimeout handler error", ZSTR_VAL(swoole_timer_ce->name)); - } - if (!tnode->interval || tnode->removed) - { - php_swoole_timer_dtor(tnode); - } -} - -static void php_swoole_timer_add(INTERNAL_FUNCTION_PARAMETERS, bool persistent) -{ - zend_long ms; - php_swoole_fci *fci = (php_swoole_fci *) ecalloc(1, sizeof(php_swoole_fci)); - swTimer_node *tnode; - - ZEND_PARSE_PARAMETERS_START(2, -1) - Z_PARAM_LONG(ms) - Z_PARAM_FUNC(fci->fci, fci->fci_cache) - Z_PARAM_VARIADIC('*', fci->fci.params, fci->fci.param_count) - ZEND_PARSE_PARAMETERS_END_EX(goto _failed); - - if (UNEXPECTED(ms < SW_TIMER_MIN_MS)) - { - php_swoole_fatal_error(E_WARNING, "Timer must be greater than or equal to " ZEND_TOSTR(SW_TIMER_MIN_MS)); - _failed: - efree(fci); - RETURN_FALSE; - } - - // no server || user worker || task process with async mode - if (!sw_server() || swIsUserWorker() || (swIsTaskWorker() && sw_server()->task_enable_coroutine)) - { - php_swoole_check_reactor(); - } - - tnode = swoole_timer_add(ms, persistent, php_swoole_onTimeout, fci); - if (UNEXPECTED(!tnode)) - { - php_swoole_fatal_error(E_WARNING, "add timer failed"); - goto _failed; - } - tnode->type = SW_TIMER_TYPE_PHP; - tnode->dtor = php_swoole_timer_dtor; - if (persistent) - { - if (fci->fci.param_count > 0) - { - uint32_t i; - zval *params = (zval *) ecalloc(fci->fci.param_count + 1, sizeof(zval)); - for (i = 0; i < fci->fci.param_count; i++) - { - ZVAL_COPY(¶ms[i + 1], &fci->fci.params[i]); - } - fci->fci.params = params; - } - else - { - fci->fci.params = (zval *) emalloc(sizeof(zval)); - } - fci->fci.param_count += 1; - ZVAL_LONG(fci->fci.params, tnode->id); - } - else - { - sw_zend_fci_params_persist(&fci->fci); - } - sw_zend_fci_cache_persist(&fci->fci_cache); - RETURN_LONG(tnode->id); -} - -static PHP_FUNCTION(swoole_timer_set) -{ - zval *zset = NULL; - HashTable *vht = NULL; - zval *ztmp; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY(zset) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - vht = Z_ARRVAL_P(zset); - - if (php_swoole_array_get_value(vht, "enable_coroutine", ztmp)) - { - settings.enable_coroutine_isset = true; - settings.enable_coroutine = zval_is_true(ztmp); - } -} - -static PHP_FUNCTION(swoole_timer_after) -{ - php_swoole_timer_add(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); -} - -static PHP_FUNCTION(swoole_timer_tick) -{ - php_swoole_timer_add(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); -} - -static PHP_FUNCTION(swoole_timer_exists) -{ - if (UNEXPECTED(!SwooleTG.timer)) - { - RETURN_FALSE; - } - else - { - zend_long id; - swTimer_node *tnode; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_LONG(id) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - tnode = swoole_timer_get(id); - RETURN_BOOL(tnode && !tnode->removed); - } -} - -static PHP_FUNCTION(swoole_timer_info) -{ - if (UNEXPECTED(!SwooleTG.timer)) - { - RETURN_FALSE; - } - else - { - zend_long id; - swTimer_node *tnode; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_LONG(id) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - tnode = swoole_timer_get(id); - if (UNEXPECTED(!tnode)) - { - RETURN_NULL(); - } - array_init(return_value); - add_assoc_long(return_value, "exec_msec", tnode->exec_msec); - add_assoc_long(return_value, "interval", tnode->interval); - add_assoc_long(return_value, "round", tnode->round); - add_assoc_bool(return_value, "removed", tnode->removed); - } -} - -static PHP_FUNCTION(swoole_timer_stats) -{ - array_init(return_value); - if (SwooleTG.timer) - { - add_assoc_bool(return_value, "initialized", 1); - add_assoc_long(return_value, "num", SwooleTG.timer->num); - add_assoc_long(return_value, "round", SwooleTG.timer->round); - } - else - { - add_assoc_bool(return_value, "initialized", 0); - add_assoc_long(return_value, "num", 0); - add_assoc_long(return_value, "round", 0); - } -} - -static PHP_FUNCTION(swoole_timer_list) -{ - zval zlist; - array_init(&zlist); - if (EXPECTED(SwooleTG.timer)) - { - swHashMap_rewind(SwooleTG.timer->map); - while (1) - { - uint64_t timer_id; - swTimer_node *tnode = (swTimer_node *) swHashMap_each_int(SwooleTG.timer->map, &timer_id); - if (UNEXPECTED(!tnode)) - { - break; - } - if (tnode->type == SW_TIMER_TYPE_PHP) - { - add_next_index_long(&zlist, timer_id); - } - } - } - object_init_ex(return_value, swoole_timer_iterator_ce); - sw_zend_call_method_with_1_params( - return_value, - swoole_timer_iterator_ce, - &swoole_timer_iterator_ce->constructor, - "__construct", - NULL, - &zlist - ); - zval_ptr_dtor(&zlist); -} - -static PHP_FUNCTION(swoole_timer_clear) -{ - if (UNEXPECTED(!SwooleTG.timer)) - { - RETURN_FALSE; - } - else - { - zend_long id; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_LONG(id) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - swTimer_node *tnode = swoole_timer_get(id); - if (!tnode || tnode->type != SW_TIMER_TYPE_PHP) - { - RETURN_FALSE; - } - RETURN_BOOL(swoole_timer_del(tnode)); - } -} - -static PHP_FUNCTION(swoole_timer_clear_all) -{ - RETURN_BOOL(php_swoole_timer_clear_all()); -} diff --git a/swoole_websocket_server.cc b/swoole_websocket_server.cc deleted file mode 100644 index 53d7d2167f6..00000000000 --- a/swoole_websocket_server.cc +++ /dev/null @@ -1,1000 +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 "swoole_http_server.h" - -#include - -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/date/php_date.h" -#include "main/php_variables.h" -} - -#include "websocket.h" -#include "base64.h" -#include "thirdparty/swoole_http_parser.h" - -using namespace swoole; -using namespace std; -using swoole::coroutine::Socket; - -zend_class_entry *swoole_websocket_server_ce; -static zend_object_handlers swoole_websocket_server_handlers; - -zend_class_entry *swoole_websocket_frame_ce; -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 PHP_METHOD(swoole_websocket_server, push); -static PHP_METHOD(swoole_websocket_server, isEstablished); -static PHP_METHOD(swoole_websocket_server, pack); -static PHP_METHOD(swoole_websocket_server, unpack); -static PHP_METHOD(swoole_websocket_server, disconnect); - -static PHP_METHOD(swoole_websocket_frame, __toString); - -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_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_FE_END -}; - -#ifdef SW_HAVE_ZLIB -static bool websocket_message_compress(swString *buffer, const char *data, size_t length, int level); -static bool websocket_message_uncompress(swString *buffer, const char *in, size_t in_len); -#endif - -static void php_swoole_websocket_construct_frame(zval *zframe, zend_long opcode, zval *zpayload, uint8_t flags) -{ - 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); - if (payload_length >= SW_WEBSOCKET_CLOSE_CODE_LEN) - { - // WebSocket Close code - zend_update_property_long( - swoole_websocket_closeframe_ce, zframe, ZEND_STRL("code"), - (payload[0] << 8) ^ (payload[1] & 0xFF) - ); - if (payload_length > SW_WEBSOCKET_CLOSE_CODE_LEN) - { - // WebSocket Close reason message - zend_update_property_stringl( - swoole_websocket_closeframe_ce, zframe, ZEND_STRL("reason"), - payload + SW_WEBSOCKET_CLOSE_CODE_LEN, payload_length - SW_WEBSOCKET_CLOSE_CODE_LEN - ); - } - } - } - else - { - object_init_ex(zframe, swoole_websocket_frame_ce); - zend_update_property(swoole_websocket_frame_ce, zframe, ZEND_STRL("data"), zpayload); - } - zend_update_property_long(swoole_websocket_frame_ce, zframe, ZEND_STRL("opcode"), opcode); - zend_update_property_long(swoole_websocket_frame_ce, zframe, ZEND_STRL("flags"), flags); - /* BC */ - zend_update_property_bool(swoole_websocket_frame_ce, zframe, ZEND_STRL("finish"), flags & SW_WEBSOCKET_FLAG_FIN); -} - -void php_swoole_websocket_frame_unpack_ex(swString *data, zval *zframe, uchar uncompress) -{ - swWebSocket_frame frame; - zval zpayload; - uint8_t flags; - - if (data->length < sizeof(frame.header)) - { - SwooleG.error = SW_ERROR_PROTOCOL_ERROR; - ZVAL_FALSE(zframe); - return; - } - - swWebSocket_decode(&frame, data); - flags = swWebSocket_get_flags(&frame); -#ifdef SW_HAVE_ZLIB - if (uncompress && frame.header.RSV1) - { - swString_clear(swoole_zlib_buffer); - if (!websocket_message_uncompress(swoole_zlib_buffer, frame.payload, frame.payload_length)) - { - SwooleG.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); - } -#endif - /* TODO: optimize memory copy */ - ZVAL_STRINGL(&zpayload, frame.payload, frame.payload_length); - php_swoole_websocket_construct_frame(zframe, frame.header.OPCODE, &zpayload, flags); - zval_ptr_dtor(&zpayload); -} - -void php_swoole_websocket_frame_unpack(swString *data, zval *zframe) -{ - return php_swoole_websocket_frame_unpack_ex(data, zframe, 0); -} - -static sw_inline int php_swoole_websocket_frame_pack_ex( - swString *buffer, - zval *zdata, zend_long opcode, zend_long code, uint8_t flags, - zend_bool mask, zend_bool allow_compress -) -{ - char *data = NULL; - size_t length = 0; - - if (sw_unlikely(opcode > SW_WEBSOCKET_OPCODE_MAX)) - { - php_swoole_fatal_error(E_WARNING, "the maximum value of opcode is %d", SW_WEBSOCKET_OPCODE_MAX); - return SW_ERR; - } - - zend::string str_zdata; - if (zdata && !ZVAL_IS_NULL(zdata)) - { - str_zdata = zdata; - data = str_zdata.val(); - length = str_zdata.len(); - } - - if (mask) - { - flags |= SW_WEBSOCKET_FLAG_MASK; - } - -#ifdef SW_HAVE_ZLIB - if (flags & SW_WEBSOCKET_FLAG_COMPRESS) - { - if (!allow_compress) - { - flags ^= SW_WEBSOCKET_FLAG_COMPRESS; - } - else if (length > 0) - { - swString_clear(swoole_zlib_buffer); - 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; - } - } - } -#endif - - switch(opcode) - { - case WEBSOCKET_OPCODE_CLOSE: - return swWebSocket_pack_close_frame(buffer, code, data, length, flags); - default: - swWebSocket_encode(buffer, data, length, opcode, flags); - } - return SW_OK; -} - -int php_swoole_websocket_frame_pack_ex(swString *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); -} - -int php_swoole_websocket_frame_object_pack_ex(swString *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; - zval *ztmp = NULL; - - zdata = NULL; - if ((ztmp = sw_zend_read_property(swoole_websocket_frame_ce, zframe, ZEND_STRL("opcode"), 0))) - { - opcode = zval_get_long(ztmp); - } - if (opcode == WEBSOCKET_OPCODE_CLOSE) - { - if ((ztmp = sw_zend_read_property_not_null(swoole_websocket_frame_ce, zframe, ZEND_STRL("code"), 1))) - { - code = zval_get_long(ztmp); - } - if ((ztmp = sw_zend_read_property_not_null(swoole_websocket_frame_ce, zframe, ZEND_STRL("reason"), 1))) - { - zdata = ztmp; - } - } - if (!zdata && (ztmp = sw_zend_read_property(swoole_websocket_frame_ce, zframe, ZEND_STRL("data"), 0))) - { - zdata = ztmp; - } - if ((ztmp = sw_zend_read_property(swoole_websocket_frame_ce, zframe, ZEND_STRL("flags"), 0))) - { - flags = zval_get_long(ztmp) & SW_WEBSOCKET_FLAGS_ALL; - } - if ((ztmp = sw_zend_read_property_not_null(swoole_websocket_frame_ce, zframe, ZEND_STRL("finish"), 0))) - { - if (zval_is_true(ztmp)) - { - flags |= SW_WEBSOCKET_FLAG_FIN; - } - else - { - flags &= ~SW_WEBSOCKET_FLAG_FIN; - } - } - - return php_swoole_websocket_frame_pack_ex(buffer, zdata, opcode, code, flags & SW_WEBSOCKET_FLAGS_ALL, mask, allow_compress); -} - -void swoole_websocket_onOpen(swServer *serv, http_context *ctx) -{ - swConnection *conn = swWorker_get_connection(serv, ctx->fd); - if (!conn) - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED, "session[%d] 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) - { - zval args[2]; - args[0] = *((zval *) serv->ptr2); - args[1] = *ctx->request.zobject; - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, NULL, SwooleG.enable_coroutine))) - { - php_swoole_error(E_WARNING, "%s->onOpen handler error", ZSTR_VAL(swoole_websocket_server_ce->name)); - serv->close(serv, ctx->fd, 0); - } - } -} - -/** - * default onRequest callback - */ -void swoole_websocket_onRequest(http_context *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" - "Cache-Control: must-revalidate,no-cache,no-store\r\n" - "Content-Length: 83\r\n" - "Server: " SW_HTTP_SERVER_SOFTWARE "\r\n\r\n" - "

HTTP 400 Bad Request


Powered by Swoole"; - - ctx->send(ctx, (char *) bad_request, strlen(bad_request)); - ctx->end = 1; - ctx->close(ctx); -} - -void swoole_sha1(const char *str, int _len, unsigned char *digest) -{ - PHP_SHA1_CTX context; - PHP_SHA1Init(&context); - PHP_SHA1Update(&context, (unsigned char *) str, _len); - PHP_SHA1Final(digest, &context); -} - -bool swoole_websocket_handshake(http_context *ctx) -{ - char sec_buf[128]; - zval *header = ctx->request.zheader; - HashTable *ht = Z_ARRVAL_P(header); - zval *pData; - zval retval; - - 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); - return false; - } - - zend::string str_pData(pData); - - if (str_pData.len() != BASE64_ENCODE_OUT_SIZE(SW_WEBSOCKET_SEC_KEY_LEN)) - { - goto _bad_request; - } - - char sha1_str[20]; - // sec_websocket_accept - memcpy(sec_buf, str_pData.val(), str_pData.len()); - memcpy(sec_buf + str_pData.len(), SW_WEBSOCKET_GUID, sizeof(SW_WEBSOCKET_GUID) - 1); - // sha1 sec_websocket_accept - swoole_sha1(sec_buf, str_pData.len() + sizeof(SW_WEBSOCKET_GUID) - 1, (unsigned char *) sha1_str); - // base64 encode - int sec_len = swBase64_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); - -#ifdef SW_HAVE_ZLIB - bool enable_websocket_compression = true; - bool websocket_compression = false; -#endif - swServer *serv = nullptr; - swConnection *conn = nullptr; - - if (!ctx->co_socket) - { - serv = (swServer *) ctx->private_data; - conn = swWorker_get_connection(serv, ctx->fd); - if (!conn) - { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SESSION_CLOSED, "session[%d] 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 - ) - { - 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; - swListenPort *port = (swListenPort *) serv->connection_list[conn->server_fd].object; - if (port && port->websocket_subprotocol) - { - swoole_http_response_set_header( - ctx, - ZEND_STRL("Sec-WebSocket-Protocol"), - port->websocket_subprotocol, port->websocket_subprotocol_length, - false - ); - } -#ifdef SW_HAVE_ZLIB - ctx->websocket_compression = conn->websocket_compression = websocket_compression; -#endif - } - 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 - } - - ctx->response.status = SW_HTTP_SWITCHING_PROTOCOLS; - ctx->upgrade = 1; - - swoole_http_response_end(ctx, nullptr, &retval); - return Z_TYPE(retval) == IS_TRUE; -} - -#ifdef SW_HAVE_ZLIB -static bool websocket_message_uncompress(swString *buffer, const char *in, size_t in_len) -{ - z_stream zstream; - int status; - bool ret = false; - - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = php_zlib_alloc; - zstream.zfree = php_zlib_free; - // gzip_stream.total_out = 0; - status = inflateInit2(&zstream, SW_ZLIB_ENCODING_RAW); - if (status != Z_OK) - { - swWarn("inflateInit2() failed by %s", zError(status)); - return false; - } - - zstream.next_in = (Bytef *) in; - zstream.avail_in = in_len; - zstream.total_in = 0; - - while (1) - { - zstream.avail_out = buffer->size - buffer->length; - zstream.next_out = (Bytef *) (buffer->str + buffer->length); - status = inflate(&zstream, Z_SYNC_FLUSH); - if (status >= 0) - { - buffer->length = zstream.total_out; - } - if (status == Z_STREAM_END || (status == Z_OK && zstream.avail_in == 0)) - { - ret = true; - break; - } - if (status != Z_OK) - { - break; - } - if (buffer->length + (SW_BUFFER_SIZE_STD / 2) >= buffer->size) - { - if (swString_extend(buffer, buffer->size * 2) < 0) - { - status = Z_MEM_ERROR; - break; - } - } - } - inflateEnd(&zstream); - - if (!ret) - { - swWarn("inflate() failed, Error: %s[%d]", zError(status), status); - return false; - } - return true; -} - -static bool websocket_message_compress(swString *buffer, const char *data, size_t length, int level) -{ - // ==== ZLIB ==== - if (level == Z_NO_COMPRESSION) - { - level = Z_DEFAULT_COMPRESSION; - } - else if (level > Z_BEST_COMPRESSION) - { - level = Z_BEST_COMPRESSION; - } - - z_stream zstream = {}; - int status; - - zstream.zalloc = php_zlib_alloc; - zstream.zfree = php_zlib_free; - - 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); - return false; - } - - zstream.next_in = (Bytef *) data; - zstream.avail_in = length; - zstream.next_out = (Bytef *) buffer->str; - - size_t max_length = deflateBound(&zstream, length); - if (max_length > buffer->size) - { - if (swString_extend(buffer, max_length) < 0) - { - return false; - } - } - - size_t bytes_written = 0; - uchar in_sync_flush; - int result; - - do - { - size_t write_remaining; - - if (zstream.avail_out == 0) - { - size_t write_position; - - zstream.avail_out = max_length; - write_position = buffer->length; - buffer->length = max_length; - zstream.next_out = (Bytef *) buffer->str + write_position; - - /* Use a fixed value for buffer increments */ - max_length = 4096; - } - - write_remaining = buffer->length - bytes_written; - in_sync_flush = zstream.avail_in == 0; - result = deflate(&zstream, in_sync_flush ? Z_SYNC_FLUSH : Z_NO_FLUSH); - bytes_written += write_remaining - zstream.avail_out; - } while (result == Z_OK); - - deflateEnd(&zstream); - - if (result != Z_BUF_ERROR || bytes_written < 4) - { - swWarn("Failed to compress outgoing frame"); - return false; - } - - if (status != Z_OK) - { - swWarn("deflate() failed, Error: [%d]", status); - return false; - } - - buffer->length = bytes_written - 4; - - return true; -} -#endif - -int swoole_websocket_onMessage(swServer *serv, swEventData *req) -{ - int fd = req->info.fd; - uchar flags = 0; - zend_long opcode = 0; - auto primary_port = serv->listen_list->front(); - - zval zdata; - char frame_header[2]; - memcpy(frame_header, &req->info.ext_flags, sizeof(frame_header)); - - php_swoole_get_recv_data(serv, &zdata, req); - - // frame info has already decoded in swWebSocket_dispatch_frame - flags = frame_header[0]; - opcode = frame_header[1]; - - if (opcode == WEBSOCKET_OPCODE_CLOSE && !primary_port->open_websocket_close_frame) - { - zval_ptr_dtor(&zdata); - return SW_OK; - } - -#ifdef SW_HAVE_ZLIB - /** - * RFC 7692 - */ - if (serv->websocket_compression && (flags & SW_WEBSOCKET_FLAG_RSV1)) - { - swString_clear(swoole_zlib_buffer); - if (!websocket_message_uncompress(swoole_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); - } -#endif - - zend_fcall_info_cache *fci_cache = php_swoole_server_get_fci_cache(serv, req->info.server_fd, SW_SERVER_CB_onMessage); - zval args[2]; - - args[0] = *(zval *) serv->ptr2; - php_swoole_websocket_construct_frame(&args[1], opcode, &zdata, flags); - zend_update_property_long(swoole_websocket_frame_ce, &args[1], ZEND_STRL("fd"), fd); - - if (UNEXPECTED(!zend::function::call(fci_cache, 2, args, NULL, SwooleG.enable_coroutine))) - { - php_swoole_error(E_WARNING, "%s->onMessage handler error", ZSTR_VAL(swoole_websocket_server_ce->name)); - serv->close(serv, fd, 0); - } - - zval_ptr_dtor(&zdata); - zval_ptr_dtor(&args[1]); - - return SW_OK; -} - -int swoole_websocket_onHandshake(swServer *serv, swListenPort *port, http_context *ctx) -{ - int fd = ctx->fd; - bool success = swoole_websocket_handshake(ctx); - if (success) - { - swoole_websocket_onOpen(serv, ctx); - } - else - { - serv->close(serv, fd, 1); - } - return SW_OK; -} - -void php_swoole_websocket_server_minit(int module_number) -{ - SW_INIT_CLASS_ENTRY_EX(swoole_websocket_server, "Swoole\\WebSocket\\Server", "swoole_websocket_server", NULL, swoole_websocket_server_methods, swoole_http_server); - SW_SET_CLASS_SERIALIZABLE(swoole_websocket_server, zend_class_serialize_deny, zend_class_unserialize_deny); - 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); - zend_declare_property_null(swoole_websocket_server_ce, ZEND_STRL("onHandshake"), ZEND_ACC_PRIVATE); - - SW_INIT_CLASS_ENTRY(swoole_websocket_frame, "Swoole\\WebSocket\\Frame", "swoole_websocket_frame", NULL, swoole_websocket_frame_methods); - 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_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", NULL, NULL, swoole_websocket_frame); - zend_declare_property_long(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); - 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); - // 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); - // 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); - // 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); - /* 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); - // 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); - // 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); -} - -static sw_inline int swoole_websocket_server_push(swServer *serv, int fd, swString *buffer) -{ - if (sw_unlikely(fd <= 0)) - { - php_swoole_fatal_error(E_WARNING, "fd[%d] is invalid", fd); - return SW_ERR; - } - - swConnection *conn = swWorker_get_connection(serv, fd); - if (!conn || conn->websocket_status < WEBSOCKET_STATUS_HANDSHAKE) - { - SwooleG.error = SW_ERROR_WEBSOCKET_UNCONNECTED; - php_swoole_fatal_error(E_WARNING, "the connected client of connection[%d] is not a websocket client or closed", (int ) fd); - return SW_ERR; - } - - int ret = serv->send(serv, fd, buffer->str, buffer->length); - if (ret < 0 && SwooleG.error == SW_ERROR_OUTPUT_SEND_YIELD) - { - zval _return_value; - zval *return_value = &_return_value; - zval _yield_data; - ZVAL_STRINGL(&_yield_data, buffer->str, buffer->length); - ZVAL_FALSE(return_value); - php_swoole_server_send_yield(serv, fd, &_yield_data, return_value); - ret = Z_BVAL_P(return_value) ? SW_OK : SW_ERR; - } - return ret; -} - -static sw_inline int swoole_websocket_server_close(swServer *serv, int fd, swString *buffer, uint8_t real_close) -{ - int ret = swoole_websocket_server_push(serv, fd, buffer); - if (ret < 0 || !real_close) - { - return ret; - } - swConnection *conn = swWorker_get_connection(serv, fd); - if (conn) - { - // Change status immediately to avoid double close - conn->websocket_status = WEBSOCKET_STATUS_CLOSING; - // Server close connection immediately - return serv->close(serv, fd, SW_FALSE); - } - else - { - return SW_ERR; - } -} - -static PHP_METHOD(swoole_websocket_server, disconnect) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - zend_long fd = 0; - zend_long code = WEBSOCKET_CLOSE_NORMAL; - char *data = NULL; - size_t length = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|ls", &fd, &code, &data, &length) == FAILURE) - { - RETURN_FALSE; - } - swString_clear(swoole_http_buffer); - if (swWebSocket_pack_close_frame(swoole_http_buffer, code, data, length, 0) < 0) - { - RETURN_FALSE; - } - SW_CHECK_RETURN(swoole_websocket_server_close(serv, fd, swoole_http_buffer, 1)); -} - -static PHP_METHOD(swoole_websocket_server, push) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - zend_long fd = 0; - zval *zdata = NULL; - zend_long opcode = WEBSOCKET_OPCODE_TEXT; - zval *zflags = NULL; - zend_long flags = SW_WEBSOCKET_FLAG_FIN; -#ifdef SW_HAVE_ZLIB - zend_bool allow_compress = 0; -#endif - - ZEND_PARSE_PARAMETERS_START(2, 4) - Z_PARAM_LONG(fd) - Z_PARAM_ZVAL(zdata) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(opcode) - Z_PARAM_ZVAL_EX(zflags, 1, 0) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (zflags != NULL) - { - flags = zval_get_long(zflags); - } - -#ifdef SW_HAVE_ZLIB - swConnection *conn = swServer_connection_verify(serv, fd); - if (!conn) - { - RETURN_FALSE; - } - allow_compress = conn->websocket_compression; -#endif - - swString_clear(swoole_http_buffer); - if (php_swoole_websocket_frame_is_object(zdata)) - { - if (php_swoole_websocket_frame_object_pack(swoole_http_buffer, zdata, 0, allow_compress) < 0) - { - RETURN_FALSE; - } - } - else - { - if (php_swoole_websocket_frame_pack(swoole_http_buffer, zdata, opcode, flags & SW_WEBSOCKET_FLAGS_ALL, 0, allow_compress) < 0) - { - RETURN_FALSE; - } - } - - switch (opcode) - { - case WEBSOCKET_OPCODE_CLOSE: - SW_CHECK_RETURN(swoole_websocket_server_close(serv, fd, swoole_http_buffer, flags & SW_WEBSOCKET_FLAG_FIN)); - break; - default: - SW_CHECK_RETURN(swoole_websocket_server_push(serv, fd, swoole_http_buffer)); - } -} - -static PHP_METHOD(swoole_websocket_server, pack) -{ - swString *buffer = SwooleTG.buffer_stack; - zval *zdata; - zend_long opcode = WEBSOCKET_OPCODE_TEXT; - zval *zflags = NULL; - zend_long flags = SW_WEBSOCKET_FLAG_FIN; - - ZEND_PARSE_PARAMETERS_START(1, 3) - Z_PARAM_ZVAL(zdata) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(opcode) - Z_PARAM_ZVAL_EX(zflags, 1, 0) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - if (zflags != NULL) - { - flags = zval_get_long(zflags); - } - - swString_clear(buffer); - if (php_swoole_websocket_frame_is_object(zdata)) - { - if (php_swoole_websocket_frame_object_pack(buffer, zdata, 0, 1) < 0) - { - RETURN_EMPTY_STRING(); - } - } - else - { - if (php_swoole_websocket_frame_pack(buffer, zdata, opcode, flags & SW_WEBSOCKET_FLAGS_ALL, 0, 1) < 0) - { - RETURN_EMPTY_STRING(); - } - } - RETURN_STRINGL(buffer->str, buffer->length); -} - -static PHP_METHOD(swoole_websocket_frame, __toString) -{ - swString *buffer = SwooleTG.buffer_stack; - - swString_clear(buffer); - if (php_swoole_websocket_frame_object_pack(buffer, ZEND_THIS, 0, 1) < 0) - { - RETURN_EMPTY_STRING(); - } - RETURN_STRINGL(buffer->str, buffer->length); -} - -static PHP_METHOD(swoole_websocket_server, unpack) -{ - swString buffer = {}; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &buffer.str, &buffer.length) == FAILURE) - { - RETURN_FALSE; - } - - php_swoole_websocket_frame_unpack(&buffer, return_value); -} - -static PHP_METHOD(swoole_websocket_server, isEstablished) -{ - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (sw_unlikely(!serv->gs->start)) - { - php_swoole_fatal_error(E_WARNING, "server is not running"); - RETURN_FALSE; - } - - zend_long fd; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &fd) == FAILURE) - { - RETURN_FALSE; - } - - swConnection *conn = swWorker_get_connection(serv, fd); - //not isEstablished - if (!conn || conn->active == 0 || conn->closed || conn->websocket_status < WEBSOCKET_STATUS_ACTIVE) - { - RETURN_FALSE; - } - else - { - RETURN_TRUE; - } -} 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 db8e5b072a3..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 | @@ -45,8 +45,11 @@ Run `./clean` to remove all of the tests log files. ## Contribute the test script Run `./new [test-script-filename]` -E.g. : `./new ./swoole-coroutine/co_sleep.phpt` +E.g. : `./new ./swoole_coroutine/co_sleep.phpt` It will generate the test script file and auto open on your ide (MacOS only). -![](https://ws1.sinaimg.cn/large/006DQdzWly1frvn56azn9g30rs0m8b29.gif) +![](https://cdn.jsdelivr.net/gh/sy-records/staticfile/images/swoole/generate-example.gif) + +## Code Style +`PSR1/PSR2` 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/htf_swoole20_https_server.php b/tests/include/api/swoole_http_server/htf_swoole20_https_server.php deleted file mode 100644 index 2d7a6a259d2..00000000000 --- a/tests/include/api/swoole_http_server/htf_swoole20_https_server.php +++ /dev/null @@ -1,11 +0,0 @@ -set([ - 'ssl_cert_file' => __DIR__ . '/localhost-ssl/swoole_server.crt', - 'ssl_key_file' => __DIR__ . '/localhost-ssl/swoole_server.key', -]); -$http->on('request', function ($request, $response) { - $response->header("Content-Type", "text/html; charset=utf-8"); - $response->end("

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

"); -}); -$http->start(); \ No newline at end of file diff --git a/tests/include/api/swoole_http_server/http_server.php b/tests/include/api/swoole_http_server/http_server.php index 11bb60aefc0..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 = [ @@ -44,10 +44,9 @@ public function __construct($host = HTTP_SERVER_HOST, $port = HTTP_SERVER_PORT, */ ]; - if ($ssl) - { - $config['ssl_cert_file'] = __DIR__ . '/localhost-ssl/server.crt'; - $config['ssl_key_file'] = __DIR__ . '/localhost-ssl/server.key'; + if ($ssl) { + $config['ssl_cert_file'] = SSL_FILE_DIR . '/server.crt'; + $config['ssl_key_file'] = SSL_FILE_DIR . '/server.key'; } $this->httpServ->set($config); } @@ -112,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") { @@ -221,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; @@ -266,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 f9000f48c2b..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 @@ -23,7 +23,7 @@ abstract class TestServer protected $count = 0; protected $show_lost_package = false; - const PKG_NUM = 10000; + public static $PKG_NUM; const LEN_MIN = 0; const LEN_MAX = 200; @@ -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']); @@ -55,7 +55,7 @@ function _receive($serv, $fd, $reactor_id, $data) $this->count++; $this->recv_bytes += strlen($data); $this->onReceive($serv, $fd, $reactor_id, $data); - if ($this->count == self::PKG_NUM) + if ($this->count == self::$PKG_NUM) { $serv->send($fd, "end\n"); } @@ -66,7 +66,7 @@ function onClose($serv, $fd, $reactor_id) echo "Total count={$this->count}, bytes={$this->recv_bytes}\n"; if ($this->show_lost_package) { - for ($i = 0; $i < self::PKG_NUM; $i++) + for ($i = 0; $i < self::$PKG_NUM; $i++) { if (!isset($this->index[$i])) { 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 fcc053a53f8..19513d4eba0 100644 --- a/tests/include/api/swoole_server/opcode_server.php +++ b/tests/include/api/swoole_server/opcode_server.php @@ -14,23 +14,22 @@ 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, 'task_worker_num' => 2, - 'open_length_check' => 1, 'package_length_type' => 'N', 'package_length_offset' => 0, 'package_body_offset' => 0, - "heartbeat_idle_time"=> 20 + 'heartbeat_idle_time' => 20, ]); if ($port1) { @@ -64,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); @@ -127,15 +98,32 @@ public function onReceive(\swoole_server $swooleServer, $fd, $fromReactorId, $re "fd" => $fd, "msg" => $msg, ]), $toWorkerId); - assert($r); + Assert::true($r); return; case "sendfile": $len = filesize(__FILE__); $r = $swooleServer->send($fd, pack("N", $len + 4)); + Assert::true($r); assert($r !== false); - $r =$swooleServer->sendfile($fd, __FILE__); + $r = $swooleServer->sendfile($fd, __FILE__); + 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: @@ -145,7 +133,7 @@ public function onReceive(\swoole_server $swooleServer, $fd, $fromReactorId, $re $r = true; } $r = $swooleServer->send($fd, opcode_encode("return", $r)); - assert($r !== false); + Assert::true($r); return; } else { @@ -153,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); @@ -168,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); @@ -176,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, 'trace_flags' => 0, + 'enable_deadlock_check' => false, + ]); +} + +$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 ]); } diff --git a/tests/include/config.php b/tests/include/config.php index a8954b769a0..ffd8dd685d5 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -11,21 +11,25 @@ /** ============== 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_IMAGE2', __DIR__ . '/../../examples/logo.svg'); +define('TEST_LINK_IMAGE', __DIR__ . '/../../examples/test_link.jpg'); +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__ . '/api/swoole_http_server/localhost-ssl'); +define('SSL_FILE_DIR', __DIR__ . '/ssl_certs/'); define('SSL_FILE_DIR2', __DIR__ . '/../include/api/ssl-ca'); define('DOCUMENT_ROOT', __DIR__ . '/../../examples/www'); @@ -46,34 +50,79 @@ /** ============== 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_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])) { + define('HTTPBIN_SERVER_PORT_IN_DOCKER', (int)$matches[1]); + } +} + +/** ============== 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); -} elseif (!empty($info = `docker ps 2>&1 | grep httpbin 2>&1`) && - preg_match('/\s+?[^:]+:(\d+)->\d+\/tcp\s+/', $info, $matches) && - is_numeric($matches[1]) -) { +} elseif (defined('HTTPBIN_SERVER_PORT_IN_DOCKER')) { define('HTTPBIN_SERVER_HOST', '127.0.0.1'); - define('HTTPBIN_SERVER_PORT', (int)$matches[1]); + define('HTTPBIN_SERVER_PORT', HTTPBIN_SERVER_PORT_IN_DOCKER); define('HTTPBIN_LOCALLY', true); } elseif (getenv('HTTPBIN_SERVER_HOST')) { define('HTTPBIN_SERVER_HOST', getenv('HTTPBIN_SERVER_HOST')); @@ -84,20 +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', '127.0.0.1'); -define('HTTP_PROXY_PORT', IS_MAC_OS ? 1087 : 8888); -define('SOCKS5_PROXY_HOST', '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 ? PRESSURE_LOW : ((IS_IN_TRAVIS || swoole_cpu_num() === 1) ? PRESSURE_MID : PRESSURE_NORMAL)); +define('PRESSURE_LEVEL', + 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); @@ -112,3 +185,10 @@ define('MAX_REQUESTS_LOW', [4, 8, 10, 25][PRESSURE_LEVEL]); 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 9ccf6671ec6..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; @@ -68,9 +72,44 @@ function get_one_free_port(): int { $hookFlags = Swoole\Runtime::getHookFlags(); Swoole\Runtime::enableCoroutine(false); - $server = stream_socket_server('tcp://127.0.0.1:0'); - $name = stream_socket_get_name($server, false); - $port = (parse_url($name)['port'] ?? -1) ?: -1; + $server = @stream_socket_server('tcp://127.0.0.1:0'); + if (!$server) { + $port = -1; + } else { + $name = stream_socket_get_name($server, false); + if (empty($name)) { + $port = -1; + } + else { + $port = (parse_url($name)['port'] ?? -1) ?: -1; + } + } + Swoole\Runtime::enableCoroutine($hookFlags); + 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(); + Swoole\Runtime::enableCoroutine(false); + $server = @stream_socket_server('tcp://[::1]:0'); + if (!$server) { + $port = -1; + } else { + $name = stream_socket_get_name($server, false); + if (empty($name)) { + $port = -1; + } + else { + $port = explode(']:', $name)[1]; + } + } + Swoole\Runtime::enableCoroutine($hookFlags); return $port; } @@ -114,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; + } } } } @@ -130,6 +177,29 @@ 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(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:')); + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + $res = curl_exec($ch); + curl_close($ch); + return $res; +} + function httpRequest(string $uri, array $options = []) { $url_info = parse_url($uri); @@ -315,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); @@ -392,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(); } @@ -449,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 { @@ -567,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; }; } @@ -581,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) { @@ -598,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]; } /** @@ -610,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]; @@ -717,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/MQTT/Helper.php b/tests/include/lib/src/MQTT/Helper.php new file mode 100644 index 00000000000..d2858cae431 --- /dev/null +++ b/tests/include/lib/src/MQTT/Helper.php @@ -0,0 +1,48 @@ +> 4; + $header['dup'] = ($byte & 0x08) >> 3; + $header['qos'] = ($byte & 0x06) >> 1; + $header['retain'] = $byte & 0x01; + + return $header; + } + + public static function encodePublish($data) + { + $cmd = 3 << 4; + $body = pack('n', strlen($data['topic'])) . $data['topic'] . $data['content']; + + $length = strlen($body); + $head = chr($cmd) . self::writeBodyLength($length); + return $head . $body; + } + + protected static function writeBodyLength($length) + { + $string = ''; + do { + $digit = $length % 128; + $length = $length >> 7; + if ($length > 0) { + $digit = ($digit | 0x80); + } + $string .= chr($digit); + } while ($length > 0); + return $string; + } + + public static function encodePing(int $cmd) + { + $cmd = $cmd << 4; + return chr($cmd) . self::writeBodyLength(0); + } +} 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 335bdf53ca5..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,9 +31,12 @@ class ProcessManager */ protected $atomic; protected $alone = false; + protected $onlyChild = false; + protected $onlyParent = false; protected $freePorts = []; protected $randomFunc = 'get_safe_random'; protected $randomData = [[]]; + protected $randomDataArray = []; /** * wait wakeup 1s default @@ -46,12 +49,15 @@ 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 */ protected $childProcess; + protected $logFileHandle; public function __construct() { @@ -115,6 +121,16 @@ public function runParentFunc($pid = 0) } } + public function setLogFile($file) + { + $this->logFileHandle = fopen($file, "a+"); + } + + public function writeLog($msg) + { + fwrite($this->logFileHandle, $msg . PHP_EOL); + } + /** * @param int $index * @return mixed @@ -134,6 +150,36 @@ public function initRandomData(int $size, int $len = null) $this->initRandomDataEx(1, $size, $len); } + /** + * 生成一个随机字节组成的数组 + * @param int $n + * @param int $len 默认为0,表示随机产生长度 + * @param bool $base64 + * @throws \Exception + */ + public function initRandomDataArray($n = 1, $len = 0, bool $base64 = false) + { + while ($n--) { + if ($len == 0) { + $len = rand(1024, 1 * 1024 * 1024); + } + $bytes = random_bytes($len); + $this->randomDataArray[] = $base64 ? base64_encode($bytes) : $bytes; + } + } + + /** + * @param $index + * @return mixed + */ + public function getRandomDataElement(int $index = 0) + { + if (!isset($this->randomDataArray[$index])) { + throw new RuntimeException("out of array"); + } + return $this->randomDataArray[$index]; + } + public function getRandomData() { return $this->getRandomDataEx(0); @@ -195,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'); @@ -204,12 +251,22 @@ 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 { - if (empty($this->freePorts)) { - for ($i = $num; $i--;) { - $this->freePorts[] = $this->useConstantPorts ? (9500 + $num - $i) : get_one_free_port(); - } + for ($i = $num; $i--;) { + $this->freePorts[] = $this->useConstantPorts ? (9500 + $num - $i + count($this->freePorts) + $increment) : get_one_free_port(); + } + } + + 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(); } } @@ -221,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(); @@ -248,7 +314,11 @@ 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']}"); + } + return true; } @@ -272,8 +342,35 @@ 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}"); } } -} \ No newline at end of file + + function getChildExitStatus() { + return $this->childExitStatus; + } + + public function setExpectExitSignal($signal = 0) + { + if (!is_array($signal)) { + $signal = [$signal]; + } + $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 new file mode 100644 index 00000000000..65beb48d1a8 --- /dev/null +++ b/tests/include/lib/src/WaitRef.php @@ -0,0 +1,44 @@ + | + +----------------------------------------------------------------------+ + */ + +namespace SwooleTest; + +use Swoole; + +class WaitRef +{ + private $cid; + + public function __destruct() + { + Swoole\Coroutine::resume($this->cid); + } + + static function create() + { + return new static(); + } + + static function wait(WaitRef &$wr) + { + $wr->cid = Swoole\Coroutine::getCid(); + $wr = null; + Swoole\Coroutine::yield(); + } +} 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)); @@ -104,7 +118,7 @@ function skip_if_file_not_exist(string $filename) function skip_if_command_not_found(string $command) { - skip("command {$command} not found", empty(`{$command} --help | grep -i usage`)); + skip("command {$command} not found", empty(`{$command} --help 2>&1 | grep -i usage`)); } function skip_if_no_ssl() @@ -132,7 +146,7 @@ function skip_if_no_http2() function skip_if_no_ipv6() { - skip('no ipv6 address', !stream_socket_server('tcp://[::1]:0')); + skip('no ipv6 address', !@stream_socket_server('tcp://[::1]:0')); } function skip_if_no_top() @@ -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') @@ -181,7 +205,7 @@ function skip_if_offline() function skip_if_no_proxy(string $host, string $port) { - skip('no available proxy', IS_IN_TRAVIS || check_tcp_port($host, $port) <= 0); + skip('no available proxy', !check_tcp_port($host, $port)); } function skip_if_no_http_proxy() @@ -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/api/swoole_http_server/localhost-ssl/ca.crt b/tests/include/ssl_certs/ca.crt similarity index 100% rename from tests/include/api/swoole_http_server/localhost-ssl/ca.crt rename to tests/include/ssl_certs/ca.crt diff --git a/tests/include/api/swoole_http_server/localhost-ssl/ca.csr b/tests/include/ssl_certs/ca.csr similarity index 100% rename from tests/include/api/swoole_http_server/localhost-ssl/ca.csr rename to tests/include/ssl_certs/ca.csr diff --git a/tests/include/api/swoole_http_server/localhost-ssl/ca.key b/tests/include/ssl_certs/ca.key similarity index 100% rename from tests/include/api/swoole_http_server/localhost-ssl/ca.key rename to tests/include/ssl_certs/ca.key diff --git a/tests/include/api/swoole_http_server/localhost-ssl/ca.srl b/tests/include/ssl_certs/ca.srl similarity index 100% rename from tests/include/api/swoole_http_server/localhost-ssl/ca.srl rename to tests/include/ssl_certs/ca.srl diff --git a/tests/include/api/swoole_http_server/localhost-ssl/client.crt b/tests/include/ssl_certs/client.crt similarity index 100% rename from tests/include/api/swoole_http_server/localhost-ssl/client.crt rename to tests/include/ssl_certs/client.crt diff --git a/tests/include/api/swoole_http_server/localhost-ssl/client.csr b/tests/include/ssl_certs/client.csr similarity index 100% rename from tests/include/api/swoole_http_server/localhost-ssl/client.csr rename to tests/include/ssl_certs/client.csr diff --git a/tests/include/api/swoole_http_server/localhost-ssl/client.key b/tests/include/ssl_certs/client.key similarity index 100% rename from tests/include/api/swoole_http_server/localhost-ssl/client.key rename to tests/include/ssl_certs/client.key diff --git a/tests/include/api/swoole_http_server/localhost-ssl/client.pem b/tests/include/ssl_certs/client.pem similarity index 100% rename from tests/include/api/swoole_http_server/localhost-ssl/client.pem rename to tests/include/ssl_certs/client.pem 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/include/api/swoole_http_server/localhost-ssl/server.crt b/tests/include/ssl_certs/server.crt similarity index 100% rename from tests/include/api/swoole_http_server/localhost-ssl/server.crt rename to tests/include/ssl_certs/server.crt diff --git a/tests/include/api/swoole_http_server/localhost-ssl/server.csr b/tests/include/ssl_certs/server.csr similarity index 100% rename from tests/include/api/swoole_http_server/localhost-ssl/server.csr rename to tests/include/ssl_certs/server.csr diff --git a/tests/include/api/swoole_http_server/localhost-ssl/server.key b/tests/include/ssl_certs/server.key similarity index 100% rename from tests/include/api/swoole_http_server/localhost-ssl/server.key rename to tests/include/ssl_certs/server.key diff --git a/tests/include/api/swoole_http_server/localhost-ssl/server.pem b/tests/include/ssl_certs/server.pem similarity index 100% rename from tests/include/api/swoole_http_server/localhost-ssl/server.pem rename to tests/include/ssl_certs/server.pem diff --git a/tests/include/ssl_certs/sni_server_ca.pem b/tests/include/ssl_certs/sni_server_ca.pem new file mode 100644 index 00000000000..b2522884565 --- /dev/null +++ b/tests/include/ssl_certs/sni_server_ca.pem @@ -0,0 +1,63 @@ +-----BEGIN CERTIFICATE----- +MIIFejCCA2KgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwQzELMAkGA1UEBhMCR0Ix +EDAOBgNVBAgMB0VuZ2xhbmQxEDAOBgNVBAoMB1BIUC5uZXQxEDAOBgNVBAMMB3Bo +cC5uZXQwHhcNMTcxMTIyMTQyODIzWhcNMjcxMTIwMTQyODIzWjBVMQswCQYDVQQG +EwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UECgwHUEhQLm5ldDEQMA4GA1UE +CwwHb3BlbnNzbDEQMA4GA1UEAwwHcGhwLm5ldDCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAMeENPus84HfIaypUVybbn5ZZyu1gXSYZAnDYYHhOlHu5kFh +26ohnHnoheapXVRbUtDZgUW5BztA+vDhKkM5G3U4p+YsqcSA/IkF2JQBkJH1jrxs +XMDtXIA4/MzYAVtyySqoMsrbkSCDzvI8imsbZI9O1x02VgaqoaQ9JZvglt1nhTIY +YpTbTKGCzMmrnFGHITeBsVose1ACFIAtqKqvUaA6TxcCtVcrENY3T3N/vEmoHHBg +O7j9+UvYN/WRPNcv0aIOlro83O9xQuJdRCygPmcv/jkqWRKm2Pu0lEkvbMCvOJr/ +Fz/kQg/6EVmIMsR/dgOMIjWjmpeTx24UJaNHUdNCeUbxIyTIyl9ISLWLM0Ipoq3L +95ygH6FSESAOpc8rVE2A5YZCtKlwvogMx3C/tMH1gWwMlfxn0CopP4Jx6lngw3yn +IpP0hnuCauQikBkyhsGu4J0Lbx5BsyDWQLwjiq4s8EcEy0vTdAvhDMRUGvpnjWaa +zBDVVrzHaRFvp6ueh35xf+dYieGL5UTICyrrQX9wwSafHV90a87y6XU+RwA25Dmc +V/kTn8/xkFv/4CbZp+0yArKpwXjqBMsQFbkFjT+Ky9MN+nedoFUMl7slgjpBs6RI +T08Hoa7/kMZGtLCiGcRXlwiIhB00gDz2avUvFTxwik9ssjDqBJGzNbBvZGb/AgMB +AAGjZjBkMB0GA1UdDgQWBBTjyuOBGnHnb+x20diIHG3sPuJFGjAfBgNVHSMEGDAW +gBQ0s99BNsV4Dszph6PArogA8HP4TDASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1Ud +DwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAFrQMmCoSe8uJbJLqPGMPW4ZE +0U81WWboc9FrSfezBYkTKuCjMTjQFv8oLp4pnuWZw22mHrOiz9izjNnXcR/rdVbG +Cip/JSHlTzHd3gnxSSd2diXvVrSb4zuLyN6WIficlsj3QdIP4Cdfv1pdHBTsRPbW +vuHpwYqqjxPBjqvoVZyZRyRM1pqpxLCvxOt4S3z4uZXyIH2UI7yWxI6MvdHBZg5m +TwiVi1gEQuRnYRy4sK9qJlBo5jERvzDr46W1e9Yz5sNMPfwWb64Hkkys3GqJt5am +OKxvgF3lvIK90vBjenGDfYEENP+0qdmdpHQkxGM2cl9iPqW8iyRGBVjdbzTIeRgB +u+CEbffSKWCpbm1Zcszv3FHMlsU4Px2pUTdLwEMgBQUPW/ngBMdRgVZE01GI7D1k +AM3hO347eehQTWoyuHgsnB2uaANEeR/XFBGDFmbs5ggAzxxRsH1FxoHXrv3iDIPW +9lKnSow+OZGcPKgaY5Nsregt4jO+pQqhzxlMa2QMfrMoGkEvrKNcDqeEI6KQa1SI +Zbq7sjf2w+VL0oQcse+gtg4q771dKy9V/XcfnPbiBAXfQqGj/v0LmZRn3ZRrkgzl +CaZocqVRJZbqu7/iu/p9pDRMR03vhBWPn7iM8VqzkhKjAlD0rolnQqvOAXyPpd/O +qYuYP+6Ymt/rUWXGVh0= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIJAJcrLHeFQrGEMA0GCSqGSIb3DQEBCwUAMEMxCzAJBgNV +BAYTAkdCMRAwDgYDVQQIDAdFbmdsYW5kMRAwDgYDVQQKDAdQSFAubmV0MRAwDgYD +VQQDDAdwaHAubmV0MB4XDTE3MTExNTE5MTEwMVoXDTM4MDUyOTE5MTEwMVowQzEL +MAkGA1UEBhMCR0IxEDAOBgNVBAgMB0VuZ2xhbmQxEDAOBgNVBAoMB1BIUC5uZXQx +EDAOBgNVBAMMB3BocC5uZXQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQC4bIXbh+e+OCbLum2FYB8i6ZnOShp1teWNEE/8A4ot20wkPxLeXFSU1Egn+3BG +fxHKrWxV/FOiL7bims/SfwptFkgNZkwE52HVk49zgQevG38smsnIGuBMXMKebLPT +wlsIWRqCmZqEmh1wNcC9+qwoHoYjvJvMYfFvofiSc7qkTFsjoBVe+ho90vjYMznr +Bs9DlD88iXO0kKN0Bsu2igz68JWkUsoNeD6UKSir16SmQZxdFPCM0O0gxmOyz4NZ +w8pJ5XGDJyS82fVp13tYCOBFhK0QBvvzaz/gN6De5arefm6cer9TX0HE+VvGhx4T +2lqydFLfAVKTTNoZHOQk2mFVUm7dlMzLvsgNKdEZH6wAa12eBImCea+CyJNkbGLC +nrN6a93iGqTft75sbDyvzRk/BdnxadkAPlOxgK1qGrOgeS0+7chPMh24MRoKj5bU +lYGdcp7Y9qIW1hZXFfgPJO0yMOug/7J14jybp+YhymBIBTZYi1zbGqGNXdLPyp77 +9KTfyJE9KkbGegBvwqJmZTW8QoUDllopDjr1zaIqHWJqfAK7vGivu0iLVJHBRbRs +w9c48RCjfIh7AkvFKsdIDt9SMdZOdm9D8SXosYPM1zMFQsFFRPh/nKyl9QnhWbwd +Vs1tIFDrb10Z2sn0pF/V0F0gubR5rvYs+3h/dLciLVFobQIDAQABo2MwYTAdBgNV +HQ4EFgQUNLPfQTbFeA7M6YejwK6IAPBz+EwwHwYDVR0jBBgwFoAUNLPfQTbFeA7M +6YejwK6IAPBz+EwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJ +KoZIhvcNAQELBQADggIBAIkctDRCNcNVs2qP/O3zZsVZ1cqlqYNjcHIVHqNZRvVb +bPtInZ/i/6BbwA5QM/giKpl2aMwetUnezjHVD+7/m24Mfs1FWyPD3joCMIANj7hR +MNbmX1LQBMt/bD3E5trq9S9rZ++/ny4gn/z2jH3x+lzDMBDiam/wXbvSmyIUPUX6 +V8FFpkTBNERpurUvX1NEFLuiilWKrI7/u1qS11PGBcX3pNQNfhKNMtuFz74hAE7C +RE8756CGu2bZzydYCarUXSNTXqNm5ly2J0UN6Al4myFwj/6XnLnDhXDAI8CaqDMD +MwmxUzJvzuphNS085ZIntrSgPJESGozevEsf9bGD8GyoBQN/cSqOJCFfnJqxtKHb +wNa2F78SXYsiWkRb5EzoyCeon+0tygG6gLBouVY8ouCerExkH9qzRLLzFInxmCm2 +ill7c8Xmml4H2BxJE33dz/Qh7PM3eF2A2VriioLDj4+HOiWq/1BjdW6/Xiwx5iP2 +nC6Cex+lBt6HkP9WIiJbGxVeamF5jxNWWvLoUkrjFhpukjb5BPR9zFt7mAty9J/T +iQt26yylP4ucmL9KB6lv4ISW4Ri8mhMcYh6nF3tIYWZMCSQKqkyvymhKdLMZa20A +IME2oxnfZcmsfMpM6BYsN4XDfGBeYctqqt2AFRNLkI4wFkFFdPTMcTi1q0iRH2Xk +-----END CERTIFICATE----- diff --git a/tests/include/ssl_certs/sni_server_cs.pem b/tests/include/ssl_certs/sni_server_cs.pem new file mode 100644 index 00000000000..9f5a201b26d --- /dev/null +++ b/tests/include/ssl_certs/sni_server_cs.pem @@ -0,0 +1,57 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAvy5NhzktzEdsHTbGB6vqYANms5rn1zXFmTJrGlWCwoIsNmTf +ahvZkrC1cCXTZ7fbPB8XQbpAtz2ZSU7OcwBW9B8okYUPo9zi/ptwcrgsQsN0hrcD +8MBRUccevwime5fLvg8E9RJ/68y9y3BnRcVWYO2sAK9juTfidNjETU3Bb05oXv8D +SD/6onXQu4uXDgsQ3cRXeld9UB0xazmQXyyiIqXc/cpTAnaEVYzn28aj7NlUbzNq +511UXMXY44x9EcXWpPVZ7heNcJNzY5DCNzmtXKrt9yiMpWQcPXEzsESVxAMqib9u +TFOlvVX17LIPxBG656PjTD9J1h6kBbMCUxzs7wIDAQABAoIBAQC85lBeY0X4ST3v +I7bJz7kWQ2YP4uhfAdeLhoDDFWjNLffniwYhfwEc6xNri0R2f/jUT9gX7qORKwEx +qPdeNCC2t67LElGg1FlJv2Z9Q7MgCKYzkdQH5s6y4e9kTHTLO/JpiceZKz1QTQ3f +XOH9032E6nIAf0wmr6xHTgOwajrN8VI5BuPEMVmEwIw3AtYeqVuPCNKyGR4HUVkC +2bAydnGngbRJRnNzmKcWJancxpHDGBSFqPyuXMFC7Jgo3ZmyCbGp99vuXVk/sW9x +5aj94M9nRE0guk05ivH2/JZao2uLYkIgjFWlhNxKdWgWRk8DEuN4djC8mKS9YH1q +crYRToMhAoGBAOspUTtKP54mpZmyhxuDqj02JaJRzNTskPHsiF1UhtXuw7uT+ryV +ekUFLNXoFmn9mbx1WVaUvGH4qjilvQOxz7u++lz0ApqJEfyM3jc/cC40Y5zcuGSu +Etbg+SyDoytlgMCIydJyrS7NNALSo5p5oG6XY2f8yd/DCAmo8LzypaHRAoGBANAf +R1SlBMc/bOsi6GrJxcBVSCFMiKYiO5woL5aUKa9yM+UQuQ/6xbQ7Q+sOlt0FH3xo +AJ2L60qTdjyXVtjOdtXs5ZC4l+C6AfnCx6yLr+fNc4SOYXEfqS4LZylgwKd9KyVB +asspIW9Idbgebmi6vPyt9LDkIp0h1VuFGjkvQJK/AoGBAI4pbS0dprXyARyYW6sb +fpgAmuG099IkrT9DUfCx/81myTclr2fAKal+BmvOIXaz0/OlMXvw8K19iVIzh7+r +B70lJ+93p/dKM/BsLI5TsHqOO0YB/QsIXOVAHgJ2FfdPJnW+e9vYba+kZ/Po6PSi +4ITaykJ8BIJcQgis89QWEGFxAoGBAJhQO+jzuDKF9ZWEf6ofrw0anOZZ16wWY5/e +PS2rk3JmVxpuibHrKqPDt+ogTELHDAsFJmYmz3VNxHuFmrajK49Wh4/JuMVr/CQo +6+8YcA1qa/94IFIlBLDBAafjujsZvOjQHnM+z8xcsGKmStF00Pjv6qNG4xoyd646 +FD4DmfOLAoGAWXehpopZKXE9gRAni881ucK6WqxPPBoofbozi09D0MmfarIVaSkv +jNVVHBfLWd7IEXTjiipPBeUqq6Jc3pscN1Vp4rrl8jTmVTdazEv0LuzpdUFqmNo2 +M+xw17uz9D9Q32/aW1Lar0PdIaL/wGEDEyzEBFwrGppcENLilPz8gzU= +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIFIjCCAwqgAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwVTELMAkGA1UEBhMCR0Ix +EDAOBgNVBAgMB0VuZ2xhbmQxEDAOBgNVBAoMB1BIUC5uZXQxEDAOBgNVBAsMB29w +ZW5zc2wxEDAOBgNVBAMMB3BocC5uZXQwHhcNMTgwMTE0MTgzNjEyWhcNMjYwNDAy +MTgzNjEyWjBGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UE +CgwHUEhQLm5ldDETMBEGA1UEAwwKY3MucGhwLm5ldDCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAL8uTYc5LcxHbB02xger6mADZrOa59c1xZkyaxpVgsKC +LDZk32ob2ZKwtXAl02e32zwfF0G6QLc9mUlOznMAVvQfKJGFD6Pc4v6bcHK4LELD +dIa3A/DAUVHHHr8IpnuXy74PBPUSf+vMvctwZ0XFVmDtrACvY7k34nTYxE1NwW9O +aF7/A0g/+qJ10LuLlw4LEN3EV3pXfVAdMWs5kF8soiKl3P3KUwJ2hFWM59vGo+zZ +VG8zauddVFzF2OOMfRHF1qT1We4XjXCTc2OQwjc5rVyq7fcojKVkHD1xM7BElcQD +Kom/bkxTpb1V9eyyD8QRuuej40w/SdYepAWzAlMc7O8CAwEAAaOCAQkwggEFMAkG +A1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRPcGVu +U1NMIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFHPfd8dK +Lz1R0Ck4WV1B9AWXd5DSMGwGA1UdIwRlMGOAFOPK44Eacedv7HbR2Igcbew+4kUa +oUekRTBDMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UECgwH +UEhQLm5ldDEQMA4GA1UEAwwHcGhwLm5ldIICEAAwDgYDVR0PAQH/BAQDAgWgMBMG +A1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4ICAQB6WSIHEyDXLZxH +hZjqSNQOA7Wc9Z2FCAiD29xYkGTL8WuPVGGP1mu4B92ytj+PMWwqSReDa7eTGLE7 +O7ozw9l+c+gNmHFNikSsGjlV2E8CToQOFMny+jAQYMSXf8UbTp9xDfgG02t/71hv +SLWqdeHMLcR0xi0nBQH0vDOkwUbuWYqFa3jejHieGhykHM6CkIk6lqnyOEO+ooIF +ZsLprrg1ss/mXCPI6niP0hze55ERKdxI7Rk8sZ4pVkf2SUWqZrUS0aJ+Ymmwi6Xd +2V7izq5N30PkJS8MtqII4FAjRBIkwPh0sy8PmW/DzkYU+lYQnDfYLKDFKcj8xJK/ +o8oZUBsQltrSj0KlM9QuqxCTCBCy1nXZ9WHOhq+jdLiTc1Oi60uEHcUMrLK8aYc4 +HqIvZS6C2iwMI0d1OP3VxmAbMQ9yqRi+FbLYavJ3H40jrU9SYqdxa0BrTaz8MJNE +6AEwgQDPChczSghvHME+Fs4mtGCY3TesbNZKVahQRjaFIhMZIZ4RP4CRc0bJOBG+ +8Me4+KHNsD2ki5b03wAN6C1P2QrMzI+gH9fXLZYp761ciDAsX6YIzrhHHYLxYpJH +BkQKKs8dCQWE5IzgVrdlvC3Z1/l9om66wHqqx7nKnPfYs/Sfnwe9MpCD6xJrXiTm +WS7NM6fbQpO9APNr7o0ZOjbbWFzlNw== +-----END CERTIFICATE----- diff --git a/tests/include/ssl_certs/sni_server_cs_cert.pem b/tests/include/ssl_certs/sni_server_cs_cert.pem new file mode 100644 index 00000000000..b77c7b83e2a --- /dev/null +++ b/tests/include/ssl_certs/sni_server_cs_cert.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFIjCCAwqgAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwVTELMAkGA1UEBhMCR0Ix +EDAOBgNVBAgMB0VuZ2xhbmQxEDAOBgNVBAoMB1BIUC5uZXQxEDAOBgNVBAsMB29w +ZW5zc2wxEDAOBgNVBAMMB3BocC5uZXQwHhcNMTgwMTE0MTgzNjEyWhcNMjYwNDAy +MTgzNjEyWjBGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UE +CgwHUEhQLm5ldDETMBEGA1UEAwwKY3MucGhwLm5ldDCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAL8uTYc5LcxHbB02xger6mADZrOa59c1xZkyaxpVgsKC +LDZk32ob2ZKwtXAl02e32zwfF0G6QLc9mUlOznMAVvQfKJGFD6Pc4v6bcHK4LELD +dIa3A/DAUVHHHr8IpnuXy74PBPUSf+vMvctwZ0XFVmDtrACvY7k34nTYxE1NwW9O +aF7/A0g/+qJ10LuLlw4LEN3EV3pXfVAdMWs5kF8soiKl3P3KUwJ2hFWM59vGo+zZ +VG8zauddVFzF2OOMfRHF1qT1We4XjXCTc2OQwjc5rVyq7fcojKVkHD1xM7BElcQD +Kom/bkxTpb1V9eyyD8QRuuej40w/SdYepAWzAlMc7O8CAwEAAaOCAQkwggEFMAkG +A1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRPcGVu +U1NMIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFHPfd8dK +Lz1R0Ck4WV1B9AWXd5DSMGwGA1UdIwRlMGOAFOPK44Eacedv7HbR2Igcbew+4kUa +oUekRTBDMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UECgwH +UEhQLm5ldDEQMA4GA1UEAwwHcGhwLm5ldIICEAAwDgYDVR0PAQH/BAQDAgWgMBMG +A1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4ICAQB6WSIHEyDXLZxH +hZjqSNQOA7Wc9Z2FCAiD29xYkGTL8WuPVGGP1mu4B92ytj+PMWwqSReDa7eTGLE7 +O7ozw9l+c+gNmHFNikSsGjlV2E8CToQOFMny+jAQYMSXf8UbTp9xDfgG02t/71hv +SLWqdeHMLcR0xi0nBQH0vDOkwUbuWYqFa3jejHieGhykHM6CkIk6lqnyOEO+ooIF +ZsLprrg1ss/mXCPI6niP0hze55ERKdxI7Rk8sZ4pVkf2SUWqZrUS0aJ+Ymmwi6Xd +2V7izq5N30PkJS8MtqII4FAjRBIkwPh0sy8PmW/DzkYU+lYQnDfYLKDFKcj8xJK/ +o8oZUBsQltrSj0KlM9QuqxCTCBCy1nXZ9WHOhq+jdLiTc1Oi60uEHcUMrLK8aYc4 +HqIvZS6C2iwMI0d1OP3VxmAbMQ9yqRi+FbLYavJ3H40jrU9SYqdxa0BrTaz8MJNE +6AEwgQDPChczSghvHME+Fs4mtGCY3TesbNZKVahQRjaFIhMZIZ4RP4CRc0bJOBG+ +8Me4+KHNsD2ki5b03wAN6C1P2QrMzI+gH9fXLZYp761ciDAsX6YIzrhHHYLxYpJH +BkQKKs8dCQWE5IzgVrdlvC3Z1/l9om66wHqqx7nKnPfYs/Sfnwe9MpCD6xJrXiTm +WS7NM6fbQpO9APNr7o0ZOjbbWFzlNw== +-----END CERTIFICATE----- diff --git a/tests/include/ssl_certs/sni_server_cs_key.pem b/tests/include/ssl_certs/sni_server_cs_key.pem new file mode 100644 index 00000000000..bf1e9f25193 --- /dev/null +++ b/tests/include/ssl_certs/sni_server_cs_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAvy5NhzktzEdsHTbGB6vqYANms5rn1zXFmTJrGlWCwoIsNmTf +ahvZkrC1cCXTZ7fbPB8XQbpAtz2ZSU7OcwBW9B8okYUPo9zi/ptwcrgsQsN0hrcD +8MBRUccevwime5fLvg8E9RJ/68y9y3BnRcVWYO2sAK9juTfidNjETU3Bb05oXv8D +SD/6onXQu4uXDgsQ3cRXeld9UB0xazmQXyyiIqXc/cpTAnaEVYzn28aj7NlUbzNq +511UXMXY44x9EcXWpPVZ7heNcJNzY5DCNzmtXKrt9yiMpWQcPXEzsESVxAMqib9u +TFOlvVX17LIPxBG656PjTD9J1h6kBbMCUxzs7wIDAQABAoIBAQC85lBeY0X4ST3v +I7bJz7kWQ2YP4uhfAdeLhoDDFWjNLffniwYhfwEc6xNri0R2f/jUT9gX7qORKwEx +qPdeNCC2t67LElGg1FlJv2Z9Q7MgCKYzkdQH5s6y4e9kTHTLO/JpiceZKz1QTQ3f +XOH9032E6nIAf0wmr6xHTgOwajrN8VI5BuPEMVmEwIw3AtYeqVuPCNKyGR4HUVkC +2bAydnGngbRJRnNzmKcWJancxpHDGBSFqPyuXMFC7Jgo3ZmyCbGp99vuXVk/sW9x +5aj94M9nRE0guk05ivH2/JZao2uLYkIgjFWlhNxKdWgWRk8DEuN4djC8mKS9YH1q +crYRToMhAoGBAOspUTtKP54mpZmyhxuDqj02JaJRzNTskPHsiF1UhtXuw7uT+ryV +ekUFLNXoFmn9mbx1WVaUvGH4qjilvQOxz7u++lz0ApqJEfyM3jc/cC40Y5zcuGSu +Etbg+SyDoytlgMCIydJyrS7NNALSo5p5oG6XY2f8yd/DCAmo8LzypaHRAoGBANAf +R1SlBMc/bOsi6GrJxcBVSCFMiKYiO5woL5aUKa9yM+UQuQ/6xbQ7Q+sOlt0FH3xo +AJ2L60qTdjyXVtjOdtXs5ZC4l+C6AfnCx6yLr+fNc4SOYXEfqS4LZylgwKd9KyVB +asspIW9Idbgebmi6vPyt9LDkIp0h1VuFGjkvQJK/AoGBAI4pbS0dprXyARyYW6sb +fpgAmuG099IkrT9DUfCx/81myTclr2fAKal+BmvOIXaz0/OlMXvw8K19iVIzh7+r +B70lJ+93p/dKM/BsLI5TsHqOO0YB/QsIXOVAHgJ2FfdPJnW+e9vYba+kZ/Po6PSi +4ITaykJ8BIJcQgis89QWEGFxAoGBAJhQO+jzuDKF9ZWEf6ofrw0anOZZ16wWY5/e +PS2rk3JmVxpuibHrKqPDt+ogTELHDAsFJmYmz3VNxHuFmrajK49Wh4/JuMVr/CQo +6+8YcA1qa/94IFIlBLDBAafjujsZvOjQHnM+z8xcsGKmStF00Pjv6qNG4xoyd646 +FD4DmfOLAoGAWXehpopZKXE9gRAni881ucK6WqxPPBoofbozi09D0MmfarIVaSkv +jNVVHBfLWd7IEXTjiipPBeUqq6Jc3pscN1Vp4rrl8jTmVTdazEv0LuzpdUFqmNo2 +M+xw17uz9D9Q32/aW1Lar0PdIaL/wGEDEyzEBFwrGppcENLilPz8gzU= +-----END RSA PRIVATE KEY----- diff --git a/tests/include/ssl_certs/sni_server_uk.pem b/tests/include/ssl_certs/sni_server_uk.pem new file mode 100644 index 00000000000..f929b698979 --- /dev/null +++ b/tests/include/ssl_certs/sni_server_uk.pem @@ -0,0 +1,57 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEArMpFePSNUHNHCiUWfsSPQxHaQTpDfogEISIMuGq5JhEuHWyl +HRe51JWZGi39a/PY9rAClfg/qqPnuxwFDoovhQsq9Kd0JnsXW11CMSqiBimf8vyg +GE/V2DIOiex7Ab8+5zEL7cCrNyXfDk+DuoT7Np3jaSE/u31OMUAzFz4/EL7Zqzlk +IMkHB/GSwLIJB8VbHt4Hunf28bLreTcjO/5QpNkSFtJ1PvbWRv3WheR5ykQQWGKF +9VgwYwS0Lu7YktLF2RGFZhIypUK8jazJiwsBRQ0faSCpAXTSPYh12GDegwAiKqGk +L+fayUdWGnnpYTVOysYL8mCoi9Ar0BPgtHEVvwIDAQABAoIBAGpg7XyWMxpAno/k +XYEWSo0kRJa75CnWW5R6fhJbwjlpglajONQ8czAqGeRARDFeI0lc+3qRJ8FKv7Q2 +f/Z/pNnoEj4liiDWz350X/gdIztgDRVv6rCNFj7QMps/eEuPDo8PZySs5bxCGJ2a +3qLKlE7/za4/xhkMAEPaHppEvFb15dpomqP2Fjqei9afp8tD+xJv9BGtkFIy9acw +94AWQi974kgW7ZzfEf8RqHM0ExKiESCc26knbcBu0tQ892YHggMccUMk/UrVgR5d +vuGOBurlYGLBstSDSKCCe6la4N/FX3FiC3WhVNescnuTZPXcX3zebh7roGjgrTdd +6UJYBIECgYEA09XYihdwW66G9Y09OatzlzBh1Rvzjv2Vl5NmWHHb6vh38H4PcOof +S1pT0JvRzpt8uEs4i+Eda5PyQyp4iGtD+DQ07Dxh2jiixsE3PRFRG2wg2QCgDHnS +6vgV1VOqA/A4on4nhxLZXxhX4YvHaJYlm1bZ+amG+j5XiI/NtHJm9k8CgYEA0NB9 +bAoaZnc06+T8rMaO45boX5SvYsCZbtY3VRl75SrbMal0IKfnXHELVHuei12LmlMs +LuFgG8jGO3+ncxUqMytoGvrmk/cM9tCNbDKVHtFbqz0qt9lkDAwLsBougKnVsNDW +Aae/tz/wYXJct86unfLh0xtvkx5Iz1we4cS1fZECgYA8YR+vfK7R8xUUuIVTAsOX +vHqmO6lYgH87DRCG9S0x9FB7g/LyxEjXOY/aTg+nTDpobUhY1nmESE8tRdXFTI27 +GWZcT3m0sZ/z9u6/wUfVAST7tWnpJHAx+TR/8bDsHnSGHF836O98Y3vpFeZosSNW ++5J1zxRiD5LzocmIPXOLkQKBgEgHApxXx898YwbTj8zRGMysay89DFpV8RboUWHL +To83/y/cMbBp+kZKwAu+MGwGMndjJSRunUY4NRik6c+qh0nrORfFX+++Efy4529g +60scEDC7Apc0J2x4Yze1cED1VD6PaqJbiKffKD2UwyKr6lOVSgwVtKDcm2Tbc9OQ +lMHhAoGBAIPwYVxzWM6I6pr8x2TucpBZZReLytz7uzybMNvbKCrwlETbNNXubnfp +nPuPKzpeRI0y26pIIAbijzFW0MXq+kIu3H7we8TaImsJ1AaQCTYeoLWBVcr6RISk +3d4i7iT35aWCuhPVve0FNIv/u3jrqX2H2C2MXMiLOsw1GFxPvpi4 +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIFIjCCAwqgAwIBAgICEAMwDQYJKoZIhvcNAQELBQAwVTELMAkGA1UEBhMCR0Ix +EDAOBgNVBAgMB0VuZ2xhbmQxEDAOBgNVBAoMB1BIUC5uZXQxEDAOBgNVBAsMB29w +ZW5zc2wxEDAOBgNVBAMMB3BocC5uZXQwHhcNMTgwMTE0MTgzNjMyWhcNMjYwNDAy +MTgzNjMyWjBGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UE +CgwHUEhQLm5ldDETMBEGA1UEAwwKdWsucGhwLm5ldDCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAKzKRXj0jVBzRwolFn7Ej0MR2kE6Q36IBCEiDLhquSYR +Lh1spR0XudSVmRot/Wvz2PawApX4P6qj57scBQ6KL4ULKvSndCZ7F1tdQjEqogYp +n/L8oBhP1dgyDonsewG/PucxC+3Aqzcl3w5Pg7qE+zad42khP7t9TjFAMxc+PxC+ +2as5ZCDJBwfxksCyCQfFWx7eB7p39vGy63k3Izv+UKTZEhbSdT721kb91oXkecpE +EFhihfVYMGMEtC7u2JLSxdkRhWYSMqVCvI2syYsLAUUNH2kgqQF00j2Iddhg3oMA +IiqhpC/n2slHVhp56WE1TsrGC/JgqIvQK9AT4LRxFb8CAwEAAaOCAQkwggEFMAkG +A1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRPcGVu +U1NMIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFJK58uJe +Lv8WDOR8TTLUcSxO1zCyMGwGA1UdIwRlMGOAFOPK44Eacedv7HbR2Igcbew+4kUa +oUekRTBDMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UECgwH +UEhQLm5ldDEQMA4GA1UEAwwHcGhwLm5ldIICEAAwDgYDVR0PAQH/BAQDAgWgMBMG +A1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4ICAQDHayU795hFnQNj +fuYV21sEmkSjgtp6X4rV9JfoCp8U2gieL4mizoCPx9Q8HYaAf98hPYTofGUaCSd+ +Xlzd8lhLt9GCU+BsCxGtJocrjOQDhyhkioN8iirQRTtJpRVC6a4RCBGpenOD7Z7C +Z32uGXohzFuigP/J8/SXBdHrlpNSa03iEkkisPR46mbGj06lFVOFHJDNKA3CnQwM +3VfqvZt0ksZi/gAOol8dtrAd3k5JBfJ2BtqsPbycEGAhsGrTK/MeqiFcO8QZiXpN +qf0wZzOMFZJ4HqTiHs4TMMQaDK8c4qfR0l1OE224ijcB5NlqMjGAZabMfHgNZcl7 +fYPX+POsOMaqubv98uoC0PCK8NCTyfB4V/el72EEoVfwKYOxm3eYXBEE2ZNyIci6 +YKYhNa9e2k+cFueYMztV06HTGgkcHcSJATP+3ISTRx3tj1BPmc4O4WAMEMx1E0gg +PaVrBVAylGzt8kaMaOQMBImpsALSfutq6+KtQiu0OTGS3yVUGamV4AyRjuwF1/ZA +kZbjmNEGsf9d2rzS89ckds24QUx0zrfJ56p7lRvyVK/pML7iadv8dUH3fKDDDerD +oocLjOjNxFGN9Woz+kfidA8Siu0zsfQ6CLPlaQrVAVQH4iMuCZraqMc3TqAiUKRj +iHKp6FFrRZmY8W8HEKHv98uGofn+3g== +-----END CERTIFICATE----- diff --git a/tests/include/ssl_certs/sni_server_uk_cert.pem b/tests/include/ssl_certs/sni_server_uk_cert.pem new file mode 100644 index 00000000000..57663855e59 --- /dev/null +++ b/tests/include/ssl_certs/sni_server_uk_cert.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFIjCCAwqgAwIBAgICEAMwDQYJKoZIhvcNAQELBQAwVTELMAkGA1UEBhMCR0Ix +EDAOBgNVBAgMB0VuZ2xhbmQxEDAOBgNVBAoMB1BIUC5uZXQxEDAOBgNVBAsMB29w +ZW5zc2wxEDAOBgNVBAMMB3BocC5uZXQwHhcNMTgwMTE0MTgzNjMyWhcNMjYwNDAy +MTgzNjMyWjBGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UE +CgwHUEhQLm5ldDETMBEGA1UEAwwKdWsucGhwLm5ldDCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAKzKRXj0jVBzRwolFn7Ej0MR2kE6Q36IBCEiDLhquSYR +Lh1spR0XudSVmRot/Wvz2PawApX4P6qj57scBQ6KL4ULKvSndCZ7F1tdQjEqogYp +n/L8oBhP1dgyDonsewG/PucxC+3Aqzcl3w5Pg7qE+zad42khP7t9TjFAMxc+PxC+ +2as5ZCDJBwfxksCyCQfFWx7eB7p39vGy63k3Izv+UKTZEhbSdT721kb91oXkecpE +EFhihfVYMGMEtC7u2JLSxdkRhWYSMqVCvI2syYsLAUUNH2kgqQF00j2Iddhg3oMA +IiqhpC/n2slHVhp56WE1TsrGC/JgqIvQK9AT4LRxFb8CAwEAAaOCAQkwggEFMAkG +A1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRPcGVu +U1NMIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFJK58uJe +Lv8WDOR8TTLUcSxO1zCyMGwGA1UdIwRlMGOAFOPK44Eacedv7HbR2Igcbew+4kUa +oUekRTBDMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UECgwH +UEhQLm5ldDEQMA4GA1UEAwwHcGhwLm5ldIICEAAwDgYDVR0PAQH/BAQDAgWgMBMG +A1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4ICAQDHayU795hFnQNj +fuYV21sEmkSjgtp6X4rV9JfoCp8U2gieL4mizoCPx9Q8HYaAf98hPYTofGUaCSd+ +Xlzd8lhLt9GCU+BsCxGtJocrjOQDhyhkioN8iirQRTtJpRVC6a4RCBGpenOD7Z7C +Z32uGXohzFuigP/J8/SXBdHrlpNSa03iEkkisPR46mbGj06lFVOFHJDNKA3CnQwM +3VfqvZt0ksZi/gAOol8dtrAd3k5JBfJ2BtqsPbycEGAhsGrTK/MeqiFcO8QZiXpN +qf0wZzOMFZJ4HqTiHs4TMMQaDK8c4qfR0l1OE224ijcB5NlqMjGAZabMfHgNZcl7 +fYPX+POsOMaqubv98uoC0PCK8NCTyfB4V/el72EEoVfwKYOxm3eYXBEE2ZNyIci6 +YKYhNa9e2k+cFueYMztV06HTGgkcHcSJATP+3ISTRx3tj1BPmc4O4WAMEMx1E0gg +PaVrBVAylGzt8kaMaOQMBImpsALSfutq6+KtQiu0OTGS3yVUGamV4AyRjuwF1/ZA +kZbjmNEGsf9d2rzS89ckds24QUx0zrfJ56p7lRvyVK/pML7iadv8dUH3fKDDDerD +oocLjOjNxFGN9Woz+kfidA8Siu0zsfQ6CLPlaQrVAVQH4iMuCZraqMc3TqAiUKRj +iHKp6FFrRZmY8W8HEKHv98uGofn+3g== +-----END CERTIFICATE----- diff --git a/tests/include/ssl_certs/sni_server_uk_key.pem b/tests/include/ssl_certs/sni_server_uk_key.pem new file mode 100644 index 00000000000..3baa6efd776 --- /dev/null +++ b/tests/include/ssl_certs/sni_server_uk_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEArMpFePSNUHNHCiUWfsSPQxHaQTpDfogEISIMuGq5JhEuHWyl +HRe51JWZGi39a/PY9rAClfg/qqPnuxwFDoovhQsq9Kd0JnsXW11CMSqiBimf8vyg +GE/V2DIOiex7Ab8+5zEL7cCrNyXfDk+DuoT7Np3jaSE/u31OMUAzFz4/EL7Zqzlk +IMkHB/GSwLIJB8VbHt4Hunf28bLreTcjO/5QpNkSFtJ1PvbWRv3WheR5ykQQWGKF +9VgwYwS0Lu7YktLF2RGFZhIypUK8jazJiwsBRQ0faSCpAXTSPYh12GDegwAiKqGk +L+fayUdWGnnpYTVOysYL8mCoi9Ar0BPgtHEVvwIDAQABAoIBAGpg7XyWMxpAno/k +XYEWSo0kRJa75CnWW5R6fhJbwjlpglajONQ8czAqGeRARDFeI0lc+3qRJ8FKv7Q2 +f/Z/pNnoEj4liiDWz350X/gdIztgDRVv6rCNFj7QMps/eEuPDo8PZySs5bxCGJ2a +3qLKlE7/za4/xhkMAEPaHppEvFb15dpomqP2Fjqei9afp8tD+xJv9BGtkFIy9acw +94AWQi974kgW7ZzfEf8RqHM0ExKiESCc26knbcBu0tQ892YHggMccUMk/UrVgR5d +vuGOBurlYGLBstSDSKCCe6la4N/FX3FiC3WhVNescnuTZPXcX3zebh7roGjgrTdd +6UJYBIECgYEA09XYihdwW66G9Y09OatzlzBh1Rvzjv2Vl5NmWHHb6vh38H4PcOof +S1pT0JvRzpt8uEs4i+Eda5PyQyp4iGtD+DQ07Dxh2jiixsE3PRFRG2wg2QCgDHnS +6vgV1VOqA/A4on4nhxLZXxhX4YvHaJYlm1bZ+amG+j5XiI/NtHJm9k8CgYEA0NB9 +bAoaZnc06+T8rMaO45boX5SvYsCZbtY3VRl75SrbMal0IKfnXHELVHuei12LmlMs +LuFgG8jGO3+ncxUqMytoGvrmk/cM9tCNbDKVHtFbqz0qt9lkDAwLsBougKnVsNDW +Aae/tz/wYXJct86unfLh0xtvkx5Iz1we4cS1fZECgYA8YR+vfK7R8xUUuIVTAsOX +vHqmO6lYgH87DRCG9S0x9FB7g/LyxEjXOY/aTg+nTDpobUhY1nmESE8tRdXFTI27 +GWZcT3m0sZ/z9u6/wUfVAST7tWnpJHAx+TR/8bDsHnSGHF836O98Y3vpFeZosSNW ++5J1zxRiD5LzocmIPXOLkQKBgEgHApxXx898YwbTj8zRGMysay89DFpV8RboUWHL +To83/y/cMbBp+kZKwAu+MGwGMndjJSRunUY4NRik6c+qh0nrORfFX+++Efy4529g +60scEDC7Apc0J2x4Yze1cED1VD6PaqJbiKffKD2UwyKr6lOVSgwVtKDcm2Tbc9OQ +lMHhAoGBAIPwYVxzWM6I6pr8x2TucpBZZReLytz7uzybMNvbKCrwlETbNNXubnfp +nPuPKzpeRI0y26pIIAbijzFW0MXq+kIu3H7we8TaImsJ1AaQCTYeoLWBVcr6RISk +3d4i7iT35aWCuhPVve0FNIv/u3jrqX2H2C2MXMiLOsw1GFxPvpi4 +-----END RSA PRIVATE KEY----- diff --git a/tests/include/ssl_certs/sni_server_us.pem b/tests/include/ssl_certs/sni_server_us.pem new file mode 100644 index 00000000000..170a8a96e75 --- /dev/null +++ b/tests/include/ssl_certs/sni_server_us.pem @@ -0,0 +1,57 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA1QkC3tiNYDY+ZxMmPbagYUbMpzuXo9mVBvYh86bYZaeB7bts +QCBK+6VD4D2LjR3RszpzmOzhJXjm8j0t+GeRS3OMIM75/BKAnixXicRSIb8zdIPz +JP992vvMq8p46/XftAfBhAMOaCqcD85zpyX4PhfC9733nOyN4yqx58O4UhVTKih6 +W9/ldp3uwSYAW+HyoINnHls/bFO8vv60K5VIhkxK30LHnC5PvByGfuHOgrscEThs +jW0ESqO0+9l88KhGdmLgzvbBlGxNziCMfn0LcFH6p2ITc3foD4LSzGEFtJ92OZi3 +buCOfbFsN7vWHTsEi89fRcCnWGtMwLUx0TCluQIDAQABAoIBAQCnsUQ1Lrl6trhA +Yu6DPbLZX+XQ7jPbonaQ2Ea5iOhmfIjmHdaEU+cyV1EqvseO+Z4MO0KraiuAV79T +h50cIEpa3kW7vbFCHz5nQ/hUVdlg/yT93rASu5rSOctOnz64Xv8Ms948kDtS+9eF +Cbo4JMdX+VRbt4mmWP8HhqAsFACPexEoWxJcIxwFcI24GTGzySjemNjQzbmcVhzM +a4k6n8DolCL1cRS54C5Aaf5g4+IFDgyydcgZXp1lnX3MnqivSNkejnPnY55NcmrH +X3ZWPlAi9GHOJE33uy8bGWnip7Tn4iTt6tJvjz/yP82TGACDg1B8XsKrqsuQLsoU +cNBVGcQBAoGBAPteCgNmuNOpo4SRA1UVRw1WgnE8YtnNA6vYyVcTLSpqabq33UaD +03L9CQsbHtj88U+E8OH24Iqj3U9x7QJfH8DVmWuBrlwez80JsKGnLdViHydjKcAz +H2Cbv+SiWeaWXkFCkN4Jf7k3q0Ew4SG2LOq5PVUy/NB4bilbJD2ExKpZAoGBANj2 +Hpwo35IQ4XfSSsGaCdn+8ajMcNUMMGZ6YkZqmVO4kogqobyrPL/2KE9ol/hlacw6 +U/6Digox5/wqruYfqyM8lqGOq2/0Xf7c4XfiOTS9Na4JN3OGzlyqPvcn2zdqhYFY +iHPu2RqpA+LhCHW9Zs8C1Bp/KAEPdRP6OabqVaphAoGACLrHVj7nBFLL3vq6RuYq +RYhPl2cld7LrAbjRpTiBRQvVCCsCgERrv36SJdSXSanfJ4fSZcaRHb97HBs0w/RR +wfypC1bBm2lmhhRkEfkgWlzCADgtZwNff5dpHqOUw7FNLK8HIO7rhJ8uT2FHMEiH +Xs94FdFjfknwaXdE1u4ZdmECgYEAgxfbkQHFbO2UPqErGGXp0/WOsS6ucpyF1jXW +kbOxZ3vb1jjkNyrEbzzeSHTrdmRYk9UekWeLjfNvt9dWjKfP8V+XqJCbF+9wqCFw +fs6LQEmfWMQq5DwtDqKznwVPGOHdPzVuZZaJSemb9oeAZBwINccAv+3bDyD23hZQ +pYFsN6ECgYEA33QYDNG/spki4D8rlxyxZ+1MdB/efnrGBhO8FsJpG5+AtmYhWgD9 +sl29+3aiRkmDznoy36z+hoeZePILEAKMcbHyXOymixOHPuaZJ95hbvq6sqd6WMAe +w5tHnxlfEuu11zatolk6WiAmTmG3sZpN5Tqloq0Ye4dvlhVKNV3Bn3E= +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIFIjCCAwqgAwIBAgICEAQwDQYJKoZIhvcNAQELBQAwVTELMAkGA1UEBhMCR0Ix +EDAOBgNVBAgMB0VuZ2xhbmQxEDAOBgNVBAoMB1BIUC5uZXQxEDAOBgNVBAsMB29w +ZW5zc2wxEDAOBgNVBAMMB3BocC5uZXQwHhcNMTgwMTE0MTgzNjQ0WhcNMjYwNDAy +MTgzNjQ0WjBGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UE +CgwHUEhQLm5ldDETMBEGA1UEAwwKdXMucGhwLm5ldDCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANUJAt7YjWA2PmcTJj22oGFGzKc7l6PZlQb2IfOm2GWn +ge27bEAgSvulQ+A9i40d0bM6c5js4SV45vI9LfhnkUtzjCDO+fwSgJ4sV4nEUiG/ +M3SD8yT/fdr7zKvKeOv137QHwYQDDmgqnA/Oc6cl+D4Xwve995zsjeMqsefDuFIV +Uyooelvf5Xad7sEmAFvh8qCDZx5bP2xTvL7+tCuVSIZMSt9Cx5wuT7wchn7hzoK7 +HBE4bI1tBEqjtPvZfPCoRnZi4M72wZRsTc4gjH59C3BR+qdiE3N36A+C0sxhBbSf +djmYt27gjn2xbDe71h07BIvPX0XAp1hrTMC1MdEwpbkCAwEAAaOCAQkwggEFMAkG +A1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRPcGVu +U1NMIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFOXnUeNs +grKQb+EvG36DXjjDDmsFMGwGA1UdIwRlMGOAFOPK44Eacedv7HbR2Igcbew+4kUa +oUekRTBDMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UECgwH +UEhQLm5ldDEQMA4GA1UEAwwHcGhwLm5ldIICEAAwDgYDVR0PAQH/BAQDAgWgMBMG +A1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4ICAQBDLwuLbx75ACSU +5cF2L/D17JEnhlna62MgKNdpNLJSpaofK2Lk2BqsmnQf5JdkrXWMUN/DsmXZc9pq +25XmprfABUP9Cx18KPVqLQ43Z9o+R9xI1Ospt5mrpxGp6l2BHSs/4G69nuPFpcIJ +iabnLYdUk2Z+64lPe4EMBrZH+pj4xn3JA59BACJYNYn0nLaw45DIAyzyLJ0vVSwc +0JtjsztXQov4UqdWXxLRFfe2nEGoK8ZkTJ8ELcCYu6sNSBjw9Ech78uXN1BQOBTK +lhAgN3FKqOp3hqf0umqf35gDvmWwLB/eptUYZ96gBYT0tbPA0P+YsW+iZmamxXma +Odgg8iRcPxKl9bVPt57NLaDy/RQhOxTGXQs1Q2jp7UhzqoZDClwVSDxd6DEppAFA +OZAY+Rsrm7VoCwVQ/1KbcJHmJ/79tArvaWJk3KHLGMpdZq4KwrC3hM8/QxYtyX/6 +cfnXvShBYCdfTGgNlj3t/mNAgp1ZB3s9ClGqRBR/P0Db+ryv5DuxYM6nzEB3Od0y +kT5tHbXDQY+1HCExjOMi7Al0cmC2r3+oxDA4UjGv+npgcfeoxQhXmm/SQRiPdKlb +vT0D594sLoB23jqA9bMehpxEyI7eGjfFUmXwMeu0tJhipvpJI3ogJoM+SCFTyLkc +12cPiz/sR/ALhvhUJXTeUH8wxPjzbA== +-----END CERTIFICATE----- diff --git a/tests/include/ssl_certs/sni_server_us_cert.pem b/tests/include/ssl_certs/sni_server_us_cert.pem new file mode 100644 index 00000000000..4452e3c6cfb --- /dev/null +++ b/tests/include/ssl_certs/sni_server_us_cert.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFIjCCAwqgAwIBAgICEAQwDQYJKoZIhvcNAQELBQAwVTELMAkGA1UEBhMCR0Ix +EDAOBgNVBAgMB0VuZ2xhbmQxEDAOBgNVBAoMB1BIUC5uZXQxEDAOBgNVBAsMB29w +ZW5zc2wxEDAOBgNVBAMMB3BocC5uZXQwHhcNMTgwMTE0MTgzNjQ0WhcNMjYwNDAy +MTgzNjQ0WjBGMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UE +CgwHUEhQLm5ldDETMBEGA1UEAwwKdXMucGhwLm5ldDCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANUJAt7YjWA2PmcTJj22oGFGzKc7l6PZlQb2IfOm2GWn +ge27bEAgSvulQ+A9i40d0bM6c5js4SV45vI9LfhnkUtzjCDO+fwSgJ4sV4nEUiG/ +M3SD8yT/fdr7zKvKeOv137QHwYQDDmgqnA/Oc6cl+D4Xwve995zsjeMqsefDuFIV +Uyooelvf5Xad7sEmAFvh8qCDZx5bP2xTvL7+tCuVSIZMSt9Cx5wuT7wchn7hzoK7 +HBE4bI1tBEqjtPvZfPCoRnZi4M72wZRsTc4gjH59C3BR+qdiE3N36A+C0sxhBbSf +djmYt27gjn2xbDe71h07BIvPX0XAp1hrTMC1MdEwpbkCAwEAAaOCAQkwggEFMAkG +A1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRPcGVu +U1NMIEdlbmVyYXRlZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFOXnUeNs +grKQb+EvG36DXjjDDmsFMGwGA1UdIwRlMGOAFOPK44Eacedv7HbR2Igcbew+4kUa +oUekRTBDMQswCQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDEQMA4GA1UECgwH +UEhQLm5ldDEQMA4GA1UEAwwHcGhwLm5ldIICEAAwDgYDVR0PAQH/BAQDAgWgMBMG +A1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4ICAQBDLwuLbx75ACSU +5cF2L/D17JEnhlna62MgKNdpNLJSpaofK2Lk2BqsmnQf5JdkrXWMUN/DsmXZc9pq +25XmprfABUP9Cx18KPVqLQ43Z9o+R9xI1Ospt5mrpxGp6l2BHSs/4G69nuPFpcIJ +iabnLYdUk2Z+64lPe4EMBrZH+pj4xn3JA59BACJYNYn0nLaw45DIAyzyLJ0vVSwc +0JtjsztXQov4UqdWXxLRFfe2nEGoK8ZkTJ8ELcCYu6sNSBjw9Ech78uXN1BQOBTK +lhAgN3FKqOp3hqf0umqf35gDvmWwLB/eptUYZ96gBYT0tbPA0P+YsW+iZmamxXma +Odgg8iRcPxKl9bVPt57NLaDy/RQhOxTGXQs1Q2jp7UhzqoZDClwVSDxd6DEppAFA +OZAY+Rsrm7VoCwVQ/1KbcJHmJ/79tArvaWJk3KHLGMpdZq4KwrC3hM8/QxYtyX/6 +cfnXvShBYCdfTGgNlj3t/mNAgp1ZB3s9ClGqRBR/P0Db+ryv5DuxYM6nzEB3Od0y +kT5tHbXDQY+1HCExjOMi7Al0cmC2r3+oxDA4UjGv+npgcfeoxQhXmm/SQRiPdKlb +vT0D594sLoB23jqA9bMehpxEyI7eGjfFUmXwMeu0tJhipvpJI3ogJoM+SCFTyLkc +12cPiz/sR/ALhvhUJXTeUH8wxPjzbA== +-----END CERTIFICATE----- diff --git a/tests/include/ssl_certs/sni_server_us_key.pem b/tests/include/ssl_certs/sni_server_us_key.pem new file mode 100644 index 00000000000..8b0d73d7d63 --- /dev/null +++ b/tests/include/ssl_certs/sni_server_us_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA1QkC3tiNYDY+ZxMmPbagYUbMpzuXo9mVBvYh86bYZaeB7bts +QCBK+6VD4D2LjR3RszpzmOzhJXjm8j0t+GeRS3OMIM75/BKAnixXicRSIb8zdIPz +JP992vvMq8p46/XftAfBhAMOaCqcD85zpyX4PhfC9733nOyN4yqx58O4UhVTKih6 +W9/ldp3uwSYAW+HyoINnHls/bFO8vv60K5VIhkxK30LHnC5PvByGfuHOgrscEThs +jW0ESqO0+9l88KhGdmLgzvbBlGxNziCMfn0LcFH6p2ITc3foD4LSzGEFtJ92OZi3 +buCOfbFsN7vWHTsEi89fRcCnWGtMwLUx0TCluQIDAQABAoIBAQCnsUQ1Lrl6trhA +Yu6DPbLZX+XQ7jPbonaQ2Ea5iOhmfIjmHdaEU+cyV1EqvseO+Z4MO0KraiuAV79T +h50cIEpa3kW7vbFCHz5nQ/hUVdlg/yT93rASu5rSOctOnz64Xv8Ms948kDtS+9eF +Cbo4JMdX+VRbt4mmWP8HhqAsFACPexEoWxJcIxwFcI24GTGzySjemNjQzbmcVhzM +a4k6n8DolCL1cRS54C5Aaf5g4+IFDgyydcgZXp1lnX3MnqivSNkejnPnY55NcmrH +X3ZWPlAi9GHOJE33uy8bGWnip7Tn4iTt6tJvjz/yP82TGACDg1B8XsKrqsuQLsoU +cNBVGcQBAoGBAPteCgNmuNOpo4SRA1UVRw1WgnE8YtnNA6vYyVcTLSpqabq33UaD +03L9CQsbHtj88U+E8OH24Iqj3U9x7QJfH8DVmWuBrlwez80JsKGnLdViHydjKcAz +H2Cbv+SiWeaWXkFCkN4Jf7k3q0Ew4SG2LOq5PVUy/NB4bilbJD2ExKpZAoGBANj2 +Hpwo35IQ4XfSSsGaCdn+8ajMcNUMMGZ6YkZqmVO4kogqobyrPL/2KE9ol/hlacw6 +U/6Digox5/wqruYfqyM8lqGOq2/0Xf7c4XfiOTS9Na4JN3OGzlyqPvcn2zdqhYFY +iHPu2RqpA+LhCHW9Zs8C1Bp/KAEPdRP6OabqVaphAoGACLrHVj7nBFLL3vq6RuYq +RYhPl2cld7LrAbjRpTiBRQvVCCsCgERrv36SJdSXSanfJ4fSZcaRHb97HBs0w/RR +wfypC1bBm2lmhhRkEfkgWlzCADgtZwNff5dpHqOUw7FNLK8HIO7rhJ8uT2FHMEiH +Xs94FdFjfknwaXdE1u4ZdmECgYEAgxfbkQHFbO2UPqErGGXp0/WOsS6ucpyF1jXW +kbOxZ3vb1jjkNyrEbzzeSHTrdmRYk9UekWeLjfNvt9dWjKfP8V+XqJCbF+9wqCFw +fs6LQEmfWMQq5DwtDqKznwVPGOHdPzVuZZaJSemb9oeAZBwINccAv+3bDyD23hZQ +pYFsN6ECgYEA33QYDNG/spki4D8rlxyxZ+1MdB/efnrGBhO8FsJpG5+AtmYhWgD9 +sl29+3aiRkmDznoy36z+hoeZePILEAKMcbHyXOymixOHPuaZJ95hbvq6sqd6WMAe +w5tHnxlfEuu11zatolk6WiAmTmG3sZpN5Tqloq0Ye4dvlhVKNV3Bn3E= +-----END RSA PRIVATE KEY----- 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/new b/tests/new index 6c217625621..5e8e9c75559 100755 --- a/tests/new +++ b/tests/new @@ -48,9 +48,9 @@ if (!pathinfo($filename, PATHINFO_EXTENSION)) { $path['dirname'] = trim($path['dirname'], './'); // i know arg2 is a list but it's no problem $replacement = []; -$tip = swoole_color("[Test name]: ", SWOOLE_COLOR_BLUE); +$tip = swoole_color("[Test name]", SWOOLE_COLOR_BLUE); $path['filename'] = fgetsin($tip, false);// use test name to be filename -$tip = swoole_color("[Test intro]: ", SWOOLE_COLOR_BLUE); +$tip = swoole_color("[Test intro]", SWOOLE_COLOR_BLUE); $replacement['test_intro'] = fgetsin($tip, false); $this_dir_name = explode('/', $path['dirname']); $replacement['test_name'] = end($this_dir_name); // use dir name to be test name @@ -100,14 +100,14 @@ if (file_put_contents($filename, $template)) { @shell_exec('/usr/bin/env git add ' . __DIR__ . "/$filename"); if (\stripos(PHP_OS, 'Darwin') !== false) { //MacOS - $pstorm = '/usr/local/bin/pstorm'; + $pstorm = '/usr/local/bin/phpstorm'; if (file_exists($pstorm) || ( file_exists('/Applications/PhpStorm.app') && file_put_contents($pstorm, file_get_contents(__DIR__ . '/include/macos/phpstorm.py')) && chmod($pstorm, 0744) ) ) { - @shell_exec("/usr/local/bin/phpstorm {$filename}"); + @shell_exec("{$pstorm} {$filename}"); } } } else { 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 new file mode 100644 index 00000000000..c2ba8f8b557 --- /dev/null +++ b/tests/swoole_atomic/dtor_in_child.phpt @@ -0,0 +1,25 @@ +--TEST-- +swoole_atomic: destruct objects in child processe +--SKIPIF-- + +--FILE-- +wait(); + echo "Child OK\n"; + exit(0); +}); +$p->start(); + +usleep(200000); +echo "Master OK\n"; +$atomic->wakeup(1); +$status = Swoole\Process::wait(); +?> +--EXPECT-- +Master OK +Child 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_and_wakeup.phpt b/tests/swoole_atomic/wait_and_wakeup.phpt new file mode 100644 index 00000000000..ff5f46ca5ca --- /dev/null +++ b/tests/swoole_atomic/wait_and_wakeup.phpt @@ -0,0 +1,30 @@ +--TEST-- +swoole_atomic: wait & wakeup +--SKIPIF-- + +--FILE-- +wakeup(), $atomic->get()); + +$atomic = new Swoole\Atomic(0); +var_dump($atomic->wakeup(), $atomic->get()); + +$atomic = new Swoole\Atomic(0); +var_dump($atomic->wait(1), $atomic->get()); + +$atomic = new Swoole\Atomic(1); +var_dump($atomic->wait(1), $atomic->get()); + +?> +--EXPECT-- +bool(true) +int(1) +bool(true) +int(1) +bool(false) +int(0) +bool(true) +int(0) 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 ee2a1fc2f09..f096a5a45cd 100644 --- a/tests/swoole_channel_coro/blocking_timeout.phpt +++ b/tests/swoole_channel_coro/blocking_timeout.phpt @@ -6,21 +6,22 @@ swoole_channel_coro: blocking and timeout 0]); + $chan = new chan(); -go(function () use ($chan) -{ + +go(function () use ($chan){ $data = $chan->pop(0.5); Assert::assert($data); $data = $chan->pop(0.5); Assert::false($data); }); -go(function () use ($chan) -{ +go(function () use ($chan) { sleep(1); $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/coro_wait.phpt b/tests/swoole_channel_coro/coro_wait.phpt index ceb7e67b48d..8d605be6705 100644 --- a/tests/swoole_channel_coro/coro_wait.phpt +++ b/tests/swoole_channel_coro/coro_wait.phpt @@ -54,7 +54,7 @@ $pm->childFunc = function () use ($pm) }); go(function () use ($chan) { - $cli = new Swoole\Coroutine\Http\Client('www.163.com', 80); + $cli = new Swoole\Coroutine\Http\Client('www.163.com', 443, true); $cli->set(['timeout' => 10]); $cli->setHeaders([ 'Host' => "www.163.com", diff --git a/tests/swoole_channel_coro/discard.phpt b/tests/swoole_channel_coro/discard.phpt index 5f43e4ea8f3..03c54cfbe6f 100644 --- a/tests/swoole_channel_coro/discard.phpt +++ b/tests/swoole_channel_coro/discard.phpt @@ -23,3 +23,4 @@ var_dump('scheduler end'); --EXPECTF-- string(12) "push success" string(13) "scheduler end" +[%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 new file mode 100644 index 00000000000..1404f9065da --- /dev/null +++ b/tests/swoole_channel_coro/no_ctor.phpt @@ -0,0 +1,26 @@ +--TEST-- +swoole_channel_coro: no ctor +--SKIPIF-- + +--FILE-- +pop(); + }); +}); + +Assert::contains($pm->getChildOutput(), "must call constructor first"); +?> +--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_after_close.phpt b/tests/swoole_channel_coro/pop_after_close.phpt new file mode 100644 index 00000000000..90a100ab4a1 --- /dev/null +++ b/tests/swoole_channel_coro/pop_after_close.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_channel_coro: pop after close +--SKIPIF-- + +--FILE-- +push($i)) { + echo "push ok\n"; + } + } + $chan->close(); + }); + + go(function () use ($chan) { + while (true) { + $data = $chan->pop(); + var_dump($data); + if (!$data) { + break; + } + } + }); +}); +?> +--EXPECT-- +push ok +push ok +int(1) +push ok +int(2) +int(3) +bool(false) 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 new file mode 100644 index 00000000000..9dadbaf2bf5 --- /dev/null +++ b/tests/swoole_client_coro/reconnect.phpt @@ -0,0 +1,56 @@ +--TEST-- +swoole_client_coro: reconnect 1 +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + run(function () use ($pm) { + $flag = 0; + $client = new Client(SWOOLE_SOCK_TCP); + reconnect: + if (!$client->connect('127.0.0.1', 9501)) { + /** + * if we want to reconnect server, we should call $client->close() first + */ + Assert::eq($client->errCode, SOCKET_EISCONN); + Assert::eq($client->errMsg, swoole_strerror(SOCKET_EISCONN)); + } + + $pm->kill(); + + $data = $client->recv(); + if (empty($data)) { + if ($flag === 0) { + $flag += 1; + goto reconnect; + } + } + echo "DONE\n"; + }); +}; +$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_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_bad_packet.phpt b/tests/swoole_client_coro/recv_bad_packet.phpt new file mode 100644 index 00000000000..6677c5feaa3 --- /dev/null +++ b/tests/swoole_client_coro/recv_bad_packet.phpt @@ -0,0 +1,69 @@ +--TEST-- +swoole_client_coro: recv bad packet +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + for ($i = 0; $i < MAX_CONCURRENCY_MID; $i++) { + go(function () use ($pm, $i) { + $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', $pm->getFreePort(), 0.2) == false) { + echo "ERROR\n"; + return; + } + for ($i = 0; $i < 3; $i++) { + $sid = strval(rand(100000, 999999)); + $send_data = str_repeat('A', 1000) . $sid; + $cli->send(pack('N', strlen($send_data)) . $send_data); + $data = $cli->recv(); + Assert::isEmpty($data); + } + }); + } + Swoole\Event::wait(); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $serv->set(array( + "worker_num" => 4, + '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, + )); + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { + $len = rand(1024, 8192); + $send_data = str_repeat('A', $len); + //bad packet + $serv->send($fd, pack('N', SIZE) . $send_data); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- 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 new file mode 100644 index 00000000000..32e9c739e04 --- /dev/null +++ b/tests/swoole_client_sync/bind_address.phpt @@ -0,0 +1,41 @@ +--TEST-- +swoole_client_sync: bind address +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + $client = new Client(SWOOLE_SOCK_TCP); + $client->set([ + 'bind_address' => '127.0.0.1', + 'bind_port' => $pm->getFreePort(), + ]); + + Assert::false($client->connect('127.0.0.1', 9501)); + Assert::eq($client->errCode, SOCKET_EADDRINUSE); + $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(['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) { + $response->end('OK'); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +Warning: Swoole\Client::connect(): bind address or port error in set method in %s +DONE diff --git a/tests/swoole_client_sync/connect_1.phpt b/tests/swoole_client_sync/connect_1.phpt index 99980243bdb..a2da61523e9 100644 --- a/tests/swoole_client_sync/connect_1.phpt +++ b/tests/swoole_client_sync/connect_1.phpt @@ -6,13 +6,11 @@ swoole_client_sync: connect 1 - 1 connect("11.11.11.11", 80, 0.5); -echo intval($r); +$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); ?> --EXPECT-- -0 diff --git a/tests/swoole_client_sync/connect_2.phpt b/tests/swoole_client_sync/connect_2.phpt index 3bb94cff1c6..d0ac6a5935b 100644 --- a/tests/swoole_client_sync/connect_2.phpt +++ b/tests/swoole_client_sync/connect_2.phpt @@ -6,14 +6,9 @@ swoole_client_sync: connect 1 - 2 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 new file mode 100644 index 00000000000..db424e34dc5 --- /dev/null +++ b/tests/swoole_client_sync/recv_with_open_eof_check.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_client_sync: recv witch open_eof_check and check errCode +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + $client = new \Swoole\Client(SWOOLE_SOCK_TCP); + + $client->set([ + 'open_eof_check' => true, + 'package_eof' => "\n", + 'package_max_length' => 1024 * 1024 * 2, + ]); + if (!$client->connect('127.0.0.1', $pm->getFreePort())) { + throw new Exception("connect failed"); + } + + $data = @$client->recv(1024 * 1024 * 2); + Assert::false($data); + Assert::eq(11, $client->errCode); + $client->close(); + $pm->kill(); + echo "DONE\n"; +}; +$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('receive', function (Server $serv, $fd, $reactor_id, $data) { + }); + + $serv->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE 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 new file mode 100644 index 00000000000..db1bef07743 --- /dev/null +++ b/tests/swoole_client_sync/sendto.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_client_sync: sendto +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + $cli = new Client(SWOOLE_SOCK_UDP); + + 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); + Assert::true($cli->sendto('localhost', $pm->getFreePort(), "packet-3")); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm) { + run(function () use ($pm) { + $socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_DGRAM, 0); + $socket->bind('127.0.0.1', $pm->getFreePort()); + $pm->wakeup(); + $peer = null; + $ret = $socket->recvfrom($peer); + Assert::eq($ret, 'packet-1'); + $ret = $socket->recvfrom($peer); + Assert::eq($ret, 'packet-2'); + $ret = $socket->recvfrom($peer); + Assert::eq($ret, 'packet-3'); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +Warning: Swoole\Client::sendto(): sendto to server[error_domain:%d] failed. Error: DNS Lookup resolve failed[704] in %ssendto.php on line %d +DONE 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 d03dd629bdf..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,16 +20,16 @@ $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' => dirname(__DIR__) . '/include/api/swoole_http_server/localhost-ssl/server.crt', - 'ssl_key_file' => dirname(__DIR__) . '/include/api/swoole_http_server/localhost-ssl/server.key', + 'ssl_cert_file' => SSL_FILE_DIR.'/server.crt', + 'ssl_key_file' => SSL_FILE_DIR.'/server.key', ]); $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 new file mode 100644 index 00000000000..b0be8c31922 --- /dev/null +++ b/tests/swoole_coroutine/all_asleep.phpt @@ -0,0 +1,54 @@ +--TEST-- +swoole_coroutine: all asleep +--SKIPIF-- + +--FILE-- + true, +]); + +function test1() +{ + $f = function () { + Co::yield(); + }; + $f(); +} + +function test2() +{ + Co::yield(); +} + +Co\run(function () { + go(function () { + test1(); + }); + go(function () { + test2(); + }); + Co::sleep(0.1); +}); +echo "DONE\n"; +?> +--EXPECTF-- +=================================================================== + [FATAL ERROR]: all coroutines (count: 2) are asleep - deadlock! +=================================================================== + + [Coroutine-3] +-------------------------------------------------------------------- +%A +%A + + + [Coroutine-2] +-------------------------------------------------------------------- +%A +%A +%A + +DONE diff --git a/tests/swoole_coroutine/async_callback/event_cycle.phpt b/tests/swoole_coroutine/async_callback/event_cycle.phpt new file mode 100644 index 00000000000..b54b13e2e48 --- /dev/null +++ b/tests/swoole_coroutine/async_callback/event_cycle.phpt @@ -0,0 +1,37 @@ +--TEST-- +swoole_coroutine/async_callback: event cycle +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_coroutine/async_callback/signal.phpt b/tests/swoole_coroutine/async_callback/signal.phpt new file mode 100644 index 00000000000..6a11a84775c --- /dev/null +++ b/tests/swoole_coroutine/async_callback/signal.phpt @@ -0,0 +1,23 @@ +--TEST-- +swoole_coroutine/async_callback: signal +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(10) diff --git a/tests/swoole_coroutine/async_callback/timer.phpt b/tests/swoole_coroutine/async_callback/timer.phpt new file mode 100644 index 00000000000..b542adb6294 --- /dev/null +++ b/tests/swoole_coroutine/async_callback/timer.phpt @@ -0,0 +1,29 @@ +--TEST-- +swoole_coroutine/async_callback: timer +--SKIPIF-- + +--FILE-- + +--EXPECT-- +tick +tick +tick +tick +tick 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_internal.phpt b/tests/swoole_coroutine/bailout/error_internal.phpt index b97ff4e43e7..e927aa5a366 100644 --- a/tests/swoole_coroutine/bailout/error_internal.phpt +++ b/tests/swoole_coroutine/bailout/error_internal.phpt @@ -1,7 +1,9 @@ --TEST-- swoole_coroutine/bailout: error --SKIPIF-- - + --FILE-- + --FILE-- + --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/c_stack_size.phpt b/tests/swoole_coroutine/c_stack_size.phpt index 5405b20a92a..9bdfed00826 100644 --- a/tests/swoole_coroutine/c_stack_size.phpt +++ b/tests/swoole_coroutine/c_stack_size.phpt @@ -32,7 +32,7 @@ for ($n = 100; $n--;) { } // echo "256K\n"; $info = co::stats(); -Assert::assert($info['c_stack_size'] == 256 * K); +Assert::assert($info['c_stack_size'] == 64 * K); Assert::assert($info['coroutine_num'] == 200); Assert::assert($info['coroutine_peak_num'] == 200); 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 ce842060616..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,27 +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 - }, ]; -if (class_exists(Co\Postgresql::class)) { - $map[] = function () { - (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\Http2\Client::class)) { $map[] = function () { (new Co\Http2\Client('127.0.0.1', 1234))->connect(); @@ -107,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() { @@ -133,12 +109,19 @@ foreach ($map as $i => $f) { $info = $process->read(8192); $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); - $info_list[] = $info; - if (!Assert::assert($info_list[0] === $info)) { + $_info = trim($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]); var_dump($info_list[0]); var_dump($info); 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/dead_lock.phpt b/tests/swoole_coroutine/dead_lock.phpt index 1b83e2b83a5..a7b265dcf41 100644 --- a/tests/swoole_coroutine/dead_lock.phpt +++ b/tests/swoole_coroutine/dead_lock.phpt @@ -9,7 +9,7 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Coroutine; use Swoole\Process; -$pm = new ProcessManager; +$pm = new SwooleTest\ProcessManager; $pm->parentFunc = function () use ($pm) { for ($n = 3; $n--;) { $ret = Process::wait(false); 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 725807e154a..655804d02f8 100644 --- a/tests/swoole_coroutine/gethostbyname.phpt +++ b/tests/swoole_coroutine/gethostbyname.phpt @@ -8,21 +8,27 @@ skip_if_offline(); --FILE-- null, 'www.youtube.com' => null, - 'www.facebook.com' => null + 'www.facebook.com' => null, ] : [ 'www.baidu.com' => null, 'www.taobao.com' => null, - 'www.qq.com' => null + 'www.qq.com' => null, ]; $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,8 @@ 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))); }); } @@ -62,14 +69,13 @@ 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); Assert::assert($cache_time < $no_cache_multi_time); Assert::assert($no_cache_multi_time < $no_cache_time); } - Assert::assert(filter_var(co::gethostbyname('m.cust.edu.cn'), FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)); }); ?> --EXPECTF-- 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 f4b05bf648c..926771363f5 100644 --- a/tests/swoole_coroutine/new_server.phpt +++ b/tests/swoole_coroutine/new_server.phpt @@ -6,14 +6,11 @@ swoole_coroutine: new server on('receive', function () { }); + $server = new Swoole\Server('127.0.0.1', 0, SWOOLE_PROCESS); + $server->on('receive', function () { + }); $server->start(); }); ?> --EXPECTF-- -Fatal error: Uncaught Swoole\Exception: eventLoop has already been created. unable to create Swoole\Server in %s:%d -Stack trace: -#0 %s(%d): Swoole\Server->__construct('127.0.0.1') -#1 {main} - thrown in %s on line %d +Warning: Swoole\Server::start(): eventLoop has already been created, unable to start %s in %s on line %d 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 a5c9f7aaba4..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,6 +9,8 @@ require __DIR__ . '/../include/bootstrap.php'; use Swoole\Coroutine; use Swoole\Process; +swoole_async_set(['enable_coroutine' => false]); + $pm = new ProcessManager; $pm->parentFunc = function () use ($pm) { for ($n = 3; $n--;) { @@ -25,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/getOptions.phpt b/tests/swoole_coroutine_scheduler/getOptions.phpt new file mode 100644 index 00000000000..ebbca444907 --- /dev/null +++ b/tests/swoole_coroutine_scheduler/getOptions.phpt @@ -0,0 +1,15 @@ +--TEST-- +swoole_coroutine_scheduler: getOptions +--SKIPIF-- + +--FILE-- +set(['max_coroutine' => 100, 'deadlock_check_max_stack' => 64, ]); +$options = $sch->getOptions(); +ASsert::isArray($options); +Assert::eq($options['deadlock_check_max_stack'], 64); +?> +--EXPECT-- diff --git a/tests/swoole_coroutine_scheduler/hook_flags_2.phpt b/tests/swoole_coroutine_scheduler/hook_flags_2.phpt index baea19fef69..99710d7ad2f 100644 --- a/tests/swoole_coroutine_scheduler/hook_flags_2.phpt +++ b/tests/swoole_coroutine_scheduler/hook_flags_2.phpt @@ -8,22 +8,23 @@ skip_if_offline(); set(['hook_flags' => SWOOLE_HOOK_ALL,]); $sch->add(function () { - Assert::contains(file_get_contents(URL), '新华网'); + Assert::contains(file_get_contents(URL), KEYWORDS); }); $sch->start(); -Assert::contains(file_get_contents(URL), '新华网'); +Assert::contains(file_get_contents(URL), KEYWORDS); // 2 $sch = new Swoole\Coroutine\Scheduler(); $sch->set(['hook_flags' => SWOOLE_HOOK_ALL,]); $sch->add(function () { - Assert::contains(file_get_contents(URL), '新华网'); + Assert::contains(file_get_contents(URL), KEYWORDS); }); $sch->start(); diff --git a/tests/swoole_coroutine_scheduler/preemptive/disable.phpt b/tests/swoole_coroutine_scheduler/preemptive/disable.phpt index e592ada9677..3f69a95b738 100644 --- a/tests/swoole_coroutine_scheduler/preemptive/disable.phpt +++ b/tests/swoole_coroutine_scheduler/preemptive/disable.phpt @@ -10,7 +10,11 @@ require __DIR__ . '/../../include/bootstrap.php'; $default = 10; $max_msec = 10; -co::set(['enable_preemptive_scheduler' => true]); + +co::set([ + 'enable_preemptive_scheduler' => true, + 'hook_flags' => 0, +]); $start = microtime(1); echo "start\n"; @@ -41,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 2d03445cb50..44758850e31 100644 --- a/tests/swoole_coroutine_system/waitSignal.phpt +++ b/tests/swoole_coroutine_system/waitSignal.phpt @@ -28,16 +28,17 @@ $killer = new Process(function () use ($pid, $atomic) { $killer->start(); Coroutine\run(function () use ($atomic) { + Coroutine::sleep(0.001); 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_system/writefile_append.phpt b/tests/swoole_coroutine_system/writefile_append.phpt new file mode 100644 index 00000000000..d553a677d7d --- /dev/null +++ b/tests/swoole_coroutine_system/writefile_append.phpt @@ -0,0 +1,24 @@ +--TEST-- +swoole_coroutine_system: writeFile use FILE_APPEND +--SKIPIF-- + +--FILE-- + +--EXPECT-- 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 4dc17a06c41..5e45518ef62 100644 --- a/tests/swoole_coroutine_util/fgets.phpt +++ b/tests/swoole_coroutine_util/fgets.phpt @@ -6,31 +6,24 @@ swoole_coroutine_util: fgets 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_curl/close_before_resume.phpt b/tests/swoole_curl/close_before_resume.phpt new file mode 100644 index 00000000000..73e670c2485 --- /dev/null +++ b/tests/swoole_curl/close_before_resume.phpt @@ -0,0 +1,78 @@ +--TEST-- +swoole_curl: close before resume +--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); + curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $strHeader) { + return strlen($strHeader); + }); + + go(function() use ($ch) { + Co::sleep(0.1); + $res = curl_close($ch); + echo "close [1]\n"; + Assert::true($res); + }); + + $output = curl_exec($ch); + var_dump($output); + + Assert::eq($output, "Hello World\n".$code); + if ($output === false) { + echo "CURL Error:" . curl_error($ch); + } + curl_close($ch); + echo "close [2]\n"; + }); + 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(); +?> +--EXPECTF-- +Fatal error: Uncaught Swoole\Error: cURL is executing, cannot be operated in %s:%d +Stack trace: +#0 %s(%d): curl_close(%s) +%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_curl/exec_twice.phpt b/tests/swoole_curl/exec_twice.phpt new file mode 100644 index 00000000000..012b1957506 --- /dev/null +++ b/tests/swoole_curl/exec_twice.phpt @@ -0,0 +1,79 @@ +--TEST-- +swoole_curl: exec twice +--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); + curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $strHeader) { + return strlen($strHeader); + }); + + go(function() use ($ch) { + Co::sleep(0.1); + 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); + } + curl_close($ch); + echo "close [2]\n"; + }); + 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(); +?> +--EXPECTF-- +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) +%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_curl/https.phpt b/tests/swoole_curl/https.phpt new file mode 100644 index 00000000000..6b6cb605122 --- /dev/null +++ b/tests/swoole_curl/https.phpt @@ -0,0 +1,54 @@ +--TEST-- +swoole_curl: https +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done diff --git a/tests/swoole_curl/multi/bug4393.phpt b/tests/swoole_curl/multi/bug4393.phpt new file mode 100644 index 00000000000..6ea973d2e47 --- /dev/null +++ b/tests/swoole_curl/multi/bug4393.phpt @@ -0,0 +1,61 @@ +--TEST-- +swoole_curl/multi: guzzle +--SKIPIF-- + +--FILE-- + $guzzle->getAsync('https://www.qq.com/'), + 'baidu' => $guzzle->getAsync('http://www.baidu.com/'), + ]; + } else { + $promises = [ + 'httpbin' => $guzzle->getAsync('https://www.httpbin.org/'), + 'nghttp2' => $guzzle->getAsync('https://nghttp2.org/'), + ]; + } + + $responses = []; + foreach (Promise\Utils::settle($promises)->wait() as $k => $v) { + $responses[$k] = $v['value']; + } + + if (IS_IN_CI) { + Assert::contains($responses['baidu']->getBody(), '百度'); + Assert::contains($responses['qq']->getBody(), '腾讯'); + } else { + Assert::contains($responses['httpbin']->getBody(), 'httpbin'); + Assert::contains($responses['nghttp2']->getBody(), 'nghttp2'); + } + }; + + $n = 2; + while ($n--) { + $s = microtime(true); + $test(); + Assert::lessThan(microtime(true) - $s, 3.0); + } + + echo 'Done' . PHP_EOL; +}); +?> +--EXPECT-- +Done diff --git a/tests/swoole_curl/multi/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 f5dbe3cfcc9..9fe59067db6 100644 --- a/tests/swoole_event/add_after_server_start.phpt +++ b/tests/swoole_event/add_after_server_start.phpt @@ -10,27 +10,28 @@ 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) { $pm->kill(); - Assert::contains(file_get_contents(FILE), 'HTTP/1.1 302 Moved Temporarily'); + $str = swoole_string(file_get_contents(FILE)); + Assert::true($str->contains('HTTP/1.1 302 Moved Temporarily') or $str->contains('HTTP/1.1 301 Moved Permanently')); }; $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 new file mode 100644 index 00000000000..c2e2e4ea76d --- /dev/null +++ b/tests/swoole_event/del_after_close.phpt @@ -0,0 +1,46 @@ +--TEST-- +swoole_event: del after close +--SKIPIF-- + +--FILE-- + SWOOLE_LOG_ERROR]); + +$cli = new Swoole\Client(SWOOLE_SOCK_TCP); + +$cli->connect("www.qq.com", 80); + +$fd = $cli->sock; + +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"); + +$cli->close(); + +if (Event::isset($fd)) { + if (!Event::del($fd)) { + echo "Unable to release fd {$fd} from EventLoop\n"; + } else { + echo "FD {$fd} released from EventLoop\n"; + } +} + +$eventNum = Swoole\Coroutine::stats()['event_num']; +var_dump($eventNum); + +Event::wait(); +?> +--EXPECTF-- +FD %d released from EventLoop +int(0) 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 new file mode 100644 index 00000000000..c93adab3008 --- /dev/null +++ b/tests/swoole_event/rshutdown.phpt @@ -0,0 +1,24 @@ +--TEST-- +swoole_event: read stdin +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Deprecated: Swoole\Event::rshutdown(): Event::wait() in shutdown function is deprecated in Unknown on line 0 +string(0) "" 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/full_duplex_by_server.phpt b/tests/swoole_feature/cross_close/full_duplex_by_server.phpt index 498bc063d80..46acdca72ad 100644 --- a/tests/swoole_feature/cross_close/full_duplex_by_server.phpt +++ b/tests/swoole_feature/cross_close/full_duplex_by_server.phpt @@ -21,8 +21,8 @@ $pm->parentFunc = function () use ($pm) { }); go(function () use ($cli) { echo "RECV\n"; - Assert::assert(!$cli->recv(-1)); - Assert::same($cli->errCode, SOCKET_ECONNRESET); + Assert::eq($cli->recv(-1), ""); + // Assert::same($cli->errCode, SOCKET_ECONNRESET); echo "RECV CLOSED\n"; }); $pm->wakeup(); 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 3b186e3cea5..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,43 +5,52 @@ 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::assert(!$errno); + Assert::true(!$errno); go(function () use ($pm, $cli) { Co::sleep(0.001); echo "CLOSE\n"; - Assert::assert(fclose($cli)); + Assert::eq(get_resource_type($cli), 'stream'); + Assert::true(fclose($cli)); // double close - Assert::assert(!@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::assert(fwrite($cli, str_repeat('S', $size)) < $size); - Assert::assert(!@fclose($cli)); + Assert::eq(get_resource_type($cli), 'stream'); + Assert::true(@fwrite($cli, str_repeat('S', $size)) < $size); + Assert::eq(get_resource_type($cli), 'Unknown'); + // Assert::true(!@fclose($cli)); echo "SEND CLOSED\n"; }); go(function () use ($cli) { echo "RECV\n"; - Assert::assert(empty(fread($cli, 8192))); - Assert::assert(!@fclose($cli)); + Assert::eq(get_resource_type($cli), 'stream'); + Assert::true(empty(fread($cli, 8192))); + 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::assert($server->bind('127.0.0.1', $pm->getFreePort())); - Assert::assert($server->listen()); + Assert::true($server->bind('127.0.0.1', $pm->getFreePort())); + Assert::true($server->listen()); go(function () use ($pm, $server) { - if (Assert::assert(($conn = $server->accept()) && $conn instanceof Co\Socket)) { + if (Assert::true(($conn = $server->accept()) && $conn instanceof Co\Socket)) { switch_process(); co::sleep(5); $conn->close(); diff --git a/tests/swoole_feature/cross_close/php_stream_full_duplex_by_server.phpt b/tests/swoole_feature/cross_close/php_stream_full_duplex_by_server.phpt index afef05dfef8..f47e131fb8c 100644 --- a/tests/swoole_feature/cross_close/php_stream_full_duplex_by_server.phpt +++ b/tests/swoole_feature/cross_close/php_stream_full_duplex_by_server.phpt @@ -10,16 +10,16 @@ $pm = new ProcessManager(); $pm->parentFunc = function () use ($pm) { go(function () use ($pm) { $cli = stream_socket_client("tcp://127.0.0.1:{$pm->getFreePort()}", $errno, $errstr, 1); - Assert::assert(!$errno); + Assert::true(!$errno); go(function () use ($cli) { echo "SEND\n"; $size = 64 * 1024 * 1024; - Assert::assert(fwrite($cli, str_repeat('S', $size)) < $size); + Assert::true(@fwrite($cli, str_repeat('S', $size)) < $size); echo "SEND CLOSED\n"; }); go(function () use ($cli) { echo "RECV\n"; - Assert::assert(!fread($cli, 8192)); + Assert::true(!fread($cli, 8192)); echo "RECV CLOSED\n"; }); $pm->wakeup(); @@ -30,10 +30,10 @@ $pm->parentFunc = function () use ($pm) { $pm->childFunc = function () use ($pm) { go(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()); + Assert::true($server->bind('127.0.0.1', $pm->getFreePort())); + Assert::true($server->listen()); go(function () use ($pm, $server) { - if (Assert::assert(($conn = $server->accept()) && $conn instanceof Co\Socket)) { + if (Assert::true(($conn = $server->accept()) && $conn instanceof Co\Socket)) { $pm->wait(); echo "CLOSE\n"; $conn->close(); 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 8a70500e4cf..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"; }; @@ -89,8 +89,8 @@ $pm->childFunc = function () use ($pm) { $server = new Co\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); $server->setProtocol([ 'open_ssl' => true, - 'ssl_cert_file' => __DIR__ . '/../../include/api/swoole_http_server/localhost-ssl/client.crt', - 'ssl_key_file' => __DIR__ . '/../../include/api/swoole_http_server/localhost-ssl/client.key' + 'ssl_cert_file' => SSL_FILE_DIR.'/client.crt', + 'ssl_key_file' => SSL_FILE_DIR.'/client.key' ]); Assert::assert($server->bind('127.0.0.1', $pm->getFreePort())); Assert::assert($server->listen(MAX_CONCURRENCY)); diff --git a/tests/swoole_function/substr_json_decode.phpt b/tests/swoole_function/substr_json_decode.phpt new file mode 100644 index 00000000000..0436951fbbb --- /dev/null +++ b/tests/swoole_function/substr_json_decode.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_function: substr_json_decode +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_function/substr_unserialize.phpt b/tests/swoole_function/substr_unserialize.phpt new file mode 100644 index 00000000000..efdd0324776 --- /dev/null +++ b/tests/swoole_function/substr_unserialize.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_function: ext_unserialize +--SKIPIF-- + +--FILE-- + +--EXPECT-- 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 new file mode 100644 index 00000000000..532b58877a2 --- /dev/null +++ b/tests/swoole_http2_client_coro/host.phpt @@ -0,0 +1,67 @@ +--TEST-- +swoole_http2_client_coro: host +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + Co\run(function () use ($pm) { + $domain = '127.0.0.1'; + $cli = new Swoole\Coroutine\Http2\Client($domain, $pm->getFreePort(), true); + $cli->set(['timeout' => 5]); + $cli->connect(); + $request = new Swoole\Http2\Request; + for ($n = MAX_REQUESTS; $n--;) { + $request->path = '/'; + $request->headers = [ + 'user-agent' => 'Chrome/49.0.2587.3', + 'accept' => 'text/html,application/xhtml+xml,application/xml', + 'connection' => 'keep-alive' + ]; + for ($i = 32; $i--;) { + $request->headers[md5(mt_rand(1, 65535))] = sha1(get_safe_random(32)); + } + Assert::assert($cli->send($request)); + $response = $cli->recv(); + Assert::same($response->statusCode, 200); + Assert::same(json_encode($request->headers), $response->data); + unset( + $response->headers['host'], + $response->headers['server'], + $response->headers['date'], + $response->headers['content-type'], + $response->headers['content-length'] + ); + Assert::same($request->headers, $response->headers); + } + }); + $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->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' + ]); + $http->on("WorkerStart", function () use ($pm) { $pm->wakeup(); }); + $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) { + $response->header($name, $value); + } + $response->end(json_encode($request->header)); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- diff --git a/tests/swoole_http2_client_coro/http_proxy.phpt b/tests/swoole_http2_client_coro/http_proxy.phpt new file mode 100644 index 00000000000..336cdcf2935 --- /dev/null +++ b/tests/swoole_http2_client_coro/http_proxy.phpt @@ -0,0 +1,37 @@ +--TEST-- +swoole_http2_client_coro: get with http_proxy +--SKIPIF-- + +--FILE-- +set([ + 'timeout' => 10, + 'ssl_host_name' => $domain, + 'http_proxy_host' => HTTP_PROXY_HOST, + 'http_proxy_port' => HTTP_PROXY_PORT, + ]); + Assert::true($c->connect(), var_dump_return($c)); + $r = new Request(); + $r->method = 'GET'; + $r->path = '/'; + $r->headers = [ + 'host' => $domain, + ]; + $c->send($r); + $response = $c->recv(); + Assert::notEmpty($response); + Assert::eq($response->statusCode, 301); + Assert::eq($response->headers['location'], 'https://www.cloudflare.com/'); +}); +?> +--EXPECT-- 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 37ac7193254..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::same(json_decode($response->data)->error->code, 602); + Assert::true(in_array(json_decode($response->data)->error->code, [602, 10002, 100], true)); }); ?> --EXPECT-- diff --git a/tests/swoole_http2_client_coro/sock_type_unix.phpt b/tests/swoole_http2_client_coro/sock_type_unix.phpt new file mode 100644 index 00000000000..98893450e4b --- /dev/null +++ b/tests/swoole_http2_client_coro/sock_type_unix.phpt @@ -0,0 +1,67 @@ +--TEST-- +swoole_http2_client_coro: sock type unix +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + go(function () use ($pm) { + $client = new Client('unix:/' . UNIXSOCK_PATH, 0, false); + Assert::eq($client->connect(), true); + $req = new Request(); + $req->method = 'POST'; + $req->path = '/'; + $client->send($req); + $result = $client->recv(); + Assert::eq($result->data, "OK"); + + $client = new Client('unix://' . UNIXSOCK_PATH, 0, false); + Assert::eq($client->connect(), true); + $req = new Request(); + $req->method = 'POST'; + $req->path = '/'; + $client->send($req); + $result = $client->recv(); + Assert::eq($result->data, "OK"); + + $client = new Client('unix:///' . UNIXSOCK_PATH, 0, false); + Assert::eq($client->connect(), true); + $req = new Request(); + $req->method = 'POST'; + $req->path = '/'; + $client->send($req); + $result = $client->recv(); + Assert::eq($result->data, "OK"); + }); + Swoole\Event::wait(); + @unlink(UNIXSOCK_PATH); + $pm->kill(); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm) { + $server = new Server(UNIXSOCK_PATH, 0, SWOOLE_BASE, SWOOLE_SOCK_UNIX_STREAM); + $server->set([ + 'worker_num' => 1, + 'open_http2_protocol' => true + ]); + $server->on("workerStart", function ($server) use ($pm) { + $pm->wakeup(); + }); + $server->on('request', function (\Swoole\Http\Request $request, Swoole\Http\Response $response) { + $response->end('OK'); + }); + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE 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 new file mode 100644 index 00000000000..8bd19ec755a --- /dev/null +++ b/tests/swoole_http2_server/goaway.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_http2_server: goaway +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + 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); + $cli->recv(); + Assert::same($cli->serverLastStreamId, $streamId); + Assert::same($cli->errCode, SWOOLE_HTTP2_ERROR_NO_ERROR); + Assert::same($cli->errMsg, 'NO_ERROR'); + $pm->kill(); + }); + Swoole\Event::wait(); +}; +$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->goaway(SWOOLE_HTTP2_ERROR_NO_ERROR, 'NO_ERROR'); + $response->end($request->rawcontent()); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- 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 d1a34838698..0f9775dc654 100644 --- a/tests/swoole_http2_server/ping.phpt +++ b/tests/swoole_http2_server/ping.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_http2_server: big data +swoole_http2_server: ping --SKIPIF-- --FILE-- @@ -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/bind_address.phpt b/tests/swoole_http_client_coro/bind_address.phpt new file mode 100644 index 00000000000..02dd3826283 --- /dev/null +++ b/tests/swoole_http_client_coro/bind_address.phpt @@ -0,0 +1,145 @@ +--TEST-- +swoole_http_client_coro: bind address and port +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + run(function () use ($pm) { + $wg = new WaitGroup(); + + $wg->add(5); + //test1: valid address + go(function () use ($pm, $wg) { + $client = new Client('127.0.0.1', $pm->getFreePort()); + $bindAddress = current(swoole_get_local_ip()); + $bindPort = get_one_free_port(); + + $client->set([ + 'bind_address' => $bindAddress, + 'bind_port' => $bindPort, + ]); + $client->post('/validaddress', ['bind_address' => $bindAddress, 'bind_port' => $bindPort]); + $wg->done(); + }); + + // test2: invalid address + go(function () use ($pm, $wg) { + $client = new Client('127.0.0.1', $pm->getFreePort()); + $bindAddress = 11111; + $bindPort = get_one_free_port(); + + $client->set([ + 'bind_address' => $bindAddress, + 'bind_port' => $bindPort, + ]); + $client->post('/invalidaddress', ['bind_address' => $bindAddress, 'bind_port' => $bindPort]); + $wg->done(); + }); + + // test3: invalid port + go(function () use ($pm, $wg) { + $client = new Client('127.0.0.1', $pm->getFreePort()); + $bindAddress = current(swoole_get_local_ip()); + $bindPort = -1; + + $client->set([ + 'bind_address' => $bindAddress, + 'bind_port' => -1, + ]); + $client->post('/invalidport', ['bind_address' => $bindAddress, 'bind_port' => $bindPort]); + $wg->done(); + }); + + // test4: not bind port + go(function () use ($pm, $wg) { + $client = new Client('127.0.0.1', $pm->getFreePort()); + $bindAddress = current(swoole_get_local_ip()); + $bindPort = null; + + $client->set([ + 'bind_address' => $bindAddress, + ]); + $client->post('/notbindport', ['bind_address' => $bindAddress, 'bind_port' => $bindPort]); + $wg->done(); + }); + + //test5: request baidu.com + go(function () use ($pm, $wg) { + $client = new Client('www.baidu.com', 80); + $bindAddress = current(swoole_get_local_ip()); + $bindPort = get_one_free_port(); + + $client->set([ + 'bind_address' => $bindAddress, + 'bind_port' => $bindPort, + ]); + Assert::true($client->get('/')); + + $client = new Client('www.baidu.com', 80); + $bindAddress = '127.0.0.1'; + $bindPort = get_one_free_port(); + + $client->set([ + 'bind_address' => $bindAddress, + 'bind_port' => $bindPort, + ]); + Assert::false($client->get('/')); + $wg->done(); + }); + + $wg->wait(); + + $client = new Client('127.0.0.1', $pm->getFreePort()); + $client->get('/stop?hello=1'); + echo $client->body . PHP_EOL; + echo "DONE\n"; + }); +}; +$pm->childFunc = function () use ($pm) { + run(function () use ($pm) { + $server = new Server('127.0.0.1', $pm->getFreePort()); + $server->handle('/validaddress', function (Request $request, Response $response) { + Assert::eq($request->server['remote_addr'], $request->post['bind_address']); + Assert::eq($request->server['remote_port'], $request->post['bind_port']); + }); + $server->handle('/invalidaddress', function (Request $request, Response $response) { + Assert::eq($request->post['bind_address'], '11111'); + Assert::eq($request->server['remote_addr'], '127.0.0.1'); + Assert::eq($request->server['remote_port'], $request->post['bind_port']); + }); + $server->handle('/invalidport', function (Request $request, Response $response) { + Assert::eq($request->post['bind_port'], '-1'); + Assert::eq($request->server['remote_addr'], $request->post['bind_address']); + Assert::greaterThan($request->server['remote_port'], 0); + }); + $server->handle('/notbindport', function (Request $request, Response $response) { + Assert::keyNotExists($request->post, 'bind_port'); + Assert::eq($request->server['remote_addr'], $request->post['bind_address']); + Assert::greaterThan($request->server['remote_port'], 0); + }); + $server->handle('/stop', function ($request, $response) use ($server) { + $response->end("

Stop

"); + $server->shutdown(); + }); + $pm->wakeup(); + $server->start(); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +

Stop

+DONE 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_failed.phpt b/tests/swoole_http_client_coro/download_failed.phpt new file mode 100644 index 00000000000..bf55a6af1f9 --- /dev/null +++ b/tests/swoole_http_client_coro/download_failed.phpt @@ -0,0 +1,56 @@ +--TEST-- +swoole_http_client_coro: http download io failure +--SKIPIF-- + +--FILE-- +bind('127.0.0.1', 0)); + Assert::assert($server->listen()); + $oort = $server->getsockname()['port']; + go(function () use ($server) { + $client = $server->accept(); + while ($client->recv(1)) { + CO::sleep(0.01); + } + $server->close(); + }); + $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $oort); + $cli->set(['timeout' => 0.1]); + Assert::false($cli->download('/get', FILE)); + Assert::false(file_exists(FILE)); + echo "OK\n"; +}); +Co\run(function () { + $server = new Co\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + Assert::assert($server->bind('127.0.0.1', 0)); + Assert::assert($server->listen()); + $oort = $server->getsockname()['port']; + go(function () use ($server) { + $client = $server->accept(); + $client->send("HTTP/1.1 200 OK\r\nContent-Length: 99999\r\n\r\n"); + while ($client->send('a')) { + CO::sleep(0.001); + } + $server->close(); + }); + $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $oort); + $cli->set(['timeout' => 0.1]); + Assert::false($cli->download('/get', FILE)); + Assert::true(file_exists(FILE)); + echo "OK\n"; +}); +@unlink(FILE); + +?> +--EXPECT-- +OK +OK diff --git a/tests/swoole_http_client_coro/download_filename_bug.phpt b/tests/swoole_http_client_coro/download_filename_bug.phpt new file mode 100644 index 00000000000..7bf17f36ab0 --- /dev/null +++ b/tests/swoole_http_client_coro/download_filename_bug.phpt @@ -0,0 +1,74 @@ +--TEST-- +swoole_http_client_coro: The bug of the filename parameter of download() +--SKIPIF-- + +--FILE-- +f = $f; + } + + public function withSavedFileName($savedFileName) + { + $self = clone $this; + $self->savedFileName = $savedFileName; + return $self; + } + + public function getSavedFileName() + { + return $this->savedFileName; + } + +} + +function download($pm, $fileName) +{ + $basename = substr($fileName, 0, -2); + $fileName = $basename . '.jpg'; + $c1 = new C1($fileName); + $c1 = $c1->withSavedFileName($fileName); + + $client = new \Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + $client->set(['timeout' => 5]); + + $client->download('/', $fileName); +} + +$pm = new SwooleTest\ProcessManager; +$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.*'); + }); + + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $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) { + $response->sendfile(TEST_IMAGE); + }); + $serv->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- 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 new file mode 100644 index 00000000000..2b2327de638 --- /dev/null +++ b/tests/swoole_http_client_coro/get_header_out_after_close.phpt @@ -0,0 +1,23 @@ +--TEST-- +swoole_http_client_coro: getHeaderOut after close +--SKIPIF-- + +--FILE-- +set([ + 'timeout' => -1, + 'keep_alive' => false, + ]); + $http->execute('/get'); + swoole_string($http->getHeaderOut())->contains('httpbin.org'); +}); +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 new file mode 100644 index 00000000000..7273cb7aca9 --- /dev/null +++ b/tests/swoole_http_client_coro/http_upload_big.phpt @@ -0,0 +1,60 @@ +--TEST-- +swoole_http_client_coro: upload a big file +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + run(function () use ($pm) { + $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + $content = str_repeat(get_safe_random(1024), 5 * 1024); + file_put_contents('/tmp/test.jpg', $content); + $cli->addFile('/tmp/test.jpg', 'test.jpg'); + $cli->setHeaders([ + 'md5' => md5($content), + ]); + $ret = $cli->post('/', ['name' => 'rango']); + Assert::assert($ret); + Assert::assert(count($cli->headers) > 0); + Assert::same($cli->statusCode, 200); + Assert::eq($cli->body, 'success'); + $cli->close(); + @unlink('/tmp/test.jpg'); + 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', + 'package_max_length' => 10 * 1024 * 1024, + ]); + + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + Assert::eq(md5_file($request->files['test_jpg']['tmp_name']) ,$request->header['md5']); + $response->end('success'); + }); + + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE 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/https_upload_big.phpt b/tests/swoole_http_client_coro/https_upload_big.phpt new file mode 100644 index 00000000000..c510fa7617a --- /dev/null +++ b/tests/swoole_http_client_coro/https_upload_big.phpt @@ -0,0 +1,63 @@ +--TEST-- +swoole_http_client_coro: upload a big file +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + run(function () use ($pm) { + $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort(), true); + $content = str_repeat(get_safe_random(1024), 5 * 1024); + file_put_contents('/tmp/test.jpg', $content); + $cli->addFile('/tmp/test.jpg', 'test.jpg'); + $cli->setHeaders([ + 'md5' => md5($content), + ]); + $ret = $cli->post('/', ['name' => 'rango']); + Assert::assert($ret); + Assert::same($cli->statusCode, 200); + Assert::eq($cli->body, 'success'); + $cli->close(); + @unlink('/tmp/test.jpg'); + 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 | SWOOLE_SSL); + + $http->set([ + 'log_file' => '/dev/null', + 'package_max_length' => 10 * 1024 * 1024, + '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) { + Assert::eq(md5_file($request->files['test_jpg']['tmp_name']) ,$request->header['md5']); + $response->end('success'); + }); + + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE 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 new file mode 100644 index 00000000000..1fb41cbd630 --- /dev/null +++ b/tests/swoole_http_client_coro/lowercase_header.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_http_client_coro: lowercase header +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + go(function () use ($pm) { + + $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort(), false); + $cli->set(['timeout' => 10, + 'lowercase_header' => false, + ]); + Assert::assert($cli->get('/')); + Assert::assert(array_key_exists('Hello-world', $cli->getHeaders())); + Assert::assert(!array_key_exists('hello-world', $cli->getHeaders())); + + + $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort(), false); + Assert::assert($cli->get('/')); + Assert::assert(!array_key_exists('Hello-world', $cli->getHeaders())); + Assert::assert(array_key_exists('hello-world', $cli->getHeaders())); + + $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' + ]); + $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) { + $response->header('Hello-world', 'swoole', false); + $response->end(); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_http_client_coro/multi.phpt b/tests/swoole_http_client_coro/multi.phpt index 04835e626c7..45c1683bd72 100644 --- a/tests/swoole_http_client_coro/multi.phpt +++ b/tests/swoole_http_client_coro/multi.phpt @@ -31,9 +31,10 @@ go(function () { $ret1 = ($cli1->get('/')); $ret2 = ($cli2->get('/')); - if (!$ret1 or !$ret2) - { + if (!$ret1 or !$ret2) { echo "ERROR\n"; + var_dump($cli1->errCode, $cli1->errMsg); + var_dump($cli2->errCode, $cli2->errMsg); } else { 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 new file mode 100644 index 00000000000..a92dfeccb62 --- /dev/null +++ b/tests/swoole_http_client_coro/slow_server.phpt @@ -0,0 +1,70 @@ +--TEST-- +swoole_http_client_coro: slow server +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + co::create(function () use ($pm) { + $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + $cli->set([ + 'timeout' => 10 + ]); + $cli->setHeaders([ + 'Connection' => 'close', + 'Accept' => '*/*' + ]); + $ret = $cli->get('/'); + Assert::true($ret); + Assert::same($cli->statusCode, 200); + Assert::assert(strlen($cli->body) > 1024 * 5); + + Assert::same($cli->headers['server'], 'nginx'); + Assert::same($cli->headers['x-server'], 'swoole'); + Assert::same($cli->headers['content-type'], 'text/html'); + Assert::eq($cli->headers['content-length'], strlen($cli->body) ); + + $pm->kill(); + echo "OK\n"; + }); + Swoole\Event::wait(); +}; + +$pm->childFunc = function () use ($pm) +{ + $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) + { + /** + * @var $pm ProcessManager + */ + global $pm; + $pm->wakeup(); + }); + $serv->on('receive', function ($serv, $fd, $threadId, $data) + { + $html = base64_encode(random_bytes(rand(1024, 65536))); + $len = strlen($html); + $data = "HTTP/1.1 200 OK\r\nServer: nginx\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: $len\r\nX-Server: swoole\r\n\r\n$html"; + $chunks = str_split($data, 5); + foreach ($chunks as $out) { + $serv->send($fd, $out); + usleep(100); + } + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +OK diff --git a/tests/swoole_http_client_coro/socks5_proxy.phpt b/tests/swoole_http_client_coro/socks5_proxy.phpt index 8ecec35ec9c..790a57d1ae7 100644 --- a/tests/swoole_http_client_coro/socks5_proxy.phpt +++ b/tests/swoole_http_client_coro/socks5_proxy.phpt @@ -19,7 +19,7 @@ go(function () $cli->set([ 'timeout' => 5, 'socks5_host' => SOCKS5_PROXY_HOST, - 'socks5_port' => SOCKS5_PROXY_PORT + 'socks5_port' => SOCKS5_PROXY_PORT, ]); $ret = $cli->get('/'); @@ -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 0036a3c348f..5b873b92bf7 100644 --- a/tests/swoole_http_client_coro/unixsocket.phpt +++ b/tests/swoole_http_client_coro/unixsocket.phpt @@ -14,18 +14,21 @@ $pm->parentFunc = function () use ($pm) { go(function () use ($pm) { $client = new Swoole\Coroutine\Http\Client('unix:' . str_repeat('/', mt_rand(0, 2)) . UNIXSOCK_PATH); for ($n = MAX_REQUESTS; $n--;) { - Assert::assert($client->get('/')); + Assert::assert($client->get('/'), "statusCode={$client->statusCode}, error={$client->errCode}"); Assert::same($client->body, 'Hello Swoole!'); } }); } - swoole_event_wait(); + Swoole\Event::wait(); echo "SUCCESS\n"; $pm->kill(); }; $pm->childFunc = function () use ($pm) { $server = new Swoole\Http\Server(UNIXSOCK_PATH, 0, SERVER_MODE_RANDOM, SWOOLE_UNIX_STREAM); $server->set(['log_file' => '/dev/null']); + $server->on(\Swoole\Constant::EVENT_START, function () use ($pm) { + $pm->wakeup(); + }); $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->end('Hello Swoole!'); }); 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_big.phpt b/tests/swoole_http_client_coro/upload_big.phpt deleted file mode 100644 index bdbf134f4a1..00000000000 --- a/tests/swoole_http_client_coro/upload_big.phpt +++ /dev/null @@ -1,27 +0,0 @@ ---TEST-- -swoole_http_client_coro: upload a big file ---SKIPIF-- - ---FILE-- -set(['timeout' => 30]); - $content = str_repeat(get_safe_random(1024), 5 * 1024); - file_put_contents('/tmp/test.jpg', $content); - $cli->addFile('/tmp/test.jpg', 'test.jpg'); - $ret = $cli->post('/', ['name' => 'rango']); - Assert::assert($ret); - Assert::assert(count($cli->headers) > 0); - Assert::same($cli->statusCode, 200); - Assert::assert(strpos($cli->body, IS_IN_TRAVIS ? 'MIT News' : 'cust.edu.cn') !== false); - $cli->close(); - @unlink('/tmp/test.jpg'); - echo "DONE\n"; -}); -?> ---EXPECT-- -DONE 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.phpt b/tests/swoole_http_client_coro/websocket.phpt deleted file mode 100644 index b23e2fb5a68..00000000000 --- a/tests/swoole_http_client_coro/websocket.phpt +++ /dev/null @@ -1,76 +0,0 @@ ---TEST-- -swoole_http_client_coro: websocket client & server ---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; - } - echo $cli->recv()->data; - for ($i = 0; $i < 5; $i++) - { - $cli->push('hello server'); - echo ($cli->recv())->data; - co::sleep(0.1); - } - }); - 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) { - /** - * @var $pm ProcessManager - */ - global $pm; - $pm->wakeup(); - }); - - $ws->on('open', function ($serv, swoole_http_request $request) { - $ip = co::gethostbyname('www.baidu.com'); - if ($ip) - { - $serv->push($request->fd, "start\n"); - } - }); - - $ws->on('message', function ($serv, $frame) { - co::sleep(0.1); - $serv->push($frame->fd, "hello client\n"); - }); - - $ws->start(); -}; - -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -start -hello client -hello client -hello client -hello client -hello client diff --git a/tests/swoole_http_client_coro/websocket/1.phpt b/tests/swoole_http_client_coro/websocket/1.phpt new file mode 100644 index 00000000000..2c8f8701c00 --- /dev/null +++ b/tests/swoole_http_client_coro/websocket/1.phpt @@ -0,0 +1,76 @@ +--TEST-- +swoole_http_client_coro/websocket: client & server +--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; + } + echo $cli->recv()->data; + for ($i = 0; $i < 5; $i++) + { + $cli->push('hello server'); + echo ($cli->recv())->data; + co::sleep(0.1); + } + }); + 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) { + /** + * @var $pm ProcessManager + */ + global $pm; + $pm->wakeup(); + }); + + $ws->on('open', function ($serv, Swoole\Http\Request $request) { + $ip = co::gethostbyname('www.baidu.com'); + if ($ip) + { + $serv->push($request->fd, "start\n"); + } + }); + + $ws->on('message', function ($serv, $frame) { + co::sleep(0.1); + $serv->push($frame->fd, "hello client\n"); + }); + + $ws->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +start +hello client +hello client +hello client +hello client +hello client diff --git a/tests/swoole_http_client_coro/websocket/bug_01.phpt b/tests/swoole_http_client_coro/websocket/bug_01.phpt new file mode 100644 index 00000000000..07216de64ba --- /dev/null +++ b/tests/swoole_http_client_coro/websocket/bug_01.phpt @@ -0,0 +1,55 @@ +--TEST-- +swoole_http_client_coro/websocket: handshake + frame +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + Co\run(function () use ($pm) { + $cli = new Co\http\Client('127.0.0.1', $pm->getFreePort()); + $ret = $cli->upgrade('/'); + if (!$ret) { + echo "ERROR\n"; + return; + } + echo "CONNECTED\n"; + echo $cli->recv()->data; + }); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) +{ + $ws = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $ws->set(array( + 'log_file' => '/dev/null' + )); + $ws->on('WorkerStart', function (Server $serv) { + global $pm; + $pm->wakeup(); + }); + + $ws->on('receive', function ($serv, $fd, $threadId, $data) { + $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 .= WebSockerServer::pack("hello world\n"); + $serv->send($fd, $sendData); + }); + + $ws->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +CONNECTED +hello world diff --git a/tests/swoole_http_client_coro/websocket/bug_02.phpt b/tests/swoole_http_client_coro/websocket/bug_02.phpt new file mode 100644 index 00000000000..da334153058 --- /dev/null +++ b/tests/swoole_http_client_coro/websocket/bug_02.phpt @@ -0,0 +1,63 @@ +--TEST-- +swoole_http_client_coro/websocket: bug use client in server +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + go(function () use ($pm) { + $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + $cli->set(['timeout' => -1]); + $ret = $cli->upgrade('/'); + Assert::assert($ret); + echo $cli->recv()->data; + for ($i = 0; $i < 5; $i++) { + $cli->push("hello server\n", SWOOLE_WEBSOCKET_OPCODE_TEXT, true); + echo ($cli->recv(1))->data; + co::sleep(0.1); + } + $cli->close(); + }); + Swoole\Event::wait(); + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $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) { + $pm->wakeup(); + }); + $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) { + 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) { + echo "client-{$fd} is closed\n"; + }); + $ws->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +server: hello, welcome +client: hello server +server-reply: hello client +client: hello server +server-reply: hello client +client: hello server +server-reply: hello client +client: hello server +server-reply: hello client +client: hello server +server-reply: hello client +client-1 is closed 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 new file mode 100644 index 00000000000..4b404dab1d5 --- /dev/null +++ b/tests/swoole_http_client_coro/websocket/server_push_first.phpt @@ -0,0 +1,60 @@ +--TEST-- +swoole_http_client_coro/websocket: websocket server push first +--SKIPIF-- + +--FILE-- +initFreePorts(); +$pm->initRandomDataArray(2, 0, true); + +Co\run(function () use ($pm) { + $server = new Co\Http\Server("127.0.0.1", $pm->getFreePort(), false); + go(function () use ($pm, $server) { + $server->handle( + '/websocket', + function ($request, $ws) use ($pm) { + $ws->upgrade(); + $ws->push($pm->getRandomDataElement(0)); + $ws->push($pm->getRandomDataElement(1)); + } + ); + $server->handle( + '/shutdown', + function ($request, $response) use ($server) { + echo "shutdown\n"; + $response->status(200); + $server->shutdown(); + } + ); + $server->start(); + }); + + go(function () use ($pm, $server) { + $wr = WaitRef::create(); + $childs = []; + for ($c = MAX_CONCURRENCY_LOW; $c--;) { + $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'); + Assert::assert($ret); + $ret = $cli->recv(); + Assert::same($ret->data, $pm->getRandomDataElement(0)); + $ret = $cli->recv(); + Assert::same($ret->data, $pm->getRandomDataElement(1)); + }); + } + 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 new file mode 100644 index 00000000000..49514e592b1 --- /dev/null +++ b/tests/swoole_http_client_coro/websocket/ssl_1.phpt @@ -0,0 +1,29 @@ +--TEST-- +swoole_http_client_coro/websocket: ssl recv +--SKIPIF-- + +--FILE-- +upgrade('/'); + + if (!$ret) { + echo "ERROR\n"; + return; + } + $n = 16; + while ($n--) { + $data = base64_encode(random_bytes(rand(1, 16*1024))); + $cli->push($data); + $frame = $cli->recv(); + Assert::true(is_object($frame)); + Assert::eq($frame->data, $data); + } +}); +?> +--EXPECT-- diff --git a/tests/swoole_http_client_coro/websocket/ssl_2.phpt b/tests/swoole_http_client_coro/websocket/ssl_2.phpt new file mode 100644 index 00000000000..14f505fe6c3 --- /dev/null +++ b/tests/swoole_http_client_coro/websocket/ssl_2.phpt @@ -0,0 +1,41 @@ +--TEST-- +swoole_http_client_coro/websocket: ssl recv [2] +--SKIPIF-- + +--FILE-- + SWOOLE_LOG_TRACE, 'trace_flags' => SWOOLE_TRACE_ALL]); + +Co\run(function () { + $cli = new Co\http\Client('www.bitmex.com', 443, true); + if (($http_proxy_conf = getenv('https_proxy'))) { + $uri = parse_url($http_proxy_conf); + $cli->set([ + 'socks5_host' => $uri['host'], + 'socks5_port' => $uri['port'], + ]); + } + $ret = $cli->upgrade('/realtime'); + if (!$ret) { + echo "ERROR\n"; + return; + } + echo "CONNECT SUCCESS, StatusCode={$cli->getStatusCode()}\n"; + $n = 16; + $cli->push('{"op": "subscribe", "args": ["orderBookL2_25:XBTUSD"]}'); + while ($n--) { + $frame = $cli->recv(); + if (!$frame or empty($frame->data)) { + echo "ERROR $n [2]\n"; + var_dump($cli->errCode, $cli->errMsg); + break; + } + } + echo "FINISH\n"; +}); +?> +--EXPECT-- +CONNECT SUCCESS, StatusCode=101 +FINISH diff --git a/tests/swoole_http_client_coro/websocket/timeout.phpt b/tests/swoole_http_client_coro/websocket/timeout.phpt new file mode 100644 index 00000000000..b1096bc3b3a --- /dev/null +++ b/tests/swoole_http_client_coro/websocket/timeout.phpt @@ -0,0 +1,64 @@ +--TEST-- +swoole_http_client_coro/websocket: websocket client & server +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + go(function () use ($pm) { + $cli = new Co\http\Client('127.0.0.1', $pm->getFreePort()); + $ret = $cli->upgrade('/'); + + if (!$ret) + { + echo "ERROR\n"; + return; + } + echo $cli->recv()->data; + $cli->push('hello server'); + + Assert::false($cli->recv(.1)); + Assert::same($cli->errCode, SOCKET_ETIMEDOUT); + $cli->errCode = 0; + + Assert::false($cli->recv(.1)); + Assert::same($cli->errCode, SOCKET_ETIMEDOUT); + }); + 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) { + /** + * @var $pm ProcessManager + */ + global $pm; + $pm->wakeup(); + }); + + $ws->on('open', function ($serv, Swoole\Http\Request $request) { + $serv->push($request->fd, "start\n"); + }); + + $ws->on('message', function ($serv, $frame) { + + }); + + $ws->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +start 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/websocket_bug_01.phpt b/tests/swoole_http_client_coro/websocket_bug_01.phpt deleted file mode 100644 index 6e3e51360b4..00000000000 --- a/tests/swoole_http_client_coro/websocket_bug_01.phpt +++ /dev/null @@ -1,57 +0,0 @@ ---TEST-- -swoole_http_client_coro: websocket handshake + frame ---SKIPIF-- - ---FILE-- -parentFunc = function ($pid) use ($pm) { - go(function () use ($pm) { - $cli = new Co\http\Client('127.0.0.1', $pm->getFreePort()); - $ret = $cli->upgrade('/'); - 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->set(array( - 'log_file' => '/dev/null' - )); - $ws->on('WorkerStart', function (\swoole_server $serv) { - /** - * @var $pm ProcessManager - */ - global $pm; - $pm->wakeup(); - }); - - $ws->on('receive', function ($serv, $fd, $threadId, $data) { - $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"); - $serv->send($fd, $sendData); - }); - - $ws->start(); -}; - -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -CONNECTED -hello world diff --git a/tests/swoole_http_client_coro/websocket_bug_02.phpt b/tests/swoole_http_client_coro/websocket_bug_02.phpt deleted file mode 100644 index 8d4d4313afb..00000000000 --- a/tests/swoole_http_client_coro/websocket_bug_02.phpt +++ /dev/null @@ -1,65 +0,0 @@ ---TEST-- -swoole_http_client_coro: websocket bug use client in server ---SKIPIF-- - ---FILE-- -parentFunc = function () use ($pm) { - go(function () use ($pm) { - $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); - $cli->set(['timeout' => -1]); - $ret = $cli->upgrade('/'); - Assert::assert($ret); - echo $cli->recv()->data; - for ($i = 0; $i < 5; $i++) { - $cli->push("hello server\n", SWOOLE_WEBSOCKET_OPCODE_TEXT, true); - echo ($cli->recv(1))->data; - co::sleep(0.1); - } - $cli->close(); - }); - swoole_event::wait(); - $pm->kill(); -}; -$pm->childFunc = function () use ($pm) { - $ws = new swoole_websocket_server('127.0.0.1', $pm->getFreePort()); - $ws->set([ - 'log_file' => '/dev/null', - 'worker_num' => 1 - ]); - $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->push($request->fd, "server: hello, welcome\n"); - }); - $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) { - echo "client-{$fd} is closed\n"; - }); - $ws->start(); -}; -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -server: hello, welcome -client: hello server -server-reply: hello client -client: hello server -server-reply: hello client -client: hello server -server-reply: hello client -client: hello server -server-reply: hello client -client: hello server -server-reply: hello client -client-1 is closed diff --git a/tests/swoole_http_client_coro/websocket_timeout.phpt b/tests/swoole_http_client_coro/websocket_timeout.phpt deleted file mode 100644 index e60fbfe2316..00000000000 --- a/tests/swoole_http_client_coro/websocket_timeout.phpt +++ /dev/null @@ -1,64 +0,0 @@ ---TEST-- -swoole_http_client_coro: websocket client & server ---SKIPIF-- - ---FILE-- -parentFunc = function ($pid) use ($pm) { - go(function () use ($pm) { - $cli = new Co\http\Client('127.0.0.1', $pm->getFreePort()); - $ret = $cli->upgrade('/'); - - if (!$ret) - { - echo "ERROR\n"; - return; - } - echo $cli->recv()->data; - $cli->push('hello server'); - - Assert::false($cli->recv(.1)); - Assert::same($cli->errCode, SOCKET_ETIMEDOUT); - $cli->errCode = 0; - - Assert::false($cli->recv(.1)); - Assert::same($cli->errCode, SOCKET_ETIMEDOUT); - }); - 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) { - /** - * @var $pm ProcessManager - */ - global $pm; - $pm->wakeup(); - }); - - $ws->on('open', function ($serv, swoole_http_request $request) { - $serv->push($request->fd, "start\n"); - }); - - $ws->on('message', function ($serv, $frame) { - - }); - - $ws->start(); -}; - -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -start 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 30f4d41c880..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 swFactoryProcess_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_2608.phpt b/tests/swoole_http_server/bug_2608.phpt index e35e893a7ec..1d2e24ebffd 100644 --- a/tests/swoole_http_server/bug_2608.phpt +++ b/tests/swoole_http_server/bug_2608.phpt @@ -40,11 +40,12 @@ $pm->childFunc = function () use ($pm) { 'open_http2_protocol' => true, 'enable_static_handler' => true, 'document_root' => __DIR__, - 'static_file_types' => [], - 'static_file_locations' => ["/examples",] + 'static_handler_locations' => ["/examples",] ]); $http->on('workerStart', function ($serv, $wid) use ($pm) { - symlink(dirname(dirname(__DIR__)) . '/examples/', __DIR__ . '/examples'); + if (!file_exists(__DIR__ . '/examples')) { + symlink(dirname(dirname(__DIR__)) . '/examples/', __DIR__ . '/examples'); + } $pm->wakeup(); }); $http->on('request', function (Request $request, Response $response) { 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 new file mode 100644 index 00000000000..37743d67154 --- /dev/null +++ b/tests/swoole_http_server/duplicate_header.phpt @@ -0,0 +1,65 @@ +--TEST-- +swoole_http_server: duplicate header +--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_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HEADER, TRUE); + echo curl_exec($ch); + 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([ + 'worker_num' => 1, + 'enable_coroutine' => false, + 'log_file' => '/dev/null' + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $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 ", + "d5678", + "e \n ", + null, + 5678, + 3.1415926, + ]); + $response->end($msg); + }); + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +HTTP/1.1 200 OK +Content-Length: 11 +Test-Value: a +Test-Value: b1234 +Test-Value: d5678 +Test-Value: e +Test-Value: 5678 +Test-Value: 3.1415926 +Server: swoole-http-server +Date: %s +Connection: keep-alive +Content-Type: text/html + +hello world diff --git a/tests/swoole_http_server/enable_coroutine.phpt b/tests/swoole_http_server/enable_coroutine.phpt index aa29889687f..8453ff6bb66 100644 --- a/tests/swoole_http_server/enable_coroutine.phpt +++ b/tests/swoole_http_server/enable_coroutine.phpt @@ -16,11 +16,11 @@ $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, - 'log_level' => -1 + 'log_level' => SWOOLE_LOG_NONE, ]); $http->on("request", function (Swoole\Http\Request $request, Swoole\Http\Response $response) { $response->header("Content-Type", "text/plain"); 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 new file mode 100644 index 00000000000..26b5c1ef406 --- /dev/null +++ b/tests/swoole_http_server/headers_sent.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_http_server: headers sent (coroutine disabled) +--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_RETURNTRANSFER, 1); + echo curl_exec($ch); + echo curl_exec($ch); + curl_close($ch); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $http->set([ + 'worker_num' => 1, + 'enable_coroutine' => false, + 'log_file' => '/dev/null' + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + ob_start(); + echo 'Test'; + $output = ob_get_clean(); + $response->write('buffered_output=' . $output . "\n"); + $response->write('headers_sent=' . (int)headers_sent() . "\n"); + $response->end(); + }); + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +buffered_output=Test +headers_sent=0 +buffered_output=Test +headers_sent=0 diff --git a/tests/swoole_http_server/headers_sent_coroutine.phpt b/tests/swoole_http_server/headers_sent_coroutine.phpt new file mode 100644 index 00000000000..be0a89a7eb7 --- /dev/null +++ b/tests/swoole_http_server/headers_sent_coroutine.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_http_server: headers sent (coroutine enabled) +--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_RETURNTRANSFER, 1); + echo curl_exec($ch); + echo curl_exec($ch); + curl_close($ch); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $http->set([ + 'worker_num' => 1, + 'enable_coroutine' => true, + 'log_file' => '/dev/null' + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + ob_start(); + echo 'Test'; + $output = ob_get_clean(); + $response->write('buffered_output=' . $output . "\n"); + $response->write('headers_sent=' . (int)headers_sent() . "\n"); + $response->end(); + }); + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +buffered_output=Test +headers_sent=0 +buffered_output=Test +headers_sent=0 diff --git a/tests/swoole_http_server/https.phpt b/tests/swoole_http_server/https.phpt index 969d4b51330..3c465c876cf 100644 --- a/tests/swoole_http_server/https.phpt +++ b/tests/swoole_http_server/https.phpt @@ -21,11 +21,11 @@ $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' => dirname(__DIR__) . '/include/api/swoole_http_server/localhost-ssl/server.crt', - 'ssl_key_file' => dirname(__DIR__) . '/include/api/swoole_http_server/localhost-ssl/server.key', + 'ssl_cert_file' => SSL_FILE_DIR.'/server.crt', + 'ssl_key_file' => SSL_FILE_DIR.'/server.key', ]); $serv->on("workerStart", function ($serv) use ($pm) { $pm->wakeup(); 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 new file mode 100644 index 00000000000..1333bee839c --- /dev/null +++ b/tests/swoole_http_server/post.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_http_server: post +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm, $html) { + Co\run(function () use ($pm, $html) { + $index = rand(8192, strlen($html) - 8192); + $reqData = [ + 'data1' => substr($html, 0, $index), + 'data2' => substr($html, $index) + ]; + $resp = httpPost("http://127.0.0.1:{$pm->getFreePort()}/", $reqData); + Assert::assert($resp); + $respData = json_decode($resp, true); + Assert::same($respData['data1'], $reqData['data1']); + Assert::same($respData['data2'], $reqData['data2']); + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm, $html) { + $mode = SERVER_MODE_RANDOM; + $serv = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), $mode); + $serv->set([ + 'log_file' => '/dev/null', + ]); + $serv->on("workerStart", function ($serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('request', function ($req, $resp) use ($html) { + $resp->end(json_encode($req->post)); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE 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/send_empty_file.phpt b/tests/swoole_http_server/send_empty_file.phpt new file mode 100644 index 00000000000..ab890275405 --- /dev/null +++ b/tests/swoole_http_server/send_empty_file.phpt @@ -0,0 +1,37 @@ +--TEST-- +swoole_http_server: send empty file +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + Co\run(function () use ($pm) { + file_put_contents(TMP_FILE, ''); + $recv_file = httpGetBody("http://127.0.0.1:{$pm->getFreePort()}"); + unlink(TMP_FILE); + Assert::same($recv_file, ''); + }); + echo "DONE\n"; + $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]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $response->sendfile(TMP_FILE); + }); + $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 d61530e6795..9e6123e1a5f 100644 --- a/tests/swoole_http_server/sendfile.phpt +++ b/tests/swoole_http_server/sendfile.phpt @@ -18,12 +18,15 @@ $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->set(['worker_num' => 1]); + $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 (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/sni/server.phpt b/tests/swoole_http_server/sni/server.phpt new file mode 100644 index 00000000000..54dd3e16243 --- /dev/null +++ b/tests/swoole_http_server/sni/server.phpt @@ -0,0 +1,85 @@ +--TEST-- +swoole_http_server/sni: server +--SKIPIF-- + +--FILE-- +useConstantPorts = true; + +$pm->parentFunc = function () use ($pm) { + $flags = STREAM_CLIENT_CONNECT; + $ctxArr = [ + 'cafile' => __DIR__ . '/sni_server_ca.pem', + 'capture_peer_cert' => true, + 'verify_peer' => false, + ]; + + $port = $pm->getFreePort(); + $ctxArr['peer_name'] = 'cs.php.net'; + $ctx = stream_context_create(['ssl' => $ctxArr]); + $client = stream_socket_client("tls://127.0.0.1:$port", $errno, $errstr, 1, $flags, $ctx); + $cert = stream_context_get_options($ctx)['ssl']['peer_certificate']; + var_dump(openssl_x509_parse($cert)['subject']['CN']); + + $ctxArr['peer_name'] = 'uk.php.net'; + $ctx = stream_context_create(['ssl' => $ctxArr]); + $client = @stream_socket_client("tls://127.0.0.1:$port", $errno, $errstr, 1, $flags, $ctx); + $cert = stream_context_get_options($ctx)['ssl']['peer_certificate']; + var_dump(openssl_x509_parse($cert)['subject']['CN']); + + $ctxArr['peer_name'] = 'us.php.net'; + $ctx = stream_context_create(['ssl' => $ctxArr]); + $client = @stream_socket_client("tls://127.0.0.1:$port", $errno, $errstr, 1, $flags, $ctx); + $cert = stream_context_get_options($ctx)['ssl']['peer_certificate']; + var_dump(openssl_x509_parse($cert)['subject']['CN']); + + $pm->kill(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $http->set([ + 'log_file' => '/dev/null', + 'ssl_cert_file' => SSL_FILE_DIR.'/server.crt', + 'ssl_key_file' => SSL_FILE_DIR.'/server.key', + 'ssl_protocols' => SWOOLE_SSL_TLSv1_2 | SWOOLE_SSL_TLSv1_3 | SWOOLE_SSL_TLSv1_1 | SWOOLE_SSL_SSLv2, + 'ssl_sni_certs' => [ + "cs.php.net" => [ + 'ssl_cert_file' => SSL_FILE_DIR . "/sni_server_cs_cert.pem", + 'ssl_key_file' => SSL_FILE_DIR . "/sni_server_cs_key.pem" + ], + "uk.php.net" => [ + 'ssl_cert_file' => SSL_FILE_DIR . "/sni_server_uk_cert.pem", + 'ssl_key_file' => SSL_FILE_DIR . "/sni_server_uk_key.pem" + ], + "us.php.net" => [ + 'ssl_cert_file' => SSL_FILE_DIR . "/sni_server_us_cert.pem", + 'ssl_key_file' => SSL_FILE_DIR . "/sni_server_us_key.pem", + ], + ] + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Request $request, Response $response) { + $response->end("hello world"); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +string(%d) "cs.php.net" +string(%d) "uk.php.net" +string(%d) "us.php.net" +DONE diff --git a/tests/swoole_http_server/static_handler.phpt b/tests/swoole_http_server/static_handler.phpt index abb3c482dd2..5efaed99814 100644 --- a/tests/swoole_http_server/static_handler.phpt +++ b/tests/swoole_http_server/static_handler.phpt @@ -54,8 +54,7 @@ $pm->childFunc = function () use ($pm) { 'open_http2_protocol' => true, 'enable_static_handler' => true, 'document_root' => dirname(dirname(__DIR__)) . '/examples/', - 'static_file_types' => [], - 'static_file_locations' => ['/static', '/'] + 'static_handler_locations' => ['/static', '/'] ]); $http->on('workerStart', function () use ($pm) { $pm->wakeup(); 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 new file mode 100644 index 00000000000..f7bc7b2893d --- /dev/null +++ b/tests/swoole_http_server/static_handler/read_link_file.phpt @@ -0,0 +1,46 @@ +--TEST-- +swoole_http_server/static_handler: static handler with locations +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Swoole\Coroutine\run(function () use ($pm) { + $data = httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/examples/test_link.jpg"); + if (is_file(TEST_LINK_IMAGE)) { + unlink(TEST_LINK_IMAGE); + } + Assert::assert(md5($data) === md5_file(TEST_IMAGE)); + }); + $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' => dirname(__DIR__, 3) . '/', + '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(); +?> +--EXPECT-- +DONE 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 25546980560..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, @@ -31,6 +31,8 @@ int(2) object(Swoole\Server\Task)#%d (%d) { ["data"]=> string(0) "" + ["dispatch_time"]=> + float(%f) ["id"]=> int(0) ["worker_id"]=> 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 new file mode 100644 index 00000000000..fa6710ccb12 --- /dev/null +++ b/tests/swoole_http_server/trailer.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_http_server: trailer +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Swoole\Coroutine\run(function () use ($pm) { + $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + $cli->get('/'); + Assert::eq(md5('hello world'), $cli->headers['content-md5']); + $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('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $response->header('trailer', 'Content-MD5'); + $data = 'hello world'; + Assert::true($response->write($data)); + Assert::true($response->trailer('Content-MD5', md5($data))); + Assert::true($response->end()); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE 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 new file mode 100644 index 00000000000..5a6a7d26404 --- /dev/null +++ b/tests/swoole_http_server_coro/check_cookie_crlf.phpt @@ -0,0 +1,58 @@ +--TEST-- +swoole_http_server_coro: check if the HTTP cookie contains CRLF +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + run(function () use ($pm) { + $client = new Client('127.0.0.1', $pm->getFreePort()); + $client->get('/rawcookie'); + $headers = $client->getHeaders(); + Assert::false(isset($headers['malicious-header'])); + Assert::false(isset($headers['set-cookie'])); + + $client->get('/cookie'); + $headers = $client->getHeaders(); + Assert::false(isset($headers['malicious-header'])); + Assert::true(isset($headers['set-cookie'])); + + $client->close(); + $pm->kill(); + echo "DONE\n"; + }); +}; +$pm->childFunc = function () use ($pm) { + run(function () use ($pm) { + $server = new Server('127.0.0.1', $pm->getFreePort()); + + $server->handle('/rawcookie', function (Request $request, Response $response) { + $value = "cn\r\nmalicious-header:injected\r\nContent-Length:27\r\n\r\n

malicious response body"; + $response->rawcookie('lang', $value); + $response->end('hello world'); + }); + + $server->handle('/cookie', function (Request $request, Response $response) { + $value = "cn\r\nmalicious-header:injected\r\nContent-Length:27\r\n\r\n

malicious response body"; + $response->cookie('lang', $value); + $response->end('hello world'); + }); + + $server->start(); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +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/check_http_header_crlf.phpt b/tests/swoole_http_server_coro/check_http_header_crlf.phpt new file mode 100644 index 00000000000..35e012c95c6 --- /dev/null +++ b/tests/swoole_http_server_coro/check_http_header_crlf.phpt @@ -0,0 +1,45 @@ +--TEST-- +swoole_http_server_coro: check if the HTTP header contains CRLF +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + run(function () use ($pm) { + $client = new Client('127.0.0.1', $pm->getFreePort()); + $client->get('/?r=AAA%0d%0amalicious-header:injected'); + $headers = $client->getHeaders(); + Assert::false(isset($headers['malicious-header'])); + $client->close(); + $pm->kill(); + echo "DONE\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) { + $response->header('Location', $request->get['r']); + $response->status(302); + $response->end('Redirecting...'); + }); + + $server->start(); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +Warning: Swoole\Http\Response::end(): Header may not contain more than a single header, new line detected 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 new file mode 100644 index 00000000000..c0ef8582085 --- /dev/null +++ b/tests/swoole_http_server_coro/error_413.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_http_server_coro: error 413 +--SKIPIF-- + +--FILE-- +set(['package_max_length' => 65536, ]); + $server->handle('/', function ($request, $response) { + $response->end(serialize($request->server)); + }); + $server->handle('/shutdown', function ($request, $response) use ($server) { + $response->end("shutdown"); + $server->shutdown(); + }); + $server->start(); +}); + +go(function () { + $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', TEST_PORT, false); + $cli->set(['timeout' => 5]); + Assert::assert($cli->post('/world/index?b=455', ['value' => str_repeat('A', 128 * 1024 )])); + Assert::assert($cli->getStatusCode() == 413); + file_get_contents('http://127.0.0.1:' . TEST_PORT . '/shutdown'); +}); + +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 8e2f52e49ba..c8a6c8c1180 100644 --- a/tests/swoole_http_server_coro/https.phpt +++ b/tests/swoole_http_server_coro/https.phpt @@ -11,8 +11,8 @@ $port = get_one_free_port(); go(function () use ($port) { $server = new Co\Http\Server("127.0.0.1", $port, true); $server->set(['open_tcp_nodelay' => true, - 'ssl_cert_file' => dirname(__DIR__) . '/include/api/swoole_http_server/localhost-ssl/server.crt', - 'ssl_key_file' => dirname(__DIR__) . '/include/api/swoole_http_server/localhost-ssl/server.key', + 'ssl_cert_file' => SSL_FILE_DIR.'/server.crt', + 'ssl_key_file' => SSL_FILE_DIR.'/server.key', ]); $server->handle('/', function ($request, $response) { $response->end("

Index

"); @@ -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 new file mode 100644 index 00000000000..9f15f8bb260 --- /dev/null +++ b/tests/swoole_http_server_coro/slow_client.phpt @@ -0,0 +1,54 @@ +--TEST-- +swoole_http_server_coro: 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) +{ + 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'], 'text/html'); + Assert::eq($request->header['content-length'], strlen($request->getContent())); + $response->end("OK"); + }); + $server->start(); + }); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +OK 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 a1cc2ce4e96..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 swSSL_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 b6cb0e3e8cd..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(); @@ -43,6 +47,7 @@ $pm->childFunc = function () use ($pm) { } else if ($frame == '') { break; } else { + Assert::greaterThan($frame->fd, 0); $ws->push("Hello {$frame->data}!"); $ws->push("How are you, {$frame->data}?"); } @@ -56,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 new file mode 100644 index 00000000000..2e9cbbd80db --- /dev/null +++ b/tests/swoole_http_server_coro/websocket_close.phpt @@ -0,0 +1,61 @@ +--TEST-- +swoole_http_server_coro: close websocket connection +--SKIPIF-- + +--FILE-- +parentFunc = function (int $pid) use ($pm, &$count) { + go(function () use ($pm) { + global $count; + $cli = new \Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + $cli->set(['timeout' => 5]); + $ret = $cli->upgrade('/websocket'); + Assert::assert($ret); + $data = sha1(get_safe_random(mt_rand(0, 1024))); + $cli->push($data); + $ret = $cli->recv(); + Assert::same($ret->data, "Hello {$data}!"); + $s = microtime(true); + $ret = $cli->recv(); + Assert::lessThan(microtime(true) - $s, 0.002); + Assert::same($ret, false); + }); + Swoole\Event::wait(); + $pm->kill(); +}; +$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->upgrade(); + $frame = $ws->recv(); + if ($frame === false) { + echo "error : " . swoole_last_error() . "\n"; + } else if ($frame === '' or $frame->data === '') { + echo "close\n"; + } else { + Assert::greaterThan($frame->fd, 0); + $ws->push("Hello {$frame->data}!"); + $ws->close(); + } + System::sleep(0.5); + }); + $server->handle('/shutdown', function ($request, $response) use ($server) { + echo "shutdown\n"; + $response->status(200); + $server->shutdown(); + }); + $pm->wakeup(); + $server->start(); + }); + Swoole\Event::wait(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_http_server_coro/websocket_ping_pong.phpt b/tests/swoole_http_server_coro/websocket_ping_pong.phpt new file mode 100644 index 00000000000..4c68cf64c92 --- /dev/null +++ b/tests/swoole_http_server_coro/websocket_ping_pong.phpt @@ -0,0 +1,70 @@ +--TEST-- +swoole_http_server_coro: websocket ping pong +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + go(function () use ($pm) { + $cli = new Client('127.0.0.1', $pm->getFreePort()); + $cli->set(['timeout' => 5]); + $ret = $cli->upgrade('/websocket'); + Assert::assert($ret); + $cli->push('Swoole'); + $ret = $cli->recv(); + Assert::same($ret->data, "How are you, Swoole?"); + $ret = $cli->recv(); + Assert::same($ret->opcode, WEBSOCKET_OPCODE_PING); + $pingFrame = new Frame; + $pingFrame->opcode = WEBSOCKET_OPCODE_PING; + // 发送 PING + $cli->push($pingFrame); + $ret = $cli->recv(); + Assert::same($ret->opcode, WEBSOCKET_OPCODE_PONG); + }); + Swoole\Event::wait(); + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + go(function () use ($pm) { + $server = new Server("127.0.0.1", $pm->getFreePort(), false); + $server->handle('/websocket', function ($request, $ws) { + $ws->upgrade(); + while (true) { + $frame = $ws->recv(); + if ($frame === false) { + echo "error : " . swoole_last_error() . "\n"; + break; + } else if ($frame === '') { + break; + } else { + if ($frame->opcode === 9) { + $pFrame = new Frame; + $pFrame->opcode = WEBSOCKET_OPCODE_PONG; + $ws->push($pFrame); + } else { + $ws->push("How are you, {$frame->data}?"); + $pFrame = new Frame; + // 发送 PING + $pFrame->opcode = WEBSOCKET_OPCODE_PING; + $ws->push($pFrame); + } + } + } + }); + $server->start(); + $pm->wakeup(); + }); + Swoole\Event::wait(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- 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/array_object/base.phpt b/tests/swoole_library/array_object/base.phpt index ba20c986d36..609fc88231f 100644 --- a/tests/swoole_library/array_object/base.phpt +++ b/tests/swoole_library/array_object/base.phpt @@ -16,7 +16,7 @@ Assert::isIterable($array); Assert::eq($array, _array()->unserialize($array->serialize())); $array->push('OK'); $array->pushBack($array->pop()); -echo $array->popFront() . PHP_EOL; +echo $array->popBack() . PHP_EOL; Assert::same($array->count(), count($data)); Assert::true($array->clear()->isEmpty()); Assert::isEmpty($array->__toArray()); 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 new file mode 100644 index 00000000000..29e1b832d1c --- /dev/null +++ b/tests/swoole_lock/mutex.phpt @@ -0,0 +1,50 @@ +--TEST-- +swoole_lock: mutex +--SKIPIF-- + +--FILE-- +lock(); + +$process1 = new Process(function ($p) use ($lock, $fp) { + fwrite($fp, "[Child 1] Wait Lock\n"); + $lock->lock(); + usleep(10); + fwrite($fp, "[Child 1] Get Lock\n"); + $lock->unlock(); + fwrite($fp, "[Child 1] exit\n"); +}); +$process1->start(); + +$process2 = new Process(function ($p) use ($lock, $fp) { + fwrite($fp, "[Child 2] Sleep\n"); + sleep(1); + fwrite($fp, "[Child 2] Release Lock\n"); + $lock->unlock(); + fwrite($fp, "[Child 2] exit\n"); +}); +$process2->start(); + +Process::wait(); +Process::wait(); +?> +--EXPECTF-- +[Master] Create Lock +[Child 1] Wait Lock +[Child 2] Sleep +[Child 2] Release Lock +[Child 2] exit +[Child 1] Get Lock +[Child 1] exit diff --git a/tests/swoole_lock/mutex_robust.phpt b/tests/swoole_lock/mutex_robust.phpt new file mode 100644 index 00000000000..3781e4f3e12 --- /dev/null +++ b/tests/swoole_lock/mutex_robust.phpt @@ -0,0 +1,56 @@ +--TEST-- +swoole_lock: mutex robust +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm, $fp) { + $lock = new Lock(SWOOLE_MUTEX); + $pid = posix_getpid(); + fwrite($fp, "[Master {$pid}] Create Lock\n"); + $lock->lock(); + $n = 2; + while ($n--) { + $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"); + $lock->unlock(); + fwrite($fp, "[Child {$p->pid}] exit\n"); + }); + $process->start(); + } + sleep(30); + $lock->unlock(); +}; + +$pm->childFirst(); +$pm->run(); +fclose($fp); +echo file_get_contents($file); +unlink($file); +?> +--EXPECTF-- +[Master %d] Create Lock +[Child %d] Wait Lock +[Child %d] Wait Lock +[Child %d] Get Lock +[Child %d] exit +[Child %d] Get Lock +[Child %d] exit 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 5b4e172b452..00000000000 --- a/tests/swoole_mysql_coro/db_destruct.phpt +++ /dev/null @@ -1,27 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql db destruct ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - '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 8c9f3386efc..00000000000 --- a/tests/swoole_mysql_coro/kill_process.phpt +++ /dev/null @@ -1,41 +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)); - $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($mysql->query('SELECT 1')); - Assert::same($mysql->errno, SWOOLE_MYSQLND_CR_SERVER_GONE_ERROR); - echo $mysql->error . PHP_EOL; -}); -Swoole\Event::wait(); -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 154329b66b2..00000000000 --- a/tests/swoole_mysql_coro/prepare_multi.phpt +++ /dev/null @@ -1,48 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare multi (insert and delete) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - '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 26de4be38e3..00000000000 --- a/tests/swoole_mysql_coro/query_multifield.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -swoole_mysql_coro: multi field ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - '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 2106e7645fe..00000000000 --- a/tests/swoole_mysql_coro/query_timeout.phpt +++ /dev/null @@ -1,31 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql query timeout ---SKIPIF-- - ---FILE-- -connect([ - 'host' => MYSQL_SERVER_HOST, - '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 1eefa8cc877..00000000000 --- a/tests/swoole_mysql_coro/statement_closed.phpt +++ /dev/null @@ -1,29 +0,0 @@ ---TEST-- -swoole_mysql_coro: mysql prepare (destruct) ---SKIPIF-- - ---FILE-- - MYSQL_SERVER_HOST, - '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 new file mode 100644 index 00000000000..77562285d20 --- /dev/null +++ b/tests/swoole_process/coro/set_protocol.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_process/coro: ipc with coroutine +--SKIPIF-- + +--FILE-- +exportSocket(); + $socket->setProtocol([ + 'open_length_check' => true, + 'package_length_type' => 'n', + 'package_length_offset' => 0, + 'package_body_offset' => 2, + ]); + + while ($data = $socket->recvPacket()) { + if (strlen($data) == 2) { + echo "END\n"; + return; + } + Assert::lengthBetween($data, 1024, 61000); + } + echo "ERROR\n"; +}, false, 1, true); + +Assert::assert($proc1->start()); + +$n = N; +while ($n--) { + $len = rand(1024, 60000); + $pkg = pack('n', $len) . random_bytes($len); + $proc1->write($pkg); +} +$proc1->write(pack('n', 0)); +Swoole\Process::wait(true); +?> +--EXPECT-- +END diff --git a/tests/swoole_process/coro/signal.phpt b/tests/swoole_process/coro/signal.phpt new file mode 100644 index 00000000000..3d6d03bed88 --- /dev/null +++ b/tests/swoole_process/coro/signal.phpt @@ -0,0 +1,35 @@ +--TEST-- +swoole_process/coro: signal with coroutine +--SKIPIF-- + +--FILE-- +start(); + +Process::signal(SIGCHLD, function ($sig) { + while ($ret = Process::wait(false)) { + echo "PID={$ret['pid']}\n"; + } +}); + +go(function () { + Co::sleep(.3); + echo "END\n"; +}); + +Event::wait(); + +?> +--EXPECTF-- +PID=%d +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 b4c1c291cb8..9aff59a3ec2 100644 --- a/tests/swoole_process/daemon.phpt +++ b/tests/swoole_process/daemon.phpt @@ -8,16 +8,13 @@ require __DIR__ . '/../include/skipif.inc'; start(); +$sockets = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); -function python_process(swoole_process $worker) -{ - $fp = fopen(FILE, 'a'); - Process::daemon(1, 1, [null, $fp, $fp]); +$process = new Process(function (Swoole\Process $worker) use ($sockets) { + fclose($sockets[1]); + Process::daemon(1, 1, [null, $sockets[0], $sockets[0]]); fwrite(STDOUT, "ERROR 1\n"); fwrite(STDOUT, "ERROR 2\n"); @@ -26,16 +23,17 @@ function python_process(swoole_process $worker) fwrite(STDERR, "ERROR 4\n"); fwrite(STDERR, "ERROR 5\n"); fwrite(STDERR, "END\n"); - -} +}, true); +$pid = $process->start(); Process::wait(); +fclose($sockets[0]); -$fp = fopen(FILE, 'r'); -for ($i = 0; $i < 100; $i++) { +while (true) { + $fp = $sockets[1]; $line = fgets($fp); if (empty($line)) { - usleep(100000); + break; } else { echo $line; if ($line == "END\n") { @@ -43,7 +41,6 @@ for ($i = 0; $i < 100; $i++) { } } } -unlink(FILE); ?> --EXPECT-- 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/ignore_sigpipe.phpt b/tests/swoole_process/ignore_sigpipe.phpt new file mode 100644 index 00000000000..d85112923f9 --- /dev/null +++ b/tests/swoole_process/ignore_sigpipe.phpt @@ -0,0 +1,58 @@ +--TEST-- +swoole_process: ignore SIGPIPE +--SKIPIF-- + +--FILE-- +setWaitTimeout(5); + +$pm->parentFunc = function ($pid) use ($pm) { + echo "DONE\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" => 1, + 'hook_flags' => SWOOLE_HOOK_ALL, + 'log_level' => SWOOLE_LOG_WARNING, + )); + $serv->on("WorkerStart", function (Server $serv) use ($pm) { + $cli = new Co\Client(SWOOLE_SOCK_TCP); + if ($cli->connect('127.0.0.1', $pm->getFreePort(), 1) == false) { + echo "ERROR\n"; + return; + } + while (1) { + usleep(10000); + if ($cli->send($GLOBALS['data']) == false) { + Assert::eq($cli->errCode, SOCKET_EPIPE); + break; + } + } + $pm->wakeup(); + }); + + $serv->on(Constant::EVENT_CONNECT, function (Server $serv, $fd, $rid) { + $serv->close($fd); + }); + $serv->on(Constant::EVENT_RECEIVE, function (Server $serv, $fd, $rid, $data) { + + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_process/ignore_sigpipe_2.phpt b/tests/swoole_process/ignore_sigpipe_2.phpt new file mode 100644 index 00000000000..e200fc8e825 --- /dev/null +++ b/tests/swoole_process/ignore_sigpipe_2.phpt @@ -0,0 +1,64 @@ +--TEST-- +swoole_process: close +--SKIPIF-- + +--FILE-- +setWaitTimeout(5); + +$pm->parentFunc = function ($pid) use ($pm) { + Co\run(function () use ($pm) { + $cli = new Co\Client(SWOOLE_SOCK_TCP); + if ($cli->connect('127.0.0.1', $pm->getFreePort(), 1) == false) { + echo "ERROR\n"; + return; + } + while (1) { + usleep(10000); + if ($cli->send($GLOBALS['data']) == false) { + Assert::eq($cli->errCode, SOCKET_EPIPE); + break; + } + } + }); + echo "DONE\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" => 1, + 'hook_flags' => SWOOLE_HOOK_ALL, + 'log_level' => SWOOLE_LOG_WARNING, + )); + $serv->on("WorkerStart", function (Server $serv) use ($pm) { + $pm->wakeup(); + }); + + $serv->on(Constant::EVENT_CONNECT, function (Server $serv, $fd, $rid) { + $serv->close($fd); + }); + $serv->on(Constant::EVENT_RECEIVE, function (Server $serv, $fd, $rid, $data) { + + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE 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.phpt b/tests/swoole_process/priority.phpt new file mode 100644 index 00000000000..1333eda42d2 --- /dev/null +++ b/tests/swoole_process/priority.phpt @@ -0,0 +1,24 @@ +--TEST-- +swoole_process: priority +--SKIPIF-- + +--FILE-- +setPriority(PRIO_PROCESS, PRIORITY); + $priority = $worker->getPriority(PRIO_PROCESS); + Assert::eq($priority, PRIORITY); + usleep(20000); + $worker->exit(0); +}, false, false); + +$pid = $process->start(); +Process::wait(); +?> +--EXPECT-- 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_push.phpt b/tests/swoole_process/process_push.phpt index 2753fcabc6d..dbd18186919 100644 --- a/tests/swoole_process/process_push.phpt +++ b/tests/swoole_process/process_push.phpt @@ -6,12 +6,14 @@ swoole_process: push pop(); echo "$recv"; - sleep(2); + usleep(20000); $worker->exit(0); }, false, false); @@ -20,6 +22,7 @@ $process->useQueue(); $pid = $process->start(); $process->push("hello worker\n"); +Process::wait(); ?> ---EXPECTREGEX-- +--EXPECT-- hello worker 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 new file mode 100644 index 00000000000..f036f6d9ef6 --- /dev/null +++ b/tests/swoole_process/wait_signal.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_process: wait signal +--SKIPIF-- + +--FILE-- + true]); + Process::signal(SIGINT, function () { + echo "SIGINT\n"; + Process::signal(SIGINT, null); + }); + echo "START\n"; + Event::wait(); +}, true, true); + +$r = $proc->start(); +Assert::assert($r > 0); + +echo $proc->read(); +Process::kill($r, SIGINT); +echo $proc->read(); + +$retval = Process::wait(true); +Assert::eq($retval['pid'], $r); +Assert::eq($retval['code'], 0); +Assert::eq($retval['signal'], 0); +?> +--EXPECT-- +START +SIGINT 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 new file mode 100644 index 00000000000..140aefda44c --- /dev/null +++ b/tests/swoole_process_pool/create_websocket_server.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_process_pool: create websocket server in process pool +--SKIPIF-- + +--FILE-- +on("WorkerStart", function ($pool, $workerId) use ($port) { + if ($workerId === 0) { + $server = new Server('127.0.0.1', $port, SWOOLE_PROCESS); + $server->on("message", function ($server, $frame) { + $server->push($frame->fd, $frame->data); + }); + $server->start(); + } elseif ($workerId === 1) { + run(function () use ($port, $pool) { + $client = new Client('127.0.0.1', $port); + while (!$client->upgrade('/')) {} + $data = 'data'; + $client->push($data); + $frame = $client->recv(); + Assert::eq($frame->data, $data); + $pool->shutdown(); + }); + } +}); + +$pool->start(); +echo "DONE\n"; +?> +--EXPECTF-- +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/start_pool_twice_in_same_process.phpt b/tests/swoole_process_pool/start_pool_twice_in_same_process.phpt new file mode 100644 index 00000000000..dce007f4cd0 --- /dev/null +++ b/tests/swoole_process_pool/start_pool_twice_in_same_process.phpt @@ -0,0 +1,29 @@ +--TEST-- +swoole_process_pool: start pool twice in the same process +--SKIPIF-- + +--FILE-- +on("WorkerStart", function (Pool $pool, $workerId) { + $pool->shutdown(); +}); + +$pool->start(); + +$pool = new Swoole\Process\Pool(1); + +$pool->on("WorkerStart", function (Pool $pool, $workerId) { + $pool->shutdown(); +}); + +$pool->start(); +echo "DONE\n"; +?> +--EXPECT-- +DONE diff --git a/tests/swoole_process_pool/start_twice.phpt b/tests/swoole_process_pool/start_twice.phpt new file mode 100644 index 00000000000..abe85c2124a --- /dev/null +++ b/tests/swoole_process_pool/start_twice.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_process_pool: start twice +--SKIPIF-- + +--FILE-- +on("WorkerStart", function (Pool $pool, $workerId) { + echo "CHILD START\n"; + $pool->shutdown(); + sleep(1); +}); + +$pool->start(); +echo "START 1\n"; +$pool->start(); +echo "START 2\n"; +?> +--EXPECT-- +CHILD START +START 1 +CHILD START +START 2 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 e125a1c09c3..00000000000 --- a/tests/swoole_redis_coro/auto_reconnect_ex.phpt +++ /dev/null @@ -1,66 +0,0 @@ ---TEST-- -swoole_redis_coro: auto reconnect after server side close the connection ---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::true($ret); - for ($n = MAX_REQUESTS; $n--;) { - $ret = $redis->set('random_val', $random = get_safe_random(128)); - Assert::true($ret); - $ret = $redis->get('random_val'); - Assert::true($ret && $ret === $random); - } - $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); - } - $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 a50eb01e687..00000000000 --- a/tests/swoole_redis_coro/getOptions.phpt +++ /dev/null @@ -1,50 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client get options ---SKIPIF-- - ---FILE-- - 1, - 'socket_timeout' => -1 -]); -$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(1) - ["timeout"]=> - float(-1) - ["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 188a9663b9f..00000000000 --- a/tests/swoole_redis_coro/timeout.phpt +++ /dev/null @@ -1,47 +0,0 @@ ---TEST-- -swoole_redis_coro: redis client timeout ---SKIPIF-- - ---FILE-- - 0.5]); - $redis->connect(REDIS_SERVER_HOST, REDIS_SERVER_PORT); - - $s = microtime(true); - $res = $redis->blpop(['test', 'test2'], 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('test', 'test2', 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(['test', 'test2'], 1); - Assert::same($res, null); - Assert::same($redis->errCode, 0); - $s = microtime(true) - $s; - time_approximate(1, $s); - - $s = microtime(true); - $res = $redis->brpoplpush('test', 'test2', 1); - Assert::same($res, null); - Assert::same($redis->errCode, 0); - $s = microtime(true) - $s; - time_approximate(1, $s); -}); -swoole_event_wait(); -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 new file mode 100644 index 00000000000..cc1858815bb --- /dev/null +++ b/tests/swoole_redis_server/format.phpt @@ -0,0 +1,51 @@ +--TEST-- +swoole_redis_server: format +--SKIPIF-- + +--FILE-- + '99', "java" => '88', "c++" => '666', 9999 => 'hello']); +?> +--EXPECT-- +-ERR +-BAD REQUEST +$-1 ++OK ++SUCCESS +:1000 +$12 +hello swoole +*3 +$3 +php +$2 +is +$4 +best +*8 +$3 +php +$2 +99 +$4 +java +$2 +88 +$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/bindto.phpt b/tests/swoole_runtime/bindto.phpt new file mode 100644 index 00000000000..1103a11f15a --- /dev/null +++ b/tests/swoole_runtime/bindto.phpt @@ -0,0 +1,48 @@ +--TEST-- +swoole_runtime: socket context bindto +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Runtime::enableCoroutine(); + + run(function () use($pm) { + $context = stream_context_create([ + 'socket' => [ + 'bindto' => '0:9100', + ], + ]); + + file_get_contents("http://127.0.0.1:{$pm->getFreePort()}", false, $context); + $pm->kill(); + echo "Done\n"; + }); +}; +$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) { + Assert::eq($request->server['remote_addr'], '127.0.0.1'); + Assert::eq($request->server['remote_port'], 9100); + $response->end('success'); + }); + $pm->wakeup(); + $server->start(); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +Done diff --git a/tests/swoole_runtime/block.phpt b/tests/swoole_runtime/block.phpt index 825f185c4bf..9cd9e44f6b9 100644 --- a/tests/swoole_runtime/block.phpt +++ b/tests/swoole_runtime/block.phpt @@ -27,7 +27,7 @@ for ($i = 5; $i--;) { Assert::same($pdo->errorCode(), PDO::ERR_NONE); }); } -swoole_event_wait(); +Swoole\Event::wait(); Assert::assert((microtime(true) - $start) > 5 * 0.1); echo "DONE\n"; ?> diff --git a/tests/swoole_runtime/bug_4657.phpt b/tests/swoole_runtime/bug_4657.phpt new file mode 100644 index 00000000000..9989bb6a9dc --- /dev/null +++ b/tests/swoole_runtime/bug_4657.phpt @@ -0,0 +1,25 @@ +--TEST-- +swoole_runtime: bug 4657 +--SKIPIF-- + +--FILE-- + +--EXPECTF-- diff --git a/tests/swoole_runtime/bug_5104.phpt b/tests/swoole_runtime/bug_5104.phpt new file mode 100644 index 00000000000..191676a14a5 --- /dev/null +++ b/tests/swoole_runtime/bug_5104.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_runtime: Github#5104 https://github.com/swoole/swoole-src/issues/5104 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +DONE diff --git a/tests/swoole_runtime/enable_crypto.phpt b/tests/swoole_runtime/enable_crypto.phpt index 9204c16aad6..bdddae6f9c6 100644 --- a/tests/swoole_runtime/enable_crypto.phpt +++ b/tests/swoole_runtime/enable_crypto.phpt @@ -10,7 +10,7 @@ skip_if_offline(); --EXPECT-- diff --git a/tests/swoole_runtime/file_hook/bug_4327.phpt b/tests/swoole_runtime/file_hook/bug_4327.phpt new file mode 100644 index 00000000000..1eaa823f2c6 --- /dev/null +++ b/tests/swoole_runtime/file_hook/bug_4327.phpt @@ -0,0 +1,63 @@ +--TEST-- +swoole_runtime/file_hook: bug #4372 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +SUCCESS +SUCCESS +SUCCESS +SUCCESS diff --git a/tests/swoole_runtime/file_hook/bug_ftell_2g.phpt b/tests/swoole_runtime/file_hook/bug_ftell_2g.phpt new file mode 100644 index 00000000000..c726861e14c --- /dev/null +++ b/tests/swoole_runtime/file_hook/bug_ftell_2g.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_runtime/file_hook: fseek ftell file larger than 2G bug +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_runtime/file_hook/bug_scandir.phpt b/tests/swoole_runtime/file_hook/bug_scandir.phpt new file mode 100644 index 00000000000..3b806fc483f --- /dev/null +++ b/tests/swoole_runtime/file_hook/bug_scandir.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_runtime/file_hook: bug #3792 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +DONE 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 f87ac6afbff..c76e0950969 100644 --- a/tests/swoole_runtime/file_hook/lock_nb_1.phpt +++ b/tests/swoole_runtime/file_hook/lock_nb_1.phpt @@ -8,27 +8,31 @@ 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/get_hook_flags.phpt b/tests/swoole_runtime/get_hook_flags.phpt index f1b62c47f9d..f9b66aa2846 100644 --- a/tests/swoole_runtime/get_hook_flags.phpt +++ b/tests/swoole_runtime/get_hook_flags.phpt @@ -10,8 +10,5 @@ require __DIR__ . '/../include/bootstrap.php'; Swoole\Runtime::enableCoroutine(); Assert::same(Swoole\Runtime::getHookFlags(), SWOOLE_HOOK_ALL); - -Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL | SWOOLE_HOOK_CURL); -Assert::same(Swoole\Runtime::getHookFlags(), SWOOLE_HOOK_ALL | SWOOLE_HOOK_CURL); ?> --EXPECT-- diff --git a/tests/swoole_runtime/hook_default.phpt b/tests/swoole_runtime/hook_default.phpt new file mode 100644 index 00000000000..ff7e02bac53 --- /dev/null +++ b/tests/swoole_runtime/hook_default.phpt @@ -0,0 +1,16 @@ +--TEST-- +swoole_runtime: enable hook by default +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_runtime/hook_enable_coroutine.phpt b/tests/swoole_runtime/hook_enable_coroutine.phpt new file mode 100644 index 00000000000..4b413f4cb1c --- /dev/null +++ b/tests/swoole_runtime/hook_enable_coroutine.phpt @@ -0,0 +1,22 @@ +--TEST-- +swoole_runtime: enableCoroutine +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_runtime/hook_set_flags.phpt b/tests/swoole_runtime/hook_set_flags.phpt new file mode 100644 index 00000000000..bec1fca91ad --- /dev/null +++ b/tests/swoole_runtime/hook_set_flags.phpt @@ -0,0 +1,24 @@ +--TEST-- +swoole_runtime: set hook flags +--SKIPIF-- + +--FILE-- + 0]); + +Co\run(function () { + Assert::eq(Swoole\Runtime::getHookFlags(), 0); +}); + +Co::set(['hook_flags' => SWOOLE_HOOK_CURL]); + +Co\run(function () { + Assert::eq(Swoole\Runtime::getHookFlags(), SWOOLE_HOOK_CURL); +}); + +?> +--EXPECT-- 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/proc/close_after_wait.phpt b/tests/swoole_runtime/proc/close_after_wait.phpt new file mode 100644 index 00000000000..55c731d7b7b --- /dev/null +++ b/tests/swoole_runtime/proc/close_after_wait.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_runtime/proc: proc_close() after wait +--SKIPIF-- + +--FILE-- + array("pipe", "/dev/null"), + 1 => array("pipe", "/dev/null"), + 2 => array("pipe", "/dev/null") + ); + + $proc = proc_open('/bin/sleep 30', $descriptorspec, $pipes); + Assert::notEmpty($proc); + echo "wait begin\n"; + + go(function() use($proc) { + usleep(100000); + proc_terminate($proc); + }); + + $info = Co\System::wait(); + Assert::notEmpty($info); + echo "wait end\n"; + proc_close($proc); + echo "proc_close end\n"; +}); +?> +--EXPECTF-- +wait begin +wait end +proc_close end 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/sleep.phpt b/tests/swoole_runtime/sleep.phpt index 1c96961257f..381250ea0b4 100644 --- a/tests/swoole_runtime/sleep.phpt +++ b/tests/swoole_runtime/sleep.phpt @@ -9,10 +9,10 @@ Swoole\Runtime::enableCoroutine(); go(function () { // sleep $s = microtime(true); - sleep(1); + Assert::eq(sleep(1), 0); time_approximate(1, microtime(true) - $s); - sleep(0); - sleep(-1); + Assert::eq(sleep(0), 0); + Assert::false(sleep(-1), -1); // usleep $s = microtime(true); @@ -23,9 +23,15 @@ go(function () { usleep(-1); // time_nanosleep - time_nanosleep(-1, 1); - time_nanosleep(0, 1); - time_nanosleep(0, 1000 * 1000); + Assert::false(time_nanosleep(-1, 1)); + Assert::true(time_nanosleep(0, 1)); + Assert::true(time_nanosleep(0, 1000 * 1000)); + + // time_sleep_until + $s = microtime(true); + Assert::true(time_sleep_until($s + 1)); + time_approximate(1, microtime(true) - $s); + Assert::false(time_sleep_until($s)); }); echo "NON-BLOCKED\n"; Swoole\Event::wait(); @@ -40,4 +46,6 @@ Warning: usleep(): Number of seconds must be greater than or equal to 0 in %s on Warning: time_nanosleep(): The seconds value must be greater than 0 in %s on line %d +Warning: time_sleep_until(): Sleep until to time is less than current time in %s on line %d + DONE diff --git a/tests/swoole_runtime/sleep_yield.phpt b/tests/swoole_runtime/sleep_yield.phpt new file mode 100644 index 00000000000..52912be8780 --- /dev/null +++ b/tests/swoole_runtime/sleep_yield.phpt @@ -0,0 +1,24 @@ +--TEST-- +swoole_runtime: sleep yield +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +string(1) "a" +string(1) "b" 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/error.phpt b/tests/swoole_runtime/sockets/error.phpt new file mode 100644 index 00000000000..d80d0f326b6 --- /dev/null +++ b/tests/swoole_runtime/sockets/error.phpt @@ -0,0 +1,35 @@ +--TEST-- +swoole_runtime/sockets: error +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done diff --git a/tests/swoole_runtime/sockets/get_name.phpt b/tests/swoole_runtime/sockets/get_name.phpt new file mode 100644 index 00000000000..75d623907eb --- /dev/null +++ b/tests/swoole_runtime/sockets/get_name.phpt @@ -0,0 +1,48 @@ +--TEST-- +swoole_runtime/sockets: getsockname & getpeername +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done 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/nonblock.phpt b/tests/swoole_runtime/sockets/nonblock.phpt new file mode 100644 index 00000000000..fc775bb961f --- /dev/null +++ b/tests/swoole_runtime/sockets/nonblock.phpt @@ -0,0 +1,54 @@ +--TEST-- +swoole_runtime/sockets: timeout +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done diff --git a/tests/swoole_runtime/sockets/socketpair.phpt b/tests/swoole_runtime/sockets/socketpair.phpt new file mode 100644 index 00000000000..8bb2ba13265 --- /dev/null +++ b/tests/swoole_runtime/sockets/socketpair.phpt @@ -0,0 +1,61 @@ +--TEST-- +swoole_runtime/sockets: tcp server +--SKIPIF-- + +--FILE-- + +--EXPECT-- +hello co-2, #3 +hello co-2, #2 +hello co-2, #1 +hello co-2, #0 +hello co-1, #3 +hello co-1, #2 +hello co-1, #1 +hello co-1, #0 +Done diff --git a/tests/swoole_runtime/sockets/tcp_client.phpt b/tests/swoole_runtime/sockets/tcp_client.phpt new file mode 100644 index 00000000000..f605b949064 --- /dev/null +++ b/tests/swoole_runtime/sockets/tcp_client.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_runtime/sockets: tcp client +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done diff --git a/tests/swoole_runtime/sockets/tcp_server.phpt b/tests/swoole_runtime/sockets/tcp_server.phpt new file mode 100644 index 00000000000..118be98754c --- /dev/null +++ b/tests/swoole_runtime/sockets/tcp_server.phpt @@ -0,0 +1,59 @@ +--TEST-- +swoole_runtime/sockets: tcp server +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done diff --git a/tests/swoole_runtime/sockets/timeout.phpt b/tests/swoole_runtime/sockets/timeout.phpt new file mode 100644 index 00000000000..c5d28fd916d --- /dev/null +++ b/tests/swoole_runtime/sockets/timeout.phpt @@ -0,0 +1,56 @@ +--TEST-- +swoole_runtime/sockets: timeout +--SKIPIF-- + +--FILE-- + 0, + "usec"=> 50000, + ); + socket_set_option($sock, SOL_SOCKET, SO_RCVTIMEO, $timeout); + Assert::eq(socket_get_option($sock, SOL_SOCKET, SO_RCVTIMEO), $timeout); + Assert::eq(socket_recv($sock, $buf, 1024, 0), false); + Assert::eq(socket_last_error($sock), SOCKET_ETIMEDOUT); + $n = socket_recv($sock, $buf, 1024, 0); + Assert::greaterThanEq($n, 10); + Assert::eq(strlen($buf), $n); + Assert::eq($buf, 'Swoole: hello world'); + socket_close($sock); + }); +}); +echo "Done\n"; +?> +--EXPECT-- +Done diff --git a/tests/swoole_runtime/sockets/udp.phpt b/tests/swoole_runtime/sockets/udp.phpt new file mode 100644 index 00000000000..217b82795a7 --- /dev/null +++ b/tests/swoole_runtime/sockets/udp.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_runtime/sockets: udp +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done 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 99bf5bbb2ad..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/return_val.phpt b/tests/swoole_runtime/stream_select/return_val.phpt new file mode 100644 index 00000000000..c9c4b8299fd --- /dev/null +++ b/tests/swoole_runtime/stream_select/return_val.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_runtime/stream_select: Bug the result of stream_select() is not equal to $read + $write + $error +--SKIPIF-- + +--FILE-- + ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']] + , $pipes, __DIR__, [], [] + ); + var_dump($proc); + if (!$proc) { + exit(1); + } + $r = $pipes; + $w = []; + $e = []; + $ret = stream_select($r, $w, $e, 1); + var_dump($ret, (count($r) + count($w) + count($e))); + + foreach ($pipes as $pipe) { + fclose($pipe); + } + proc_terminate($proc); + if (defined('SIGKILL')) { + proc_terminate($proc, SIGKILL); + } else { + proc_terminate($proc); + } + proc_close($proc); +}); +?> +--EXPECTF-- +resource(%d) of type (process/coroutine) +int(0) +int(0) 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_select/stream_set_blocking.phpt b/tests/swoole_runtime/stream_select/stream_set_blocking.phpt new file mode 100644 index 00000000000..bc01acd1e62 --- /dev/null +++ b/tests/swoole_runtime/stream_select/stream_set_blocking.phpt @@ -0,0 +1,51 @@ +--TEST-- +swoole_runtime/stream_select: swoole_runtime/stream_set_blocking +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +string(7) "0:test +" +string(2) "1:" 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_runtime/unsafe/pcntl_fork.phpt b/tests/swoole_runtime/unsafe/pcntl_fork.phpt new file mode 100644 index 00000000000..a6857aa3bd7 --- /dev/null +++ b/tests/swoole_runtime/unsafe/pcntl_fork.phpt @@ -0,0 +1,18 @@ +--TEST-- +swoole_runtime/unsafe: pcntl_fork +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Warning: pcntl_fork() has been disabled for security reasons in %s on line %d diff --git a/tests/swoole_server/accept_zero.phpt b/tests/swoole_server/accept_zero.phpt new file mode 100644 index 00000000000..bd84bd0920d --- /dev/null +++ b/tests/swoole_server/accept_zero.phpt @@ -0,0 +1,54 @@ +--TEST-- +swoole_server: accept zero fd +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) 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("stats"); + $data = $client->recv(); + $json = json_decode($data, true); + Assert::eq($json[1], 1); + 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, + ]); + $serv->on("Start", function ($serv) use ($pm) { + fclose(STDIN); + $pm->wakeup(); + }); + $serv->on('receive', function (Server $serv, $fd, $reactor_id, $data) { + $serv->send($fd, json_encode(iterator_to_array($serv->connections))."\r\n"); + }); + $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/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 eaf5bee170f..c18e7c89486 100644 --- a/tests/swoole_server/addProcess_with_tick.phpt +++ b/tests/swoole_server/addProcess_with_tick.phpt @@ -5,42 +5,48 @@ swoole_server: addProcess with swoole_timer_tick fatal error --FILE-- parentFunc = function () use ($pm) { - $pm->kill(); - echo "DONE\n"; -}; -$pm->childFunc = function () use ($pm) { - class Process3 extends Swoole\Process +class Process3 extends Swoole\Process +{ + public function __construct() { - public function __construct() - { - parent::__construct([$this, 'run']); - } + parent::__construct([$this, 'run']); + } - public function run() - { - swoole_timer_tick(100, function () { - global $atomic; - if ($atomic->add() > 5) { - global $pm; - $pm->wakeup(); - Co::yield(); - return; - } - echo "sleep start then "; - Co::sleep(0.01); - echo "sleep end\n"; - trigger_error('ERROR', E_USER_ERROR); - }); - } + public function run() + { + Swoole\Timer::tick(100, function () { + global $atomic; + if ($atomic->add() > 5) { + global $pm; + $pm->wakeup(); + Co::yield(); + return; + } + echo "sleep start then "; + Co::sleep(0.01); + echo "sleep end\n"; + trigger_error('ERROR', E_USER_ERROR); + }); } +} +$pm->parentFunc = function () use ($pm) { + $pm->kill(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm) { $server = new Swoole\Server('127.0.0.1', get_one_free_port(), SWOOLE_PROCESS, SWOOLE_SOCK_UDP); - $server->set(['log_file' => '/dev/null']); - $server->on('packet', function () { }); + $server->set([ + 'log_file' => '/dev/null', +// 'worker_num' => 1, + ]); + $server->on('packet', function () { + }); $server->addProcess(new Process3); $server->start(); }; 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 new file mode 100644 index 00000000000..bf69f681a8d --- /dev/null +++ b/tests/swoole_server/big_session_id.phpt @@ -0,0 +1,71 @@ +--TEST-- +swoole_server: big session id +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($port) { + global $count; + for ($i = 0; $i < N; $i++) { + go(function () use ($port, $i) { + $cli = new Client(SWOOLE_SOCK_TCP); + $r = $cli->connect(TCP_SERVER_HOST, $port, 1); + Assert::assert($r); + $data = $cli->recv(); + Assert::same($data, (START_SESSION_ID + 1 + $i) . "-OK"); + global $count; + $count++; + $cli->close(); + }); + } + Event::wait(); + Assert::same($count, N); + Swoole\Process::kill($pid); +}; + +$pm->childFunc = function () use ($pm, $port) { + $serv = new Server('127.0.0.1', $port, SWOOLE_BASE); + $serv->set(array( + "worker_num" => 1, + 'log_file' => '/dev/null', + 'start_session_id' => START_SESSION_ID, + )); + $serv->on("WorkerStart", function (Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('connect', function (Server $serv, $fd, $rid) { + global $count; + $count++; + if ($count == N) { + Swoole\Event::defer(function () use ($serv) { + foreach ($serv->connections as $fd) { + $serv->send($fd, "$fd-OK"); + } + }); + } + }); + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { + + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- 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 796a334ca4b..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,28 +31,4 @@ $pm->childFunc = function () { $pm->childFirst(); $pm->run(); ?> ---EXPECTF-- -array(11) { - ["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) - ["tasking_num"]=> - int(0) - ["request_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 4d9e010d16a..253bb68defb 100644 --- a/tests/swoole_server/bug_2308.phpt +++ b/tests/swoole_server/bug_2308.phpt @@ -6,39 +6,37 @@ swoole_server: bug Github#2308 parentFunc = function ($pid) use ($pm) -{ +$pm->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); +$pm->childFunc = function () use ($pm) { + $server = new Server('0.0.0.0', 9501, SWOOLE_BASE, SWOOLE_SOCK_TCP); $server->set([ - 'worker_num' => 4, + '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(); }; @@ -47,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_force.phpt b/tests/swoole_server/close_force.phpt new file mode 100644 index 00000000000..78155e78154 --- /dev/null +++ b/tests/swoole_server/close_force.phpt @@ -0,0 +1,70 @@ +--TEST-- +swoole_server: force to close +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + go(function () use ($pm) { + $client = new Swoole\Coroutine\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(2); + $data = ''; + + while(true) { + $ret = $client->recv(); + if (empty($ret)) { + break; + } + $data .= $ret; + if (substr($ret, -2, 2) == "\r\n") { + break; + } + } + Assert::greaterThan(strlen($data), 8192); + 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, + 'buffer_output_size' => 4*1024*1024, + 'heartbeat_idle_time' => 2, + 'heartbeat_check_interval' => 1, + ]); + $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', 2 * 1024 * 1024)."\r\n"); + $serv->close($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/close_in_connect_callback.phpt b/tests/swoole_server/close_in_connect_callback.phpt new file mode 100644 index 00000000000..5bd279d8cfa --- /dev/null +++ b/tests/swoole_server/close_in_connect_callback.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_server: close in onConnect callback +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) 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 "DONE\n"; + }); + Swoole\Event::wait(); + $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("workerStart", function ($serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Server $serv, $fd, $reactor_id, $data) { + }); + $serv->on(Constant::EVENT_CONNECT, function (Server $serv, $fd, $reactor_id) { + $serv->close($fd); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +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_in_master.phpt b/tests/swoole_server/close_in_master.phpt new file mode 100644 index 00000000000..6b8a867ab31 --- /dev/null +++ b/tests/swoole_server/close_in_master.phpt @@ -0,0 +1,58 @@ +--TEST-- +swoole_server: close in master process +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) 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 "DONE\n"; + }); + Swoole\Event::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' => TEST_LOG_FILE, + ]); + $serv->on('start', function ($serv) { + Timer::tick(100, function ($timer) use ($serv) { + if ($serv->exists(1)) { + $serv->close(1); + Timer::clear($timer); + } + }); + }); + $serv->on("workerStart", function ($serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Server $serv, $fd, $reactor_id, $data) { + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_server/close_in_non_current_worker.phpt b/tests/swoole_server/close_in_non_current_worker.phpt index 31330677f39..4fe2f86806c 100644 --- a/tests/swoole_server/close_in_non_current_worker.phpt +++ b/tests/swoole_server/close_in_non_current_worker.phpt @@ -56,7 +56,7 @@ $pm->childFunc = function () use ($pm) { fwrite($socket[0], $serv->worker_id . "\n"); }); $serv->on(Constant::EVENT_PIPE_MESSAGE, function (Server $serv, $workerId, $msg) { - $serv->close($msg['close_fd']); + Assert::true($serv->close($msg['close_fd'])); }); $serv->start(); }; diff --git a/tests/swoole_server/close_in_other_worker_with_base.phpt b/tests/swoole_server/close_in_other_worker_with_base.phpt new file mode 100644 index 00000000000..fc35cb2cc36 --- /dev/null +++ b/tests/swoole_server/close_in_other_worker_with_base.phpt @@ -0,0 +1,62 @@ +--TEST-- +swoole_server: close in another process with base mode +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + Co\run(function () use ($pm) { + $cli = new Co\Client(SWOOLE_SOCK_TCP); + if ($cli->connect('127.0.0.1', $pm->getFreePort(), 100) == false) { + echo "ERROR\n"; + return; + } + $data = base64_encode(random_bytes(128)); + $cli->send($data); + Assert::same($cli->recv(), ''); + }); + $pm->kill(); + Assert::eq($GLOBALS['atomic']->get(), 1); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv->set(array( + "worker_num" => 2, + 'log_file' => TEST_LOG_FILE, + )); + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { + $pm->wakeup(); + }); + + $serv->on(Constant::EVENT_PIPE_MESSAGE, function ($serv, $workerId, $msg) { + Assert::assert($serv->close($msg['fd'])); + Assert::false($serv->close(99999)); + Assert::eq($serv->getLastError(), SWOOLE_ERROR_SESSION_NOT_EXIST); + }); + + $serv->on(Constant::EVENT_RECEIVE, function (Swoole\Server $serv, $fd, $rid, $data) { + $serv->sendMessage(['data' => $data, 'fd' => $fd], 1 - $serv->getWorkerId()); + }); + + $serv->on(Constant::EVENT_CLOSE, function ($serv, $fd, $tid) { + $GLOBALS['atomic']->set($fd); + }); + + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_server/close_in_task_worker.phpt b/tests/swoole_server/close_in_task_worker.phpt index b7fefe58787..3f38d1c4cbd 100644 --- a/tests/swoole_server/close_in_task_worker.phpt +++ b/tests/swoole_server/close_in_task_worker.phpt @@ -57,7 +57,7 @@ $pm->childFunc = function () use ($pm) { fwrite($socket[0], $serv->worker_id . "\n"); }); $serv->on(Constant::EVENT_TASK, function (Server $serv, $task_id, $worker_id, $msg) { - $serv->close($msg['close_fd']); + Assert::true($serv->close($msg['close_fd'])); }); $serv->start(); }; 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_queued.phpt b/tests/swoole_server/close_queued.phpt new file mode 100644 index 00000000000..7b462f18b3b --- /dev/null +++ b/tests/swoole_server/close_queued.phpt @@ -0,0 +1,68 @@ +--TEST-- +swoole_server: close queued +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + go(function () use ($pm) { + $client = new Swoole\Coroutine\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::eq(strlen($data), 2*1024*1024 +2); + 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, + 'buffer_output_size' => 4*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', 2 * 1024 * 1024)."\r\n"); + $serv->close($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/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 new file mode 100644 index 00000000000..07285db43e5 --- /dev/null +++ b/tests/swoole_server/discard_timeout_packet.phpt @@ -0,0 +1,62 @@ +--TEST-- +swoole_server: discard timeout packet +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $n = 2; + while($n--) { + $client = new Client(SWOOLE_SOCK_TCP); + $client->set([ + 'open_eof_check' => true, + 'open_eof_split' => true, + "package_eof" => "\r\n", + ]); + $client->connect('127.0.0.1', $pm->getFreePort()); + $client->send("Swoole\r\nhello world\r\nphp\r\njava\r\n"); + $client->close(); + } + Co::sleep(0.8); + }); + $pm->kill(); + Assert::eq(substr_count(file_get_contents(TMP_LOG_FILE), + 'Worker_discard_data() (ERRNO 1007): [2] ignore data'), 8); + unlink(TMP_LOG_FILE); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $serv->set([ + "package_eof" => "\r\n", + 'open_eof_check' => true, + 'open_eof_split' => true, + 'dispatch_mode' => 3, + 'discard_timeout_request' => true, + "worker_num" => 1, + 'log_file' => TMP_LOG_FILE, + ]); + $serv->on('workerStart', function (Server $serv) use ($pm) { + $pm->wakeup(); + Co::sleep(0.5); + }); + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { + $serv->send($fd, "hello {$data}\r\n\r\n"); + }); + $serv->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_server/dispatch_fallback.phpt b/tests/swoole_server/dispatch_fallback.phpt index 71449f3783f..d4f38c26832 100644 --- a/tests/swoole_server/dispatch_fallback.phpt +++ b/tests/swoole_server/dispatch_fallback.phpt @@ -5,8 +5,6 @@ swoole_server: dispatch_fallback --FILE-- parentFunc = function ($pid) use ($port) { global $count, $stats; - for ($i = 0; $i < CLIENT_N; $i++) + for ($i = 0; $i < MAX_CONCURRENCY_MID; $i++) { go(function () use ($port) { $cli = new Client(SWOOLE_SOCK_TCP); @@ -33,12 +31,12 @@ $pm->parentFunc = function ($pid) use ($port) ]); $r = $cli->connect(TCP_SERVER_HOST, $port, 1); Assert::assert($r); - for ($i = 0; $i < REQ_N; $i++) + for ($i = 0; $i < MAX_REQUESTS; $i++) { $cli->send("hello world\r\n\r\n"); } $cli->count = 0; - for ($i = 0; $i < REQ_N; $i++) + for ($i = 0; $i < MAX_REQUESTS; $i++) { $data = $cli->recv(); global $stats; @@ -52,7 +50,7 @@ $pm->parentFunc = function ($pid) use ($port) $stats[$wid] = 1; } $cli->count++; - if ($cli->count == REQ_N) + if ($cli->count == MAX_REQUESTS) { $cli->close(); } @@ -64,7 +62,7 @@ $pm->parentFunc = function ($pid) use ($port) phpt_var_dump($stats); foreach ($stats as $s) { - Assert::same($s, REQ_N * CLIENT_N / WORKER_N); + Assert::same($s, MAX_REQUESTS * MAX_CONCURRENCY_MID / WORKER_N); } echo "DONE\n"; }; diff --git a/tests/swoole_server/dispatch_func_memory_leak.phpt b/tests/swoole_server/dispatch_func_memory_leak.phpt index 0cfcf1d5879..4d05db22bba 100644 --- a/tests/swoole_server/dispatch_func_memory_leak.phpt +++ b/tests/swoole_server/dispatch_func_memory_leak.phpt @@ -39,12 +39,12 @@ $pm->childFunc = function () use ($pm) { $server->set([ 'worker_num' => rand(4, 8), 'log_file' => '/dev/null', + 'reactor_num' => 1, 'dispatch_func' => 'dispatch_packet', 'trace_flags' => SWOOLE_TRACE_EVENT, 'log_level' => 0, ]); - $server->on("WorkerStart", function (Server $serv) use ($pm) - { + $server->on("WorkerStart", function (Server $serv) use ($pm) { $pm->wakeup(); }); $server->on('receive', function (Server $server, $data, $client) { diff --git a/tests/swoole_server/dispatch_mode_1.phpt b/tests/swoole_server/dispatch_mode_1.phpt index 870b45fe7e5..1abc7d4b18f 100644 --- a/tests/swoole_server/dispatch_mode_1.phpt +++ b/tests/swoole_server/dispatch_mode_1.phpt @@ -5,26 +5,24 @@ swoole_server: dispatch_mode = 1 --FILE-- parentFunc = function ($pid) use ($port) -{ +$pm->parentFunc = function ($pid) use ($port) { global $count, $stats; - for ($i = 0; $i < CLIENT_N; $i++) - { + for ($i = 0; $i < MAX_CONCURRENCY_MID; $i++) { go(function () use ($port) { $cli = new Client(SWOOLE_SOCK_TCP); $cli->set([ @@ -33,27 +31,21 @@ $pm->parentFunc = function ($pid) use ($port) ]); $r = $cli->connect(TCP_SERVER_HOST, $port, 1); Assert::assert($r); - for ($i = 0; $i < REQ_N; $i++) - { + for ($i = 0; $i < MAX_REQUESTS; $i++) { $cli->send("hello world\r\n\r\n"); } $cli->count = 0; - for ($i = 0; $i < REQ_N; $i++) - { + for ($i = 0; $i < MAX_REQUESTS; $i++) { $data = $cli->recv(); global $stats; $wid = trim($data); - if (isset($stats[$wid])) - { + if (isset($stats[$wid])) { $stats[$wid]++; - } - else - { + } else { $stats[$wid] = 1; } $cli->count++; - if ($cli->count == REQ_N) - { + if ($cli->count == MAX_REQUESTS) { $cli->close(); } } @@ -62,15 +54,13 @@ $pm->parentFunc = function ($pid) use ($port) Event::wait(); Swoole\Process::kill($pid); phpt_var_dump($stats); - foreach ($stats as $s) - { - Assert::same($s, REQ_N * CLIENT_N / WORKER_N); + foreach ($stats as $s) { + Assert::same($s, MAX_REQUESTS * MAX_CONCURRENCY_MID / WORKER_N); } 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, @@ -79,12 +69,12 @@ $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->on('receive', function (Server $serv, $fd, $rid, $data) { $serv->send($fd, $serv->worker_id . "\r\n\r\n"); }); $serv->start(); 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 a5a48d4a80f..ebb8b801f94 100644 --- a/tests/swoole_server/dispatch_mode_3.phpt +++ b/tests/swoole_server/dispatch_mode_3.phpt @@ -8,26 +8,24 @@ skip_if_in_valgrind(); --FILE-- parentFunc = function ($pid) use ($port) -{ +$pm->parentFunc = function ($pid) use ($port) { global $count, $stats; - for ($i = 0; $i < CLIENT_N; $i++) - { + for ($i = 0; $i < MAX_CONCURRENCY; $i++) { go(function () use ($port) { $cli = new Client(SWOOLE_SOCK_TCP); $cli->set([ @@ -36,27 +34,21 @@ $pm->parentFunc = function ($pid) use ($port) ]); $r = $cli->connect(TCP_SERVER_HOST, $port, 1); Assert::assert($r); - for ($i = 0; $i < REQ_N; $i++) - { + for ($i = 0; $i < MAX_REQUESTS; $i++) { $cli->send("hello world\r\n\r\n"); } $cli->count = 0; - for ($i = 0; $i < REQ_N; $i++) - { + for ($i = 0; $i < MAX_REQUESTS; $i++) { $data = $cli->recv(); global $stats; $wid = trim($data); - if (isset($stats[$wid])) - { + if (isset($stats[$wid])) { $stats[$wid]++; - } - else - { + } else { $stats[$wid] = 1; } $cli->count++; - if ($cli->count == REQ_N) - { + if ($cli->count == MAX_REQUESTS) { $cli->close(); } } @@ -65,29 +57,30 @@ $pm->parentFunc = function ($pid) use ($port) Event::wait(); Swoole\Process::kill($pid); phpt_var_dump($stats); - Assert::assert(($stats[5] + $stats[10]) < REQ_N); - Assert::same(array_sum($stats) / count($stats), REQ_N); + Assert::eq(count($stats), WORKER_N); + Assert::lessThan($stats[5], MAX_REQUESTS); + Assert::lessThan($stats[10], MAX_REQUESTS); + Assert::same(array_sum($stats), MAX_REQUESTS * MAX_CONCURRENCY); 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) - { + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { + if ($serv->worker_id == 10 or $serv->worker_id == 5) { usleep(5000); } $serv->send($fd, $serv->worker_id . "\r\n\r\n"); diff --git a/tests/swoole_server/dispatch_mode_7.phpt b/tests/swoole_server/dispatch_mode_7.phpt index 336b7b19726..70c8c36b721 100644 --- a/tests/swoole_server/dispatch_mode_7.phpt +++ b/tests/swoole_server/dispatch_mode_7.phpt @@ -12,11 +12,9 @@ use Swoole\Event; use Swoole\Server; $pm = new SwooleTest\ProcessManager; -$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(), 0.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(), 0.5, 0)) { echo "Over flow. errno=" . $client->errCode; die("\n"); } @@ -27,19 +25,15 @@ $pm->parentFunc = function ($pid) use ($pm) 'content' => str_repeat('A', 8192 * rand(1, 3)), ); - $_serialize_data = serialize($data). "\r\n\r\n"; + $_serialize_data = serialize($data) . "\r\n\r\n"; $chunk_size = 2048; $len = strlen($_serialize_data); $chunk_num = intval($len / $chunk_size) + 1; - for ($i = 0; $i < $chunk_num; $i++) - { - if ($len < ($i + 1) * $chunk_size) - { + for ($i = 0; $i < $chunk_num; $i++) { + if ($len < ($i + 1) * $chunk_size) { $sendn = $len - ($i * $chunk_size); - } - else - { + } else { $sendn = $chunk_size; } $client->send(substr($_serialize_data, $i * $chunk_size, $sendn)); @@ -49,32 +43,26 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->kill(); }; -$pm->childFunc = function () use ($pm) -{ - $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); +$pm->childFunc = function () use ($pm) { + $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, )); - $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) { $_data = unserialize(rtrim($data)); - if ($_data and is_array($_data) and $_data['sid'] == 1000236) - { + if ($_data and is_array($_data) and $_data['sid'] == 1000236) { $serv->send($fd, "SUCCESS\n"); - } - else - { + } else { $serv->send($fd, "ERROR\n"); } }); 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 c6e7cc9242a..ff2c32949a2 100644 --- a/tests/swoole_server/duplicate_registered.phpt +++ b/tests/swoole_server/duplicate_registered.phpt @@ -5,12 +5,16 @@ swoole_server: duplicate registered --FILE-- on('start', function () { }); -$mem = memory_get_usage(); +Assert::same(true, !!'load Assert'); +$mem = null; for ($n = 1000; $n--;) { - Assert::same($mem, memory_get_usage()); $server->on('start', function () { }); + if ($mem === null) { + $mem = memory_get_usage(); + } + Assert::same(memory_get_usage(), $mem); } echo "DONE\n"; ?> 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 5dc59fa7901..83056f5766b 100644 --- a/tests/swoole_server/eof_protocol.phpt +++ b/tests/swoole_server/eof_protocol.phpt @@ -7,6 +7,8 @@ swoole_server: (eof protocol) recv 100k packet require __DIR__ . '/../include/bootstrap.php'; require __DIR__ . '/../include/api/swoole_server/TestServer.php'; +TestServer::$PKG_NUM = MAX_PACKET_NUM; + class EofServer extends TestServer { protected $show_lost_package = false; @@ -21,7 +23,7 @@ class EofServer extends TestServer { exit("error packet"); } - if ($pkg['index'] > self::PKG_NUM) + if ($pkg['index'] > self::$PKG_NUM) { echo "invalid index #{$pkg['index']}\n"; } @@ -37,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"); @@ -45,7 +47,7 @@ $pm->parentFunc = function ($pid) use ($pm) $bytes = 0; $pkg_bytes = 0; - for ($i = 0; $i < TestServer::PKG_NUM; $i++) + for ($i = 0; $i < TestServer::$PKG_NUM; $i++) { $len = rand(1000, 1024 * 128 - 8); $sid = rand(10000, 99999); @@ -70,7 +72,7 @@ $pm->parentFunc = function ($pid) use ($pm) $recv = $client->recv(); echo $recv; -// echo "send ".TestServer::PKG_NUM." packet sucess, send $bytes bytes\n"; +// echo "send ".MAX_PACKET_NUM." packet sucess, send $bytes bytes\n"; $client->close(); usleep(1); $pm->kill(); @@ -96,4 +98,4 @@ $pm->run(); ?> --EXPECTREGEX-- end -Total count=100000?, bytes=\d+ +Total count=\d+, bytes=\d+ 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 ed2550b2b62..a8f3d8c7d2c 100644 --- a/tests/swoole_server/event/manager_stop.phpt +++ b/tests/swoole_server/event/manager_stop.phpt @@ -7,7 +7,6 @@ swoole_server/event: onManagerStop require __DIR__ . '/../../include/bootstrap.php'; use Swoole\Server; -use SwooleTest\ProcessManager; use Swoole\Atomic; $pm = new SwooleTest\ProcessManager; @@ -22,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 new file mode 100644 index 00000000000..a2f54bef5ac --- /dev/null +++ b/tests/swoole_server/event/worker_exit.phpt @@ -0,0 +1,98 @@ +--TEST-- +swoole_server/event: onWorkerExit +--SKIPIF-- + +--FILE-- +setWaitTimeout(5); + +const FILE = __DIR__ . '/tmp_result.txt'; + +$atomic = new Atomic(); + +$pm->setWaitTimeout(5); +$pm->setLogFile(FILE); + +$pm->parentFunc = function () use ($pm) { + usleep(10000); + Process::kill($pm->getChildPid(), SIGUSR1); + echo "done\n"; +}; + +$pm->childFunc = function () use ($pm, $atomic) { + $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 ($atomic, $pm) { + $pm->writeLog('master start'); + }); + + $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 { + $GLOBALS['timer'] = Timer::tick(100, function () use ($serv, $pm) { + $pm->writeLog( + 'tick, id=' . $serv->getWorkerId() . ', status=' . $serv->getWorkerStatus()); + $pm->wakeup(); + }); + } + }); + + $serv->on(Constant::EVENT_WORKER_EXIT, function (Server $serv) use ($atomic, $pm) { + $pm->writeLog( + 'worker exit, id=' . $serv->getWorkerId() . ', status=' . $serv->getWorkerStatus()); + Timer::clear($GLOBALS['timer']); + }); + + $serv->on(Constant::EVENT_WORKER_STOP, function (Server $serv) use ($pm) { + $pm->writeLog('worker stop'); + }); + + $serv->on("Receive", function () { + }); + + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); + +echo file_get_contents(FILE); +unlink(FILE); + +?> +--EXPECT-- +done +master start +manager start +worker start, id=0, status=2 +tick, id=0, status=2 +worker exit, id=0, status=3 +worker stop +worker start, id=0, status=2 +worker stop diff --git a/tests/swoole_server/force_reload.phpt b/tests/swoole_server/force_reload.phpt index f8adc0da764..b3b63be021e 100644 --- a/tests/swoole_server/force_reload.phpt +++ b/tests/swoole_server/force_reload.phpt @@ -29,21 +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(); @@ -57,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 swManager_kill_timeout_process (ERRNO 9012): [Manager] Worker#%d[pid=%d] exit timeout, forced kill -[%s] WARNING swManager_kill_timeout_process (ERRNO 9012): [Manager] Worker#%d[pid=%d] exit timeout, forced kill -[%s] WARNING swManager_kill_timeout_process (ERRNO 9012): [Manager] Worker#%d[pid=%d] exit timeout, forced kill -[%s] WARNING swManager_kill_timeout_process (ERRNO 9012): [Manager] Worker#%d[pid=%d] exit timeout, forced kill -[%s] WARNING swManager_check_exit_status: worker#%d[pid=%d] abnormal exit, status=0, signal=9 -[%s] WARNING swManager_check_exit_status: worker#%d[pid=%d] abnormal exit, status=0, signal=9 -[%s] WARNING swManager_check_exit_status: worker#%d[pid=%d] abnormal exit, status=0, signal=9 -[%s] WARNING swManager_check_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 12489904f46..039f47e8b87 100644 --- a/tests/swoole_server/force_reload2.phpt +++ b/tests/swoole_server/force_reload2.phpt @@ -8,37 +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(); @@ -48,14 +55,14 @@ $pm->childFirst(); $pm->run(); ?> --EXPECTF-- -%s -%s -1 [%s] start to reload -[%s] INFO reload workers -[%s] WARNING swProcessPool_kill_timeout_worker: swKill(%d, SIGKILL) [%d] -[%s] WARNING swProcessPool_kill_timeout_worker: swKill(%d, SIGKILL) [%d] -[%s] WARNING swProcessPool_wait: worker#%d abnormal exit, status=0, signal=9 -[%s] WARNING swProcessPool_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 81a50d555bd..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 swWorker_reactor_is_empty (ERRNO 9012): worker exit timeout, forced to terminate +[%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 683ce21fbc3..4852a67008c 100644 --- a/tests/swoole_server/idle_worekr_num.phpt +++ b/tests/swoole_server/idle_worekr_num.phpt @@ -32,10 +32,11 @@ $pm->childFunc = function () use ($pm, $counter) { $serv->set([ 'worker_num' => 3, 'log_level' => SWOOLE_LOG_ERROR, + 'enable_coroutine' => false, ]); $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 ae1a91f79a8..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,6 +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 swReactorProcess_send2client (ERRNO 1005): send %d byte failed, session#100 does not exist - -Warning: Swoole\Server::send(): invalid fd[9223372036854775807] in %s/tests/swoole_server/invalid_fd.php on line %d%s diff --git a/tests/swoole_server/invalid_option.phpt b/tests/swoole_server/invalid_option.phpt new file mode 100644 index 00000000000..8cd5b434dd3 --- /dev/null +++ b/tests/swoole_server/invalid_option.phpt @@ -0,0 +1,29 @@ +--TEST-- +swoole_server: invalid option +--SKIPIF-- + +--FILE-- + 1, + 'backlog' => 128, + 'invalid_option' => true, +]; + +try { + $serv->set($options); +} catch (\Swoole\Exception $e) { + echo $e->getMessage(); +} + +?> +--EXPECTF-- +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 60d84dce13c..ce7fa9a8de2 100644 --- a/tests/swoole_server/kill_user_process_01.phpt +++ b/tests/swoole_server/kill_user_process_01.phpt @@ -4,7 +4,7 @@ swoole_server: kill user process [SWOOLE_PROCESS] --FILE-- 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/last_time.phpt b/tests/swoole_server/last_time.phpt new file mode 100644 index 00000000000..f117701e7e2 --- /dev/null +++ b/tests/swoole_server/last_time.phpt @@ -0,0 +1,56 @@ +--TEST-- +swoole_server: last time +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + Co\Run(function () use ($pm) { + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + $r = $cli->connect(TCP_SERVER_HOST, $pm->getFreePort(), 1); + Assert::assert($r); + $cli->send("test1\r\ntest2\r\n"); + $result = [json_decode($cli->recv(), true), json_decode($cli->recv(), true)]; + Assert::lessThanEq($result[0]['php_time'] - $result[0]['last_dispatch_time'], 0.01); + Assert::greaterThanEq($result[1]['php_time'] - $result[1]['last_dispatch_time'], 0.3); + $cli->close(); + echo "SUCCESS\n"; + }); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort()); + $serv->set([ + "worker_num" => 1, + 'log_file' => '/dev/null', + 'open_eof_split' => true, + 'package_eof' => "\r\n", + ]); + $serv->on("WorkerStart", function (Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on("Receive", function (Server $serv, $fd, $rid, $data) { + $info = $serv->getClientInfo($fd); + $info ['php_time'] = microtime(true); + $serv->send($fd, json_encode($info)); + if (trim($data) == 'test1') { + usleep(300000); + } + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +SUCCESS diff --git a/tests/swoole_server/length/00.phpt b/tests/swoole_server/length/00.phpt new file mode 100644 index 00000000000..956ac388e04 --- /dev/null +++ b/tests/swoole_server/length/00.phpt @@ -0,0 +1,95 @@ +--TEST-- +swoole_server/length: (length protocol) recv 100k packet +--SKIPIF-- + +--FILE-- +recv_bytes}\n"; + } + if ($header['index'] > self::$PKG_NUM) { + echo "invalid index #{$header['index']}\n"; + } + $this->index[$header['index']] = true; + } + + function onWorkerStart($serv, $wid) + { + global $pm; + $pm->wakeup(); + } +} + +$pm = new SwooleTest\ProcessManager; +$pm->parentFunc = function ($pid) use ($pm) { + $client = new Swoole\Client(SWOOLE_SOCK_TCP); + if (!$client->connect('127.0.0.1', $pm->getFreePort())) { + exit("connect failed\n"); + } + + $bytes = 0; + $pkg_bytes = 0; + + for ($i = 0; $i < TestServer::$PKG_NUM; $i++) { + $len = rand(1000, 1024 * 128 - 8); + $sid = rand(10000, 99999); + + $pkt = pack('NNN', $len + 8, $i, $sid); + $pkt .= str_repeat('A', $len); + + $pkg_bytes += strlen($pkt); + +// if ($i % 1000 == 0) +// { +// echo "#{$i} send package. sid={$sid}, length=" . ($len + 10) . ", total bytes={$pkg_bytes}\n"; +// } + if (!$client->send($pkt)) { + break; + } + $bytes += strlen($pkt); + } + + $recv = $client->recv(); + echo $recv; + //echo "send ".TestServer::$PKG_NUM." packet sucess, send $bytes bytes\n"; + $client->close(); + + usleep(1); + Swoole\Process::kill($pid); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new PkgServer($pm->getFreePort(), true); + $serv->set([ + 'worker_num' => 1, + //'dispatch_mode' => 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' => 4, + 'task_worker_num' => 0 + ]); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECTREGEX-- +end +Total count=\d+, bytes=\d+ diff --git a/tests/swoole_server/length/01.phpt b/tests/swoole_server/length/01.phpt index 0ee29d40c1d..81b2d53e9a7 100644 --- a/tests/swoole_server/length/01.phpt +++ b/tests/swoole_server/length/01.phpt @@ -17,6 +17,7 @@ use Swoole\Atomic; use Swoole\Client; $max = file_get_contents('/proc/sys/net/core/wmem_max'); +$max = min(8 * 1024 * 1024, $max); $size = intval($max) * 2 - 32 - 4; @@ -32,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 c9158bdbdf3..b48f9740417 100644 --- a/tests/swoole_server/length/02.phpt +++ b/tests/swoole_server/length/02.phpt @@ -7,9 +7,12 @@ swoole_server/length: (length protocol) no body require __DIR__ . '/../../include/bootstrap.php'; require __DIR__ . '/../../include/api/swoole_server/TestServer.php'; +TestServer::$PKG_NUM = MAX_PACKET_NUM; + class PkgServer_2 extends TestServer { protected $show_lost_package = false; + function onReceive($serv, $fd, $reactor_id, $data) { static $index = 0; @@ -19,7 +22,7 @@ class PkgServer_2 extends TestServer if ($index % 1000 == 0) { //echo "#{$header['index']} recv package. sid={$header['sid']}, length=" . strlen($data) . ", bytes={$this->recv_bytes}\n"; } - if ($index > self::PKG_NUM) { + if ($index > self::$PKG_NUM) { echo "invalid index #{$index}\n"; } $this->index[$index] = true; @@ -34,25 +37,21 @@ class PkgServer_2 extends TestServer } $pm = new SwooleTest\ProcessManager; -$pm->parentFunc = function ($pid) use ($pm) -{ - $client = new swoole_client(SWOOLE_SOCK_TCP); - if (!$client->connect('127.0.0.1', $pm->getFreePort())) - { +$pm->parentFunc = function ($pid) use ($pm) { + $client = new Swoole\Client(SWOOLE_SOCK_TCP); + if (!$client->connect('127.0.0.1', $pm->getFreePort())) { exit("connect failed\n"); } $bytes = 0; $pkg_bytes = 0; - for ($i = 0; $i < TestServer::PKG_NUM; $i++) - { + for ($i = 0; $i < TestServer::$PKG_NUM; $i++) { // if ($i % 1000 == 0) // { // echo "#{$i} send package. sid={$sid}, length=" . ($len + 10) . ", total bytes={$pkg_bytes}\n"; // } - if (!$client->send(pack('n', 2))) - { + if (!$client->send(pack('n', 2))) { break; } $bytes += 2; @@ -60,11 +59,11 @@ $pm->parentFunc = function ($pid) use ($pm) $recv = $client->recv(); echo $recv; - //echo "send ".TestServer::PKG_NUM." packet sucess, send $bytes bytes\n"; + //echo "send ".TestServer::$PKG_NUM." packet sucess, send $bytes bytes\n"; $client->close(); usleep(1); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm) { @@ -88,4 +87,4 @@ $pm->run(); ?> --EXPECTREGEX-- end -Total count=100000?, bytes=\d+ +Total count=\d+, bytes=\d+ 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 new file mode 100644 index 00000000000..112ae523e04 --- /dev/null +++ b/tests/swoole_server/length/length_func.phpt @@ -0,0 +1,61 @@ +--TEST-- +swoole_server/length: package_length_func +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); + $cli->connect(TCP_SERVER_HOST, $pm->getFreePort(), 1); + $data = str_repeat('A', LEN * COUNT); + $cli->send($data); + + $data = ''; + while (1) { + $recv_data = $cli->recv(); + if (!$recv_data) { + break; + } + $data .= $recv_data; + if (strlen($data) >= LEN * COUNT) { + break; + } + } + Assert::eq($data, str_repeat("OK\n", 12)); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_PROCESS); + $serv->set([ + "worker_num" => 1, + 'log_file' => '/dev/null', + 'open_length_check' => true, + 'package_body_offset' => 4, + 'package_length_func' => function(){ + return 3; + } + ]); + $serv->on("WorkerStart", function (Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on("receive", function ($serv, $fd, $rid, $data) { + Assert::assert(strlen($data) == LEN); + $serv->send($fd, "OK\n"); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_server/length_protocol.phpt b/tests/swoole_server/length_protocol.phpt deleted file mode 100644 index 0cfd0c29bfd..00000000000 --- a/tests/swoole_server/length_protocol.phpt +++ /dev/null @@ -1,97 +0,0 @@ ---TEST-- -swoole_server: (length protocol) recv 100k packet ---SKIPIF-- - ---FILE-- -recv_bytes}\n"; - } - if ($header['index'] > self::PKG_NUM) - { - echo "invalid index #{$header['index']}\n"; - } - $this->index[$header['index']] = true; - } - - function onWorkerStart($serv, $wid) - { - global $pm; - $pm->wakeup(); - } -} - -$pm = new SwooleTest\ProcessManager; -$pm->parentFunc = function ($pid) use ($pm) -{ - $client = new swoole_client(SWOOLE_SOCK_TCP); - if (!$client->connect('127.0.0.1', $pm->getFreePort())) - { - exit("connect failed\n"); - } - - $bytes = 0; - $pkg_bytes = 0; - - for ($i = 0; $i < TestServer::PKG_NUM; $i++) - { - $len = rand(1000, 1024 * 128 - 8); - $sid = rand(10000, 99999); - - $pkt = pack('NNN', $len + 8, $i, $sid); - $pkt .= str_repeat('A', $len); - - $pkg_bytes += strlen($pkt); - -// if ($i % 1000 == 0) -// { -// echo "#{$i} send package. sid={$sid}, length=" . ($len + 10) . ", total bytes={$pkg_bytes}\n"; -// } - if (!$client->send($pkt)) - { - break; - } - $bytes += strlen($pkt); - } - - $recv = $client->recv(); - echo $recv; - //echo "send ".TestServer::PKG_NUM." packet sucess, send $bytes bytes\n"; - $client->close(); - - usleep(1); - swoole_process::kill($pid); -}; - -$pm->childFunc = function () use ($pm) { - $serv = new PkgServer($pm->getFreePort(), true); - $serv->set([ - 'worker_num' => 1, - //'dispatch_mode' => 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' => 4, - 'task_worker_num' => 0 - ]); - $serv->start(); -}; - -$pm->childFirst(); -$pm->run(); -?> ---EXPECTREGEX-- -end -Total count=100000?, bytes=\d+ diff --git a/tests/swoole_server/listen_fail.phpt b/tests/swoole_server/listen_fail.phpt index 225e11d7460..96b1b19315e 100644 --- a/tests/swoole_server/listen_fail.phpt +++ b/tests/swoole_server/listen_fail.phpt @@ -5,14 +5,10 @@ swoole_server: listen fail --FILE-- -1]); -try -{ - $serv = new swoole_server('192.0.0.1', 80); -} -catch(swoole_exception $e) -{ +Co::set(['log_level' => SWOOLE_LOG_NONE]); +try { + $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 new file mode 100644 index 00000000000..a9797739a00 --- /dev/null +++ b/tests/swoole_server/max_idle_time_1.phpt @@ -0,0 +1,70 @@ +--TEST-- +swoole_server: max_idle_time +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm, $time1, $time2) { + $client = new Client(SWOOLE_SOCK_UNIX_STREAM, SWOOLE_SOCK_SYNC); + if (!$client->connect(SOCK_FILE, 0, 0.5)) { + exit("connect failed\n"); + } + $socket = $client->getSocket(); + socket_set_option($socket, SOL_SOCKET, SO_SNDBUF, 65536); + socket_set_option($socket, SOL_SOCKET, SO_RCVBUF, 65536); + $client->send('hello world'); + $s = microtime(true); + sleep(1); + usleep(200000); + Assert::greaterThanEq($time2->get() - $time1->get(), 1000); + $result = ''; + while(true) { + $data = $client->recv(); + if (empty($data)) { + break; + } + $result .= $data; + } + Assert::greaterThan(strlen($result), 65536); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm, $time1, $time2) { + $serv = new Server(SOCK_FILE, 0, SWOOLE_BASE, SWOOLE_SOCK_UNIX_STREAM); + $serv->set([ + 'worker_num' => 1, + 'kernel_socket_send_buffer_size' => 65536, + 'log_file' => '/dev/null', + 'max_idle_time' => 1, + ]); + $serv->on("workerStart", function ($serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('Receive', function ($serv, $fd, $tid, $data) use ($time1) { + $time1->set(microtime(true) * 1000); + $serv->send($fd, str_repeat('A', 1024 * 1024 * 4)); + }); + $serv->on('close', function ($serv, $fd, $tid) use ($time2) { + $time2->set(microtime(true) * 1000); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_server/max_idle_time_2.phpt b/tests/swoole_server/max_idle_time_2.phpt new file mode 100644 index 00000000000..7d11301fb4d --- /dev/null +++ b/tests/swoole_server/max_idle_time_2.phpt @@ -0,0 +1,59 @@ +--TEST-- +swoole_server: max_idle_time [recv] +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm, $time1, $time2) { + $client = new Client(SWOOLE_SOCK_UNIX_STREAM, SWOOLE_SOCK_SYNC); + if (!$client->connect(SOCK_FILE, 0, 0.5)) { + exit("connect failed\n"); + } + sleep(1); + usleep(200000); + Assert::greaterThanEq($time2->get() - $time1->get(), 1000); + $data = $client->recv(); + Assert::isEmpty($data); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm, $time1, $time2) { + $serv = new Server(SOCK_FILE, 0, SWOOLE_BASE, SWOOLE_SOCK_UNIX_STREAM); + $serv->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null', + 'max_idle_time' => 1, + ]); + $serv->on("workerStart", function ($serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on(Constant::EVENT_CONNECT, function ($serv, $fd, $tid) use ($time1) { + $time1->set(microtime(true) * 1000); + }); + $serv->on('Receive', function ($serv, $fd, $tid, $data) use ($time1) { + $serv->send($fd, str_repeat('A', 1024 * 1024)); + }); + $serv->on(Constant::EVENT_CLOSE, function ($serv, $fd, $tid) use ($time2) { + $time2->set(microtime(true) * 1000); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- 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/mqtt/length_offset.phpt b/tests/swoole_server/mqtt/length_offset.phpt new file mode 100644 index 00000000000..1f77ac73dfa --- /dev/null +++ b/tests/swoole_server/mqtt/length_offset.phpt @@ -0,0 +1,46 @@ +--TEST-- +swoole_server/mqtt: length_offset +--SKIPIF-- + +--FILE-- +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(Helper::encodePublish([ + 'cmd' => 3, + 'topic' => 'swoole/mqtt/test', + 'content' => '{"name":"swoole", "type":"mqtt", "data":'. str_repeat("swoole", 100) .'}'])) + ); + echo $client->recv(); + $client->close(); + $pm->kill(); + }); +}; +$pm->childFunc = function () use ($pm) { + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server->set([ + 'worker_num' => 1, + 'open_mqtt_protocol' => 1, + ]); + + $server->on('receive', function (Swoole\Server $serv, int $fd, int $rid, string $data) { + $header = Helper::getHeader($data); + Assert::eq($header['type'], 3); + Assert::eq(strlen($data), 662); + $serv->send($fd, strlen($data)); + }); + + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +662 diff --git a/tests/swoole_server/mqtt/recv_fail.phpt b/tests/swoole_server/mqtt/recv_fail.phpt new file mode 100644 index 00000000000..dd7cb748239 --- /dev/null +++ b/tests/swoole_server/mqtt/recv_fail.phpt @@ -0,0 +1,58 @@ +--TEST-- +swoole_server/mqtt: recv_fail +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $client = new Co\Client(SWOOLE_SOCK_TCP); + $client->set(['open_mqtt_protocol' => true]); + Assert::assert($client->connect('127.0.0.1', $pm->getFreePort())); + $buffer = Helper::encodePing(12); // PINGREQ +// $client->send($buffer); + $client->send($buffer[0]); + sleep(1); + $client->send($buffer[1]); + $response = $client->recv(); + $header = Helper::getHeader($response); + var_dump($header); + Assert::eq($header['type'], 13); // PINGRESP + $client->close(); + $pm->kill(); + }); +}; +$pm->childFunc = function () use ($pm) { + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server->set([ + 'worker_num' => 1, + 'open_mqtt_protocol' => 1, + ]); + + $server->on('receive', function (Swoole\Server $serv, int $fd, int $rid, string $data) { + $header = Helper::getHeader($data); + Assert::eq($header['type'], 12); + $serv->send($fd, Helper::encodePing(13)); + }); + + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +array(4) { + ["type"]=> + int(13) + ["dup"]=> + int(0) + ["qos"]=> + int(0) + ["retain"]=> + int(0) +} diff --git a/tests/swoole_server/mqtt/send_big_pack.phpt b/tests/swoole_server/mqtt/send_big_pack.phpt new file mode 100644 index 00000000000..d7e3628a8bb --- /dev/null +++ b/tests/swoole_server/mqtt/send_big_pack.phpt @@ -0,0 +1,57 @@ +--TEST-- +swoole_server/mqtt: send_big_pack +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $client = new Co\Client(SWOOLE_SOCK_TCP); + $client->set(['open_mqtt_protocol' => true, 'package_max_length' => 5 * 1024 *1024]); + Assert::assert($client->connect('127.0.0.1', $pm->getFreePort())); + Assert::assert($client->send(Helper::encodePublish([ + 'cmd' => 3, + 'topic' => 'swoole/mqtt/test', + 'content' => '{"name":"swoole", "type":"mqtt", "data":'. str_repeat("sw", 2 * 1024 * 1024 ) .'}'])) + ); + var_dump(Helper::getHeader($client->recv())); + $client->close(); + $pm->kill(); + }); +}; +$pm->childFunc = function () use ($pm) { + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server->set([ + 'worker_num' => 1, + 'open_mqtt_protocol' => 1, + 'package_max_length' => 5 * 1024 *1024 + ]); + + $server->on('receive', function (Swoole\Server $serv, int $fd, int $rid, string $data) { + $header = Helper::getHeader($data); + Assert::eq($header['type'], 3); + Assert::eq(strlen($data), 4194368); + $serv->send($fd, Helper::encodePing(13)); + }); + + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +array(4) { + ["type"]=> + int(13) + ["dup"]=> + int(0) + ["qos"]=> + int(0) + ["retain"]=> + int(0) +} 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 new file mode 100644 index 00000000000..b7bf7ab6925 --- /dev/null +++ b/tests/swoole_server/new_twice.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_server: new twice +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $fn = function () use ($pm) { + $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"); + }; + $fn(); + $pm->wait(); + $fn(); +}; + +$pm->childFunc = function () use ($pm) { + ini_set('swoole.display_errors', 'Off'); + $n = 2; + while ($n--) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv->set(array( + "worker_num" => 1, + 'enable_coroutine' => false, + 'log_file' => '/dev/null', + )); + $serv->on("WorkerStart", function (Server $serv) use ($pm) { + echo "WorkerStart\n"; + $pm->wakeup(); + }); + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { + $serv->shutdown(); + }); + $serv->start(); + } +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +WorkerStart +WorkerStart diff --git a/tests/swoole_server/object/event.phpt b/tests/swoole_server/object/event.phpt new file mode 100644 index 00000000000..673515bd672 --- /dev/null +++ b/tests/swoole_server/object/event.phpt @@ -0,0 +1,72 @@ +--TEST-- +swoole_server/object: event object +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + $client = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + if (!$client->connect('127.0.0.1', $pm->getFreePort())) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); + } + + $data = base64_encode(random_bytes(rand(1024, 8192))) . "\r\n\r\n";; + $client->send($data); + $recv_data = $client->recv(); + Assert::assert($recv_data); + $json = json_decode($recv_data); + Assert::eq($json->data, $data); + }); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv->set( + array( + "worker_num" => 1, + 'event_object' => true, + 'log_file' => '/dev/null', + ) + ); + $serv->on( + 'WorkerStart', + function (Server $serv) use ($pm) { + $pm->wakeup(); + } + ); + $serv->on( + 'Connect', + function (Server $serv, Event $object) { + Assert::eq($object->fd, 1); + } + ); + $serv->on( + 'Close', + function (Server $serv, Event $object) { + Assert::eq($object->fd, 1); + } + ); + $serv->on( + 'receive', + function (Server $serv, Event $object) { + $serv->send($object->fd, json_encode(['worker' => $serv->getWorkerId(), 'data' => $object->data])); + } + ); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_server/object/packet.phpt b/tests/swoole_server/object/packet.phpt new file mode 100644 index 00000000000..73b410fa135 --- /dev/null +++ b/tests/swoole_server/object/packet.phpt @@ -0,0 +1,58 @@ +--TEST-- +swoole_server/object: packet object +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + $client = new Swoole\Coroutine\Client(SWOOLE_SOCK_UDP); + if (!$client->connect('127.0.0.1', $pm->getFreePort())) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); + } + + $data = base64_encode(random_bytes(rand(1024, 8192))) . "\r\n\r\n";; + $client->send($data); + $recv_data = $client->recv(); + Assert::eq($recv_data, $data); + }); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_UDP); + $serv->set( + array( + "worker_num" => 1, + 'event_object' => true, + 'log_file' => '/dev/null', + ) + ); + $serv->on( + 'WorkerStart', + function (Server $serv) use ($pm) { + $pm->wakeup(); + } + ); + $serv->on( + 'packet', + function (Server $serv, Packet $object) { + $serv->sendto($object->address, $object->port, $object->data, $object->server_socket); + } + ); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_server/object/pipe_message.phpt b/tests/swoole_server/object/pipe_message.phpt new file mode 100644 index 00000000000..71df29a7eed --- /dev/null +++ b/tests/swoole_server/object/pipe_message.phpt @@ -0,0 +1,69 @@ +--TEST-- +swoole_server/object: pipe message +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + $client = new Swoole\Coroutine\Client(SWOOLE_SOCK_UDP); + if (!$client->connect('127.0.0.1', $pm->getFreePort())) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); + } + + $data = base64_encode(random_bytes(rand(1024, 8192))) . "\r\n\r\n";; + $client->send($data); + $recv_data = $client->recv(); + Assert::eq($recv_data, $data); + }); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_UDP); + $serv->set( + array( + "worker_num" => 2, + 'event_object' => true, + 'log_file' => '/dev/null', + ) + ); + $serv->on( + 'WorkerStart', + function (Server $serv) use ($pm) { + $pm->wakeup(); + } + ); + + $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); + }); + + $serv->on( + 'packet', + function (Server $serv, Packet $object) { + $serv->sendMessage($object, 1 - $serv->getWorkerId()); + } + ); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_server/object/status_info.phpt b/tests/swoole_server/object/status_info.phpt new file mode 100644 index 00000000000..3450052f9f8 --- /dev/null +++ b/tests/swoole_server/object/status_info.phpt @@ -0,0 +1,96 @@ +--TEST-- +swoole_server/object: status info +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) +{ + $cli = new 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(), "hello world"); + $cli->close(); + echo "DONE\n"; + $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', + 'event_object' => true, + 'enable_coroutine' => false, + ]); + + $serv->on("ManagerStart", function (Server $serv) use ($pm) { + $pm->wakeup(); + Timer::after(100, function () use ($serv) { + Process::kill($serv->getWorkerPid(0), SIGKILL); + }); + Timer::after(200, function () use ($serv) { + $serv->sendMessage("exit", 0); + Timer::after(100, function () use ($serv) { + $serv->send(1, 'hello world'); + }); + }); + }); + + $serv->on(Constant::EVENT_PIPE_MESSAGE, function (Server $serv, $msg) { + if ($msg->data == 'exit') { + throw new RuntimeException("error"); + } + }); + + $serv->on("Receive", function (Server $serv, $event) { + + }); + + $serv->on(Constant::EVENT_WORKER_ERROR, function (Server $serv, StatusInfo $info) { + static $count = 0; + $count++; + if ($count == 1) { + Assert::eq($info->signal, SIGKILL); + Assert::eq($info->exit_code, 0); + } elseif ($count == 2) { + Assert::eq($info->signal, 0); + Assert::eq($info->exit_code, 255); + } + }); + + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- + +Fatal error: Uncaught RuntimeException: error in %s:%d +Stack trace: +#0 [internal function]: {closure%S}(Object(Swoole\Server), Object(Swoole\Server\PipeMessage)) +#1 %s(%d): Swoole\Server->start() +#2 [internal function]: {closure%S}() +#3 %s(%d): call_user_func(Object(Closure)) +#4 %s(%d): SwooleTest\ProcessManager->runChildFunc() +#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/object/task_result.phpt b/tests/swoole_server/object/task_result.phpt new file mode 100644 index 00000000000..fedd86507f9 --- /dev/null +++ b/tests/swoole_server/object/task_result.phpt @@ -0,0 +1,64 @@ +--TEST-- +swoole_server/object: task result +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $cli = new 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(), "hello world"); + $cli->close(); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $server = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $server->set([ + 'log_level' => SWOOLE_LOG_NOTICE, + 'task_worker_num' => 4, + 'worker_num' => 1, + 'event_object' => true, + 'enable_coroutine' => false, + ]); + + $server->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + + $server->on('receive', function (Server $server, $object) { + $n = N; + while ($n--) { + $server->task($object); + } + }); + + $server->on('task', function ($server, $task) { + $server->finish(['data' => str_repeat('A', 8000), 'task' => $task->data]); + }); + + $server->on('finish', function ($server, $result) { + static $count = 0; + $count++; + if ($count == N) { + $server->send($result->data['task']->fd, 'hello world'); + } + }); + + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- 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/reload_process.phpt b/tests/swoole_server/reload_process.phpt index c15e656d389..af39035f8ba 100644 --- a/tests/swoole_server/reload_process.phpt +++ b/tests/swoole_server/reload_process.phpt @@ -3,7 +3,7 @@ swoole_server: reload in process mode --SKIPIF-- --FILE-- ---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 7e154f5971d..72fd9da61ba 100644 --- a/tests/swoole_server/send_2.phpt +++ b/tests/swoole_server/send_2.phpt @@ -8,14 +8,13 @@ require __DIR__ . '/../include/skipif.inc'; parentFunc = function ($pid) use ($pm) { $total = 0; - for ($i = 0; $i < CLIENT_N; $i++) { + for ($i = 0; $i < MAX_CONCURRENCY_MID; $i++) { go(function () use ($pm, $i, &$total) { $cli = new Co\Client(SWOOLE_SOCK_TCP); $cli->set([ @@ -29,29 +28,30 @@ $pm->parentFunc = function ($pid) use ($pm) { echo "ERROR\n"; return; } - $n = REQ_N; + $n = MAX_REQUESTS; while ($n--) { $data = $cli->recv(); 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, @@ -64,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"; - $n = REQ_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_task_worker.phpt b/tests/swoole_server/send_2m_in_task_worker.phpt index 6775bbe6ae4..5d7583a46b1 100644 --- a/tests/swoole_server/send_2m_in_task_worker.phpt +++ b/tests/swoole_server/send_2m_in_task_worker.phpt @@ -8,8 +8,6 @@ require __DIR__ . '/../include/skipif.inc'; parentFunc = function ($pid) use ($pm) { - for ($i = 0; $i < CLIENT_N; $i++) { + for ($i = 0; $i < MAX_CONCURRENCY_MID; $i++) { go(function () use ($pm, $i) { $cli = new Co\Client(SWOOLE_SOCK_TCP); $cli->set([ @@ -31,7 +29,7 @@ $pm->parentFunc = function ($pid) use ($pm) { echo "ERROR\n"; return; } - for ($i = 0; $i < REQ_N; $i++) { + for ($i = 0; $i < MAX_REQUESTS; $i++) { $sid = strval(rand(10000000, 99999999)); $send_data = str_repeat('A', 1000) . $sid; $cli->send(pack('N', strlen($send_data)) . $send_data); @@ -66,7 +64,7 @@ $pm->childFunc = function () use ($pm) { }); $serv->on('task', function (Server $serv, Server\Task $task) { $send_data = str_repeat('A', SIZE - 12) . substr($task->data['data'], -8, 8); - $serv->send($task->data['fd'], pack('N', strlen($send_data)) . $send_data); + Assert::true($serv->send($task->data['fd'], pack('N', strlen($send_data)) . $send_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 cb2846bcebe..d7c41d485cf 100644 --- a/tests/swoole_server/send_2m_in_user_process.phpt +++ b/tests/swoole_server/send_2m_in_user_process.phpt @@ -8,8 +8,6 @@ require __DIR__ . '/../include/skipif.inc'; parentFunc = function ($pid) use ($pm) { - for ($i = 0; $i < CLIENT_N; $i++) { + for ($i = 0; $i < MAX_CONCURRENCY_MID; $i++) { go(function () use ($pm, $i) { $cli = new Co\Client(SWOOLE_SOCK_TCP); $cli->set([ @@ -31,7 +29,7 @@ $pm->parentFunc = function ($pid) use ($pm) { echo "ERROR\n"; return; } - for ($i = 0; $i < REQ_N; $i++) { + for ($i = 0; $i < MAX_REQUESTS; $i++) { $sid = strval(rand(10000000, 99999999)); $send_data = str_repeat('A', 1000) . $sid; $cli->send(pack('N', strlen($send_data)) . $send_data); @@ -58,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 de685f20e96..dd8e0f79b69 100644 --- a/tests/swoole_server/send_3.phpt +++ b/tests/swoole_server/send_3.phpt @@ -8,14 +8,11 @@ require __DIR__ . '/../include/skipif.inc'; parentFunc = function ($pid) use ($pm) { $total = 0; - for ($i = 0; $i < CLIENT_N; $i++) { + for ($i = 0; $i < MAX_CONCURRENCY_MID; $i++) { go(function () use ($pm, $i, &$total) { $cli = new Co\Client(SWOOLE_SOCK_TCP); $cli->set([ @@ -29,7 +26,7 @@ $pm->parentFunc = function ($pid) use ($pm) { echo "ERROR\n"; return; } - $n = REQ_N; + $n = MAX_REQUESTS; while ($n--) { $data = $cli->recv(); Assert::assert($data); @@ -43,7 +40,7 @@ $pm->parentFunc = function ($pid) use ($pm) { } }); } - swoole_event::wait(); + Swoole\Event::wait(); echo $total . " bytes\n"; $pm->kill(); }; @@ -65,7 +62,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); }); $serv->on('connect', function (Swoole\Server $serv, $fd, $rid) { - $n = REQ_N; + $n = MAX_REQUESTS; while ($n--) { $len = rand(65536, 1024 * 1024); $send_data = str_repeat(chr(ord('A') + $n % 10), $len); diff --git a/tests/swoole_server/send_big_packet.phpt b/tests/swoole_server/send_big_packet.phpt index edb72f23072..299da107ca6 100644 --- a/tests/swoole_server/send_big_packet.phpt +++ b/tests/swoole_server/send_big_packet.phpt @@ -8,14 +8,12 @@ require __DIR__ . '/../include/skipif.inc'; parentFunc = function ($pid) use ($pm) { - for ($i = 0; $i < CLIENT_N; $i++) { + for ($i = 0; $i < MAX_CONCURRENCY_LOW; $i++) { go(function () use ($pm, $i) { $cli = new Co\Client(SWOOLE_SOCK_TCP); $cli->set([ @@ -29,7 +27,7 @@ $pm->parentFunc = function ($pid) use ($pm) { echo "ERROR\n"; return; } - for ($i = 0; $i < REQ_N; $i++) { + for ($i = 0; $i < MAX_REQUESTS_LOW; $i++) { $sid = strval(rand(10000000, 99999999)); $send_data = str_repeat('A', 1000) . $sid; $cli->send(pack('N', strlen($send_data)) . $send_data); diff --git a/tests/swoole_server/send_in_master.phpt b/tests/swoole_server/send_in_master.phpt new file mode 100644 index 00000000000..e7748236f46 --- /dev/null +++ b/tests/swoole_server/send_in_master.phpt @@ -0,0 +1,65 @@ +--TEST-- +swoole_server: send data in master process +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + Co\run(function () use ($pm) { + $cli = new Co\Client(SWOOLE_SOCK_TCP); + if ($cli->connect('127.0.0.1', $pm->getFreePort(), 100) == false) { + echo "ERROR\n"; + return; + } + $cli->send($GLOBALS['data']); + Assert::same($cli->recv(), 'Swoole: '.$GLOBALS['data']); + }); + echo "DONE\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" => 2, + 'log_file' => TEST_LOG_FILE, + )); + $serv->on(Constant::EVENT_START, function (Server $server) { + Timer::tick(50, function ($timer) use ($server) { + if ($GLOBALS['atomic']->get() == 0) { + return; + } + foreach ($server->connections as $fd) { + $server->send($fd, 'Swoole: ' . $GLOBALS['data']); + } + Timer::clear($timer); + }); + }); + $serv->on("WorkerStart", function (Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on(Constant::EVENT_RECEIVE, function (Server $serv, $fd, $rid, $data) { + $GLOBALS['atomic']->set(1); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_server/send_in_other_worker_with_base.phpt b/tests/swoole_server/send_in_other_worker_with_base.phpt new file mode 100644 index 00000000000..d5345eb9721 --- /dev/null +++ b/tests/swoole_server/send_in_other_worker_with_base.phpt @@ -0,0 +1,50 @@ +--TEST-- +swoole_server: send data in another process with base mode +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + Co\run(function () use ($pm) { + $cli = new Co\Client(SWOOLE_SOCK_TCP); + if ($cli->connect('127.0.0.1', $pm->getFreePort(), 100) == false) { + echo "ERROR\n"; + return; + } + $data = base64_encode(random_bytes(128)); + $cli->send($data); + Assert::same($cli->recv(), 'Swoole: '.$data); + }); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv->set(array( + "worker_num" => 2, + 'log_file' => TEST_LOG_FILE, + )); + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on(Constant::EVENT_PIPE_MESSAGE, function ($serv, $workerId, $msg) { + $serv->send($msg['fd'], 'Swoole: '.$msg['data']); + }); + $serv->on(Constant::EVENT_RECEIVE, function (Swoole\Server $serv, $fd, $rid, $data) { + $serv->sendMessage(['data' => $data, 'fd' => $fd], 1 - $serv->getWorkerId()); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_server/sendfile_02.phpt b/tests/swoole_server/sendfile_02.phpt index 8ecea2b2ca9..45b11456a53 100644 --- a/tests/swoole_server/sendfile_02.phpt +++ b/tests/swoole_server/sendfile_02.phpt @@ -9,11 +9,9 @@ skip_if_extension_not_exist('sockets'); parentFunc = function ($pid) use ($pm) -{ - $client = new swoole_client(SWOOLE_SOCK_TCP , SWOOLE_SOCK_SYNC); //同步阻塞 - if (!$client->connect('127.0.0.1', $pm->getFreePort())) - { +$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())) { exit("connect failed\n"); } @@ -23,12 +21,10 @@ $pm->parentFunc = function ($pid) use ($pm) $N = filesize(TEST_IMAGE); $bytes = 0; - while($bytes < $N) - { + while($bytes < $N) { $n = rand(8192, 65536); $r = $client->recv($n); - if (!$r) - { + if (!$r) { break; } usleep(10000); @@ -38,23 +34,19 @@ $pm->parentFunc = function ($pid) use ($pm) $pm->kill(); }; -$pm->childFunc = function () use ($pm) -{ - $serv = new swoole_server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP); +$pm->childFunc = function () use ($pm) { + $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, ]); - $serv->on("workerStart", function ($serv) use ($pm) - { + $serv->on("workerStart", function ($serv) use ($pm) { $pm->wakeup(); }); - $serv->on('connect', function (swoole_server $serv, $fd) - { - $serv->sendfile($fd, TEST_IMAGE); + $serv->on('connect', function (Swoole\Server $serv, $fd) { + Assert::true($serv->sendfile($fd, TEST_IMAGE)); }); - $serv->on('receive', function ($serv, $fd, $reactor_id, $data) - { + $serv->on('receive', function ($serv, $fd, $reactor_id, $data) { }); $serv->start(); diff --git a/tests/swoole_server/sendfile_ssl.phpt b/tests/swoole_server/sendfile_ssl.phpt index 39536e3584a..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,18 +43,18 @@ $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, - 'ssl_cert_file' => dirname(__DIR__) . '/include/api/swoole_http_server/localhost-ssl/server.crt', - 'ssl_key_file' => dirname(__DIR__) . '/include/api/swoole_http_server/localhost-ssl/server.key', + 'ssl_cert_file' => SSL_FILE_DIR.'/server.crt', + 'ssl_key_file' => SSL_FILE_DIR.'/server.key', ]); $serv->on("workerStart", function ($serv) use ($pm) { $pm->wakeup(); }); - $serv->on('connect', function (swoole_server $serv, $fd) { - $serv->sendfile($fd, TEST_IMAGE); + $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 new file mode 100644 index 00000000000..dc6b5aa3c52 --- /dev/null +++ b/tests/swoole_server/sleep.phpt @@ -0,0 +1,76 @@ +--TEST-- +swoole_server: sleep +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + $s = microtime(true); + Co::set([Constant::OPTION_HOOK_FLAGS => SWOOLE_HOOK_ALL]); + 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_BASE); + + $http->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null', + Constant::OPTION_ENABLE_COROUTINE => true, + Constant::OPTION_HOOK_FLAGS => SWOOLE_HOOK_ALL, + ]); + + $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_server/slow_client.phpt b/tests/swoole_server/slow_client.phpt index 0da0cbafa9f..5a0cf95237c 100644 --- a/tests/swoole_server/slow_client.phpt +++ b/tests/swoole_server/slow_client.phpt @@ -16,7 +16,7 @@ $pm = new SwooleTest\ProcessManager; $pm->parentFunc = function ($pid) use ($port) { - $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 + $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); //同步阻塞 if (!$client->connect('127.0.0.1', $port)) { exit("connect failed\n"); @@ -39,12 +39,12 @@ $pm->parentFunc = function ($pid) use ($port) $bytes += strlen($r); } Assert::same($bytes, N); - swoole_process::kill($pid); + Swoole\Process::kill($pid); }; $pm->childFunc = function () use ($pm, $port) { - $serv = new swoole_server('127.0.0.1', $port); + $serv = new Swoole\Server('127.0.0.1', $port, SWOOLE_PROCESS); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null', @@ -54,7 +54,7 @@ $pm->childFunc = function () use ($pm, $port) { $pm->wakeup(); }); - $serv->on('connect', function (swoole_server $serv, $fd) + $serv->on('connect', function (Swoole\Server $serv, $fd) { $_send_data = str_repeat("A", N); $serv->send($fd, $_send_data); diff --git a/tests/swoole_server/slow_master.phpt b/tests/swoole_server/slow_master.phpt 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 97ded29cb8f..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 swSSL_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 new file mode 100644 index 00000000000..b0a594df4f9 --- /dev/null +++ b/tests/swoole_server/ssl/dtls_big_packet.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_server/ssl: big dtls packet +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $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"); + } + //TLS max record size = 16K + $client->send("hello world".str_repeat('A', 16000)); + Assert::same($client->recv(65535), "Swoole hello world".str_repeat('A', 16000)); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $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', + 'ssl_key_file' => SSL_FILE_DIR . '/server.key', + ]); + $serv->on("workerStart", function ($serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function ($serv, $fd, $tid, $data) { + $serv->send($fd, "Swoole $data"); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_server/ssl/golang.phpt b/tests/swoole_server/ssl/golang.phpt index c58a36403b9..4f760faeb08 100644 --- a/tests/swoole_server/ssl/golang.phpt +++ b/tests/swoole_server/ssl/golang.phpt @@ -5,7 +5,7 @@ swoole_server/ssl: golang client require __DIR__ . '/../../include/skipif.inc'; $dir = __DIR__.'/code'; chdir($dir); -`go build -o go_client client.go`; +`go build -o go_client client.go >/dev/null 2>&1`; skip_if_file_not_exist( __DIR__.'/code/go_client'); ?> --FILE-- 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/ssl_send_wait.phpt b/tests/swoole_server/ssl/ssl_send_wait.phpt new file mode 100644 index 00000000000..e2566ff1293 --- /dev/null +++ b/tests/swoole_server/ssl/ssl_send_wait.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_server/ssl: send_wait support ssl +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + run(function () use ($pm) { + $client = new Client('127.0.0.1', $pm->getFreePort(), true); + + $ret = $client->get('/'); + Assert::eq('swoole', $client->body); + $pm->kill(); + echo "DONE\n"; + }); +}; +$pm->childFunc = function () use ($pm) { + $server = new Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE, SWOOLE_SOCK_TCP | SWOOLE_SSL); + + $server->set([ + 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', + 'ssl_key_file' => SSL_FILE_DIR . '/server.key', + 'ssl_ciphers' => 'ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP', + 'ssl_protocols' => defined('SWOOLE_SSL_SSLv3') ? SWOOLE_SSL_SSLv3 : 0, + 'ssl_verify_peer' => false, + 'ssl_allow_self_signed' => true, + ]); + + $server->on("workerStart", function ($serv) use ($pm) { + $pm->wakeup(); + }); + + $server->on('Receive', function (Server $s, int $clientId, int $threadId, string $data) use ($server) { + $message = "HTTP/1.1 200 OK\r\nContent-Length:6\r\nContent-Type: text/html; charset=UTF-8\r\n\r\nswoole"; + $server->sendwait($clientId, $message); + }); + + $server->start(); + +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE 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 5ff65fe1779..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 swSSL_verify (ERRNO %d): could 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 new file mode 100644 index 00000000000..2f3e9c042d7 --- /dev/null +++ b/tests/swoole_server/start_twice.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_server: start twice +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $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"); +}; + +$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, + 'enable_coroutine' => false, + 'log_file' => '/dev/null', + )); + $serv->on("WorkerStart", function (Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { + $serv->shutdown(); + }); + $n = 2; + while ($n--) { + $serv->start(); + } +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +Warning: Swoole\Server::start(): server have been shutdown, unable to execute Swoole\Server->start() in %s on line %d diff --git a/tests/swoole_server/stats_file.phpt b/tests/swoole_server/stats_file.phpt new file mode 100644 index 00000000000..62a438c7ae4 --- /dev/null +++ b/tests/swoole_server/stats_file.phpt @@ -0,0 +1,67 @@ +--TEST-- +swoole_server: stats_file +--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 = []; + 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; + } + } + }); + 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_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 new file mode 100644 index 00000000000..d8585111d8d --- /dev/null +++ b/tests/swoole_server/stop_in_workerStart.phpt @@ -0,0 +1,27 @@ +--TEST-- +swoole_server: stop worker in worker start +--SKIPIF-- + +--FILE-- +set([ + 'worker_num' => 1 +]); +$server->on('Receive', function (Server $server, int $fd, int $reactorId, string $data) { +}); +$server->on('WorkerStart', function (Server $server, int $workid) { + $server->stop(); +}); +$server->on('WorkerStop', function (Server $server, int $workid) { + usleep(100000); + $server->shutdown(); +}); +$server->start(); +?> +--EXPECTF-- diff --git a/tests/swoole_server/systemd_fds.phpt b/tests/swoole_server/systemd_fds.phpt new file mode 100644 index 00000000000..e36d5c3b273 --- /dev/null +++ b/tests/swoole_server/systemd_fds.phpt @@ -0,0 +1,94 @@ +--TEST-- +swoole_server: systemd fds +--SKIPIF-- + +--FILE-- +initFreePorts(2); +if (HAVE_IPV6) { + $pm->initFreeIPv6Ports(2); +} +$pm->parentFunc = function ($pid) use ($pm) { + $test_func = function ($type, $host, $port = 0) { + $client = new Client($type); + Assert::notEmpty($client->connect($host, $port)); + $client->send("SUCCESS"); + Assert::eq($client->recv(), 'SUCCESS' . PHP_EOL); + $client->close(); + }; + + $test_func(SWOOLE_SOCK_TCP, "127.0.0.1", $pm->getFreePort(0)); + $test_func(SWOOLE_SOCK_UDP, "127.0.0.1", $pm->getFreePort(1)); + + if (HAVE_IPV6) { + $test_func(SWOOLE_SOCK_TCP6, "::1", $pm->getFreePort(2)); + $test_func(SWOOLE_SOCK_UDP6, "::1", $pm->getFreePort(3)); + } + + $test_func(SWOOLE_SOCK_UNIX_STREAM, UNIX_SOCK_1); + $test_func(SWOOLE_SOCK_UNIX_DGRAM, UNIX_SOCK_2); + + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $sockets = []; + $start_fd = swoole_array(scandir('/proc/self/fd'))->sort()->last(); + 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); + 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('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)); + + $serv = new Server('SYSTEMD', 0, SWOOLE_BASE); + + $serv->set(["worker_num" => 1, 'log_file' => '/dev/null',]); + $serv->on("WorkerStart", function (Server $serv) use ($pm) { + $pm->wakeup(); + }); + + $serv->on("packet", function (Server $serv, $data, $addr) { + // var_dump($addr); + $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->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- 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 9289c6fc5a3..bc4ab48e951 100644 --- a/tests/swoole_server/task/finish_timeout.phpt +++ b/tests/swoole_server/task/finish_timeout.phpt @@ -24,12 +24,16 @@ $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( [ 'log_file' => TMP_LOG_FILE, + 'log_level' => SWOOLE_LOG_NOTICE, 'task_worker_num' => 1, + 'socket_send_timeout' => 1.0, 'worker_num' => 1, + 'enable_coroutine' => false, ] ); $server->on('workerStart', function () use ($pm) { @@ -60,5 +64,5 @@ $pm->run(); unlink(TMP_LOG_FILE); ?> --EXPECTF-- -[%s] WARNING swSocket_write_blocking(:%d): write %d bytes failed, Error: Resource temporarily unavailable[11] -[%s] WARNING swTaskWorker_finish(:%d): TaskWorker: send result to worker failed, Error: Resource temporarily unavailable[11] +[%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/invalid_packet.phpt b/tests/swoole_server/task/invalid_packet.phpt new file mode 100644 index 00000000000..184d9f479c9 --- /dev/null +++ b/tests/swoole_server/task/invalid_packet.phpt @@ -0,0 +1,62 @@ +--TEST-- +swoole_server/task: invalid packet +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $data = '{"tid":17732683638813521,"out_trade_no":"dm5601993521","runMethod":"\\Action\\Mpay\\Uni\\UniApiV3Act:jsonDrive"}'; + $queueId = msg_get_queue(MSGQ_KEY); + if ($queueId === false) { + throw new \Swoole\Exception("msg_get_queue() failed."); + } + Assert::true(msg_send($queueId, 1, $data)); + Assert::true(msg_send($queueId, 1, Swoole\Server\Task::pack($data), false)); + $pm->wait(); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm, $file, $result) { + ini_set('swoole.display_errors', 'Off'); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv->set(array( + "worker_num" => 2, + 'task_worker_num' => 1, + 'task_ipc_mode' => 3, + 'message_queue_key' => MSGQ_KEY, + 'log_file' => $file, + )); + $serv->on("WorkerStart", function (Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { + + }); + $serv->on('task', function (Server $serv, $task_id, $worker_id, $data) use ($pm, $result) { + $pm->wakeup(); + $result->add(1); + }); + + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); + +// echo file_get_contents($file); +Assert::true(swoole_string(file_get_contents($file))->contains('ProcessPool_worker_loop: bad task packet')); +unlink($file); +Assert::eq($result->get(), 1); +?> +--EXPECT-- 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 new file mode 100644 index 00000000000..9ddbf8564d0 --- /dev/null +++ b/tests/swoole_server/task/scheduler_warning.phpt @@ -0,0 +1,75 @@ +--TEST-- +swoole_server/task: scheduler warning +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + $client = new Client(SWOOLE_SOCK_UDP, SWOOLE_SOCK_SYNC); + if (!$client->connect('127.0.0.1', $pm->getFreePort(), 2)) { + exit("connect failed\n"); + } + $client->send("ping"); + echo $client->recv(); + sleep(2); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm, $counter) { + $serv = new Server("127.0.0.1", $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_UDP); + + $serv->set([ + 'worker_num' => 1, + 'task_worker_num' => 2, + 'log_file' => LOG_FILE, + 'log_level' => SWOOLE_LOG_NOTICE, + ]); + + $serv->on("WorkerStart", function (Server $serv) use ($pm) { + if ($serv->taskworker) { + $pm->wakeup(); + } + }); + + $serv->on('Packet', function (Server $serv, string $data, array $clientInfo) { + $n = N; + while ($n--) { + $serv->task(['data' => $data, 'client' => $clientInfo]); + usleep(10000); + } + }); + + $serv->on('Task', function (Server $serv, $taskId, int $workerId, $data) use ($pm, $counter) { + static $sleep = false; + if (!$sleep) { + $sleep = true; + sleep(1); + } + if ($counter->add() == N) { + $serv->sendto($data['client']['address'], $data['client']['port'], "DONE\n"); + } + }); + + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); + +echo file_get_contents(LOG_FILE); +unlink(LOG_FILE); +?> +--EXPECTF-- +DONE +[%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 fc13c3acf64..8abab97ec24 100644 --- a/tests/swoole_server/unregistered_signal.phpt +++ b/tests/swoole_server/unregistered_signal.phpt @@ -3,7 +3,7 @@ swoole_server: unregistered signal --SKIPIF-- --FILE-- 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 16a8d9ab5ee..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"); @@ -56,7 +56,7 @@ $pm->parentFunc = function ($pid) use ($pm) $recv = $client->recv(); echo $recv; - //echo "send ".TestServer::PKG_NUM." packet sucess, send $bytes bytes\n"; + //echo "send ".TestServer::$PKG_NUM." packet sucess, send $bytes bytes\n"; $client->close(); }; @@ -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 b3fad0933fc..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"); @@ -27,8 +26,8 @@ $pm->childFunc = function () use ($pm) { $server = new Server('0.0.0.0', $pm->getFreePort(), true); $server->set([ 'log_file' => '/dev/null', - 'ssl_cert_file' => dirname(__DIR__) . '/include/api/swoole_http_server/localhost-ssl/server.crt', - 'ssl_key_file' => dirname(__DIR__) . '/include/api/swoole_http_server/localhost-ssl/server.key', + 'ssl_cert_file' => SSL_FILE_DIR.'/server.crt', + 'ssl_key_file' => SSL_FILE_DIR.'/server.key', ]); $server->handle(function (Connection $conn) use ($server) { $data = $conn->recv(); @@ -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 81b18cd5808..fcbc99a56e1 100644 --- a/tests/swoole_server_port/duplicate_registered.phpt +++ b/tests/swoole_server_port/duplicate_registered.phpt @@ -5,12 +5,16 @@ swoole_server_port: duplicate registered --FILE-- on('receive', function () { }); -$mem = memory_get_usage(); +Assert::same(true, !!'load Assert'); +$mem = null; for ($n = 1000; $n--;) { - Assert::same($mem, memory_get_usage()); $server->on('receive', function () { }); + if ($mem === null) { + $mem = memory_get_usage(); + } + Assert::same(memory_get_usage(), $mem); } echo "DONE\n"; ?> 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/check_writev_readv_param_type.phpt b/tests/swoole_socket_coro/check_writev_readv_param_type.phpt new file mode 100644 index 00000000000..561d4d54e65 --- /dev/null +++ b/tests/swoole_socket_coro/check_writev_readv_param_type.phpt @@ -0,0 +1,36 @@ +--TEST-- +swoole_socket_coro: check writev and readv param type +--SKIPIF-- + +--FILE-- +writeVectorAll($iovector); + + } catch (\Exception $e) { + echo $e->getMessage() . PHP_EOL; + } + + try { + $iovector = [1, '!']; + $conn->readVectorAll($iovector); + } catch (\Exception $e) { + echo $e->getMessage() . PHP_EOL; + } +}); + +echo "DONE\n"; +?> +--EXPECTF-- +Item #[0] must be of type string, %s given +Item #[1] must be of type int, %s given +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/iov_max.phpt b/tests/swoole_socket_coro/iov_max.phpt new file mode 100644 index 00000000000..81e8fe57f9e --- /dev/null +++ b/tests/swoole_socket_coro/iov_max.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_socket_coro: iov max +--SKIPIF-- + +--FILE-- +writeVectorAll($iovector)); + Assert::eq($conn->errCode, SOCKET_EINVAL); + Assert::eq($conn->errMsg, "The maximum of iov count is " . SWOOLE_IOV_MAX); + + Assert::false($conn->readVectorAll($iovectorLength)); + Assert::eq($conn->errCode, SOCKET_EINVAL); + Assert::eq($conn->errMsg, "The maximum of iov count is " . SWOOLE_IOV_MAX); +}); + +echo "DONE\n"; +?> +--EXPECT-- +DONE diff --git a/tests/swoole_socket_coro/protocol/bug_3586.phpt b/tests/swoole_socket_coro/protocol/bug_3586.phpt new file mode 100644 index 00000000000..391c57d6263 --- /dev/null +++ b/tests/swoole_socket_coro/protocol/bug_3586.phpt @@ -0,0 +1,68 @@ +--TEST-- +swoole_socket_coro/protocol: bug GitHub#3586 +--SKIPIF-- + +--FILE-- +bind('127.0.0.1')); + $server->setProtocol([ + 'open_length_check' => true, + 'package_length_func' => function (string $data) { + if (strlen($data) < strlen(GREETER)) { + return 0; + } else { + return strlen(GREETER); + } + } + ]); + Assert::assert($server->listen()); + while (true) { + $client = $server->accept(-1); + if (!$client) { + break; + } + /* @var $client Socket */ + Coroutine::create(function () use ($client, $server) { + Assert::isInstanceOf($client, Socket::class); + Assert::eq($client->recvPacket(), GREETER); + Assert::greaterThan($client->sendAll(GREETER), 1); + $client->close(); + $GLOBALS['counter'] ++; + if ($GLOBALS['counter'] == CO_NUM) { + $server->close(); + } + }); + } + }); + + for ($i = 0; $i < CO_NUM; $i++) { + Coroutine::create(function () use ($server) { + $client = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + Assert::true($client->connect('127.0.0.1', $server->getsockname()['port'])); + if (rand(0, 5) > 3) { + $client->send(substr(GREETER, 0, 5)); + Co::sleep(0.1); + $client->send(substr(GREETER, 5)); + } else { + $client->send(GREETER); + } + Assert::eq($client->recvPacket(), GREETER); + $client->close(); + }); + } +}); +?> +--EXPECT-- diff --git a/tests/swoole_socket_coro/readVectorAll.phpt b/tests/swoole_socket_coro/readVectorAll.phpt new file mode 100644 index 00000000000..a6625fdc065 --- /dev/null +++ b/tests/swoole_socket_coro/readVectorAll.phpt @@ -0,0 +1,70 @@ +--TEST-- +swoole_socket_coro: readVector all +--SKIPIF-- + +--FILE-- +bind('127.0.0.1', $port)); + Assert::assert($server->listen(512)); + $conn = $server->accept(); + Assert::assert($conn instanceof Socket); + Assert::assert($conn->fd > 0); + + global $packedStr, $iovector, $totalLength2; + Assert::assert($conn instanceof Socket); + $iov = []; + for ($i = 0; $i < 10; $i++) { + $iov[] = 1024 * 128; + } + + Assert::eq($conn->readVectorAll($iov), $iovector); + Assert::eq(implode('', $conn->readVectorAll($iov)), substr($packedStr, 0, $totalLength2)); + + // has close + Assert::eq($conn->readVectorAll($iov), []); + }); + + go(function () use ($server, $port) { + global $packedStr, $totalLength, $totalLength2; + + $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + Assert::assert($conn->connect('127.0.0.1', $port)); + + $ret = $conn->sendAll($packedStr); + Assert::eq($ret, $totalLength); + + $ret = $conn->sendAll(substr($packedStr, 0, $totalLength2)); + Assert::eq($ret, $totalLength2); + + $server->close(); + }); +}); + +echo "DONE\n"; +?> +--EXPECT-- +DONE diff --git a/tests/swoole_socket_coro/readVectorAll_ssl.phpt b/tests/swoole_socket_coro/readVectorAll_ssl.phpt new file mode 100644 index 00000000000..8aeca4d79db --- /dev/null +++ b/tests/swoole_socket_coro/readVectorAll_ssl.phpt @@ -0,0 +1,71 @@ +--TEST-- +swoole_socket_coro: readVectorAll with ssl +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + global $totalLength, $packedStr; + $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + $conn->setProtocol([ + 'open_ssl' => true, + ]); + $conn->connect('127.0.0.1', $pm->getFreePort()); + + $ret = $conn->sendAll($packedStr); + Assert::eq($ret, $totalLength); + $conn->recv(); + echo "DONE\n"; + }); +}; + +$pm->childFunc = function () use ($pm) { + run(function () use ($pm) { + global $totalLength, $iovector; + $socket = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + $socket->setProtocol([ + 'open_ssl' => true, + 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', + 'ssl_key_file' => SSL_FILE_DIR . '/server.key', + ]); + Assert::assert($socket->bind('127.0.0.1', $pm->getFreePort())); + Assert::assert($socket->listen(MAX_CONCURRENCY)); + + /** @var Socket */ + $conn = $socket->accept(); + $conn->sslHandshake(); + + $iov = []; + for ($i = 0; $i < 10; $i++) { + $iov[] = 1024 * 128; + } + + Assert::eq($conn->readVectorAll($iov), $iovector); + $conn->send('close'); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_socket_coro/readVector_ssl.phpt b/tests/swoole_socket_coro/readVector_ssl.phpt new file mode 100644 index 00000000000..77a2d6f5b34 --- /dev/null +++ b/tests/swoole_socket_coro/readVector_ssl.phpt @@ -0,0 +1,55 @@ +--TEST-- +swoole_socket_coro: readVector with ssl +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + + $conn->setProtocol([ + 'open_ssl' => true, + ]); + $conn->connect('127.0.0.1', $pm->getFreePort()); + + $ret = $conn->send('helloworld'); + Assert::eq($ret, strlen('helloworld')); + $conn->recv(); + echo "DONE\n"; + }); +}; + +$pm->childFunc = function () use ($pm) { + run(function () use ($pm) { + global $totalLength, $packedStr; + $socket = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + $socket->setProtocol([ + 'open_ssl' => true, + 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', + 'ssl_key_file' => SSL_FILE_DIR . '/server.key', + ]); + Assert::assert($socket->bind('127.0.0.1', $pm->getFreePort())); + Assert::assert($socket->listen(MAX_CONCURRENCY)); + + /** @var Socket */ + $conn = $socket->accept(); + $conn->sslHandshake(); + + Assert::eq($conn->readVector([5, 5]), ['hello', 'world']); + $conn->send('close'); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_socket_coro/readVector_ssl_eagain.phpt b/tests/swoole_socket_coro/readVector_ssl_eagain.phpt new file mode 100644 index 00000000000..28cd27f5fe8 --- /dev/null +++ b/tests/swoole_socket_coro/readVector_ssl_eagain.phpt @@ -0,0 +1,73 @@ +--TEST-- +swoole_socket_coro: readVector_ssl eagain +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + global $totalLength, $packedStr; + $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + $conn->setProtocol([ + 'open_ssl' => true, + ]); + $conn->connect('127.0.0.1', $pm->getFreePort()); + + Coroutine::sleep(0.5); + $ret = $conn->sendAll($packedStr); + Assert::eq($ret, $totalLength); + $conn->recv(); + echo "DONE\n"; + }); +}; + +$pm->childFunc = function () use ($pm) { + run(function () use ($pm) { + global $totalLength, $iovector; + $socket = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + $socket->setProtocol([ + 'open_ssl' => true, + 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', + 'ssl_key_file' => SSL_FILE_DIR . '/server.key', + ]); + Assert::assert($socket->bind('127.0.0.1', $pm->getFreePort())); + Assert::assert($socket->listen(MAX_CONCURRENCY)); + + /** @var Socket */ + $conn = $socket->accept(); + $conn->sslHandshake(); + + $iov = []; + for ($i = 0; $i < 10; $i++) { + $iov[] = 1024 * 128; + } + + Assert::eq($conn->readVectorAll($iov), $iovector); + $conn->send('close'); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_socket_coro/readv.phpt b/tests/swoole_socket_coro/readv.phpt new file mode 100644 index 00000000000..d94dd15fd21 --- /dev/null +++ b/tests/swoole_socket_coro/readv.phpt @@ -0,0 +1,56 @@ +--TEST-- +swoole_socket_coro: readv test +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + run(function () use ($pm) { + $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + $conn->connect('127.0.0.1', $pm->getFreePort()); + $conn->send('hello'); + $iov = [5, 5]; + $ret = $conn->readVector($iov); + Assert::same($ret, ['hello', 'world']); + + $conn->send('hello'); + $iov = [5, 7]; + $ret = $conn->readVector($iov); + Assert::same($ret, ['hello', 'world']); + + $conn->send('hello'); + $iov = [5, 7, 7]; + $ret = $conn->readVector($iov); + Assert::same($ret, ['hello', 'world']); + $pm->kill(); + echo "DONE\n"; + }); +}; +$pm->childFunc = function () use ($pm) { + run(function () use ($pm) { + $server = new Server('127.0.0.1', $pm->getFreePort(), false); + + $server->handle(function (Connection $conn) use ($server) { + while (true) { + $conn->recv(); + $conn->send("helloworld"); + } + }); + + $server->start(); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_socket_coro/readv_eagain.phpt b/tests/swoole_socket_coro/readv_eagain.phpt new file mode 100644 index 00000000000..3e82abdfdea --- /dev/null +++ b/tests/swoole_socket_coro/readv_eagain.phpt @@ -0,0 +1,74 @@ +--TEST-- +swoole_socket_coro: readv with eagain +--SKIPIF-- + +--FILE-- +bind('127.0.0.1', $port)); + Assert::assert($server->listen(512)); + $conn = $server->accept(); + Assert::assert($conn instanceof Socket); + Assert::assert($conn->fd > 0); + + global $packedStr, $iovector, $totalLength2; + Assert::assert($conn instanceof Socket); + $iov = []; + for ($i = 0; $i < 10; $i++) { + $iov[] = 1024 * 128; + } + + Assert::eq($conn->readVectorAll($iov), $iovector); + Assert::eq(implode('', $conn->readVectorAll($iov)), substr($packedStr, 0, $totalLength2)); + + // has close + Assert::eq($conn->readVectorAll($iov), []); + }); + + go(function () use ($server, $port) { + global $packedStr, $totalLength, $totalLength2; + + $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + Assert::assert($conn->connect('127.0.0.1', $port)); + + // Let readVectorAll trigger EAGAIN (verify the correctness of the error returned by readVectorAll) + Coroutine::sleep(0.5); + + $ret = $conn->sendAll($packedStr); + Assert::eq($ret, $totalLength); + + $ret = $conn->sendAll(substr($packedStr, 0, $totalLength2)); + Assert::eq($ret, $totalLength2); + + $server->close(); + }); +}); + +echo "DONE\n"; +?> +--EXPECT-- +DONE diff --git a/tests/swoole_socket_coro/recvAll.phpt b/tests/swoole_socket_coro/recvAll.phpt new file mode 100644 index 00000000000..78e149550b2 --- /dev/null +++ b/tests/swoole_socket_coro/recvAll.phpt @@ -0,0 +1,103 @@ +--TEST-- +swoole_socket_coro: recvAll +--SKIPIF-- + +--FILE-- +initRandomDataArray(4, 512 * 1024); + +const CASE_LIST = '4'; + +$pm->parentFunc = function ($pid) use ($pm) { + Co\run(function () use ($pm) { + $conn = new Swoole\Coroutine\Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + Assert::assert($conn->connect('127.0.0.1', $pm->getFreePort())); + /** + * Case1,第一次接收到少量內容,随后分多次触发可读事件接收更多数据片 + */ + if (swoole_string(CASE_LIST)->split(',')->contains(1, false)) { + $conn->send("case-1"); + Co::sleep(0.001); + $header = $conn->recvAll(4); + $len = unpack('Nlen', $header)['len']; + $body = $conn->recvAll($len); + Assert::eq($body, $pm->getRandomDataElement(0)); + } + /** + * Case2,第一次未接收到任何内容,返回 EAGAIN,监听可读,随后分多次触发可读事件接收更多数据片 + */ + if (swoole_string(CASE_LIST)->split(',')->contains(2, false)) { + $conn->send("case-2"); + $header = $conn->recvAll(4); + $len = unpack('Nlen', $header)['len']; + $body = $conn->recvAll($len); + Assert::eq($body, $pm->getRandomDataElement(1)); + } + /** + * Case3,第一次收到少量内容,第二次服务端关闭连接 + */ + if (swoole_string(CASE_LIST)->split(',')->contains(3, false)) { + $conn->send("case-3"); + Co::sleep(0.001); + $header = $conn->recvAll(4); + $len = unpack('Nlen', $header)['len']; + $body = $conn->recvAll($len); + Assert::eq($body, substr($pm->getRandomDataElement(2), 0,1024)); + } + /** + * Case4,接收4次,第5次服务端关闭连接 + */ + if (swoole_string(CASE_LIST)->split(',')->contains(4, false)) { + $conn->send("case-4"); + Co::sleep(0.001); + $header = $conn->recvAll(4); + $len = unpack('Nlen', $header)['len']; + $body = $conn->recvAll($len); + Assert::eq($body, substr($pm->getRandomDataElement(3), 0, 65536 * 3)); + } + }); + $pm->kill(); + echo "DONE\n"; +}; +$pm->childFunc = function () use ($pm) { + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $server->on('WorkerStart', function () use ($pm) { + $pm->wakeup(); + }); + $server->on('Receive', function (Swoole\Server $server, $fd, $rid, $data) use ($pm) { + if ($data == 'case-1') { + $body = $pm->getRandomDataElement(0); + $data = pack('N', strlen($body)) . $body; + $server->send($fd, substr($data, 0, 1024)); + usleep(40000); + $server->send($fd, substr($data, 1024)); + } elseif ($data == 'case-2') { + $body = $pm->getRandomDataElement(1); + $server->send($fd, pack('N', strlen($body))); + usleep(40000); + $server->send($fd, $body); + } elseif ($data == 'case-3') { + $body = $pm->getRandomDataElement(2); + $server->send($fd, pack('N', strlen($body))); + $server->send($fd, substr($body, 0, 1024)); + usleep(40000); + $server->close($fd); + } elseif ($data == 'case-4') { + $body = $pm->getRandomDataElement(3); + $server->send($fd, pack('N', strlen($body))); + for ($i = 0; $i < 3; $i++) { + $server->send($fd, substr($body, $i * 65536, 65536)); + usleep(40000); + } + $server->close($fd); + } + }); + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_socket_coro/recv_bad_packet.phpt b/tests/swoole_socket_coro/recv_bad_packet.phpt new file mode 100644 index 00000000000..77f7263e286 --- /dev/null +++ b/tests/swoole_socket_coro/recv_bad_packet.phpt @@ -0,0 +1,69 @@ +--TEST-- +swoole_socket_coro: recv bad packet +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + for ($i = 0; $i < MAX_CONCURRENCY_MID; $i++) { + go(function () use ($pm, $i) { + $cli = new Co\Socket(AF_INET, SOCK_STREAM, 0); + $cli->setProtocol([ + '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()) == false) { + echo "ERROR\n"; + return; + } + for ($i = 0; $i < 3; $i++) { + $sid = strval(rand(100000, 999999)); + $send_data = str_repeat('A', 1000) . $sid; + $cli->send(pack('N', strlen($send_data)) . $send_data); + $data = $cli->recvPacket(0.2); + Assert::isEmpty($data); + } + }); + } + Swoole\Event::wait(); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $serv->set(array( + "worker_num" => 4, + '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, + )); + $serv->on("WorkerStart", function (Swoole\Server $serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Swoole\Server $serv, $fd, $rid, $data) { + $len = rand(1024, 8192); + $send_data = str_repeat('A', $len); + //bad packet + $serv->send($fd, pack('N', SIZE) . $send_data); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--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_timeout.phpt b/tests/swoole_socket_coro/recv_timeout.phpt index f75f9981959..0cb0144f920 100644 --- a/tests/swoole_socket_coro/recv_timeout.phpt +++ b/tests/swoole_socket_coro/recv_timeout.phpt @@ -14,7 +14,8 @@ $pm->parentFunc = function ($pid) use ($pm) { $conn->send(json_encode(['data' => 'hello'])); $timeout = ms_random(0.1, 1); $s = microtime(true); - switch (mt_rand(0, 2)) { + $case = mt_rand(0, 2); + switch ($case) { case 0: $ret = $conn->recv(1024, $timeout); break; @@ -26,7 +27,7 @@ $pm->parentFunc = function ($pid) use ($pm) { break; } time_approximate($timeout, microtime(true) - $s); - Assert::assert($ret === false); + Assert::assert($ret === false, "[case=$case]"); Assert::assert($conn->errCode == SOCKET_ETIMEDOUT); }); } 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_socket_coro/writeVectorAll.phpt b/tests/swoole_socket_coro/writeVectorAll.phpt new file mode 100644 index 00000000000..2beeb04232e --- /dev/null +++ b/tests/swoole_socket_coro/writeVectorAll.phpt @@ -0,0 +1,54 @@ +--TEST-- +swoole_socket_coro: writeVector all +--SKIPIF-- + +--FILE-- +bind('127.0.0.1', $port)); + Assert::assert($server->listen(512)); + $conn = $server->accept(); + Assert::assert($conn instanceof Socket); + Assert::assert($conn->fd > 0); + + global $totalLength, $packedStr; + Assert::assert($conn instanceof Socket); + Assert::eq($conn->recvAll($totalLength), $packedStr); + }); + + go(function () use ($server, $port) { + global $iovector, $totalLength; + + $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + Assert::assert($conn->connect('127.0.0.1', $port)); + $ret = $conn->writeVectorAll($iovector); + Assert::eq($ret, $totalLength); + $server->close(); + }); +}); + +echo "DONE\n"; +?> +--EXPECT-- +DONE diff --git a/tests/swoole_socket_coro/writeVectorAll_ssl.phpt b/tests/swoole_socket_coro/writeVectorAll_ssl.phpt new file mode 100644 index 00000000000..d2d69e795cc --- /dev/null +++ b/tests/swoole_socket_coro/writeVectorAll_ssl.phpt @@ -0,0 +1,67 @@ +--TEST-- +swoole_socket_coro: writeVector with ssl +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + global $totalLength, $iovector; + $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + $conn->setProtocol([ + 'open_ssl' => true, + ]); + $conn->connect('127.0.0.1', $pm->getFreePort()); + + $ret = $conn->writeVectorAll($iovector); + Assert::eq($ret, $totalLength); + $conn->recv(); + echo "DONE\n"; + }); +}; + +$pm->childFunc = function () use ($pm) { + run(function () use ($pm) { + global $totalLength, $packedStr; + $socket = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + $socket->setProtocol([ + 'open_ssl' => true, + 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', + 'ssl_key_file' => SSL_FILE_DIR . '/server.key', + ]); + Assert::assert($socket->bind('127.0.0.1', $pm->getFreePort())); + Assert::assert($socket->listen(MAX_CONCURRENCY)); + + /** @var Socket */ + $conn = $socket->accept(); + $conn->sslHandshake(); + + Assert::eq($conn->recvAll($totalLength), $packedStr, -1); + $conn->send('close'); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_socket_coro/writeVector_ssl.phpt b/tests/swoole_socket_coro/writeVector_ssl.phpt new file mode 100644 index 00000000000..2491486a25c --- /dev/null +++ b/tests/swoole_socket_coro/writeVector_ssl.phpt @@ -0,0 +1,50 @@ +--TEST-- +swoole_socket_coro: writeVector with ssl +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + + $conn->setProtocol([ + 'open_ssl' => true, + ]); + $conn->connect('127.0.0.1', $pm->getFreePort()); + + $iov = ['hello', 'world']; + $ret = $conn->writeVector($iov); + Assert::eq($ret, strlen('helloworld')); + $pm->kill(); + echo "DONE\n"; + }); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new 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', + ]); + $serv->on("workerStart", function ($serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function ($serv, $fd, $tid, $data) { + Assert::eq($data, 'helloworld'); + }); + $serv->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_socket_coro/writeVector_ssl_eagain.phpt b/tests/swoole_socket_coro/writeVector_ssl_eagain.phpt new file mode 100644 index 00000000000..682c9ab9acb --- /dev/null +++ b/tests/swoole_socket_coro/writeVector_ssl_eagain.phpt @@ -0,0 +1,70 @@ +--TEST-- +swoole_socket_coro: writeVector_ssl eagain +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + global $totalLength, $iovector; + $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + $conn->setProtocol([ + 'open_ssl' => true, + ]); + $conn->connect('127.0.0.1', $pm->getFreePort()); + + $ret = $conn->writeVectorAll($iovector); + Assert::eq($ret, $totalLength); + $conn->recv(); + echo "DONE\n"; + }); +}; + +$pm->childFunc = function () use ($pm) { + run(function () use ($pm) { + global $totalLength, $packedStr; + $socket = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + $socket->setProtocol([ + 'open_ssl' => true, + 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', + 'ssl_key_file' => SSL_FILE_DIR . '/server.key', + ]); + Assert::assert($socket->bind('127.0.0.1', $pm->getFreePort())); + Assert::assert($socket->listen(MAX_CONCURRENCY)); + + /** @var Socket */ + $conn = $socket->accept(); + $conn->sslHandshake(); + + Coroutine::sleep(0.5); + + Assert::eq($conn->recvAll($totalLength), $packedStr, -1); + $conn->send('close'); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_socket_coro/writev.phpt b/tests/swoole_socket_coro/writev.phpt new file mode 100644 index 00000000000..5fd903de91a --- /dev/null +++ b/tests/swoole_socket_coro/writev.phpt @@ -0,0 +1,53 @@ +--TEST-- +swoole_socket_coro: writev test +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + run(function () use ($pm) { + $requestLine = "POST / HTTP/1.1\r\n"; + $header = "Host: 127.0.0.1\r\n"; + $header .= "Connection: keep-alive\r\n"; + $header .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n"; + $header .= "Content-Length: 5\r\n"; + $header .= "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36\r\n"; + $header .= "\r\n"; + + $body = 'hello'; + + $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + $conn->connect('127.0.0.1', $pm->getFreePort()); + $ret = $conn->writeVector([$requestLine, $header, $body]); + Assert::same($ret, strlen($requestLine) + strlen($header) + strlen($body)); + $ret = $conn->recv(); + Assert::contains($ret, 'world'); + $pm->kill(); + echo "DONE\n"; + }); +}; +$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) { + Assert::same($request->getContent(), 'hello'); + $response->end('world'); + }); + + $server->start(); + }); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_socket_coro/writev_eagain.phpt b/tests/swoole_socket_coro/writev_eagain.phpt new file mode 100644 index 00000000000..92322e73e19 --- /dev/null +++ b/tests/swoole_socket_coro/writev_eagain.phpt @@ -0,0 +1,58 @@ +--TEST-- +swoole_socket_coro: writev with eagain +--SKIPIF-- + +--FILE-- +bind('127.0.0.1', $port)); + Assert::assert($server->listen(512)); + $conn = $server->accept(); + Assert::assert($conn instanceof Socket); + Assert::assert($conn->fd > 0); + + global $totalLength, $packedStr; + Assert::assert($conn instanceof Socket); + + // Let writeVectorAll trigger EAGAIN (verify the correctness of the error returned by writeVectorAll) + Coroutine::sleep(0.5); + Assert::eq($conn->recvAll($totalLength), $packedStr); + }); + + go(function () use ($server, $port) { + global $iovector, $totalLength; + + $conn = new Socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + Assert::assert($conn->connect('127.0.0.1', $port)); + $ret = $conn->writeVectorAll($iovector); + Assert::eq($ret, $totalLength); + $server->close(); + }); +}); + +echo "DONE\n"; +?> +--EXPECT-- +DONE diff --git a/tests/swoole_table/bug_2263.phpt b/tests/swoole_table/bug_2263.phpt index dd8763c6881..225f6a42499 100644 --- a/tests/swoole_table/bug_2263.phpt +++ b/tests/swoole_table/bug_2263.phpt @@ -18,7 +18,7 @@ $table->set("44984", ['data' => '2']); $table->del("1234567890"); foreach($table as $ip => $row) { - echo $ip; + echo $ip."\n"; } ?> 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/get_after_destroy.phpt b/tests/swoole_table/get_after_destroy.phpt new file mode 100644 index 00000000000..59dc9c4b7d3 --- /dev/null +++ b/tests/swoole_table/get_after_destroy.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_table: get after destroy +--SKIPIF-- + +--FILE-- +column('id', Table::TYPE_INT); +$table->column('name', Table::TYPE_STRING, 10); +$table->create(); + +$table->set('1', ['id' => 1, 'name' => 'rango']); + +Assert::eq($table->get('1')['id'], 1); +Assert::eq($table->get('1')['name'], 'rango'); + +$proc = new Process(function () use ($table) { + usleep(10000); + Assert::eq($table->get('1')['id'], 2); + Assert::eq($table->get('1')['name'], ''); +}, true, SOCK_STREAM); + +$proc->start(); + +$table->destroy(); + +$output = $proc->read(); +Assert::contains($output, 'table is not created or has been destroyed'); +$retval = Process::wait(); +Assert::eq($retval['code'], 255); +?> +--EXPECT-- diff --git a/tests/swoole_table/get_before_create.phpt b/tests/swoole_table/get_before_create.phpt new file mode 100644 index 00000000000..4ceabb97f02 --- /dev/null +++ b/tests/swoole_table/get_before_create.phpt @@ -0,0 +1,29 @@ +--TEST-- +swoole_table: get before create +--SKIPIF-- + +--FILE-- +column('id', Table::TYPE_INT); + $table->column('name', Table::TYPE_STRING, 10); + + Assert::eq($table->get('1')['id'], 1); + Assert::eq($table->get('1')['name'], 'rango'); +}, true, SOCK_STREAM); + +$proc->start(); + +$output = $proc->read(); +Assert::contains($output, 'table is not created or has been destroyed'); +$retval = Process::wait(); +Assert::eq($retval['code'], 255); +?> +--EXPECT-- diff --git a/tests/swoole_table/incr_after_del.phpt b/tests/swoole_table/incr_after_del.phpt new file mode 100644 index 00000000000..76c537c6f5b --- /dev/null +++ b/tests/swoole_table/incr_after_del.phpt @@ -0,0 +1,27 @@ +--TEST-- +swoole_table: incr after del +--SKIPIF-- + +--FILE-- +column('id', \Swoole\Table::TYPE_INT); +$table->create(); + +$table->set('1', ['id' => 1,]); + +for($i=0;$i<10;$i++) { + $table->incr('1', 'id', 1); +} + +Assert::eq($table->get('1')['id'], 11); +Assert::true($table->del('1')); +Assert::false($table->get('1')); + +for($i = 0; $i < 10; $i++) { + $table->incr('1', 'id', 1); +} +Assert::eq($table->get('1')['id'], 10); +?> +--EXPECT-- 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/set_after_del.phpt b/tests/swoole_table/set_after_del.phpt new file mode 100644 index 00000000000..5b88a8db3d1 --- /dev/null +++ b/tests/swoole_table/set_after_del.phpt @@ -0,0 +1,24 @@ +--TEST-- +swoole_table: set after del +--SKIPIF-- + +--FILE-- +column('id', \Swoole\Table::TYPE_INT); +$table->column('name', \Swoole\Table::TYPE_STRING, 10); +$table->create(); + +$table->set('1', ['id' => 1, 'name' => 'rango']); + +Assert::eq($table->get('1')['id'], 1); +Assert::eq($table->get('1')['name'], 'rango'); +Assert::true($table->del('1')); +Assert::false($table->get('1')); +$table->set('1', ['id' => 2, ]); + +Assert::eq($table->get('1')['id'], 2); +Assert::eq($table->get('1')['name'], ''); +?> +--EXPECT-- 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 new file mode 100644 index 00000000000..265b91cd533 --- /dev/null +++ b/tests/swoole_timer/after.phpt @@ -0,0 +1,46 @@ +--TEST-- +swoole_timer: swoole_timer_after,swoole_timer_exists,swoole_timer_clear +--SKIPIF-- + +--FILE-- +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')); + Assert::assert($this->timer_id > 0); + } + + public function onTimerTick() { + self::$count++; + echo "onTimerTick:" . self::$count . "\n"; + $this->resetTimer(10); + } +} + +$timer_test = new TimerTest(); +$timer_test->onTimerTick(); + +?> +--EXPECT-- +onTimerTick:1 +onTimerTick:2 +onTimerTick:3 +onTimerTick:4 +onTimerTick:5 +onTimerTick:6 +onTimerTick:7 +onTimerTick:8 +onTimerTick:9 +onTimerTick:10 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/list.phpt b/tests/swoole_timer/list.phpt index a27014594ec..f47504ca0fe 100644 --- a/tests/swoole_timer/list.phpt +++ b/tests/swoole_timer/list.phpt @@ -11,7 +11,10 @@ for ($c = MAX_CONCURRENCY; $c--;) { } $iterator = Swoole\Timer::list(); Assert::isInstanceOf($iterator, ArrayIterator::class); -Assert::same(iterator_to_array($iterator), $timers); +$timers_2 = iterator_to_array($iterator); +sort($timers_2); +sort($timers); +Assert::same($timers_2, $timers); Swoole\Event::wait(); echo "DONE\n"; ?> 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 new file mode 100644 index 00000000000..5dd7997eb3d --- /dev/null +++ b/tests/swoole_timer/memory.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_timer: memory leak test +--SKIPIF-- + +--FILE-- +count = 0; +$stat->m0 = memory_get_usage(); +$stat->data = []; + +Swoole\Timer::tick(1, function ( $id ) use ( &$stat ) { + $obj = new stdClass(); + $obj->name = random_bytes(8192); + $stat->data = $obj->name; + $stat->count++; + if ($stat->count == 1) { + $stat->m1 = memory_get_usage(); + echo 'diff[0] ' . ($stat->m1 - $stat->m0) . "\n"; + } elseif ($stat->count == 99) { + $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\Event::wait(); +$stat->m3 = memory_get_usage(); +$stat->data = null; +echo 'diff[2] ' . ($stat->m3 - $stat->m0) . "\n"; + +?> +--EXPECTF-- +diff[0] %d +diff[1] %d +diff[2] %d 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_after.phpt b/tests/swoole_timer/swoole_timer_after.phpt deleted file mode 100644 index fed9f97631f..00000000000 --- a/tests/swoole_timer/swoole_timer_after.phpt +++ /dev/null @@ -1,46 +0,0 @@ ---TEST-- -swoole_timer: swoole_timer_after,swoole_timer_exists,swoole_timer_clear ---SKIPIF-- - ---FILE-- -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')); - Assert::assert($this->timer_id > 0); - } - - public function onTimerTick() { - self::$count++; - echo "onTimerTick:" . self::$count . "\n"; - $this->resetTimer(10); - } -} - -$timer_test = new TimerTest(); -$timer_test->onTimerTick(); - -?> ---EXPECT-- -onTimerTick:1 -onTimerTick:2 -onTimerTick:3 -onTimerTick:4 -onTimerTick:5 -onTimerTick:6 -onTimerTick:7 -onTimerTick:8 -onTimerTick:9 -onTimerTick:10 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 new file mode 100644 index 00000000000..7cc3fe7898c --- /dev/null +++ b/tests/swoole_websocket_server/empty_message.phpt @@ -0,0 +1,46 @@ +--TEST-- +swoole_websocket_server: empty message +--SKIPIF-- + +--FILE-- +useConstantPorts = true; +$pm->initFreePorts(); + +$pm->parentFunc = function (int $pid) use ($pm) { + go(function() use ($pm) { + $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + $connected = $cli->upgrade('/?test=a&b=hello'); + Assert::assert($connected); + $cli->push(''); + $response = $cli->recv(); + Assert::assert($response); + $json = json_decode($response->data); + Assert::isEmpty($json->data); + }); + 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->set([ + // 'worker_num' => 1, + 'log_file' => '/dev/null' + ]); + $serv->on('WorkerStart', function () use ($pm) { + $pm->wakeup(); + }); + $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->push($frame->fd, json_encode($frame)); + }); + $serv->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_websocket_server/fin.phpt b/tests/swoole_websocket_server/fin.phpt index c82cc9b305f..3f0d8df307d 100644 --- a/tests/swoole_websocket_server/fin.phpt +++ b/tests/swoole_websocket_server/fin.phpt @@ -4,8 +4,6 @@ swoole_websocket_server: websocket server recv and merge fin packages --FILE-- 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; @@ -40,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' @@ -53,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 new file mode 100644 index 00000000000..cd039535ffd --- /dev/null +++ b/tests/swoole_websocket_server/fin2.phpt @@ -0,0 +1,64 @@ +--TEST-- +swoole_websocket_server: fin [2] +--SKIPIF-- + +--FILE-- +parentFunc = function (int $pid) use ($pm) { + Co\run(function () use ($pm) { + $cli = new \Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + $cli->set(['timeout' => 5]); + $ret = $cli->upgrade('/'); + Assert::assert($ret); + $rand_list = []; + $rand = get_safe_random(mt_rand(1, 1280)); + $rand_list[] = $rand; + $times = rand(8, 32); + for ($n = $times; $n--;) { + $opcode = $n === $times - 1 ? WEBSOCKET_OPCODE_TEXT : WEBSOCKET_OPCODE_CONTINUATION; + $finish = $n === 0; + if (mt_rand(0, 1)) { + $frame = new Swoole\WebSocket\Frame; + $frame->opcode = $opcode; + $frame->data = $rand; + $frame->finish = $finish; + $ret = $cli->push($frame); + } else { + $ret = $cli->push($rand, $opcode, $finish); + } + } + Assert::assert($ret); + $frame = $cli->recv(); + Assert::assert($frame instanceof Swoole\WebSocket\Frame); + $data = json_decode($frame->data); + Assert::assert($data->finish); + Assert::assert($data->data, implode('', $rand_list)); + }); + + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $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 () use ($pm) { + $pm->wakeup(); + }); + $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); + } else { + $serv->push($frame->fd, json_encode($frame), $frame->opcode, true); + } + }); + $serv->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- 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/memory.phpt b/tests/swoole_websocket_server/memory.phpt index 02332ba7f02..e2a8cae14a7 100644 --- a/tests/swoole_websocket_server/memory.phpt +++ b/tests/swoole_websocket_server/memory.phpt @@ -10,11 +10,10 @@ skip_if_no_top(); require __DIR__ . '/../include/bootstrap.php'; define('FRAME_DATA_SIZE', 100 * 1024); -define('REQUESTS_TIMES', 64); $pm = new ProcessManager(); $pm->parentFunc = function () use ($pm) { - phpt_echo("start to benchmark " . REQUESTS_TIMES . " times...\n"); + phpt_echo("start to benchmark " . MAX_REQUESTS_MID . " times...\n"); $concurrency = PRESSURE_LEVEL === PRESSURE_NORMAL ? MAX_CONCURRENCY * 4 : MAX_CONCURRENCY; Co::set(['max_coroutine' => $concurrency + 1]); Co\run(function () use ($pm, $concurrency) { @@ -61,7 +60,7 @@ $pm->childFunc = function () use ($pm) { 'worker_real' => $worker_top['RES'] ]; phpt_var_dump(end($mem_records)); - if (($records_count = count($mem_records)) === REQUESTS_TIMES) { + if (($records_count = count($mem_records)) === MAX_REQUESTS_MID) { phpt_echo("=== master virtual ===\n"); phpt_var_dump($master_virtual = array_column($mem_records, 'master_virtual')); phpt_echo("=== master real ===\n"); 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 new file mode 100644 index 00000000000..2d62ad4960c --- /dev/null +++ b/tests/swoole_websocket_server/pingloop_open_ping_pong_frame.phpt @@ -0,0 +1,101 @@ +--TEST-- +swoole_websocket_server: ping loop +--SKIPIF-- + +--FILE-- +parentFunc = function (int $pid) use ($pm) { + for ($i = MAX_CONCURRENCY_MID; $i--;) { + go(function () use ($pm) { + $cli = new \Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + $ret = $cli->upgrade('/'); + Assert::assert($ret); + $loop = 0; + while ($response = $cli->recv(-1)) { + switch ($response->opcode) { + case WEBSOCKET_OPCODE_PING: + Assert::same($response->data, 'ping'); + global $count; + $count++; + $loop++; + if (mt_rand(0, 1)) { + $pong = new Swoole\WebSocket\Frame; + $pong->data = 'pong'; + $pong->opcode = WEBSOCKET_OPCODE_PONG; + $ret = $cli->push($pong); + } else { + $ret = $cli->push('pong', WEBSOCKET_OPCODE_PONG); + } + Assert::assert($ret); + break; + case WEBSOCKET_OPCODE_CLOSE: + break 2; + default: + Assert::assert(0, 'never hear.'); + } + } + Assert::same($loop, PING_LOOP); + }); + } + 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->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 = Swoole\Timer::tick(PING_INTERVAL, function () use ($server) { + foreach ($server->connections as $fd) { + if (mt_rand(0, 1)) { + $ping = new Swoole\WebSocket\Frame; + $ping->data = 'ping'; + $ping->opcode = WEBSOCKET_OPCODE_PING; + $server->push($fd, $ping); + } else { + $server->push($fd, 'ping', WEBSOCKET_OPCODE_PING); + } + } + }); + 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); + } + }); + $pm->wakeup(); + }); + $serv->on('open', function ($server, $req) { + }); + $atomic = new Swoole\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(); + } + }); + $serv->on('close', function ($server, $fd) { + }); + $serv->start(); + Assert::same($atomic->get(), PING_LOOP * MAX_CONCURRENCY_MID); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE 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 new file mode 100644 index 00000000000..7aa4b157de0 --- /dev/null +++ b/tests/swoole_websocket_server/pingpong_open_ping_pong_frame.phpt @@ -0,0 +1,61 @@ +--TEST-- +swoole_websocket_server: websocket ping pong (auto) +--SKIPIF-- + +--FILE-- +parentFunc = function (int $pid) use ($pm) { + go(function () use ($pm) { + $cli = new \Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort()); + $cli->set(['timeout' => 5]); + $ret = $cli->upgrade('/'); + Assert::assert($ret); + for ($i = 100; $i--;) { + $ping = new Swoole\WebSocket\Frame; + $ping->opcode = WEBSOCKET_OPCODE_PING; + $ping->data = 'ping'; + $ret = $cli->push($ping); + Assert::assert($ret); + $pong = $cli->recv(); + Assert::same($pong->opcode, WEBSOCKET_OPCODE_PONG); + Assert::same($pong->data, 'pong'); + } + $pm->kill(); + }); + Swoole\Event::wait(); +}; +$pm->childFunc = function () use ($pm) { + $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 () use ($pm) { + $pm->wakeup(); + }); + $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) { + if ($frame->opcode === WEBSOCKET_OPCODE_PING) { + Assert::same($frame->data, 'ping'); + $atomic->add(); + $pongFrame = new Swoole\WebSocket\Frame; + $pongFrame->opcode = WEBSOCKET_OPCODE_PONG; + $pongFrame->data = 'pong'; + $server->push($frame->fd, $pongFrame); + } + }); + $serv->on('close', function ($swoole_server, $fd) { + }); + $serv->start(); + Assert::same($atomic->get(), 100); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- 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/Makefile b/thirdparty/hiredis/Makefile deleted file mode 100644 index 06ca9946832..00000000000 --- a/thirdparty/hiredis/Makefile +++ /dev/null @@ -1,213 +0,0 @@ -# Hiredis Makefile -# Copyright (C) 2010-2011 Salvatore Sanfilippo -# Copyright (C) 2010-2011 Pieter Noordhuis -# This file is released under the BSD license, see the COPYING file - -OBJ=net.o hiredis.o sds.o async.o read.o -EXAMPLES=hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib -TESTS=hiredis-test -LIBNAME=libhiredis -PKGCONFNAME=hiredis.pc - -HIREDIS_MAJOR=$(shell grep HIREDIS_MAJOR hiredis.h | awk '{print $$3}') -HIREDIS_MINOR=$(shell grep HIREDIS_MINOR hiredis.h | awk '{print $$3}') -HIREDIS_PATCH=$(shell grep HIREDIS_PATCH hiredis.h | awk '{print $$3}') -HIREDIS_SONAME=$(shell grep HIREDIS_SONAME hiredis.h | awk '{print $$3}') - -# Installation related variables and target -PREFIX?=/usr/local -INCLUDE_PATH?=include/hiredis -LIBRARY_PATH?=lib -PKGCONF_PATH?=pkgconfig -INSTALL_INCLUDE_PATH= $(DESTDIR)$(PREFIX)/$(INCLUDE_PATH) -INSTALL_LIBRARY_PATH= $(DESTDIR)$(PREFIX)/$(LIBRARY_PATH) -INSTALL_PKGCONF_PATH= $(INSTALL_LIBRARY_PATH)/$(PKGCONF_PATH) - -# redis-server configuration used for testing -REDIS_PORT=56379 -REDIS_SERVER=redis-server -define REDIS_TEST_CONFIG - daemonize yes - pidfile /tmp/hiredis-test-redis.pid - port $(REDIS_PORT) - bind 127.0.0.1 - unixsocket /tmp/hiredis-test-redis.sock -endef -export REDIS_TEST_CONFIG - -# Fallback to gcc when $CC is not in $PATH. -CC:=$(shell sh -c 'type $${CC%% *} >/dev/null 2>/dev/null && echo $(CC) || echo gcc') -CXX:=$(shell sh -c 'type $${CXX%% *} >/dev/null 2>/dev/null && echo $(CXX) || echo g++') -OPTIMIZATION?=-O3 -WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings -DEBUG_FLAGS?= -g -ggdb -REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CPPFLAGS) $(CFLAGS) $(WARNINGS) $(DEBUG_FLAGS) -REAL_LDFLAGS=$(LDFLAGS) - -DYLIBSUFFIX=so -STLIBSUFFIX=a -DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME) -DYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR) -DYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX) -DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS) -STLIBNAME=$(LIBNAME).$(STLIBSUFFIX) -STLIB_MAKE_CMD=ar rcs $(STLIBNAME) - -# Platform-specific overrides -uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') -ifeq ($(uname_S),SunOS) - REAL_LDFLAGS+= -ldl -lnsl -lsocket - DYLIB_MAKE_CMD=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS) -endif -ifeq ($(uname_S),Darwin) - DYLIBSUFFIX=dylib - DYLIB_MINOR_NAME=$(LIBNAME).$(HIREDIS_SONAME).$(DYLIBSUFFIX) - DYLIB_MAKE_CMD=$(CC) -dynamiclib -Wl,-install_name,$(PREFIX)/$(LIBRARY_PATH)/$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS) -endif - -all: $(DYLIBNAME) $(STLIBNAME) hiredis-test $(PKGCONFNAME) - -# Deps (use make dep to generate this) -async.o: async.c fmacros.h async.h hiredis.h read.h sds.h net.h dict.c dict.h -dict.o: dict.c fmacros.h dict.h -hiredis.o: hiredis.c fmacros.h hiredis.h read.h sds.h net.h -net.o: net.c fmacros.h net.h hiredis.h read.h sds.h -read.o: read.c fmacros.h read.h sds.h -sds.o: sds.c sds.h -test.o: test.c fmacros.h hiredis.h read.h sds.h - -$(DYLIBNAME): $(OBJ) - $(DYLIB_MAKE_CMD) $(OBJ) - -$(STLIBNAME): $(OBJ) - $(STLIB_MAKE_CMD) $(OBJ) - -dynamic: $(DYLIBNAME) -static: $(STLIBNAME) - -# Binaries: -hiredis-example-libevent: examples/example-libevent.c adapters/libevent.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -levent $(STLIBNAME) - -hiredis-example-libev: examples/example-libev.c adapters/libev.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -lev $(STLIBNAME) - -hiredis-example-glib: examples/example-glib.c adapters/glib.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< $(shell pkg-config --cflags --libs glib-2.0) $(STLIBNAME) - -hiredis-example-ivykis: examples/example-ivykis.c adapters/ivykis.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -livykis $(STLIBNAME) - -hiredis-example-macosx: examples/example-macosx.c adapters/macosx.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -framework CoreFoundation $(STLIBNAME) - -ifndef AE_DIR -hiredis-example-ae: - @echo "Please specify AE_DIR (e.g. /src)" - @false -else -hiredis-example-ae: examples/example-ae.c adapters/ae.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(AE_DIR) $< $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o $(AE_DIR)/../deps/jemalloc/lib/libjemalloc.a -pthread $(STLIBNAME) -endif - -ifndef LIBUV_DIR -hiredis-example-libuv: - @echo "Please specify LIBUV_DIR (e.g. ../libuv/)" - @false -else -hiredis-example-libuv: examples/example-libuv.c adapters/libuv.h $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(LIBUV_DIR)/include $< $(LIBUV_DIR)/.libs/libuv.a -lpthread -lrt $(STLIBNAME) -endif - -ifeq ($(and $(QT_MOC),$(QT_INCLUDE_DIR),$(QT_LIBRARY_DIR)),) -hiredis-example-qt: - @echo "Please specify QT_MOC, QT_INCLUDE_DIR AND QT_LIBRARY_DIR" - @false -else -hiredis-example-qt: examples/example-qt.cpp adapters/qt.h $(STLIBNAME) - $(QT_MOC) adapters/qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \ - $(CXX) -x c++ -o qt-adapter-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore - $(QT_MOC) examples/example-qt.h -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore | \ - $(CXX) -x c++ -o qt-example-moc.o -c - $(REAL_CFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore - $(CXX) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(QT_INCLUDE_DIR) -I$(QT_INCLUDE_DIR)/QtCore -L$(QT_LIBRARY_DIR) qt-adapter-moc.o qt-example-moc.o $< -pthread $(STLIBNAME) -lQtCore -endif - -hiredis-example: examples/example.c $(STLIBNAME) - $(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< $(STLIBNAME) - -examples: $(EXAMPLES) - -hiredis-test: test.o $(STLIBNAME) - -hiredis-%: %.o $(STLIBNAME) - $(CC) $(REAL_CFLAGS) -o $@ $(REAL_LDFLAGS) $< $(STLIBNAME) - -test: hiredis-test - ./hiredis-test - -check: hiredis-test - @echo "$$REDIS_TEST_CONFIG" | $(REDIS_SERVER) - - $(PRE) ./hiredis-test -h 127.0.0.1 -p $(REDIS_PORT) -s /tmp/hiredis-test-redis.sock || \ - ( kill `cat /tmp/hiredis-test-redis.pid` && false ) - kill `cat /tmp/hiredis-test-redis.pid` - -.c.o: - $(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $< - -clean: - rm -rf $(DYLIBNAME) $(STLIBNAME) $(TESTS) $(PKGCONFNAME) examples/hiredis-example* *.o *.gcda *.gcno *.gcov - -dep: - $(CC) -MM *.c - -INSTALL?= cp -pPR - -$(PKGCONFNAME): hiredis.h - @echo "Generating $@ for pkgconfig..." - @echo prefix=$(PREFIX) > $@ - @echo exec_prefix=\$${prefix} >> $@ - @echo libdir=$(PREFIX)/$(LIBRARY_PATH) >> $@ - @echo includedir=$(PREFIX)/$(INCLUDE_PATH) >> $@ - @echo >> $@ - @echo Name: hiredis >> $@ - @echo Description: Minimalistic C client library for Redis. >> $@ - @echo Version: $(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH) >> $@ - @echo Libs: -L\$${libdir} -lhiredis >> $@ - @echo Cflags: -I\$${includedir} -D_FILE_OFFSET_BITS=64 >> $@ - -install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME) - mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_INCLUDE_PATH)/adapters $(INSTALL_LIBRARY_PATH) - $(INSTALL) hiredis.h async.h read.h sds.h $(INSTALL_INCLUDE_PATH) - $(INSTALL) adapters/*.h $(INSTALL_INCLUDE_PATH)/adapters - $(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME) - cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIBNAME) - $(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH) - mkdir -p $(INSTALL_PKGCONF_PATH) - $(INSTALL) $(PKGCONFNAME) $(INSTALL_PKGCONF_PATH) - -32bit: - @echo "" - @echo "WARNING: if this fails under Linux you probably need to install libc6-dev-i386" - @echo "" - $(MAKE) CFLAGS="-m32" LDFLAGS="-m32" - -32bit-vars: - $(eval CFLAGS=-m32) - $(eval LDFLAGS=-m32) - -gprof: - $(MAKE) CFLAGS="-pg" LDFLAGS="-pg" - -gcov: - $(MAKE) CFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS="-fprofile-arcs" - -coverage: gcov - make check - mkdir -p tmp/lcov - lcov -d . -c -o tmp/lcov/hiredis.info - genhtml --legend -o tmp/lcov/report tmp/lcov/hiredis.info - -noopt: - $(MAKE) OPTIMIZATION="" - -.PHONY: all test check clean dep install 32bit 32bit-vars gprof gcov noopt diff --git a/thirdparty/hiredis/adapters/ae.h b/thirdparty/hiredis/adapters/ae.h deleted file mode 100644 index 5c551c2ed74..00000000000 --- a/thirdparty/hiredis/adapters/ae.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * 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_AE_H__ -#define __HIREDIS_AE_H__ -#include -#include -#include "../hiredis.h" -#include "../async.h" - -typedef struct redisAeEvents { - redisAsyncContext *context; - aeEventLoop *loop; - int fd; - int reading, writing; -} redisAeEvents; - -static void redisAeReadEvent(aeEventLoop *el, int fd, void *privdata, int mask) { - ((void)el); ((void)fd); ((void)mask); - - redisAeEvents *e = (redisAeEvents*)privdata; - redisAsyncHandleRead(e->context); -} - -static void redisAeWriteEvent(aeEventLoop *el, int fd, void *privdata, int mask) { - ((void)el); ((void)fd); ((void)mask); - - redisAeEvents *e = (redisAeEvents*)privdata; - redisAsyncHandleWrite(e->context); -} - -static void redisAeAddRead(void *privdata) { - redisAeEvents *e = (redisAeEvents*)privdata; - aeEventLoop *loop = e->loop; - if (!e->reading) { - e->reading = 1; - aeCreateFileEvent(loop,e->fd,AE_READABLE,redisAeReadEvent,e); - } -} - -static void redisAeDelRead(void *privdata) { - redisAeEvents *e = (redisAeEvents*)privdata; - aeEventLoop *loop = e->loop; - if (e->reading) { - e->reading = 0; - aeDeleteFileEvent(loop,e->fd,AE_READABLE); - } -} - -static void redisAeAddWrite(void *privdata) { - redisAeEvents *e = (redisAeEvents*)privdata; - aeEventLoop *loop = e->loop; - if (!e->writing) { - e->writing = 1; - aeCreateFileEvent(loop,e->fd,AE_WRITABLE,redisAeWriteEvent,e); - } -} - -static void redisAeDelWrite(void *privdata) { - redisAeEvents *e = (redisAeEvents*)privdata; - aeEventLoop *loop = e->loop; - if (e->writing) { - e->writing = 0; - aeDeleteFileEvent(loop,e->fd,AE_WRITABLE); - } -} - -static void redisAeCleanup(void *privdata) { - redisAeEvents *e = (redisAeEvents*)privdata; - redisAeDelRead(privdata); - redisAeDelWrite(privdata); - free(e); -} - -static int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) { - redisContext *c = &(ac->c); - redisAeEvents *e; - - /* Nothing should be attached when something is already attached */ - if (ac->ev.data != NULL) - return REDIS_ERR; - - /* Create container for context and r/w events */ - e = (redisAeEvents*)malloc(sizeof(*e)); - e->context = ac; - e->loop = loop; - e->fd = c->fd; - e->reading = e->writing = 0; - - /* Register functions to start/stop listening for events */ - ac->ev.addRead = redisAeAddRead; - ac->ev.delRead = redisAeDelRead; - ac->ev.addWrite = redisAeAddWrite; - ac->ev.delWrite = redisAeDelWrite; - ac->ev.cleanup = redisAeCleanup; - ac->ev.data = e; - - return REDIS_OK; -} -#endif diff --git a/thirdparty/hiredis/adapters/glib.h b/thirdparty/hiredis/adapters/glib.h deleted file mode 100644 index e0a6411d395..00000000000 --- a/thirdparty/hiredis/adapters/glib.h +++ /dev/null @@ -1,153 +0,0 @@ -#ifndef __HIREDIS_GLIB_H__ -#define __HIREDIS_GLIB_H__ - -#include - -#include "../hiredis.h" -#include "../async.h" - -typedef struct -{ - GSource source; - redisAsyncContext *ac; - GPollFD poll_fd; -} RedisSource; - -static void -redis_source_add_read (gpointer data) -{ - RedisSource *source = (RedisSource *)data; - g_return_if_fail(source); - source->poll_fd.events |= G_IO_IN; - g_main_context_wakeup(g_source_get_context((GSource *)data)); -} - -static void -redis_source_del_read (gpointer data) -{ - RedisSource *source = (RedisSource *)data; - g_return_if_fail(source); - source->poll_fd.events &= ~G_IO_IN; - g_main_context_wakeup(g_source_get_context((GSource *)data)); -} - -static void -redis_source_add_write (gpointer data) -{ - RedisSource *source = (RedisSource *)data; - g_return_if_fail(source); - source->poll_fd.events |= G_IO_OUT; - g_main_context_wakeup(g_source_get_context((GSource *)data)); -} - -static void -redis_source_del_write (gpointer data) -{ - RedisSource *source = (RedisSource *)data; - g_return_if_fail(source); - source->poll_fd.events &= ~G_IO_OUT; - g_main_context_wakeup(g_source_get_context((GSource *)data)); -} - -static void -redis_source_cleanup (gpointer data) -{ - RedisSource *source = (RedisSource *)data; - - g_return_if_fail(source); - - redis_source_del_read(source); - redis_source_del_write(source); - /* - * It is not our responsibility to remove ourself from the - * current main loop. However, we will remove the GPollFD. - */ - if (source->poll_fd.fd >= 0) { - g_source_remove_poll((GSource *)data, &source->poll_fd); - source->poll_fd.fd = -1; - } -} - -static gboolean -redis_source_prepare (GSource *source, - gint *timeout_) -{ - RedisSource *redis = (RedisSource *)source; - *timeout_ = -1; - return !!(redis->poll_fd.events & redis->poll_fd.revents); -} - -static gboolean -redis_source_check (GSource *source) -{ - RedisSource *redis = (RedisSource *)source; - return !!(redis->poll_fd.events & redis->poll_fd.revents); -} - -static gboolean -redis_source_dispatch (GSource *source, - GSourceFunc callback, - gpointer user_data) -{ - RedisSource *redis = (RedisSource *)source; - - if ((redis->poll_fd.revents & G_IO_OUT)) { - redisAsyncHandleWrite(redis->ac); - redis->poll_fd.revents &= ~G_IO_OUT; - } - - if ((redis->poll_fd.revents & G_IO_IN)) { - redisAsyncHandleRead(redis->ac); - redis->poll_fd.revents &= ~G_IO_IN; - } - - if (callback) { - return callback(user_data); - } - - return TRUE; -} - -static void -redis_source_finalize (GSource *source) -{ - RedisSource *redis = (RedisSource *)source; - - if (redis->poll_fd.fd >= 0) { - g_source_remove_poll(source, &redis->poll_fd); - redis->poll_fd.fd = -1; - } -} - -static GSource * -redis_source_new (redisAsyncContext *ac) -{ - static GSourceFuncs source_funcs = { - .prepare = redis_source_prepare, - .check = redis_source_check, - .dispatch = redis_source_dispatch, - .finalize = redis_source_finalize, - }; - redisContext *c = &ac->c; - RedisSource *source; - - g_return_val_if_fail(ac != NULL, NULL); - - source = (RedisSource *)g_source_new(&source_funcs, sizeof *source); - source->ac = ac; - source->poll_fd.fd = c->fd; - source->poll_fd.events = 0; - source->poll_fd.revents = 0; - g_source_add_poll((GSource *)source, &source->poll_fd); - - ac->ev.addRead = redis_source_add_read; - ac->ev.delRead = redis_source_del_read; - ac->ev.addWrite = redis_source_add_write; - ac->ev.delWrite = redis_source_del_write; - ac->ev.cleanup = redis_source_cleanup; - ac->ev.data = source; - - return (GSource *)source; -} - -#endif /* __HIREDIS_GLIB_H__ */ diff --git a/thirdparty/hiredis/adapters/ivykis.h b/thirdparty/hiredis/adapters/ivykis.h deleted file mode 100644 index 6a12a868a35..00000000000 --- a/thirdparty/hiredis/adapters/ivykis.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef __HIREDIS_IVYKIS_H__ -#define __HIREDIS_IVYKIS_H__ -#include -#include "../hiredis.h" -#include "../async.h" - -typedef struct redisIvykisEvents { - redisAsyncContext *context; - struct iv_fd fd; -} redisIvykisEvents; - -static void redisIvykisReadEvent(void *arg) { - redisAsyncContext *context = (redisAsyncContext *)arg; - redisAsyncHandleRead(context); -} - -static void redisIvykisWriteEvent(void *arg) { - redisAsyncContext *context = (redisAsyncContext *)arg; - redisAsyncHandleWrite(context); -} - -static void redisIvykisAddRead(void *privdata) { - redisIvykisEvents *e = (redisIvykisEvents*)privdata; - iv_fd_set_handler_in(&e->fd, redisIvykisReadEvent); -} - -static void redisIvykisDelRead(void *privdata) { - redisIvykisEvents *e = (redisIvykisEvents*)privdata; - iv_fd_set_handler_in(&e->fd, NULL); -} - -static void redisIvykisAddWrite(void *privdata) { - redisIvykisEvents *e = (redisIvykisEvents*)privdata; - iv_fd_set_handler_out(&e->fd, redisIvykisWriteEvent); -} - -static void redisIvykisDelWrite(void *privdata) { - redisIvykisEvents *e = (redisIvykisEvents*)privdata; - iv_fd_set_handler_out(&e->fd, NULL); -} - -static void redisIvykisCleanup(void *privdata) { - redisIvykisEvents *e = (redisIvykisEvents*)privdata; - - iv_fd_unregister(&e->fd); - free(e); -} - -static int redisIvykisAttach(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - redisIvykisEvents *e; - - /* Nothing should be attached when something is already attached */ - if (ac->ev.data != NULL) - return REDIS_ERR; - - /* Create container for context and r/w events */ - e = (redisIvykisEvents*)malloc(sizeof(*e)); - e->context = ac; - - /* Register functions to start/stop listening for events */ - ac->ev.addRead = redisIvykisAddRead; - ac->ev.delRead = redisIvykisDelRead; - ac->ev.addWrite = redisIvykisAddWrite; - ac->ev.delWrite = redisIvykisDelWrite; - ac->ev.cleanup = redisIvykisCleanup; - ac->ev.data = e; - - /* Initialize and install read/write events */ - IV_FD_INIT(&e->fd); - e->fd.fd = c->fd; - e->fd.handler_in = redisIvykisReadEvent; - e->fd.handler_out = redisIvykisWriteEvent; - e->fd.handler_err = NULL; - e->fd.cookie = e->context; - - iv_fd_register(&e->fd); - - return REDIS_OK; -} -#endif diff --git a/thirdparty/hiredis/adapters/libev.h b/thirdparty/hiredis/adapters/libev.h deleted file mode 100644 index 2bf8d521fc2..00000000000 --- a/thirdparty/hiredis/adapters/libev.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * 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_LIBEV_H__ -#define __HIREDIS_LIBEV_H__ -#include -#include -#include -#include "../hiredis.h" -#include "../async.h" - -typedef struct redisLibevEvents { - redisAsyncContext *context; - struct ev_loop *loop; - int reading, writing; - ev_io rev, wev; -} redisLibevEvents; - -static void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) { -#if EV_MULTIPLICITY - ((void)loop); -#endif - ((void)revents); - - redisLibevEvents *e = (redisLibevEvents*)watcher->data; - redisAsyncHandleRead(e->context); -} - -static void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) { -#if EV_MULTIPLICITY - ((void)loop); -#endif - ((void)revents); - - redisLibevEvents *e = (redisLibevEvents*)watcher->data; - redisAsyncHandleWrite(e->context); -} - -static void redisLibevAddRead(void *privdata) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - struct ev_loop *loop = e->loop; - ((void)loop); - if (!e->reading) { - e->reading = 1; - ev_io_start(EV_A_ &e->rev); - } -} - -static void redisLibevDelRead(void *privdata) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - struct ev_loop *loop = e->loop; - ((void)loop); - if (e->reading) { - e->reading = 0; - ev_io_stop(EV_A_ &e->rev); - } -} - -static void redisLibevAddWrite(void *privdata) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - struct ev_loop *loop = e->loop; - ((void)loop); - if (!e->writing) { - e->writing = 1; - ev_io_start(EV_A_ &e->wev); - } -} - -static void redisLibevDelWrite(void *privdata) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - struct ev_loop *loop = e->loop; - ((void)loop); - if (e->writing) { - e->writing = 0; - ev_io_stop(EV_A_ &e->wev); - } -} - -static void redisLibevCleanup(void *privdata) { - redisLibevEvents *e = (redisLibevEvents*)privdata; - redisLibevDelRead(privdata); - redisLibevDelWrite(privdata); - free(e); -} - -static int redisLibevAttach(EV_P_ redisAsyncContext *ac) { - redisContext *c = &(ac->c); - redisLibevEvents *e; - - /* Nothing should be attached when something is already attached */ - if (ac->ev.data != NULL) - return REDIS_ERR; - - /* Create container for context and r/w events */ - e = (redisLibevEvents*)malloc(sizeof(*e)); - e->context = ac; -#if EV_MULTIPLICITY - e->loop = loop; -#else - e->loop = NULL; -#endif - e->reading = e->writing = 0; - e->rev.data = e; - e->wev.data = e; - - /* Register functions to start/stop listening for events */ - ac->ev.addRead = redisLibevAddRead; - ac->ev.delRead = redisLibevDelRead; - ac->ev.addWrite = redisLibevAddWrite; - ac->ev.delWrite = redisLibevDelWrite; - ac->ev.cleanup = redisLibevCleanup; - ac->ev.data = e; - - /* Initialize read/write events */ - ev_io_init(&e->rev,redisLibevReadEvent,c->fd,EV_READ); - ev_io_init(&e->wev,redisLibevWriteEvent,c->fd,EV_WRITE); - return REDIS_OK; -} - -#endif diff --git a/thirdparty/hiredis/adapters/libevent.h b/thirdparty/hiredis/adapters/libevent.h deleted file mode 100644 index 7d2bef180f6..00000000000 --- a/thirdparty/hiredis/adapters/libevent.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * 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_LIBEVENT_H__ -#define __HIREDIS_LIBEVENT_H__ -#include -#include "../hiredis.h" -#include "../async.h" - -typedef struct redisLibeventEvents { - redisAsyncContext *context; - struct event *rev, *wev; -} redisLibeventEvents; - -static void redisLibeventReadEvent(int fd, short event, void *arg) { - ((void)fd); ((void)event); - redisLibeventEvents *e = (redisLibeventEvents*)arg; - redisAsyncHandleRead(e->context); -} - -static void redisLibeventWriteEvent(int fd, short event, void *arg) { - ((void)fd); ((void)event); - redisLibeventEvents *e = (redisLibeventEvents*)arg; - redisAsyncHandleWrite(e->context); -} - -static void redisLibeventAddRead(void *privdata) { - redisLibeventEvents *e = (redisLibeventEvents*)privdata; - event_add(e->rev,NULL); -} - -static void redisLibeventDelRead(void *privdata) { - redisLibeventEvents *e = (redisLibeventEvents*)privdata; - event_del(e->rev); -} - -static void redisLibeventAddWrite(void *privdata) { - redisLibeventEvents *e = (redisLibeventEvents*)privdata; - event_add(e->wev,NULL); -} - -static void redisLibeventDelWrite(void *privdata) { - redisLibeventEvents *e = (redisLibeventEvents*)privdata; - event_del(e->wev); -} - -static void redisLibeventCleanup(void *privdata) { - redisLibeventEvents *e = (redisLibeventEvents*)privdata; - event_free(e->rev); - event_free(e->wev); - free(e); -} - -static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) { - redisContext *c = &(ac->c); - redisLibeventEvents *e; - - /* Nothing should be attached when something is already attached */ - if (ac->ev.data != NULL) - return REDIS_ERR; - - /* Create container for context and r/w events */ - e = (redisLibeventEvents*)malloc(sizeof(*e)); - e->context = ac; - - /* Register functions to start/stop listening for events */ - ac->ev.addRead = redisLibeventAddRead; - ac->ev.delRead = redisLibeventDelRead; - ac->ev.addWrite = redisLibeventAddWrite; - ac->ev.delWrite = redisLibeventDelWrite; - ac->ev.cleanup = redisLibeventCleanup; - ac->ev.data = e; - - /* Initialize and install read/write events */ - e->rev = event_new(base, c->fd, EV_READ, redisLibeventReadEvent, e); - e->wev = event_new(base, c->fd, EV_WRITE, redisLibeventWriteEvent, e); - event_add(e->rev, NULL); - event_add(e->wev, NULL); - return REDIS_OK; -} -#endif diff --git a/thirdparty/hiredis/adapters/libuv.h b/thirdparty/hiredis/adapters/libuv.h deleted file mode 100644 index ff08c25e1ff..00000000000 --- a/thirdparty/hiredis/adapters/libuv.h +++ /dev/null @@ -1,122 +0,0 @@ -#ifndef __HIREDIS_LIBUV_H__ -#define __HIREDIS_LIBUV_H__ -#include -#include -#include "../hiredis.h" -#include "../async.h" -#include - -typedef struct redisLibuvEvents { - redisAsyncContext* context; - uv_poll_t handle; - int events; -} redisLibuvEvents; - - -static void redisLibuvPoll(uv_poll_t* handle, int status, int events) { - redisLibuvEvents* p = (redisLibuvEvents*)handle->data; - - if (status != 0) { - return; - } - - if (p->context != NULL && (events & UV_READABLE)) { - redisAsyncHandleRead(p->context); - } - if (p->context != NULL && (events & UV_WRITABLE)) { - redisAsyncHandleWrite(p->context); - } -} - - -static void redisLibuvAddRead(void *privdata) { - redisLibuvEvents* p = (redisLibuvEvents*)privdata; - - p->events |= UV_READABLE; - - uv_poll_start(&p->handle, p->events, redisLibuvPoll); -} - - -static void redisLibuvDelRead(void *privdata) { - redisLibuvEvents* p = (redisLibuvEvents*)privdata; - - p->events &= ~UV_READABLE; - - if (p->events) { - uv_poll_start(&p->handle, p->events, redisLibuvPoll); - } else { - uv_poll_stop(&p->handle); - } -} - - -static void redisLibuvAddWrite(void *privdata) { - redisLibuvEvents* p = (redisLibuvEvents*)privdata; - - p->events |= UV_WRITABLE; - - uv_poll_start(&p->handle, p->events, redisLibuvPoll); -} - - -static void redisLibuvDelWrite(void *privdata) { - redisLibuvEvents* p = (redisLibuvEvents*)privdata; - - p->events &= ~UV_WRITABLE; - - if (p->events) { - uv_poll_start(&p->handle, p->events, redisLibuvPoll); - } else { - uv_poll_stop(&p->handle); - } -} - - -static void on_close(uv_handle_t* handle) { - redisLibuvEvents* p = (redisLibuvEvents*)handle->data; - - free(p); -} - - -static void redisLibuvCleanup(void *privdata) { - redisLibuvEvents* p = (redisLibuvEvents*)privdata; - - p->context = NULL; // indicate that context might no longer exist - uv_close((uv_handle_t*)&p->handle, on_close); -} - - -static int redisLibuvAttach(redisAsyncContext* ac, uv_loop_t* loop) { - redisContext *c = &(ac->c); - - if (ac->ev.data != NULL) { - return REDIS_ERR; - } - - ac->ev.addRead = redisLibuvAddRead; - ac->ev.delRead = redisLibuvDelRead; - ac->ev.addWrite = redisLibuvAddWrite; - ac->ev.delWrite = redisLibuvDelWrite; - ac->ev.cleanup = redisLibuvCleanup; - - redisLibuvEvents* p = (redisLibuvEvents*)malloc(sizeof(*p)); - - if (!p) { - return REDIS_ERR; - } - - memset(p, 0, sizeof(*p)); - - if (uv_poll_init(loop, &p->handle, c->fd) != 0) { - return REDIS_ERR; - } - - ac->ev.data = p; - p->handle.data = p; - p->context = ac; - - return REDIS_OK; -} -#endif diff --git a/thirdparty/hiredis/adapters/macosx.h b/thirdparty/hiredis/adapters/macosx.h deleted file mode 100644 index 72121f606f4..00000000000 --- a/thirdparty/hiredis/adapters/macosx.h +++ /dev/null @@ -1,114 +0,0 @@ -// -// Created by Дмитрий Бахвалов on 13.07.15. -// Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved. -// - -#ifndef __HIREDIS_MACOSX_H__ -#define __HIREDIS_MACOSX_H__ - -#include - -#include "../hiredis.h" -#include "../async.h" - -typedef struct { - redisAsyncContext *context; - CFSocketRef socketRef; - CFRunLoopSourceRef sourceRef; -} RedisRunLoop; - -static int freeRedisRunLoop(RedisRunLoop* redisRunLoop) { - if( redisRunLoop != NULL ) { - if( redisRunLoop->sourceRef != NULL ) { - CFRunLoopSourceInvalidate(redisRunLoop->sourceRef); - CFRelease(redisRunLoop->sourceRef); - } - if( redisRunLoop->socketRef != NULL ) { - CFSocketInvalidate(redisRunLoop->socketRef); - CFRelease(redisRunLoop->socketRef); - } - free(redisRunLoop); - } - return REDIS_ERR; -} - -static void redisMacOSAddRead(void *privdata) { - RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; - CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack); -} - -static void redisMacOSDelRead(void *privdata) { - RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; - CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack); -} - -static void redisMacOSAddWrite(void *privdata) { - RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; - CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack); -} - -static void redisMacOSDelWrite(void *privdata) { - RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; - CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack); -} - -static void redisMacOSCleanup(void *privdata) { - RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata; - freeRedisRunLoop(redisRunLoop); -} - -static void redisMacOSAsyncCallback(CFSocketRef __unused s, CFSocketCallBackType callbackType, CFDataRef __unused address, const void __unused *data, void *info) { - redisAsyncContext* context = (redisAsyncContext*) info; - - switch (callbackType) { - case kCFSocketReadCallBack: - redisAsyncHandleRead(context); - break; - - case kCFSocketWriteCallBack: - redisAsyncHandleWrite(context); - break; - - default: - break; - } -} - -static int redisMacOSAttach(redisAsyncContext *redisAsyncCtx, CFRunLoopRef runLoop) { - redisContext *redisCtx = &(redisAsyncCtx->c); - - /* Nothing should be attached when something is already attached */ - if( redisAsyncCtx->ev.data != NULL ) return REDIS_ERR; - - RedisRunLoop* redisRunLoop = (RedisRunLoop*) calloc(1, sizeof(RedisRunLoop)); - if( !redisRunLoop ) return REDIS_ERR; - - /* Setup redis stuff */ - redisRunLoop->context = redisAsyncCtx; - - redisAsyncCtx->ev.addRead = redisMacOSAddRead; - redisAsyncCtx->ev.delRead = redisMacOSDelRead; - redisAsyncCtx->ev.addWrite = redisMacOSAddWrite; - redisAsyncCtx->ev.delWrite = redisMacOSDelWrite; - redisAsyncCtx->ev.cleanup = redisMacOSCleanup; - redisAsyncCtx->ev.data = redisRunLoop; - - /* Initialize and install read/write events */ - CFSocketContext socketCtx = { 0, redisAsyncCtx, NULL, NULL, NULL }; - - redisRunLoop->socketRef = CFSocketCreateWithNative(NULL, redisCtx->fd, - kCFSocketReadCallBack | kCFSocketWriteCallBack, - redisMacOSAsyncCallback, - &socketCtx); - if( !redisRunLoop->socketRef ) return freeRedisRunLoop(redisRunLoop); - - redisRunLoop->sourceRef = CFSocketCreateRunLoopSource(NULL, redisRunLoop->socketRef, 0); - if( !redisRunLoop->sourceRef ) return freeRedisRunLoop(redisRunLoop); - - CFRunLoopAddSource(runLoop, redisRunLoop->sourceRef, kCFRunLoopDefaultMode); - - return REDIS_OK; -} - -#endif - diff --git a/thirdparty/hiredis/adapters/qt.h b/thirdparty/hiredis/adapters/qt.h deleted file mode 100644 index 5cc02e6ce5e..00000000000 --- a/thirdparty/hiredis/adapters/qt.h +++ /dev/null @@ -1,135 +0,0 @@ -/*- - * Copyright (C) 2014 Pietro Cerutti - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * - * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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_QT_H__ -#define __HIREDIS_QT_H__ -#include -#include "../async.h" - -static void RedisQtAddRead(void *); -static void RedisQtDelRead(void *); -static void RedisQtAddWrite(void *); -static void RedisQtDelWrite(void *); -static void RedisQtCleanup(void *); - -class RedisQtAdapter : public QObject { - - Q_OBJECT - - friend - void RedisQtAddRead(void * adapter) { - RedisQtAdapter * a = static_cast(adapter); - a->addRead(); - } - - friend - void RedisQtDelRead(void * adapter) { - RedisQtAdapter * a = static_cast(adapter); - a->delRead(); - } - - friend - void RedisQtAddWrite(void * adapter) { - RedisQtAdapter * a = static_cast(adapter); - a->addWrite(); - } - - friend - void RedisQtDelWrite(void * adapter) { - RedisQtAdapter * a = static_cast(adapter); - a->delWrite(); - } - - friend - void RedisQtCleanup(void * adapter) { - RedisQtAdapter * a = static_cast(adapter); - a->cleanup(); - } - - public: - RedisQtAdapter(QObject * parent = 0) - : QObject(parent), m_ctx(0), m_read(0), m_write(0) { } - - ~RedisQtAdapter() { - if (m_ctx != 0) { - m_ctx->ev.data = NULL; - } - } - - int setContext(redisAsyncContext * ac) { - if (ac->ev.data != NULL) { - return REDIS_ERR; - } - m_ctx = ac; - m_ctx->ev.data = this; - m_ctx->ev.addRead = RedisQtAddRead; - m_ctx->ev.delRead = RedisQtDelRead; - m_ctx->ev.addWrite = RedisQtAddWrite; - m_ctx->ev.delWrite = RedisQtDelWrite; - m_ctx->ev.cleanup = RedisQtCleanup; - return REDIS_OK; - } - - private: - void addRead() { - if (m_read) return; - m_read = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Read, 0); - connect(m_read, SIGNAL(activated(int)), this, SLOT(read())); - } - - void delRead() { - if (!m_read) return; - delete m_read; - m_read = 0; - } - - void addWrite() { - if (m_write) return; - m_write = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Write, 0); - connect(m_write, SIGNAL(activated(int)), this, SLOT(write())); - } - - void delWrite() { - if (!m_write) return; - delete m_write; - m_write = 0; - } - - void cleanup() { - delRead(); - delWrite(); - } - - private slots: - void read() { redisAsyncHandleRead(m_ctx); } - void write() { redisAsyncHandleWrite(m_ctx); } - - private: - redisAsyncContext * m_ctx; - QSocketNotifier * m_read; - QSocketNotifier * m_write; -}; - -#endif /* !__HIREDIS_QT_H__ */ 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/appveyor.yml b/thirdparty/hiredis/appveyor.yml deleted file mode 100644 index 819efbd58b3..00000000000 --- a/thirdparty/hiredis/appveyor.yml +++ /dev/null @@ -1,23 +0,0 @@ -# Appveyor configuration file for CI build of hiredis on Windows (under Cygwin) -environment: - matrix: - - CYG_BASH: C:\cygwin64\bin\bash - CC: gcc - - CYG_BASH: C:\cygwin\bin\bash - CC: gcc - TARGET: 32bit - TARGET_VARS: 32bit-vars - -clone_depth: 1 - -# Attempt to ensure we don't try to convert line endings to Win32 CRLF as this will cause build to fail -init: - - git config --global core.autocrlf input - -# Install needed build dependencies -install: - - '%CYG_BASH% -lc "cygcheck -dc cygwin"' - -build_script: - - 'echo building...' - - '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0 - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * 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 -#include -#include -#include -#include -#include -#include "async.h" -#include "net.h" -#include "dict.c" -#include "sds.h" - -#define _EL_ADD_READ(ctx) do { \ - if ((ctx)->ev.addRead) (ctx)->ev.addRead((ctx)->ev.data); \ - } while(0) -#define _EL_DEL_READ(ctx) do { \ - if ((ctx)->ev.delRead) (ctx)->ev.delRead((ctx)->ev.data); \ - } while(0) -#define _EL_ADD_WRITE(ctx) do { \ - if ((ctx)->ev.addWrite) (ctx)->ev.addWrite((ctx)->ev.data); \ - } while(0) -#define _EL_DEL_WRITE(ctx) do { \ - if ((ctx)->ev.delWrite) (ctx)->ev.delWrite((ctx)->ev.data); \ - } while(0) -#define _EL_CLEANUP(ctx) do { \ - if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \ - } while(0); - -/* Forward declaration of function in hiredis.c */ -int __redisAppendCommand(redisContext *c, const char *cmd, size_t len); - -/* Functions managing dictionary of callbacks for pub/sub. */ -static unsigned int callbackHash(const void *key) { - return dictGenHashFunction((const unsigned char *)key, - sdslen((const sds)key)); -} - -static void *callbackValDup(void *privdata, const void *src) { - ((void) privdata); - redisCallback *dup = malloc(sizeof(*dup)); - memcpy(dup,src,sizeof(*dup)); - return dup; -} - -static int callbackKeyCompare(void *privdata, const void *key1, const void *key2) { - int l1, l2; - ((void) privdata); - - l1 = sdslen((const sds)key1); - l2 = sdslen((const sds)key2); - if (l1 != l2) return 0; - return memcmp(key1,key2,l1) == 0; -} - -static void callbackKeyDestructor(void *privdata, void *key) { - ((void) privdata); - sdsfree((sds)key); -} - -static void callbackValDestructor(void *privdata, void *val) { - ((void) privdata); - free(val); -} - -static dictType callbackDict = { - callbackHash, - NULL, - callbackValDup, - callbackKeyCompare, - callbackKeyDestructor, - callbackValDestructor -}; - -static redisAsyncContext *redisAsyncInitialize(redisContext *c) { - redisAsyncContext *ac; - - ac = realloc(c,sizeof(redisAsyncContext)); - if (ac == NULL) - return NULL; - - c = &(ac->c); - - /* The regular connect functions will always set the flag REDIS_CONNECTED. - * For the async API, we want to wait until the first write event is - * received up before setting this flag, so reset it here. */ - c->flags &= ~REDIS_CONNECTED; - - ac->err = 0; - ac->errstr = NULL; - ac->data = NULL; - - ac->ev.data = NULL; - ac->ev.addRead = NULL; - ac->ev.delRead = NULL; - ac->ev.addWrite = NULL; - ac->ev.delWrite = NULL; - ac->ev.cleanup = NULL; - - ac->onConnect = NULL; - ac->onDisconnect = NULL; - - ac->replies.head = NULL; - ac->replies.tail = NULL; - ac->sub.invalid.head = NULL; - ac->sub.invalid.tail = NULL; - ac->sub.channels = dictCreate(&callbackDict,NULL); - ac->sub.patterns = dictCreate(&callbackDict,NULL); - return ac; -} - -/* We want the error field to be accessible directly instead of requiring - * an indirection to the redisContext struct. */ -static void __redisAsyncCopyError(redisAsyncContext *ac) { - if (!ac) - return; - - redisContext *c = &(ac->c); - ac->err = c->err; - ac->errstr = c->errstr; -} - -redisAsyncContext *redisAsyncConnect(const char *ip, int port) { - redisContext *c; - redisAsyncContext *ac; - - c = redisConnectNonBlock(ip,port); - if (c == NULL) - return NULL; - - ac = redisAsyncInitialize(c); - if (ac == NULL) { - redisFree(c); - return NULL; - } - - __redisAsyncCopyError(ac); - return ac; -} - -redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, - const char *source_addr) { - redisContext *c = redisConnectBindNonBlock(ip,port,source_addr); - redisAsyncContext *ac = redisAsyncInitialize(c); - __redisAsyncCopyError(ac); - return ac; -} - -redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port, - const char *source_addr) { - redisContext *c = redisConnectBindNonBlockWithReuse(ip,port,source_addr); - redisAsyncContext *ac = redisAsyncInitialize(c); - __redisAsyncCopyError(ac); - return ac; -} - -redisAsyncContext *redisAsyncConnectUnix(const char *path) { - redisContext *c; - redisAsyncContext *ac; - - c = redisConnectUnixNonBlock(path); - if (c == NULL) - return NULL; - - ac = redisAsyncInitialize(c); - if (ac == NULL) { - redisFree(c); - return NULL; - } - - __redisAsyncCopyError(ac); - return ac; -} - -int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) { - if (ac->onConnect == NULL) { - ac->onConnect = fn; - - /* The common way to detect an established connection is to wait for - * the first write event to be fired. This assumes the related event - * library functions are already set. */ - _EL_ADD_WRITE(ac); - return REDIS_OK; - } - return REDIS_ERR; -} - -int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn) { - if (ac->onDisconnect == NULL) { - ac->onDisconnect = fn; - return REDIS_OK; - } - return REDIS_ERR; -} - -/* Helper functions to push/shift callbacks */ -static int __redisPushCallback(redisCallbackList *list, redisCallback *source) { - redisCallback *cb; - - /* Copy callback from stack to heap */ - cb = malloc(sizeof(*cb)); - if (cb == NULL) - return REDIS_ERR_OOM; - - if (source != NULL) { - memcpy(cb,source,sizeof(*cb)); - cb->next = NULL; - } - - /* Store callback in list */ - if (list->head == NULL) - list->head = cb; - if (list->tail != NULL) - list->tail->next = cb; - list->tail = cb; - return REDIS_OK; -} - -static int __redisShiftCallback(redisCallbackList *list, redisCallback *target) { - redisCallback *cb = list->head; - if (cb != NULL) { - list->head = cb->next; - if (cb == list->tail) - list->tail = NULL; - - /* Copy callback from heap to stack */ - if (target != NULL) - memcpy(target,cb,sizeof(*cb)); - free(cb); - return REDIS_OK; - } - return REDIS_ERR; -} - -static void __redisRunCallback(redisAsyncContext *ac, redisCallback *cb, redisReply *reply) { - redisContext *c = &(ac->c); - if (cb->fn != NULL) { - c->flags |= REDIS_IN_CALLBACK; - cb->fn(ac,reply,cb->privdata); - c->flags &= ~REDIS_IN_CALLBACK; - } -} - -/* Helper function to free the context. */ -static void __redisAsyncFree(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - redisCallback cb; - dictIterator *it; - dictEntry *de; - - /* Execute pending callbacks with NULL reply. */ - while (__redisShiftCallback(&ac->replies,&cb) == REDIS_OK) - __redisRunCallback(ac,&cb,NULL); - - /* Execute callbacks for invalid commands */ - while (__redisShiftCallback(&ac->sub.invalid,&cb) == REDIS_OK) - __redisRunCallback(ac,&cb,NULL); - - /* Run subscription callbacks callbacks with NULL reply */ - it = dictGetIterator(ac->sub.channels); - while ((de = dictNext(it)) != NULL) - __redisRunCallback(ac,dictGetEntryVal(de),NULL); - dictReleaseIterator(it); - dictRelease(ac->sub.channels); - - it = dictGetIterator(ac->sub.patterns); - while ((de = dictNext(it)) != NULL) - __redisRunCallback(ac,dictGetEntryVal(de),NULL); - dictReleaseIterator(it); - dictRelease(ac->sub.patterns); - - /* Signal event lib to clean up */ - _EL_CLEANUP(ac); - - /* Execute disconnect callback. When redisAsyncFree() initiated destroying - * this context, the status will always be REDIS_OK. */ - if (ac->onDisconnect && (c->flags & REDIS_CONNECTED)) { - if (c->flags & REDIS_FREEING) { - ac->onDisconnect(ac,REDIS_OK); - } else { - ac->onDisconnect(ac,(ac->err == 0) ? REDIS_OK : REDIS_ERR); - } - } - - /* Cleanup self */ - redisFree(c); -} - -/* Free the async context. When this function is called from a callback, - * control needs to be returned to redisProcessCallbacks() before actual - * free'ing. To do so, a flag is set on the context which is picked up by - * redisProcessCallbacks(). Otherwise, the context is immediately free'd. */ -void redisAsyncFree(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - c->flags |= REDIS_FREEING; - if (!(c->flags & REDIS_IN_CALLBACK)) - __redisAsyncFree(ac); -} - -/* Helper function to make the disconnect happen and clean up. */ -static void __redisAsyncDisconnect(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - - /* Make sure error is accessible if there is any */ - __redisAsyncCopyError(ac); - - if (ac->err == 0) { - /* For clean disconnects, there should be no pending callbacks. */ - int ret = __redisShiftCallback(&ac->replies,NULL); - assert(ret == REDIS_ERR); - } else { - /* Disconnection is caused by an error, make sure that pending - * callbacks cannot call new commands. */ - c->flags |= REDIS_DISCONNECTING; - } - - /* For non-clean disconnects, __redisAsyncFree() will execute pending - * callbacks with a NULL-reply. */ - __redisAsyncFree(ac); -} - -/* Tries to do a clean disconnect from Redis, meaning it stops new commands - * from being issued, but tries to flush the output buffer and execute - * callbacks for all remaining replies. When this function is called from a - * callback, there might be more replies and we can safely defer disconnecting - * to redisProcessCallbacks(). Otherwise, we can only disconnect immediately - * when there are no pending callbacks. */ -void redisAsyncDisconnect(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - c->flags |= REDIS_DISCONNECTING; - if (!(c->flags & REDIS_IN_CALLBACK) && ac->replies.head == NULL) - __redisAsyncDisconnect(ac); -} - -static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, redisCallback *dstcb) { - redisContext *c = &(ac->c); - dict *callbacks; - redisCallback *cb; - dictEntry *de; - int pvariant; - char *stype; - sds sname; - - /* Custom reply functions are not supported for pub/sub. This will fail - * very hard when they are used... */ - if (reply->type == REDIS_REPLY_ARRAY) { - assert(reply->elements >= 2); - assert(reply->element[0]->type == REDIS_REPLY_STRING); - stype = reply->element[0]->str; - pvariant = (tolower(stype[0]) == 'p') ? 1 : 0; - - if (pvariant) - callbacks = ac->sub.patterns; - else - callbacks = ac->sub.channels; - - /* Locate the right callback */ - assert(reply->element[1]->type == REDIS_REPLY_STRING); - sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len); - de = dictFind(callbacks,sname); - if (de != NULL) { - cb = dictGetEntryVal(de); - - /* If this is an subscribe reply decrease pending counter. */ - if (strcasecmp(stype+pvariant,"subscribe") == 0) { - cb->pending_subs -= 1; - } - - memcpy(dstcb,cb,sizeof(*dstcb)); - - /* If this is an unsubscribe message, remove it. */ - if (strcasecmp(stype+pvariant,"unsubscribe") == 0) { - if (cb->pending_subs == 0) - dictDelete(callbacks,sname); - - /* If this was the last unsubscribe message, revert to - * non-subscribe mode. */ - assert(reply->element[2]->type == REDIS_REPLY_INTEGER); - - /* Unset subscribed flag only when no pipelined pending subscribe. */ - if (reply->element[2]->integer == 0 - && dictSize(ac->sub.channels) == 0 - && dictSize(ac->sub.patterns) == 0) - c->flags &= ~REDIS_SUBSCRIBED; - } - } - sdsfree(sname); - } else { - /* Shift callback for invalid commands. */ - __redisShiftCallback(&ac->sub.invalid,dstcb); - } - return REDIS_OK; -} - -void redisProcessCallbacks(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - redisCallback cb = {NULL, NULL, 0, NULL}; - void *reply = NULL; - int status; - - while((status = redisGetReply(c,&reply)) == REDIS_OK) { - if (reply == NULL) { - /* When the connection is being disconnected and there are - * no more replies, this is the cue to really disconnect. */ - if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0 - && ac->replies.head == NULL) { - __redisAsyncDisconnect(ac); - return; - } - - /* If monitor mode, repush callback */ - if(c->flags & REDIS_MONITORING) { - __redisPushCallback(&ac->replies,&cb); - } - - /* When the connection is not being disconnected, simply stop - * trying to get replies and wait for the next loop tick. */ - break; - } - - /* Even if the context is subscribed, pending regular callbacks will - * get a reply before pub/sub messages arrive. */ - if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) { - /* - * A spontaneous reply in a not-subscribed context can be the error - * reply that is sent when a new connection exceeds the maximum - * number of allowed connections on the server side. - * - * This is seen as an error instead of a regular reply because the - * server closes the connection after sending it. - * - * To prevent the error from being overwritten by an EOF error the - * connection is closed here. See issue #43. - * - * Another possibility is that the server is loading its dataset. - * In this case we also want to close the connection, and have the - * user wait until the server is ready to take our request. - */ - if (((redisReply*)reply)->type == REDIS_REPLY_ERROR) { - c->err = REDIS_ERR_OTHER; - snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str); - c->reader->fn->freeObject(reply); - __redisAsyncDisconnect(ac); - return; - } - /* No more regular callbacks and no errors, the context *must* be subscribed or monitoring. */ - assert((c->flags & REDIS_SUBSCRIBED || c->flags & REDIS_MONITORING)); - if(c->flags & REDIS_SUBSCRIBED) - __redisGetSubscribeCallback(ac,reply,&cb); - } - - if (cb.fn != NULL) { - __redisRunCallback(ac,&cb,reply); - c->reader->fn->freeObject(reply); - - /* Proceed with free'ing when redisAsyncFree() was called. */ - if (c->flags & REDIS_FREEING) { - __redisAsyncFree(ac); - return; - } - } else { - /* No callback for this reply. This can either be a NULL callback, - * or there were no callbacks to begin with. Either way, don't - * abort with an error, but simply ignore it because the client - * doesn't know what the server will spit out over the wire. */ - c->reader->fn->freeObject(reply); - } - } - - /* Disconnect when there was an error reading the reply */ - if (status != REDIS_OK) - __redisAsyncDisconnect(ac); -} - -/* Internal helper function to detect socket status the first time a read or - * write event fires. When connecting was not successful, the connect callback - * is called with a REDIS_ERR status and the context is free'd. */ -static int __redisAsyncHandleConnect(redisAsyncContext *ac) { - int completed = 0; - redisContext *c = &(ac->c); - if (redisCheckConnectDone(c, &completed) == REDIS_ERR) { - /* Error! */ - redisCheckSocketError(c); - if (ac->onConnect) ac->onConnect(ac, REDIS_ERR); - __redisAsyncDisconnect(ac); - return REDIS_ERR; - } else if (completed == 1) { - /* connected! */ - if (ac->onConnect) ac->onConnect(ac, REDIS_OK); - c->flags |= REDIS_CONNECTED; - return REDIS_OK; - } else { - return REDIS_OK; - } -} - -/* This function should be called when the socket is readable. - * It processes all replies that can be read and executes their callbacks. - */ -void redisAsyncHandleRead(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - - if (!(c->flags & REDIS_CONNECTED)) { - /* Abort connect was not successful. */ - if (__redisAsyncHandleConnect(ac) != REDIS_OK) - return; - /* Try again later when the context is still not connected. */ - if (!(c->flags & REDIS_CONNECTED)) - return; - } - - if (redisBufferRead(c) == REDIS_ERR) { - __redisAsyncDisconnect(ac); - } else { - /* Always re-schedule reads */ - _EL_ADD_READ(ac); - redisProcessCallbacks(ac); - } -} - -void redisAsyncHandleWrite(redisAsyncContext *ac) { - redisContext *c = &(ac->c); - int done = 0; - - if (!(c->flags & REDIS_CONNECTED)) { - /* Abort connect was not successful. */ - if (__redisAsyncHandleConnect(ac) != REDIS_OK) - return; - /* Try again later when the context is still not connected. */ - if (!(c->flags & REDIS_CONNECTED)) - return; - } - - if (redisBufferWrite(c,&done) == REDIS_ERR) { - __redisAsyncDisconnect(ac); - } else { - /* Continue writing when not done, stop writing otherwise */ - if (!done) - _EL_ADD_WRITE(ac); - else - _EL_DEL_WRITE(ac); - - /* Always schedule reads after writes */ - _EL_ADD_READ(ac); - } -} - -/* Sets a pointer to the first argument and its length starting at p. Returns - * the number of bytes to skip to get to the following argument. */ -static const char *nextArgument(const char *start, const char **str, size_t *len) { - const char *p = start; - if (p[0] != '$') { - p = strchr(p,'$'); - if (p == NULL) return NULL; - } - - *len = (int)strtol(p+1,NULL,10); - p = strchr(p,'\r'); - assert(p); - *str = p+2; - return p+2+(*len)+2; -} - -/* Helper function for the redisAsyncCommand* family of functions. Writes a - * formatted command to the output buffer and registers the provided callback - * function with the context. */ -static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) { - redisContext *c = &(ac->c); - redisCallback cb; - struct dict *cbdict; - dictEntry *de; - redisCallback *existcb; - int pvariant, hasnext; - const char *cstr, *astr; - size_t clen, alen; - const char *p; - sds sname; - int ret; - - /* Don't accept new commands when the connection is about to be closed. */ - if (c->flags & (REDIS_DISCONNECTING | REDIS_FREEING)) return REDIS_ERR; - - /* Setup callback */ - cb.fn = fn; - cb.privdata = privdata; - cb.pending_subs = 1; - - /* Find out which command will be appended. */ - p = nextArgument(cmd,&cstr,&clen); - assert(p != NULL); - hasnext = (p[0] == '$'); - pvariant = (tolower(cstr[0]) == 'p') ? 1 : 0; - cstr += pvariant; - clen -= pvariant; - - if (hasnext && strncasecmp(cstr,"subscribe\r\n",11) == 0) { - c->flags |= REDIS_SUBSCRIBED; - - /* Add every channel/pattern to the list of subscription callbacks. */ - while ((p = nextArgument(p,&astr,&alen)) != NULL) { - sname = sdsnewlen(astr,alen); - if (pvariant) - cbdict = ac->sub.patterns; - else - cbdict = ac->sub.channels; - - de = dictFind(cbdict,sname); - - if (de != NULL) { - existcb = dictGetEntryVal(de); - cb.pending_subs = existcb->pending_subs + 1; - } - - ret = dictReplace(cbdict,sname,&cb); - - if (ret == 0) sdsfree(sname); - } - } else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) { - /* It is only useful to call (P)UNSUBSCRIBE when the context is - * subscribed to one or more channels or patterns. */ - if (!(c->flags & REDIS_SUBSCRIBED)) return REDIS_ERR; - - /* (P)UNSUBSCRIBE does not have its own response: every channel or - * pattern that is unsubscribed will receive a message. This means we - * should not append a callback function for this command. */ - } else if(strncasecmp(cstr,"monitor\r\n",9) == 0) { - /* Set monitor flag and push callback */ - c->flags |= REDIS_MONITORING; - __redisPushCallback(&ac->replies,&cb); - } else { - if (c->flags & REDIS_SUBSCRIBED) - /* This will likely result in an error reply, but it needs to be - * received and passed to the callback. */ - __redisPushCallback(&ac->sub.invalid,&cb); - else - __redisPushCallback(&ac->replies,&cb); - } - - __redisAppendCommand(c,cmd,len); - - /* Always schedule a write when the write buffer is non-empty */ - _EL_ADD_WRITE(ac); - - return REDIS_OK; -} - -int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap) { - char *cmd; - int len; - int status; - len = redisvFormatCommand(&cmd,format,ap); - - /* We don't want to pass -1 or -2 to future functions as a length. */ - if (len < 0) - return REDIS_ERR; - - status = __redisAsyncCommand(ac,fn,privdata,cmd,len); - free(cmd); - return status; -} - -int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...) { - va_list ap; - int status; - va_start(ap,format); - status = redisvAsyncCommand(ac,fn,privdata,format,ap); - va_end(ap); - return status; -} - -int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) { - sds cmd; - int len; - int status; - len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen); - if (len < 0) - return REDIS_ERR; - status = __redisAsyncCommand(ac,fn,privdata,cmd,len); - sdsfree(cmd); - return status; -} - -int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) { - int status = __redisAsyncCommand(ac,fn,privdata,cmd,len); - return status; -} diff --git a/thirdparty/hiredis/async.h b/thirdparty/hiredis/async.h deleted file mode 100644 index 740555c24ca..00000000000 --- a/thirdparty/hiredis/async.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2009-2011, Salvatore Sanfilippo - * Copyright (c) 2010-2011, Pieter Noordhuis - * - * 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_ASYNC_H -#define __HIREDIS_ASYNC_H -#include "hiredis.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct redisAsyncContext; /* need forward declaration of redisAsyncContext */ -struct dict; /* dictionary header is included in async.c */ - -/* Reply callback prototype and container */ -typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*); -typedef struct redisCallback { - struct redisCallback *next; /* simple singly linked list */ - redisCallbackFn *fn; - int pending_subs; - void *privdata; -} redisCallback; - -/* List of callbacks for either regular replies or pub/sub */ -typedef struct redisCallbackList { - redisCallback *head, *tail; -} redisCallbackList; - -/* Connection callback prototypes */ -typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status); -typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status); - -/* Context for an async connection to Redis */ -typedef struct redisAsyncContext { - /* Hold the regular context, so it can be realloc'ed. */ - redisContext c; - - /* Setup error flags so they can be used directly. */ - int err; - char *errstr; - - /* Not used by hiredis */ - void *data; - - /* Event library data and hooks */ - struct { - void *data; - - /* Hooks that are called when the library expects to start - * reading/writing. These functions should be idempotent. */ - void (*addRead)(void *privdata); - void (*delRead)(void *privdata); - void (*addWrite)(void *privdata); - void (*delWrite)(void *privdata); - void (*cleanup)(void *privdata); - } ev; - - /* Called when either the connection is terminated due to an error or per - * user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */ - redisDisconnectCallback *onDisconnect; - - /* Called when the first write event was received. */ - redisConnectCallback *onConnect; - - /* Regular command callbacks */ - redisCallbackList replies; - - /* Address used for connect() */ - struct sockaddr *saddr; - size_t addrlen; - - /* Subscription callbacks */ - struct { - redisCallbackList invalid; - struct dict *channels; - struct dict *patterns; - } sub; -} redisAsyncContext; - -/* Functions that proxy to hiredis */ -redisAsyncContext *redisAsyncConnect(const char *ip, int port); -redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr); -redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port, - const char *source_addr); -redisAsyncContext *redisAsyncConnectUnix(const char *path); -int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn); -int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); -void redisAsyncDisconnect(redisAsyncContext *ac); -void redisAsyncFree(redisAsyncContext *ac); - -/* Handle read/write events */ -void redisAsyncHandleRead(redisAsyncContext *ac); -void redisAsyncHandleWrite(redisAsyncContext *ac); - -/* Command functions for an async context. Write the command to the - * output buffer and register the provided callback. */ -int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap); -int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...); -int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen); -int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/thirdparty/hiredis/dict.c b/thirdparty/hiredis/dict.c deleted file mode 100644 index e17a625461a..00000000000 --- a/thirdparty/hiredis/dict.c +++ /dev/null @@ -1,338 +0,0 @@ -/* Hash table implementation. - * - * This file implements in memory hash tables with insert/del/replace/find/ - * get-random-element operations. Hash tables will auto resize if needed - * tables of power of two in size are used, collisions are handled by - * chaining. See the source code for more information... :) - * - * Copyright (c) 2006-2010, Salvatore Sanfilippo - * 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 -#include -#include -#include "dict.h" - -/* -------------------------- private prototypes ---------------------------- */ - -static int _dictExpandIfNeeded(dict *ht); -static unsigned long _dictNextPower(unsigned long size); -static int _dictKeyIndex(dict *ht, const void *key); -static int _dictInit(dict *ht, dictType *type, void *privDataPtr); - -/* -------------------------- hash functions -------------------------------- */ - -/* Generic hash function (a popular one from Bernstein). - * I tested a few and this was the best. */ -static unsigned int dictGenHashFunction(const unsigned char *buf, int len) { - unsigned int hash = 5381; - - while (len--) - hash = ((hash << 5) + hash) + (*buf++); /* hash * 33 + c */ - return hash; -} - -/* ----------------------------- API implementation ------------------------- */ - -/* Reset an hashtable already initialized with ht_init(). - * NOTE: This function should only called by ht_destroy(). */ -static void _dictReset(dict *ht) { - ht->table = NULL; - ht->size = 0; - ht->sizemask = 0; - ht->used = 0; -} - -/* Create a new hash table */ -static dict *dictCreate(dictType *type, void *privDataPtr) { - dict *ht = malloc(sizeof(*ht)); - _dictInit(ht,type,privDataPtr); - return ht; -} - -/* Initialize the hash table */ -static int _dictInit(dict *ht, dictType *type, void *privDataPtr) { - _dictReset(ht); - ht->type = type; - ht->privdata = privDataPtr; - return DICT_OK; -} - -/* Expand or create the hashtable */ -static int dictExpand(dict *ht, unsigned long size) { - dict n; /* the new hashtable */ - unsigned long realsize = _dictNextPower(size), i; - - /* the size is invalid if it is smaller than the number of - * elements already inside the hashtable */ - if (ht->used > size) - return DICT_ERR; - - _dictInit(&n, ht->type, ht->privdata); - n.size = realsize; - n.sizemask = realsize-1; - n.table = calloc(realsize,sizeof(dictEntry*)); - - /* Copy all the elements from the old to the new table: - * note that if the old hash table is empty ht->size is zero, - * so dictExpand just creates an hash table. */ - n.used = ht->used; - for (i = 0; i < ht->size && ht->used > 0; i++) { - dictEntry *he, *nextHe; - - if (ht->table[i] == NULL) continue; - - /* For each hash entry on this slot... */ - he = ht->table[i]; - while(he) { - unsigned int h; - - nextHe = he->next; - /* Get the new element index */ - h = dictHashKey(ht, he->key) & n.sizemask; - he->next = n.table[h]; - n.table[h] = he; - ht->used--; - /* Pass to the next element */ - he = nextHe; - } - } - assert(ht->used == 0); - free(ht->table); - - /* Remap the new hashtable in the old */ - *ht = n; - return DICT_OK; -} - -/* Add an element to the target hash table */ -static int dictAdd(dict *ht, void *key, void *val) { - int index; - dictEntry *entry; - - /* Get the index of the new element, or -1 if - * the element already exists. */ - if ((index = _dictKeyIndex(ht, key)) == -1) - return DICT_ERR; - - /* Allocates the memory and stores key */ - entry = malloc(sizeof(*entry)); - entry->next = ht->table[index]; - ht->table[index] = entry; - - /* Set the hash entry fields. */ - dictSetHashKey(ht, entry, key); - dictSetHashVal(ht, entry, val); - ht->used++; - return DICT_OK; -} - -/* Add an element, discarding the old if the key already exists. - * Return 1 if the key was added from scratch, 0 if there was already an - * element with such key and dictReplace() just performed a value update - * operation. */ -static int dictReplace(dict *ht, void *key, void *val) { - dictEntry *entry, auxentry; - - /* Try to add the element. If the key - * does not exists dictAdd will succeed. */ - if (dictAdd(ht, key, val) == DICT_OK) - return 1; - /* It already exists, get the entry */ - entry = dictFind(ht, key); - /* Free the old value and set the new one */ - /* Set the new value and free the old one. Note that it is important - * to do that in this order, as the value may just be exactly the same - * as the previous one. In this context, think to reference counting, - * you want to increment (set), and then decrement (free), and not the - * reverse. */ - auxentry = *entry; - dictSetHashVal(ht, entry, val); - dictFreeEntryVal(ht, &auxentry); - return 0; -} - -/* Search and remove an element */ -static int dictDelete(dict *ht, const void *key) { - unsigned int h; - dictEntry *de, *prevde; - - if (ht->size == 0) - return DICT_ERR; - h = dictHashKey(ht, key) & ht->sizemask; - de = ht->table[h]; - - prevde = NULL; - while(de) { - if (dictCompareHashKeys(ht,key,de->key)) { - /* Unlink the element from the list */ - if (prevde) - prevde->next = de->next; - else - ht->table[h] = de->next; - - dictFreeEntryKey(ht,de); - dictFreeEntryVal(ht,de); - free(de); - ht->used--; - return DICT_OK; - } - prevde = de; - de = de->next; - } - return DICT_ERR; /* not found */ -} - -/* Destroy an entire hash table */ -static int _dictClear(dict *ht) { - unsigned long i; - - /* Free all the elements */ - for (i = 0; i < ht->size && ht->used > 0; i++) { - dictEntry *he, *nextHe; - - if ((he = ht->table[i]) == NULL) continue; - while(he) { - nextHe = he->next; - dictFreeEntryKey(ht, he); - dictFreeEntryVal(ht, he); - free(he); - ht->used--; - he = nextHe; - } - } - /* Free the table and the allocated cache structure */ - free(ht->table); - /* Re-initialize the table */ - _dictReset(ht); - return DICT_OK; /* never fails */ -} - -/* Clear & Release the hash table */ -static void dictRelease(dict *ht) { - _dictClear(ht); - free(ht); -} - -static dictEntry *dictFind(dict *ht, const void *key) { - dictEntry *he; - unsigned int h; - - if (ht->size == 0) return NULL; - h = dictHashKey(ht, key) & ht->sizemask; - he = ht->table[h]; - while(he) { - if (dictCompareHashKeys(ht, key, he->key)) - return he; - he = he->next; - } - return NULL; -} - -static dictIterator *dictGetIterator(dict *ht) { - dictIterator *iter = malloc(sizeof(*iter)); - - iter->ht = ht; - iter->index = -1; - iter->entry = NULL; - iter->nextEntry = NULL; - return iter; -} - -static dictEntry *dictNext(dictIterator *iter) { - while (1) { - if (iter->entry == NULL) { - iter->index++; - if (iter->index >= - (signed)iter->ht->size) break; - iter->entry = iter->ht->table[iter->index]; - } else { - iter->entry = iter->nextEntry; - } - if (iter->entry) { - /* We need to save the 'next' here, the iterator user - * may delete the entry we are returning. */ - iter->nextEntry = iter->entry->next; - return iter->entry; - } - } - return NULL; -} - -static void dictReleaseIterator(dictIterator *iter) { - free(iter); -} - -/* ------------------------- private functions ------------------------------ */ - -/* Expand the hash table if needed */ -static int _dictExpandIfNeeded(dict *ht) { - /* If the hash table is empty expand it to the initial size, - * if the table is "full" dobule its size. */ - if (ht->size == 0) - return dictExpand(ht, DICT_HT_INITIAL_SIZE); - if (ht->used == ht->size) - return dictExpand(ht, ht->size*2); - return DICT_OK; -} - -/* Our hash table capability is a power of two */ -static unsigned long _dictNextPower(unsigned long size) { - unsigned long i = DICT_HT_INITIAL_SIZE; - - if (size >= LONG_MAX) return LONG_MAX; - while(1) { - if (i >= size) - return i; - i *= 2; - } -} - -/* Returns the index of a free slot that can be populated with - * an hash entry for the given 'key'. - * If the key already exists, -1 is returned. */ -static int _dictKeyIndex(dict *ht, const void *key) { - unsigned int h; - dictEntry *he; - - /* Expand the hashtable if needed */ - if (_dictExpandIfNeeded(ht) == DICT_ERR) - return -1; - /* Compute the key hash value */ - h = dictHashKey(ht, key) & ht->sizemask; - /* Search if this slot does not already contain the given key */ - he = ht->table[h]; - while(he) { - if (dictCompareHashKeys(ht, key, he->key)) - return -1; - he = he->next; - } - return h; -} - diff --git a/thirdparty/hiredis/dict.h b/thirdparty/hiredis/dict.h deleted file mode 100644 index 95fcd280e2c..00000000000 --- a/thirdparty/hiredis/dict.h +++ /dev/null @@ -1,126 +0,0 @@ -/* Hash table implementation. - * - * This file implements in memory hash tables with insert/del/replace/find/ - * get-random-element operations. Hash tables will auto resize if needed - * tables of power of two in size are used, collisions are handled by - * chaining. See the source code for more information... :) - * - * Copyright (c) 2006-2010, Salvatore Sanfilippo - * 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 __DICT_H -#define __DICT_H - -#define DICT_OK 0 -#define DICT_ERR 1 - -/* Unused arguments generate annoying warnings... */ -#define DICT_NOTUSED(V) ((void) V) - -typedef struct dictEntry { - void *key; - void *val; - struct dictEntry *next; -} dictEntry; - -typedef struct dictType { - unsigned int (*hashFunction)(const void *key); - void *(*keyDup)(void *privdata, const void *key); - void *(*valDup)(void *privdata, const void *obj); - int (*keyCompare)(void *privdata, const void *key1, const void *key2); - void (*keyDestructor)(void *privdata, void *key); - void (*valDestructor)(void *privdata, void *obj); -} dictType; - -typedef struct dict { - dictEntry **table; - dictType *type; - unsigned long size; - unsigned long sizemask; - unsigned long used; - void *privdata; -} dict; - -typedef struct dictIterator { - dict *ht; - int index; - dictEntry *entry, *nextEntry; -} dictIterator; - -/* This is the initial size of every hash table */ -#define DICT_HT_INITIAL_SIZE 4 - -/* ------------------------------- Macros ------------------------------------*/ -#define dictFreeEntryVal(ht, entry) \ - if ((ht)->type->valDestructor) \ - (ht)->type->valDestructor((ht)->privdata, (entry)->val) - -#define dictSetHashVal(ht, entry, _val_) do { \ - if ((ht)->type->valDup) \ - entry->val = (ht)->type->valDup((ht)->privdata, _val_); \ - else \ - entry->val = (_val_); \ -} while(0) - -#define dictFreeEntryKey(ht, entry) \ - if ((ht)->type->keyDestructor) \ - (ht)->type->keyDestructor((ht)->privdata, (entry)->key) - -#define dictSetHashKey(ht, entry, _key_) do { \ - if ((ht)->type->keyDup) \ - entry->key = (ht)->type->keyDup((ht)->privdata, _key_); \ - else \ - entry->key = (_key_); \ -} while(0) - -#define dictCompareHashKeys(ht, key1, key2) \ - (((ht)->type->keyCompare) ? \ - (ht)->type->keyCompare((ht)->privdata, key1, key2) : \ - (key1) == (key2)) - -#define dictHashKey(ht, key) (ht)->type->hashFunction(key) - -#define dictGetEntryKey(he) ((he)->key) -#define dictGetEntryVal(he) ((he)->val) -#define dictSlots(ht) ((ht)->size) -#define dictSize(ht) ((ht)->used) - -/* API */ -static unsigned int dictGenHashFunction(const unsigned char *buf, int len); -static dict *dictCreate(dictType *type, void *privDataPtr); -static int dictExpand(dict *ht, unsigned long size); -static int dictAdd(dict *ht, void *key, void *val); -static int dictReplace(dict *ht, void *key, void *val); -static int dictDelete(dict *ht, const void *key); -static void dictRelease(dict *ht); -static dictEntry * dictFind(dict *ht, const void *key); -static dictIterator *dictGetIterator(dict *ht); -static dictEntry *dictNext(dictIterator *iter); -static void dictReleaseIterator(dictIterator *iter); - -#endif /* __DICT_H */ diff --git a/thirdparty/hiredis/examples/example-ae.c b/thirdparty/hiredis/examples/example-ae.c deleted file mode 100644 index 8efa7306aec..00000000000 --- a/thirdparty/hiredis/examples/example-ae.c +++ /dev/null @@ -1,62 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -/* Put event loop in the global scope, so it can be explicitly stopped */ -static aeEventLoop *loop; - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - aeStop(loop); - return; - } - - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - aeStop(loop); - return; - } - - printf("Disconnected...\n"); - aeStop(loop); -} - -int main (int argc, char **argv) { - signal(SIGPIPE, SIG_IGN); - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - loop = aeCreateEventLoop(64); - redisAeAttach(loop, c); - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - aeMain(loop); - return 0; -} - diff --git a/thirdparty/hiredis/examples/example-glib.c b/thirdparty/hiredis/examples/example-glib.c deleted file mode 100644 index d6e10f8e820..00000000000 --- a/thirdparty/hiredis/examples/example-glib.c +++ /dev/null @@ -1,73 +0,0 @@ -#include - -#include -#include -#include - -static GMainLoop *mainloop; - -static void -connect_cb (const redisAsyncContext *ac G_GNUC_UNUSED, - int status) -{ - if (status != REDIS_OK) { - g_printerr("Failed to connect: %s\n", ac->errstr); - g_main_loop_quit(mainloop); - } else { - g_printerr("Connected...\n"); - } -} - -static void -disconnect_cb (const redisAsyncContext *ac G_GNUC_UNUSED, - int status) -{ - if (status != REDIS_OK) { - g_error("Failed to disconnect: %s", ac->errstr); - } else { - g_printerr("Disconnected...\n"); - g_main_loop_quit(mainloop); - } -} - -static void -command_cb(redisAsyncContext *ac, - gpointer r, - gpointer user_data G_GNUC_UNUSED) -{ - redisReply *reply = r; - - if (reply) { - g_print("REPLY: %s\n", reply->str); - } - - redisAsyncDisconnect(ac); -} - -gint -main (gint argc G_GNUC_UNUSED, - gchar *argv[] G_GNUC_UNUSED) -{ - redisAsyncContext *ac; - GMainContext *context = NULL; - GSource *source; - - ac = redisAsyncConnect("127.0.0.1", 6379); - if (ac->err) { - g_printerr("%s\n", ac->errstr); - exit(EXIT_FAILURE); - } - - source = redis_source_new(ac); - mainloop = g_main_loop_new(context, FALSE); - g_source_attach(source, context); - - redisAsyncSetConnectCallback(ac, connect_cb); - redisAsyncSetDisconnectCallback(ac, disconnect_cb); - redisAsyncCommand(ac, command_cb, NULL, "SET key 1234"); - redisAsyncCommand(ac, command_cb, NULL, "GET key"); - - g_main_loop_run(mainloop); - - return EXIT_SUCCESS; -} diff --git a/thirdparty/hiredis/examples/example-ivykis.c b/thirdparty/hiredis/examples/example-ivykis.c deleted file mode 100644 index 67affcef365..00000000000 --- a/thirdparty/hiredis/examples/example-ivykis.c +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Disconnected...\n"); -} - -int main (int argc, char **argv) { - signal(SIGPIPE, SIG_IGN); - - iv_init(); - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - redisIvykisAttach(c); - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - - iv_main(); - - iv_deinit(); - - return 0; -} diff --git a/thirdparty/hiredis/examples/example-libev.c b/thirdparty/hiredis/examples/example-libev.c deleted file mode 100644 index cc8b166ec62..00000000000 --- a/thirdparty/hiredis/examples/example-libev.c +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Disconnected...\n"); -} - -int main (int argc, char **argv) { - signal(SIGPIPE, SIG_IGN); - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - redisLibevAttach(EV_DEFAULT_ c); - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - ev_loop(EV_DEFAULT_ 0); - return 0; -} diff --git a/thirdparty/hiredis/examples/example-libevent.c b/thirdparty/hiredis/examples/example-libevent.c deleted file mode 100644 index d333c22b79e..00000000000 --- a/thirdparty/hiredis/examples/example-libevent.c +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Disconnected...\n"); -} - -int main (int argc, char **argv) { - signal(SIGPIPE, SIG_IGN); - struct event_base *base = event_base_new(); - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - redisLibeventAttach(c,base); - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - event_base_dispatch(base); - return 0; -} diff --git a/thirdparty/hiredis/examples/example-libuv.c b/thirdparty/hiredis/examples/example-libuv.c deleted file mode 100644 index a5462d41051..00000000000 --- a/thirdparty/hiredis/examples/example-libuv.c +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Disconnected...\n"); -} - -int main (int argc, char **argv) { - signal(SIGPIPE, SIG_IGN); - uv_loop_t* loop = uv_default_loop(); - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - redisLibuvAttach(c,loop); - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - uv_run(loop, UV_RUN_DEFAULT); - return 0; -} diff --git a/thirdparty/hiredis/examples/example-macosx.c b/thirdparty/hiredis/examples/example-macosx.c deleted file mode 100644 index bc84ed5ba76..00000000000 --- a/thirdparty/hiredis/examples/example-macosx.c +++ /dev/null @@ -1,66 +0,0 @@ -// -// Created by Дмитрий Бахвалов on 13.07.15. -// Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved. -// - -#include - -#include -#include -#include - -void getCallback(redisAsyncContext *c, void *r, void *privdata) { - redisReply *reply = r; - if (reply == NULL) return; - printf("argv[%s]: %s\n", (char*)privdata, reply->str); - - /* Disconnect after receiving the reply to GET */ - redisAsyncDisconnect(c); -} - -void connectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - printf("Connected...\n"); -} - -void disconnectCallback(const redisAsyncContext *c, int status) { - if (status != REDIS_OK) { - printf("Error: %s\n", c->errstr); - return; - } - CFRunLoopStop(CFRunLoopGetCurrent()); - printf("Disconnected...\n"); -} - -int main (int argc, char **argv) { - signal(SIGPIPE, SIG_IGN); - - CFRunLoopRef loop = CFRunLoopGetCurrent(); - if( !loop ) { - printf("Error: Cannot get current run loop\n"); - return 1; - } - - redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->err) { - /* Let *c leak for now... */ - printf("Error: %s\n", c->errstr); - return 1; - } - - redisMacOSAttach(c, loop); - - redisAsyncSetConnectCallback(c,connectCallback); - redisAsyncSetDisconnectCallback(c,disconnectCallback); - - redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1])); - redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key"); - - CFRunLoopRun(); - - return 0; -} - diff --git a/thirdparty/hiredis/examples/example-qt.cpp b/thirdparty/hiredis/examples/example-qt.cpp deleted file mode 100644 index f524c3f3dde..00000000000 --- a/thirdparty/hiredis/examples/example-qt.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include -using namespace std; - -#include -#include - -#include "example-qt.h" - -void getCallback(redisAsyncContext *, void * r, void * privdata) { - - redisReply * reply = static_cast(r); - ExampleQt * ex = static_cast(privdata); - if (reply == nullptr || ex == nullptr) return; - - cout << "key: " << reply->str << endl; - - ex->finish(); -} - -void ExampleQt::run() { - - m_ctx = redisAsyncConnect("localhost", 6379); - - if (m_ctx->err) { - cerr << "Error: " << m_ctx->errstr << endl; - redisAsyncFree(m_ctx); - emit finished(); - } - - m_adapter.setContext(m_ctx); - - redisAsyncCommand(m_ctx, NULL, NULL, "SET key %s", m_value); - redisAsyncCommand(m_ctx, getCallback, this, "GET key"); -} - -int main (int argc, char **argv) { - - QCoreApplication app(argc, argv); - - ExampleQt example(argv[argc-1]); - - QObject::connect(&example, SIGNAL(finished()), &app, SLOT(quit())); - QTimer::singleShot(0, &example, SLOT(run())); - - return app.exec(); -} diff --git a/thirdparty/hiredis/examples/example-qt.h b/thirdparty/hiredis/examples/example-qt.h deleted file mode 100644 index 374f47666bb..00000000000 --- a/thirdparty/hiredis/examples/example-qt.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef __HIREDIS_EXAMPLE_QT_H -#define __HIREDIS_EXAMPLE_QT_H - -#include - -class ExampleQt : public QObject { - - Q_OBJECT - - public: - ExampleQt(const char * value, QObject * parent = 0) - : QObject(parent), m_value(value) {} - - signals: - void finished(); - - public slots: - void run(); - - private: - void finish() { emit finished(); } - - private: - const char * m_value; - redisAsyncContext * m_ctx; - RedisQtAdapter m_adapter; - - friend - void getCallback(redisAsyncContext *, void *, void *); -}; - -#endif /* !__HIREDIS_EXAMPLE_QT_H */ diff --git a/thirdparty/hiredis/examples/example.c b/thirdparty/hiredis/examples/example.c deleted file mode 100644 index 4d494c55ab6..00000000000 --- a/thirdparty/hiredis/examples/example.c +++ /dev/null @@ -1,78 +0,0 @@ -#include -#include -#include - -#include - -int main(int argc, char **argv) { - unsigned int j; - redisContext *c; - redisReply *reply; - const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1"; - int port = (argc > 2) ? atoi(argv[2]) : 6379; - - struct timeval timeout = { 1, 500000 }; // 1.5 seconds - c = redisConnectWithTimeout(hostname, port, timeout); - if (c == NULL || c->err) { - if (c) { - printf("Connection error: %s\n", c->errstr); - redisFree(c); - } else { - printf("Connection error: can't allocate redis context\n"); - } - exit(1); - } - - /* PING server */ - reply = redisCommand(c,"PING"); - printf("PING: %s\n", reply->str); - freeReplyObject(reply); - - /* Set a key */ - reply = redisCommand(c,"SET %s %s", "foo", "hello world"); - printf("SET: %s\n", reply->str); - freeReplyObject(reply); - - /* Set a key using binary safe API */ - reply = redisCommand(c,"SET %b %b", "bar", (size_t) 3, "hello", (size_t) 5); - printf("SET (binary API): %s\n", reply->str); - freeReplyObject(reply); - - /* Try a GET and two INCR */ - reply = redisCommand(c,"GET foo"); - printf("GET foo: %s\n", reply->str); - freeReplyObject(reply); - - reply = redisCommand(c,"INCR counter"); - printf("INCR counter: %lld\n", reply->integer); - freeReplyObject(reply); - /* again ... */ - reply = redisCommand(c,"INCR counter"); - printf("INCR counter: %lld\n", reply->integer); - freeReplyObject(reply); - - /* Create a list of numbers, from 0 to 9 */ - reply = redisCommand(c,"DEL mylist"); - freeReplyObject(reply); - for (j = 0; j < 10; j++) { - char buf[64]; - - snprintf(buf,64,"%u",j); - reply = redisCommand(c,"LPUSH mylist element-%s", buf); - freeReplyObject(reply); - } - - /* Let's check what we have inside the list */ - reply = redisCommand(c,"LRANGE mylist 0 -1"); - if (reply->type == REDIS_REPLY_ARRAY) { - for (j = 0; j < reply->elements; j++) { - printf("%u) %s\n", j, reply->element[j]->str); - } - } - freeReplyObject(reply); - - /* Disconnects and frees the context */ - redisFree(c); - - return 0; -} diff --git a/thirdparty/hiredis/hiredis.c b/thirdparty/hiredis/hiredis.c index 0a6c25b9720..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" -#include "socket_hook.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 fd759e520fa..510df7d44a7 100644 --- a/thirdparty/hiredis/net.c +++ b/thirdparty/hiredis/net.c @@ -54,18 +54,57 @@ #include "net.h" #include "sds.h" -#include "socket_hook.h" +#include "swoole_socket_hook.h" /* 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; } @@ -103,8 +142,8 @@ static int redisCreateSocket(redisContext *c, int type) { } static int redisSetBlocking(redisContext *c, int blocking) { - - return REDIS_OK; +#if 0 +#ifndef _WIN32 int flags; /* Set the socket nonblocking. @@ -112,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; } @@ -123,15 +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/hiredis/test.c b/thirdparty/hiredis/test.c deleted file mode 100644 index a5eb9c70e82..00000000000 --- a/thirdparty/hiredis/test.c +++ /dev/null @@ -1,938 +0,0 @@ -#include "fmacros.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hiredis.h" -#include "net.h" - -enum connection_type { - CONN_TCP, - CONN_UNIX, - CONN_FD -}; - -struct config { - enum connection_type type; - - struct { - const char *host; - int port; - struct timeval timeout; - } tcp; - - struct { - const char *path; - } unix_sock; -}; - -/* The following lines make up our testing "framework" :) */ -static int tests = 0, fails = 0; -#define test(_s) { printf("#%02d ", ++tests); printf(_s); } -#define test_cond(_c) if(_c) printf("\033[0;32mPASSED\033[0;0m\n"); else {printf("\033[0;31mFAILED\033[0;0m\n"); fails++;} - -static long long usec(void) { - struct timeval tv; - gettimeofday(&tv,NULL); - return (((long long)tv.tv_sec)*1000000)+tv.tv_usec; -} - -/* The assert() calls below have side effects, so we need assert() - * even if we are compiling without asserts (-DNDEBUG). */ -#ifdef NDEBUG -#undef assert -#define assert(e) (void)(e) -#endif - -static redisContext *select_database(redisContext *c) { - redisReply *reply; - - /* Switch to DB 9 for testing, now that we know we can chat. */ - reply = redisCommand(c,"SELECT 9"); - assert(reply != NULL); - freeReplyObject(reply); - - /* Make sure the DB is emtpy */ - reply = redisCommand(c,"DBSIZE"); - assert(reply != NULL); - if (reply->type == REDIS_REPLY_INTEGER && reply->integer == 0) { - /* Awesome, DB 9 is empty and we can continue. */ - freeReplyObject(reply); - } else { - printf("Database #9 is not empty, test can not continue\n"); - exit(1); - } - - return c; -} - -static int disconnect(redisContext *c, int keep_fd) { - redisReply *reply; - - /* Make sure we're on DB 9. */ - reply = redisCommand(c,"SELECT 9"); - assert(reply != NULL); - freeReplyObject(reply); - reply = redisCommand(c,"FLUSHDB"); - assert(reply != NULL); - freeReplyObject(reply); - - /* Free the context as well, but keep the fd if requested. */ - if (keep_fd) - return redisFreeKeepFd(c); - redisFree(c); - return -1; -} - -static redisContext *do_connect(struct config config) { - redisContext *c = NULL; - - if (config.type == CONN_TCP) { - c = redisConnect(config.tcp.host, config.tcp.port); - } else if (config.type == CONN_UNIX) { - c = redisConnectUnix(config.unix_sock.path); - } else if (config.type == CONN_FD) { - /* Create a dummy connection just to get an fd to inherit */ - redisContext *dummy_ctx = redisConnectUnix(config.unix_sock.path); - if (dummy_ctx) { - int fd = disconnect(dummy_ctx, 1); - printf("Connecting to inherited fd %d\n", fd); - c = redisConnectFd(fd); - } - } else { - assert(NULL); - } - - if (c == NULL) { - printf("Connection error: can't allocate redis context\n"); - exit(1); - } else if (c->err) { - printf("Connection error: %s\n", c->errstr); - redisFree(c); - exit(1); - } - - return select_database(c); -} - -static void test_format_commands(void) { - char *cmd; - int len; - - test("Format command without interpolation: "); - len = redisFormatCommand(&cmd,"SET foo bar"); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(3+2)); - free(cmd); - - test("Format command with %%s string interpolation: "); - len = redisFormatCommand(&cmd,"SET %s %s","foo","bar"); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(3+2)); - free(cmd); - - test("Format command with %%s and an empty string: "); - len = redisFormatCommand(&cmd,"SET %s %s","foo",""); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(0+2)); - free(cmd); - - test("Format command with an empty string in between proper interpolations: "); - len = redisFormatCommand(&cmd,"SET %s %s","","foo"); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$0\r\n\r\n$3\r\nfoo\r\n",len) == 0 && - len == 4+4+(3+2)+4+(0+2)+4+(3+2)); - free(cmd); - - test("Format command with %%b string interpolation: "); - len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"b\0r",(size_t)3); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nb\0r\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(3+2)); - free(cmd); - - test("Format command with %%b and an empty string: "); - len = redisFormatCommand(&cmd,"SET %b %b","foo",(size_t)3,"",(size_t)0); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(0+2)); - free(cmd); - - test("Format command with literal %%: "); - len = redisFormatCommand(&cmd,"SET %% %%"); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$1\r\n%\r\n$1\r\n%\r\n",len) == 0 && - len == 4+4+(3+2)+4+(1+2)+4+(1+2)); - free(cmd); - - /* Vararg width depends on the type. These tests make sure that the - * width is correctly determined using the format and subsequent varargs - * can correctly be interpolated. */ -#define INTEGER_WIDTH_TEST(fmt, type) do { \ - type value = 123; \ - test("Format command with printf-delegation (" #type "): "); \ - len = redisFormatCommand(&cmd,"key:%08" fmt " str:%s", value, "hello"); \ - test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:00000123\r\n$9\r\nstr:hello\r\n",len) == 0 && \ - len == 4+5+(12+2)+4+(9+2)); \ - free(cmd); \ -} while(0) - -#define FLOAT_WIDTH_TEST(type) do { \ - type value = 123.0; \ - test("Format command with printf-delegation (" #type "): "); \ - len = redisFormatCommand(&cmd,"key:%08.3f str:%s", value, "hello"); \ - test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:0123.000\r\n$9\r\nstr:hello\r\n",len) == 0 && \ - len == 4+5+(12+2)+4+(9+2)); \ - free(cmd); \ -} while(0) - - INTEGER_WIDTH_TEST("d", int); - INTEGER_WIDTH_TEST("hhd", char); - INTEGER_WIDTH_TEST("hd", short); - INTEGER_WIDTH_TEST("ld", long); - INTEGER_WIDTH_TEST("lld", long long); - INTEGER_WIDTH_TEST("u", unsigned int); - INTEGER_WIDTH_TEST("hhu", unsigned char); - INTEGER_WIDTH_TEST("hu", unsigned short); - INTEGER_WIDTH_TEST("lu", unsigned long); - INTEGER_WIDTH_TEST("llu", unsigned long long); - FLOAT_WIDTH_TEST(float); - FLOAT_WIDTH_TEST(double); - - test("Format command with invalid printf format: "); - len = redisFormatCommand(&cmd,"key:%08p %b",(void*)1234,"foo",(size_t)3); - test_cond(len == -1); - - const char *argv[3]; - argv[0] = "SET"; - argv[1] = "foo\0xxx"; - argv[2] = "bar"; - size_t lens[3] = { 3, 7, 3 }; - int argc = 3; - - test("Format command by passing argc/argv without lengths: "); - len = redisFormatCommandArgv(&cmd,argc,argv,NULL); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(3+2)); - free(cmd); - - test("Format command by passing argc/argv with lengths: "); - len = redisFormatCommandArgv(&cmd,argc,argv,lens); - test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(7+2)+4+(3+2)); - free(cmd); - - sds sds_cmd; - - sds_cmd = sdsempty(); - test("Format command into sds by passing argc/argv without lengths: "); - len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,NULL); - test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(3+2)+4+(3+2)); - sdsfree(sds_cmd); - - sds_cmd = sdsempty(); - test("Format command into sds by passing argc/argv with lengths: "); - len = redisFormatSdsCommandArgv(&sds_cmd,argc,argv,lens); - test_cond(strncmp(sds_cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 && - len == 4+4+(3+2)+4+(7+2)+4+(3+2)); - sdsfree(sds_cmd); -} - -static void test_append_formatted_commands(struct config config) { - redisContext *c; - redisReply *reply; - char *cmd; - int len; - - c = do_connect(config); - - test("Append format command: "); - - len = redisFormatCommand(&cmd, "SET foo bar"); - - test_cond(redisAppendFormattedCommand(c, cmd, len) == REDIS_OK); - - assert(redisGetReply(c, (void*)&reply) == REDIS_OK); - - free(cmd); - freeReplyObject(reply); - - disconnect(c, 0); -} - -static void test_reply_reader(void) { - redisReader *reader; - void *reply; - int ret; - int i; - - test("Error handling in reply parser: "); - reader = redisReaderCreate(); - redisReaderFeed(reader,(char*)"@foo\r\n",6); - ret = redisReaderGetReply(reader,NULL); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0); - redisReaderFree(reader); - - /* when the reply already contains multiple items, they must be free'd - * on an error. valgrind will bark when this doesn't happen. */ - test("Memory cleanup in reply parser: "); - reader = redisReaderCreate(); - redisReaderFeed(reader,(char*)"*2\r\n",4); - redisReaderFeed(reader,(char*)"$5\r\nhello\r\n",11); - redisReaderFeed(reader,(char*)"@foo\r\n",6); - ret = redisReaderGetReply(reader,NULL); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Protocol error, got \"@\" as reply type byte") == 0); - redisReaderFree(reader); - - test("Set error on nested multi bulks with depth > 7: "); - reader = redisReaderCreate(); - - for (i = 0; i < 9; i++) { - redisReaderFeed(reader,(char*)"*1\r\n",4); - } - - ret = redisReaderGetReply(reader,NULL); - test_cond(ret == REDIS_ERR && - strncasecmp(reader->errstr,"No support for",14) == 0); - redisReaderFree(reader); - - test("Correctly parses LLONG_MAX: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, ":9223372036854775807\r\n",22); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && - ((redisReply*)reply)->type == REDIS_REPLY_INTEGER && - ((redisReply*)reply)->integer == LLONG_MAX); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Set error when > LLONG_MAX: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, ":9223372036854775808\r\n",22); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Bad integer value") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Correctly parses LLONG_MIN: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, ":-9223372036854775808\r\n",23); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && - ((redisReply*)reply)->type == REDIS_REPLY_INTEGER && - ((redisReply*)reply)->integer == LLONG_MIN); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Set error when < LLONG_MIN: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, ":-9223372036854775809\r\n",23); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Bad integer value") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Set error when array < -1: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, "*-2\r\n+asdf\r\n",12); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Set error when bulk < -1: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, "$-2\r\nasdf\r\n",11); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Bulk string length out of range") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - - test("Set error when array > INT_MAX: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, "*9223372036854775807\r\n+asdf\r\n",29); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Multi-bulk length out of range") == 0); - freeReplyObject(reply); - redisReaderFree(reader); - -#if LLONG_MAX > SIZE_MAX - test("Set error when bulk > SIZE_MAX: "); - reader = redisReaderCreate(); - redisReaderFeed(reader, "$9223372036854775807\r\nasdf\r\n",28); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && - strcasecmp(reader->errstr,"Bulk string length out of range") == 0); - freeReplyObject(reply); - redisReaderFree(reader); -#endif - - test("Works with NULL functions for reply: "); - reader = redisReaderCreate(); - reader->fn = NULL; - redisReaderFeed(reader,(char*)"+OK\r\n",5); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS); - redisReaderFree(reader); - - test("Works when a single newline (\\r\\n) covers two calls to feed: "); - reader = redisReaderCreate(); - reader->fn = NULL; - redisReaderFeed(reader,(char*)"+OK\r",4); - ret = redisReaderGetReply(reader,&reply); - assert(ret == REDIS_OK && reply == NULL); - redisReaderFeed(reader,(char*)"\n",1); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS); - redisReaderFree(reader); - - test("Don't reset state after protocol error: "); - reader = redisReaderCreate(); - reader->fn = NULL; - redisReaderFeed(reader,(char*)"x",1); - ret = redisReaderGetReply(reader,&reply); - assert(ret == REDIS_ERR); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_ERR && reply == NULL); - redisReaderFree(reader); - - /* Regression test for issue #45 on GitHub. */ - test("Don't do empty allocation for empty multi bulk: "); - reader = redisReaderCreate(); - redisReaderFeed(reader,(char*)"*0\r\n",4); - ret = redisReaderGetReply(reader,&reply); - test_cond(ret == REDIS_OK && - ((redisReply*)reply)->type == REDIS_REPLY_ARRAY && - ((redisReply*)reply)->elements == 0); - freeReplyObject(reply); - redisReaderFree(reader); -} - -static void test_free_null(void) { - void *redisCtx = NULL; - void *reply = NULL; - - test("Don't fail when redisFree is passed a NULL value: "); - redisFree(redisCtx); - test_cond(redisCtx == NULL); - - test("Don't fail when freeReplyObject is passed a NULL value: "); - freeReplyObject(reply); - test_cond(reply == NULL); -} - -static void test_blocking_connection_errors(void) { - redisContext *c; - struct addrinfo hints = {.ai_family = AF_INET}; - struct addrinfo *ai_tmp = NULL; - const char *bad_domain = "idontexist.com"; - - int rv = getaddrinfo(bad_domain, "6379", &hints, &ai_tmp); - if (rv != 0) { - // Address does *not* exist - test("Returns error when host cannot be resolved: "); - // First see if this domain name *actually* resolves to NXDOMAIN - c = redisConnect("dontexist.com", 6379); - test_cond( - c->err == REDIS_ERR_OTHER && - (strcmp(c->errstr, "Name or service not known") == 0 || - strcmp(c->errstr, "Can't resolve: sadkfjaskfjsa.com") == 0 || - strcmp(c->errstr, - "nodename nor servname provided, or not known") == 0 || - strcmp(c->errstr, "No address associated with hostname") == 0 || - strcmp(c->errstr, "Temporary failure in name resolution") == 0 || - strcmp(c->errstr, - "hostname nor servname provided, or not known") == 0 || - strcmp(c->errstr, "no address associated with name") == 0)); - redisFree(c); - } else { - printf("Skipping NXDOMAIN test. Found evil ISP!\n"); - freeaddrinfo(ai_tmp); - } - - test("Returns error when the port is not open: "); - c = redisConnect((char*)"localhost", 1); - test_cond(c->err == REDIS_ERR_IO && - strcmp(c->errstr,"Connection refused") == 0); - redisFree(c); - - test("Returns error when the unix_sock socket path doesn't accept connections: "); - c = redisConnectUnix((char*)"/tmp/idontexist.sock"); - test_cond(c->err == REDIS_ERR_IO); /* Don't care about the message... */ - redisFree(c); -} - -static void test_blocking_connection(struct config config) { - redisContext *c; - redisReply *reply; - - c = do_connect(config); - - test("Is able to deliver commands: "); - reply = redisCommand(c,"PING"); - test_cond(reply->type == REDIS_REPLY_STATUS && - strcasecmp(reply->str,"pong") == 0) - freeReplyObject(reply); - - test("Is a able to send commands verbatim: "); - reply = redisCommand(c,"SET foo bar"); - test_cond (reply->type == REDIS_REPLY_STATUS && - strcasecmp(reply->str,"ok") == 0) - freeReplyObject(reply); - - test("%%s String interpolation works: "); - reply = redisCommand(c,"SET %s %s","foo","hello world"); - freeReplyObject(reply); - reply = redisCommand(c,"GET foo"); - test_cond(reply->type == REDIS_REPLY_STRING && - strcmp(reply->str,"hello world") == 0); - freeReplyObject(reply); - - test("%%b String interpolation works: "); - reply = redisCommand(c,"SET %b %b","foo",(size_t)3,"hello\x00world",(size_t)11); - freeReplyObject(reply); - reply = redisCommand(c,"GET foo"); - test_cond(reply->type == REDIS_REPLY_STRING && - memcmp(reply->str,"hello\x00world",11) == 0) - - test("Binary reply length is correct: "); - test_cond(reply->len == 11) - freeReplyObject(reply); - - test("Can parse nil replies: "); - reply = redisCommand(c,"GET nokey"); - test_cond(reply->type == REDIS_REPLY_NIL) - freeReplyObject(reply); - - /* test 7 */ - test("Can parse integer replies: "); - reply = redisCommand(c,"INCR mycounter"); - test_cond(reply->type == REDIS_REPLY_INTEGER && reply->integer == 1) - freeReplyObject(reply); - - test("Can parse multi bulk replies: "); - freeReplyObject(redisCommand(c,"LPUSH mylist foo")); - freeReplyObject(redisCommand(c,"LPUSH mylist bar")); - reply = redisCommand(c,"LRANGE mylist 0 -1"); - test_cond(reply->type == REDIS_REPLY_ARRAY && - reply->elements == 2 && - !memcmp(reply->element[0]->str,"bar",3) && - !memcmp(reply->element[1]->str,"foo",3)) - freeReplyObject(reply); - - /* m/e with multi bulk reply *before* other reply. - * specifically test ordering of reply items to parse. */ - test("Can handle nested multi bulk replies: "); - freeReplyObject(redisCommand(c,"MULTI")); - freeReplyObject(redisCommand(c,"LRANGE mylist 0 -1")); - freeReplyObject(redisCommand(c,"PING")); - reply = (redisCommand(c,"EXEC")); - test_cond(reply->type == REDIS_REPLY_ARRAY && - reply->elements == 2 && - reply->element[0]->type == REDIS_REPLY_ARRAY && - reply->element[0]->elements == 2 && - !memcmp(reply->element[0]->element[0]->str,"bar",3) && - !memcmp(reply->element[0]->element[1]->str,"foo",3) && - reply->element[1]->type == REDIS_REPLY_STATUS && - strcasecmp(reply->element[1]->str,"pong") == 0); - freeReplyObject(reply); - - disconnect(c, 0); -} - -static void test_blocking_connection_timeouts(struct config config) { - redisContext *c; - redisReply *reply; - ssize_t s; - const char *cmd = "DEBUG SLEEP 3\r\n"; - struct timeval tv; - - c = do_connect(config); - test("Successfully completes a command when the timeout is not exceeded: "); - reply = redisCommand(c,"SET foo fast"); - freeReplyObject(reply); - tv.tv_sec = 0; - tv.tv_usec = 10000; - redisSetTimeout(c, tv); - reply = redisCommand(c, "GET foo"); - test_cond(reply != NULL && reply->type == REDIS_REPLY_STRING && memcmp(reply->str, "fast", 4) == 0); - freeReplyObject(reply); - disconnect(c, 0); - - c = do_connect(config); - test("Does not return a reply when the command times out: "); - s = write(c->fd, cmd, strlen(cmd)); - tv.tv_sec = 0; - tv.tv_usec = 10000; - redisSetTimeout(c, tv); - reply = redisCommand(c, "GET foo"); - test_cond(s > 0 && reply == NULL && c->err == REDIS_ERR_IO && strcmp(c->errstr, "Resource temporarily unavailable") == 0); - freeReplyObject(reply); - - test("Reconnect properly reconnects after a timeout: "); - redisReconnect(c); - reply = redisCommand(c, "PING"); - test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0); - freeReplyObject(reply); - - test("Reconnect properly uses owned parameters: "); - config.tcp.host = "foo"; - config.unix_sock.path = "foo"; - redisReconnect(c); - reply = redisCommand(c, "PING"); - test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0); - freeReplyObject(reply); - - disconnect(c, 0); -} - -static void test_blocking_io_errors(struct config config) { - redisContext *c; - redisReply *reply; - void *_reply; - int major, minor; - - /* Connect to target given by config. */ - c = do_connect(config); - { - /* Find out Redis version to determine the path for the next test */ - const char *field = "redis_version:"; - char *p, *eptr; - - reply = redisCommand(c,"INFO"); - p = strstr(reply->str,field); - major = strtol(p+strlen(field),&eptr,10); - p = eptr+1; /* char next to the first "." */ - minor = strtol(p,&eptr,10); - freeReplyObject(reply); - } - - test("Returns I/O error when the connection is lost: "); - reply = redisCommand(c,"QUIT"); - if (major > 2 || (major == 2 && minor > 0)) { - /* > 2.0 returns OK on QUIT and read() should be issued once more - * to know the descriptor is at EOF. */ - test_cond(strcasecmp(reply->str,"OK") == 0 && - redisGetReply(c,&_reply) == REDIS_ERR); - freeReplyObject(reply); - } else { - test_cond(reply == NULL); - } - - /* On 2.0, QUIT will cause the connection to be closed immediately and - * the read(2) for the reply on QUIT will set the error to EOF. - * On >2.0, QUIT will return with OK and another read(2) needed to be - * issued to find out the socket was closed by the server. In both - * conditions, the error will be set to EOF. */ - assert(c->err == REDIS_ERR_EOF && - strcmp(c->errstr,"Server closed the connection") == 0); - redisFree(c); - - c = do_connect(config); - test("Returns I/O error on socket timeout: "); - struct timeval tv = { 0, 1000 }; - assert(redisSetTimeout(c,tv) == REDIS_OK); - test_cond(redisGetReply(c,&_reply) == REDIS_ERR && - c->err == REDIS_ERR_IO && errno == EAGAIN); - redisFree(c); -} - -static void test_invalid_timeout_errors(struct config config) { - redisContext *c; - - test("Set error when an invalid timeout usec value is given to redisConnectWithTimeout: "); - - config.tcp.timeout.tv_sec = 0; - config.tcp.timeout.tv_usec = 10000001; - - c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout); - - test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0); - redisFree(c); - - test("Set error when an invalid timeout sec value is given to redisConnectWithTimeout: "); - - config.tcp.timeout.tv_sec = (((LONG_MAX) - 999) / 1000) + 1; - config.tcp.timeout.tv_usec = 0; - - c = redisConnectWithTimeout(config.tcp.host, config.tcp.port, config.tcp.timeout); - - test_cond(c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0); - redisFree(c); -} - -static void test_throughput(struct config config) { - redisContext *c = do_connect(config); - redisReply **replies; - int i, num; - long long t1, t2; - - test("Throughput:\n"); - for (i = 0; i < 500; i++) - freeReplyObject(redisCommand(c,"LPUSH mylist foo")); - - num = 1000; - replies = malloc(sizeof(redisReply*)*num); - t1 = usec(); - for (i = 0; i < num; i++) { - replies[i] = redisCommand(c,"PING"); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - free(replies); - printf("\t(%dx PING: %.3fs)\n", num, (t2-t1)/1000000.0); - - replies = malloc(sizeof(redisReply*)*num); - t1 = usec(); - for (i = 0; i < num; i++) { - replies[i] = redisCommand(c,"LRANGE mylist 0 499"); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY); - assert(replies[i] != NULL && replies[i]->elements == 500); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - free(replies); - printf("\t(%dx LRANGE with 500 elements: %.3fs)\n", num, (t2-t1)/1000000.0); - - replies = malloc(sizeof(redisReply*)*num); - t1 = usec(); - for (i = 0; i < num; i++) { - replies[i] = redisCommand(c, "INCRBY incrkey %d", 1000000); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - free(replies); - printf("\t(%dx INCRBY: %.3fs)\n", num, (t2-t1)/1000000.0); - - num = 10000; - replies = malloc(sizeof(redisReply*)*num); - for (i = 0; i < num; i++) - redisAppendCommand(c,"PING"); - t1 = usec(); - for (i = 0; i < num; i++) { - assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - free(replies); - printf("\t(%dx PING (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0); - - replies = malloc(sizeof(redisReply*)*num); - for (i = 0; i < num; i++) - redisAppendCommand(c,"LRANGE mylist 0 499"); - t1 = usec(); - for (i = 0; i < num; i++) { - assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY); - assert(replies[i] != NULL && replies[i]->elements == 500); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - free(replies); - printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0); - - replies = malloc(sizeof(redisReply*)*num); - for (i = 0; i < num; i++) - redisAppendCommand(c,"INCRBY incrkey %d", 1000000); - t1 = usec(); - for (i = 0; i < num; i++) { - assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK); - assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_INTEGER); - } - t2 = usec(); - for (i = 0; i < num; i++) freeReplyObject(replies[i]); - free(replies); - printf("\t(%dx INCRBY (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0); - - disconnect(c, 0); -} - -// static long __test_callback_flags = 0; -// static void __test_callback(redisContext *c, void *privdata) { -// ((void)c); -// /* Shift to detect execution order */ -// __test_callback_flags <<= 8; -// __test_callback_flags |= (long)privdata; -// } -// -// static void __test_reply_callback(redisContext *c, redisReply *reply, void *privdata) { -// ((void)c); -// /* Shift to detect execution order */ -// __test_callback_flags <<= 8; -// __test_callback_flags |= (long)privdata; -// if (reply) freeReplyObject(reply); -// } -// -// static redisContext *__connect_nonblock() { -// /* Reset callback flags */ -// __test_callback_flags = 0; -// return redisConnectNonBlock("127.0.0.1", port, NULL); -// } -// -// static void test_nonblocking_connection() { -// redisContext *c; -// int wdone = 0; -// -// test("Calls command callback when command is issued: "); -// c = __connect_nonblock(); -// redisSetCommandCallback(c,__test_callback,(void*)1); -// redisCommand(c,"PING"); -// test_cond(__test_callback_flags == 1); -// redisFree(c); -// -// test("Calls disconnect callback on redisDisconnect: "); -// c = __connect_nonblock(); -// redisSetDisconnectCallback(c,__test_callback,(void*)2); -// redisDisconnect(c); -// test_cond(__test_callback_flags == 2); -// redisFree(c); -// -// test("Calls disconnect callback and free callback on redisFree: "); -// c = __connect_nonblock(); -// redisSetDisconnectCallback(c,__test_callback,(void*)2); -// redisSetFreeCallback(c,__test_callback,(void*)4); -// redisFree(c); -// test_cond(__test_callback_flags == ((2 << 8) | 4)); -// -// test("redisBufferWrite against empty write buffer: "); -// c = __connect_nonblock(); -// test_cond(redisBufferWrite(c,&wdone) == REDIS_OK && wdone == 1); -// redisFree(c); -// -// test("redisBufferWrite against not yet connected fd: "); -// c = __connect_nonblock(); -// redisCommand(c,"PING"); -// test_cond(redisBufferWrite(c,NULL) == REDIS_ERR && -// strncmp(c->error,"write:",6) == 0); -// redisFree(c); -// -// test("redisBufferWrite against closed fd: "); -// c = __connect_nonblock(); -// redisCommand(c,"PING"); -// redisDisconnect(c); -// test_cond(redisBufferWrite(c,NULL) == REDIS_ERR && -// strncmp(c->error,"write:",6) == 0); -// redisFree(c); -// -// test("Process callbacks in the right sequence: "); -// c = __connect_nonblock(); -// redisCommandWithCallback(c,__test_reply_callback,(void*)1,"PING"); -// redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING"); -// redisCommandWithCallback(c,__test_reply_callback,(void*)3,"PING"); -// -// /* Write output buffer */ -// wdone = 0; -// while(!wdone) { -// usleep(500); -// redisBufferWrite(c,&wdone); -// } -// -// /* Read until at least one callback is executed (the 3 replies will -// * arrive in a single packet, causing all callbacks to be executed in -// * a single pass). */ -// while(__test_callback_flags == 0) { -// assert(redisBufferRead(c) == REDIS_OK); -// redisProcessCallbacks(c); -// } -// test_cond(__test_callback_flags == 0x010203); -// redisFree(c); -// -// test("redisDisconnect executes pending callbacks with NULL reply: "); -// c = __connect_nonblock(); -// redisSetDisconnectCallback(c,__test_callback,(void*)1); -// redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING"); -// redisDisconnect(c); -// test_cond(__test_callback_flags == 0x0201); -// redisFree(c); -// } - -int main(int argc, char **argv) { - struct config cfg = { - .tcp = { - .host = "127.0.0.1", - .port = 6379 - }, - .unix_sock = { - .path = "/tmp/redis.sock" - } - }; - int throughput = 1; - int test_inherit_fd = 1; - - /* Ignore broken pipe signal (for I/O error tests). */ - signal(SIGPIPE, SIG_IGN); - - /* Parse command line options. */ - argv++; argc--; - while (argc) { - if (argc >= 2 && !strcmp(argv[0],"-h")) { - argv++; argc--; - cfg.tcp.host = argv[0]; - } else if (argc >= 2 && !strcmp(argv[0],"-p")) { - argv++; argc--; - cfg.tcp.port = atoi(argv[0]); - } else if (argc >= 2 && !strcmp(argv[0],"-s")) { - argv++; argc--; - cfg.unix_sock.path = argv[0]; - } else if (argc >= 1 && !strcmp(argv[0],"--skip-throughput")) { - throughput = 0; - } else if (argc >= 1 && !strcmp(argv[0],"--skip-inherit-fd")) { - test_inherit_fd = 0; - } else { - fprintf(stderr, "Invalid argument: %s\n", argv[0]); - exit(1); - } - argv++; argc--; - } - - test_format_commands(); - test_reply_reader(); - test_blocking_connection_errors(); - test_free_null(); - - printf("\nTesting against TCP connection (%s:%d):\n", cfg.tcp.host, cfg.tcp.port); - cfg.type = CONN_TCP; - test_blocking_connection(cfg); - test_blocking_connection_timeouts(cfg); - test_blocking_io_errors(cfg); - test_invalid_timeout_errors(cfg); - test_append_formatted_commands(cfg); - if (throughput) test_throughput(cfg); - - printf("\nTesting against Unix socket connection (%s):\n", cfg.unix_sock.path); - cfg.type = CONN_UNIX; - test_blocking_connection(cfg); - test_blocking_connection_timeouts(cfg); - test_blocking_io_errors(cfg); - if (throughput) test_throughput(cfg); - - if (test_inherit_fd) { - printf("\nTesting against inherited fd (%s):\n", cfg.unix_sock.path); - cfg.type = CONN_FD; - test_blocking_connection(cfg); - } - - - if (fails) { - printf("*** %d TESTS FAILED ***\n", fails); - return 1; - } - - printf("ALL TESTS PASSED\n"); - return 0; -} diff --git a/thirdparty/hiredis/win32.h b/thirdparty/hiredis/win32.h deleted file mode 100644 index 1a27c18f2ae..00000000000 --- a/thirdparty/hiredis/win32.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef _WIN32_HELPER_INCLUDE -#define _WIN32_HELPER_INCLUDE -#ifdef _MSC_VER - -#ifndef inline -#define inline __inline -#endif - -#ifndef va_copy -#define va_copy(d,s) ((d) = (s)) -#endif - -#ifndef snprintf -#define snprintf c99_snprintf - -__inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) -{ - int count = -1; - - if (size != 0) - count = _vsnprintf_s(str, size, _TRUNCATE, format, ap); - if (count == -1) - count = _vscprintf(format, ap); - - return count; -} - -__inline int c99_snprintf(char* str, size_t size, const char* format, ...) -{ - int count; - va_list ap; - - va_start(ap, format); - count = c99_vsnprintf(str, size, format, ap); - va_end(ap); - - return count; -} -#endif - -#endif -#endif \ No newline at end of file diff --git a/thirdparty/jemalloc b/thirdparty/jemalloc deleted file mode 160000 index 04380e79f1e..00000000000 --- a/thirdparty/jemalloc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 04380e79f1e2428bd0ad000bbc6e3d2dfc6b66a5 diff --git a/thirdparty/multipart_parser.c b/thirdparty/multipart_parser.c index 21ce6145aa7..96539faf816 100644 --- a/thirdparty/multipart_parser.c +++ b/thirdparty/multipart_parser.c @@ -9,281 +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"); +#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