diff options
author | Dan Callahan <d.callahan@f5.com> | 2024-02-27 15:15:42 +0000 |
---|---|---|
committer | Dan Callahan <d.callahan@f5.com> | 2024-02-27 15:15:42 +0000 |
commit | d76761901c4084bcdbc5a449e9bbb47d56b7093c (patch) | |
tree | b4b7b4e3d588b73a2adcc0094cab466d9194c679 | |
parent | c43629880472bba8d389dfb0b7ae6d883b0ba499 (diff) | |
parent | 088117008c9e8f397a58cc8d8070ce047beff12f (diff) | |
download | unit-d76761901c4084bcdbc5a449e9bbb47d56b7093c.tar.gz unit-d76761901c4084bcdbc5a449e9bbb47d56b7093c.tar.bz2 |
Merge tag '1.32.0' into branches/packaging1.32.0-1
Unit 1.32.0 release.
259 files changed, 6278 insertions, 1243 deletions
diff --git a/.gitattributes b/.gitattributes index 45ec5156..16af3a06 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,7 @@ *.c diff=cpp *.h diff=cpp + +.hg* export-ignore +pkg/** export-ignore +docs/*.* export-ignore +docs/Makefile export-ignore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..b5368ae9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,339 @@ +name: ci + +on: + pull_request: + push: + branches: + - master + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + # Core + - build: unit + os: ubuntu-latest + # Modules + - build: go-1.21 + os: ubuntu-latest + - build: go-1.22 + os: ubuntu-latest + - build: java-17 + os: ubuntu-latest + - build: java-18 + os: ubuntu-latest + - build: java-21 + os: ubuntu-latest + - build: node-20 + os: ubuntu-latest + - build: node-21 + os: ubuntu-latest + - build: perl + os: ubuntu-latest + - build: php-8.1 + os: ubuntu-latest + - build: php-8.2 + os: ubuntu-latest + - build: php-8.3 + os: ubuntu-latest + - build: python-3.11 + os: ubuntu-latest + - build: python-3.12 + os: ubuntu-latest + - build: ruby-3.2 + os: ubuntu-latest + - build: ruby-3.3 + os: ubuntu-latest + - build: wasm + os: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + # Creates and outputs directories used by tests (/usr/local is unfriendly) + - name: Configure directories + id: dir + run: | + PREFIX=${HOME}/.unit + BIN=${PREFIX}/bin + VAR=${PREFIX}/var + mkdir -p $BIN + mkdir -p $VAR + + echo "prefix=${PREFIX}" >> "$GITHUB_OUTPUT" + echo "bin=${BIN}" >> "$GITHUB_OUTPUT" + echo "bin=${BIN}" >> "$GITHUB_PATH" + echo "var=${VAR}" >> "$GITHUB_OUTPUT" + cat "$GITHUB_OUTPUT" + + # Provides module, language version and testpath from build name + - name: Output build metadata + id: metadata + run: | + # Split the build name by '-' into module and version + IFS='-' read -r module version <<< "${{ matrix.build }}" + + testpath="test/test_${module}*" + + # Run all tests for "unit" and "python" + # Python is the default module for tests + if [ "$module" = "unit" ] || [ "$module" = "python" ]; then + testpath="test" + fi + + echo "module=${module}" >> "$GITHUB_OUTPUT" + echo "version=${version}" >> "$GITHUB_OUTPUT" + echo "testpath=${testpath}" >> "$GITHUB_OUTPUT" + + NJS_VERSION=$(sed -n "s/NJS_VERSION := \(.*\)/\1/p" pkg/contrib/src/njs/version) + echo "njs_version=${NJS_VERSION}" >> "$GITHUB_OUTPUT" + + cat "$GITHUB_OUTPUT" + + # https://github.com/actions/runner-images/issues/2821 + - name: Kill mono process + run: | + sudo systemctl stop mono-xsp4.service + sudo systemctl mask mono-xsp4.service + sudo systemctl status mono-xsp4.service || true + PID=$(sudo lsof -t -i :8084) + echo "Killing PID $PID" + sudo kill -9 $PID + + ## + ## njs + ## + + - name: Clone njs repository + uses: actions/checkout@v4 + with: + repository: nginx/njs + ref: '${{ steps.metadata.outputs.njs_version }}' + path: njs + + - name: Make njs + run: | + ./configure --no-libxml2 --no-zlib + make -j4 -k + working-directory: njs + + ## + ## Unit + ## + + - name: Configure unit + run: | + ./configure \ + --prefix=${{ steps.dir.outputs.prefix }} \ + --sbindir=${{ steps.dir.outputs.bin }} \ + --logdir=${{ steps.dir.outputs.var }}/log \ + --log=${{ steps.dir.outputs.var }}/log/unit/unit.log \ + --runstatedir=${{ steps.dir.outputs.var }}/run \ + --pid=${{ steps.dir.outputs.var }}/run/unit/unit.pid \ + --control=unix:${{ steps.dir.outputs.var }}/run/unit/control.sock \ + --modules=${{ steps.dir.outputs.prefix }}/lib/unit/modules \ + --statedir=${{ steps.dir.outputs.var }}/state/unit \ + --tests \ + --openssl \ + --njs \ + --cc-opt="-I njs/src/ -I njs/build" \ + --ld-opt="-L njs/build" + + - name: Make unit + run: | + make -j4 -k || make + + ## + ## Go + ## + + - uses: actions/setup-go@v5 + with: + go-version: '${{ steps.metadata.outputs.version }}' + cache: false + if: steps.metadata.outputs.module == 'go' + + - name: Configure go + run: | + ./configure go --go-path= + if: steps.metadata.outputs.module == 'go' + + - name: Make go + run: | + make go + make go-install + if: steps.metadata.outputs.module == 'go' + + ## + ## Java + ## + + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '${{ steps.metadata.outputs.version }}' + if: steps.metadata.outputs.module == 'java' + + - name: Configure java + run: | + ./configure java + if: steps.metadata.outputs.module == 'java' + + - name: Make java + run: | + make java + if: steps.metadata.outputs.module == 'java' + + ## + ## Node + ## + + - uses: actions/setup-node@v4 + with: + node-version: '${{ steps.metadata.outputs.version }}' + if: steps.metadata.outputs.module == 'node' + + - name: Install node-gyp + run: | + npm install -g node-gyp + if: steps.metadata.outputs.module == 'node' + + - name: Configure node + run: | + ./configure nodejs + if: steps.metadata.outputs.module == 'node' + + - name: Make node + run: | + make node-local-install DESTDIR=node + if: steps.metadata.outputs.module == 'node' + + ## + ## Perl + ## + + # Uses default Actions VM Perl + # https://github.com/actions/runner-images#available-images + + - name: Install libperl-dev + run: | + sudo apt-get install libperl-dev + if: steps.metadata.outputs.module == 'perl' + + - name: Configure perl + run: | + ./configure perl + if: steps.metadata.outputs.module == 'perl' + + - name: Make perl + run: | + make perl + if: steps.metadata.outputs.module == 'perl' + + ## + ## PHP + ## + + - uses: shivammathur/setup-php@v2 + with: + php-version: '${{ steps.metadata.outputs.version }}' + extensions: none + env: + update: true + if: steps.metadata.outputs.module == 'php' + + - name: Configure php + run: | + ./configure php + if: steps.metadata.outputs.module == 'php' + + - name: Make php + run: | + make php + if: steps.metadata.outputs.module == 'php' + + ## + ## Python 3 + ## + + - uses: actions/setup-python@v5 + with: + python-version: '${{ steps.metadata.outputs.version }}' + if: steps.metadata.outputs.module == 'python' + + - name: Configure python3 + run: | + ./configure python --config=python3-config + if: steps.metadata.outputs.module == 'python' + + - name: Make python3 + run: | + make python3 + if: steps.metadata.outputs.module == 'python' + + ## + ## Ruby + ## + + - uses: ruby/setup-ruby@v1 + with: + ruby-version: '${{ steps.metadata.outputs.version }}' + if: steps.metadata.outputs.module == 'ruby' + + - name: Install rack + run: | + gem install rack + if: steps.metadata.outputs.module == 'ruby' + + - name: Configure ruby + run: | + ./configure ruby + if: steps.metadata.outputs.module == 'ruby' + + - name: Make ruby + run: | + make ruby + if: steps.metadata.outputs.module == 'ruby' + + ## + ## Wasm + ## + + - name: Make wasmtime + run: | + make -C pkg/contrib .wasmtime + if: steps.metadata.outputs.module == 'wasm' + + - name: Configure wasm + run: | + ./configure wasm --include-path=pkg/contrib/wasmtime/crates/c-api/include --lib-path=pkg/contrib/wasmtime/target/release + if: steps.metadata.outputs.module == 'wasm' + + - name: Make wasm + run: | + make wasm + if: steps.metadata.outputs.module == 'wasm' + + ## + ## Tests + ## + + # Install python3 if not present + - uses: actions/setup-python@v5 + with: + python-version: '3' + if: steps.metadata.outputs.module != 'wasm' + + - name: Install pytest + run: | + pip install pytest + if: steps.metadata.outputs.module != 'wasm' + + - name: Run ${{ steps.metadata.outputs.module }} tests + run: | + pytest --print-log ${{ steps.metadata.outputs.testpath }} + # Skip pytest if wasm build, as there are no tests yet + if: steps.metadata.outputs.module != 'wasm' @@ -1,4 +1,20 @@ -Alejandro Colomar <alx@nginx.com> <a.colomar@f5.com> -Alejandro Colomar <alx@nginx.com> <alx.manpages@gmail.com> +Alejandro Colomar <alx@kernel.org> <alx@nginx.com> +Alejandro Colomar <alx@kernel.org> <a.colomar@f5.com> +Alejandro Colomar <alx@kernel.org> <alx.manpages@gmail.com> +Andrei Zeliankou <zelenkov@nginx.com> +Andrei Zeliankou <zelenkov@nginx.com> <xim.andrew@gmail.com> Andrew Clayton <a.clayton@nginx.com> <andrew@digital-domain.net> Andrew Clayton <a.clayton@nginx.com> <a.clayton@f5.com> +Artem Konev <artem.konev@nginx.com> <41629299+artemkonev@users.noreply.github.com> +Dan Callahan <d.callahan@f5.com> <dan.callahan@gmail.com> +Danielle De Leo <d.deleo@f5.com> <danielle@fastmail.net> +Dylan Arbour <d.arbour@f5.com> <arbourd@users.noreply.github.com> +Konstantin Pavlov <thresh@nginx.com> <thresh@videolan.org> +Konstantin Pavlov <thresh@nginx.com> <pavlov.konstantin@gmail.com> +Max Romanov <max.romanov@gmail.com> <max.romanov@nginx.com> +OutOfFocus4 <jeff.iadarola@gmail.com> <jeffrey_iadarola@urmc.rochester.edu> +Sergey A. Osokin <sergey.osokin@nginx.com> <osa@FreeBSD.org.ru> +Taryn Musgrave <t.musgrave@f5.com> <34845739+tarynmusgrave@users.noreply.github.com> +Tiago Natel de Moura <t.nateldemoura@f5.com> +Timo Stark <t.stark@nginx.com> <tippexs91@googlemail.com> +Zhidao HONG <z.hong@f5.com> <hongzhidao@gmail.com> diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 00000000..df99c691 --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1 @@ +max_width = 80 @@ -1,4 +1,57 @@ +Changes with Unit 1.32.0 27 Feb 2024 + + *) Feature: WebAssembly Components using WASI interfaces defined in + wasi:http/proxy@0.2.0. + + *) Feature: conditional access logging. + + *) Feature: NJS variables access. + + *) Feature: $request_id variable contains a string that is formed using + random data and can be used as a unique request identifier. + + *) Feature: options to set control socket permissions. + + *) Feature: Ruby arrays in response headers, improving compatibility + with Rack v3.0. + + *) Feature: Python bytearray response bodies for ASGI applications. + + *) Bugfix: router could crash while sending large files. Thanks to + rustedsword. + + *) Bugfix: serving static files from a network filesystem could lead to + error. + + *) Bugfix: "uidmap" and "gidmap" isolation options validation. + + *) Bugfix: abstract UNIX socket name could be corrupted during + configuration validation. Thanks to Alejandro Colomar. + + *) Bugfix: HTTP header field value encoding could be misinterpreted in + Python module. + + *) Bugfix: Node.js http.createServer() accepts and ignores the "options" + argument, improving compatibility with strapi applications, among + others. + + *) Bugfix: ServerRequest.flushHeaders() implemented in Node.js module to + make it compatible with Next.js. + + *) Bugfix: ServerRequest.httpVersion variable format in Node.js module. + + *) Bugfix: Node.js module handles standard library imports prefixed with + "node:", making it possible to run newer Nuxt applications, among + others. + + *) Bugfix: Node.js tarball location changed to avoid build/install + errors. + + *) Bugfix: Go module sets environment variables necessary for building + on macOS/arm64 systems. + + Changes with Unit 1.31.1 19 Oct 2023 *) Feature: allow to set the HTTP response status in Wasm module. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a7bc357b..77343271 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,7 +21,7 @@ Check out the [Quick Installation](README.md#quick-installation) and Please open an [issue](https://github.com/nginx/unit/issues/new) on GitHub with the label `question`. You can also ask a question on -[Slack](https://nginxcommunity.slack.com) or the NGINX Unit mailing list, +[GitHub Discussions](https://github.com/nginx/unit/discussions) or the NGINX Unit mailing list, unit@nginx.org (subscribe [here](https://mailman.nginx.org/mailman3/lists/unit.nginx.org/)). @@ -1,13 +1,19 @@ NGINX Unit. - Copyright 2017-2023 NGINX, Inc. - Copyright 2017-2023 Andrei Zeliankou - Copyright 2018-2023 Konstantin Pavlov - Copyright 2021-2023 Zhidao Hong + Copyright 2017-2024 NGINX, Inc. + Copyright 2017-2024 Andrei Zeliankou + Copyright 2018-2024 Konstantin Pavlov + Copyright 2021-2024 Zhidao Hong + Copyright 2022-2024 Andrew Clayton + Copyright 2022-2024 Liam Crilly + Copyright 2023-2024 Dan Callahan + Copyright 2023-2024 Danielle De Leo + Copyright 2023-2024 Dylan Arbour + Copyright 2023-2024 Gabor Javorszky + Copyright 2023-2024 Igor Ippolitov + Copyright 2023-2024 Taryn Musgrave Copyright 2021-2023 Alejandro Colomar - Copyright 2022-2023 Andrew Clayton - Copyright 2022-2023 Liam Crilly Copyright 2017-2022 Valentin V. Bartenev Copyright 2017-2022 Max Romanov Copyright 2021-2022 Oisín Canty @@ -45,7 +45,7 @@ For a description of image tags, see the [docs](https://unit.nginx.org/installation/#docker-images). -### Amazon Linux, Fedora, RedHat +### Amazon Linux, Fedora, Red Hat ``` console $ wget https://raw.githubusercontent.com/nginx/unit/master/tools/setup-unit && chmod +x setup-unit @@ -115,7 +115,7 @@ section of the API. This time, we pass the config snippet straight from the command line: ``` console -# curl -X PUT -d '{"127.0.0.1:8000": {"pass": "applications/helloworld"}}' \ +# curl -X PUT -d '{"127.0.0.1:8080": {"pass": "applications/helloworld"}}' \ --unix-socket /path/to/control.unit.sock http://localhost/config/listeners ``` ``` json @@ -175,7 +175,7 @@ usability. ## Community - The go-to place to start asking questions and share your thoughts is - our [Slack channel](https://community.nginx.org/joinslack). + [GitHub Discussions](https://github.com/nginx/unit/discussions). - Our [GitHub issues page](https://github.com/nginx/unit/issues) offers space for a more technical discussion at your own pace. diff --git a/SECURITY.txt b/SECURITY.txt index a705bef7..2d2ad2b1 100644 --- a/SECURITY.txt +++ b/SECURITY.txt @@ -10,21 +10,21 @@ Contact: mailto:security-alert@nginx.org Encryption: https://nginx.org/keys/maxim.key Encryption: https://nginx.org/keys/sb.key Encryption: https://nginx.org/keys/thresh.key -Expires: 2024-01-01T00:00:00.000Z +Expires: 2025-01-01T00:00:00.000Z Policy: https://www.first.org/cvss/v3.1/specification-document Preferred-Languages: en -----BEGIN PGP SIGNATURE----- -iQHEBAEBCAAuFiEEE8gqY7YDV2FW4wpOoOqYG2aw2WcFAmN7X/UQHGsucGF2bG92 -QGY1LmNvbQAKCRCg6pgbZrDZZ4wFDADIZz5UwVUaxQ6mAfi+3Gs28NLXQp5kBILJ -PC9Rhjlksufbby5yd4lh+JMZ8U2YRQ8OWne6Kl0NvZHDcP2OyBOdiBUXvnE+ZcNz -ujT3JMk15l1FxKbIitUzwZ+QcXOKTqsoavPs5hrGrrJNQWLhqAH8uESDdI7AUM5R -BOQ9Z6ENw3rgEtrtMNMdwt+pt2/+1cuu/4PuIuFhjYyCuS7i7tyFtbkc9BTlx03I -99g9bqKltWAvxGrMi+xfFVOnTgWp0b+oKsN8jgQji1zNMSx7UmrFq8uSpNV3eR5t -a4iVZQsIRUVVSYh8VkZagtbiw4WXaEnbwUgxj/4K2rNvkn5jFk+NkzSALN8/7ocp -U5R5ctku511bJiwFSUkTx8nkd58bzqqQ0EHr/3uTmfXSTTZYdUXuXXCSMzuUBEOi -y9n+2JFRdlEXxqwhszJxAXhs6VH2su0laX2UOMMnw6X2GFF3CU4PK0qoalWLSh47 -6aiL99zgqrq0IFibRTXCo1a3RPqiYB0= -=GiB/ +iQGzBAEBCAAdFiEEE8gqY7YDV2FW4wpOoOqYG2aw2WcFAmWdrbkACgkQoOqYG2aw +2WdAIQv/UpQXSYWboNMq9DnXZsMNdeCdAg8nv1PdNYfDzr21YavHsdP3upEg2NUX +M/9WiyO5HwV7FAWKQ8J6T0vg8EZITij5Dxhia4Z/h9QE6bXTH4rD/UViJ+/RTtwF +3WvaMNGUSlTQUNCRQ0QGTAb/jXUQCE8OwFz2UM0ZgqyUmIdkuxMEhsNd4AfAUS4A +OOhM6qfXXAulPNVFZ65Lx7NIner37OyNuzhyuQxIFsnbGagMEIvptkevNIMEy8WO +BeseYx/fp1gHdLTIUKl+nvKR7as5O+fFZSm/eG3VpkS6Fall54WX6zzalhZN7Pie +pze8YdbUukdMUV6wQ/pQH4e/QyEEI8RCk95cZE9mSfxygpbIfBypj66GTaOUC/2z +iTv2tX/DXiGQbSpkNLzwntVvuN5P9BebxmSKdspwfszccPzNhhCVQMkkhzvNVeQ6 +UTorp2O3xvi5fBIUWQU5xkrKqwAmZBYHMPDA97H9hiTmHkytd7YYkvPmJKNDksSa +ui3gNrJe +=yDJD -----END PGP SIGNATURE----- diff --git a/auto/cc/deps b/auto/cc/deps index 6e7df20f..11429788 100644 --- a/auto/cc/deps +++ b/auto/cc/deps @@ -3,7 +3,7 @@ case "$NXT_CC_NAME" in SunC): nxt_gen_dep_flags() { - $echo "-xMMD -xMF $NXT_BUILD_DIR/$nxt_dep.tmp" + $echo "-xMMD -xMF $NXT_BUILD_DIR/$nxt_dep.tmp" } nxt_gen_dep_post() { @@ -15,7 +15,7 @@ case "$NXT_CC_NAME" in *) nxt_gen_dep_flags() { - $echo "-MMD -MF $NXT_BUILD_DIR/$nxt_dep -MT $NXT_BUILD_DIR/$nxt_obj" + $echo "-MMD -MF $NXT_BUILD_DIR/$nxt_dep -MT $NXT_BUILD_DIR/$nxt_obj" } nxt_gen_dep_post() { diff --git a/auto/endian b/auto/endian index cb23639b..40b5ad28 100644 --- a/auto/endian +++ b/auto/endian @@ -1,6 +1,6 @@ # Copyright (C) Igor Sysoev # Copyright (C) Andrew Clayton -# Copyright (C) Nginx, Inc. +# Copyright (C) NGINX, Inc. nxt_feature="endianness" @@ -76,7 +76,12 @@ cat << END java OPTIONS configure Java module run "./configure java --help" to see available options - wasm OPTIONS configure WebAssembly module - run "./configure wasm --help" to see available options + wasm OPTIONS configure WebAssembly module + run "./configure wasm --help" to see available options + + wasm-wasi-component OPTIONS + configure WebAssembly Component Model module + run "./configure wasm-wasi-component --help" to see + available options END diff --git a/auto/modules/conf b/auto/modules/conf index 31be751f..ab4ed351 100644 --- a/auto/modules/conf +++ b/auto/modules/conf @@ -37,6 +37,10 @@ case "$nxt_module" in . auto/modules/wasm ;; + wasm-wasi-component) + . auto/modules/wasm-wasi-component + ;; + *) echo echo $0: error: invalid module \"$nxt_module\". diff --git a/auto/modules/java b/auto/modules/java index 53f99f7e..b5c8d70a 100644 --- a/auto/modules/java +++ b/auto/modules/java @@ -238,7 +238,7 @@ cat << END > $NXT_JAVA_JARS static const char *nxt_java_system_jars[] = { END -NXT_TOMCAT_VERSION=9.0.82 +NXT_TOMCAT_VERSION=9.0.86 NXT_JAR_VERSION=$NXT_TOMCAT_VERSION @@ -284,7 +284,7 @@ static const char *nxt_java_unit_jars[] = { "$NXT_UNIT_JAR", END -NXT_JAR_VERSION=9.4.53.v20231009 +NXT_JAR_VERSION=9.4.54.v20240208 NXT_JAR_NAMESPACE=org/eclipse/jetty/ NXT_JAR_NAME=jetty-util @@ -297,7 +297,7 @@ NXT_JAR_NAME=jetty-http . auto/modules/java_get_jar NXT_JAR_NAME=classgraph -NXT_JAR_VERSION=4.8.162 +NXT_JAR_VERSION=4.8.165 NXT_JAR_NAMESPACE=io/github/classgraph/ . auto/modules/java_get_jar diff --git a/auto/modules/java_jar.sha512 b/auto/modules/java_jar.sha512 index e0a648af..a689b901 100644 --- a/auto/modules/java_jar.sha512 +++ b/auto/modules/java_jar.sha512 @@ -1,14 +1,14 @@ -a117092f4ab77ef89b3b83a45b9e33c0360cb18098df17da77e5d765a6b5cea1fae6190399217236c0d970f2b3603bc8f408c2471cf8854de1282dba7525c335 classgraph-4.8.162.jar +d0c17607eee55e181baa03f1abb2cf77f50e5114c471c2031607206768d8549c74ebb0a276d87dd3f8ea44db5e54e56087311c229ba18ad6013c388fc861beed classgraph-4.8.165.jar ab441acf5551a7dc81c353eaccb3b3df9e89a48987294d19e39acdb83a5b640fcdff7414cee29f5b96eaa8826647f1d5323e185018fe33a64c402d69c73c9158 ecj-3.26.0.jar -867afb0e69ab225a3d57c0eda4e773ca2f41dcc6eb294e14aef4d69441314bee88221081f9edc6289b9e4a99184804b60c32f4443c8ff96eb34d6508b348b755 jetty-http-9.4.53.v20231009.jar -aca14debabc0cc40e154d4de4de404c383c4611e9f2482911e17d9072f0941cef8a88511d5d49d70946e65872e1bc8d395b9401b2ec91132553d782d99140ce3 jetty-server-9.4.53.v20231009.jar -429b269e21c6e7bf86ba8a8b71569e3abf61b06ace9dd064a9c80af4282227e910a8d20be86ef6581895ff283a5aa4711bbb238de45dc7628f494624879c4d49 jetty-util-9.4.53.v20231009.jar -ee33bc0020ce5be2fbdb52352fb9b2846dc5898b2190e46b2a8efdfdb16a33a83538731a6d7eeeb91c7b81e8d1e022b15924fa30ee1e9770a9f9adf96989ffd7 tomcat-api-9.0.82.jar -dfb4a37dddf4bc4e9a41a1381544c81e3962a63833f024236d1ed28eabe8daae77cd79466881177ce9f729efad2f5169e9cf8a9e45c820b775c3a9223d258e6f tomcat-el-api-9.0.82.jar -db764d29d882458d8cc2aeded7b25b6129eeeb7d9ec5b77d380ca14add659a8c12f233802a5e8dfa287a1c1b9dbfd6a12fa053ec506443bde0dce9fb36081782 tomcat-jasper-9.0.82.jar -bcc9ffc0f4d50defb0fdb12c2f9a8bd89fd8758f768c2495baa9c0e77a0ae08f3e610f6893ecd30843cfda6021120d9886aab3e377309ded68cd83f3d32b654a tomcat-jasper-el-9.0.82.jar -d2d9154b622b18ef190146631984188d6353ad2cb3c6ec1922387c76ae4d279a511d76680271f29b861ee18b444894ed66e5e41030e0beb265bf47eecc1a3a81 tomcat-jsp-api-9.0.82.jar -e1c92251e2e1cd5fc99d304399fbb13af50b7d86e56ffca59edb85934474df2b8dd6b4ea3d949cc1d7cc21e673094a044b22d05bc45e540c9a0a211974ebee5c tomcat-juli-9.0.82.jar -7d30076e306403c243ed4d802fca6de7e827e7f6cef8827353fff4514bee484ff71abc61597fd92b63470c6477bb26c398d4cd9b293f059e0ff94156f0210106 tomcat-servlet-api-9.0.82.jar -b06b112011526911b08849093d61a4d4337283f4a54dee2d4f8f4ce55687eabdf5df97b9326e1430fe7cd4d043a16076e86c1354cc2661c19e87918c4635e3d1 tomcat-util-9.0.82.jar -dfe0beac3b4b8466454fb790e9dd7a17b97e62edb0f5caaaedab0360b32a0536b7d788f3d5511eb47ea3abca4f5751ab32c814c73dbf465529898d78b69fb8d6 tomcat-util-scan-9.0.82.jar +6e1d6fdffcb2acf8daa9ce5b3ad973526a30b3556dc8e950254c68c64cd70e101b28a8acac41b3bd74de6b9c8eac10676afdc3c58ccb1f61a74323721592e0b5 jetty-http-9.4.54.v20240208.jar +780ee47a8722bdfb4b159f440acbfb69afdb73cc329906392b10eba8d30c564aa6377fab129e61b85a56945f01c4403913c80b6ce3158d108d88a3ad64527f06 jetty-server-9.4.54.v20240208.jar +fbe9cf7efeba9f29297f75de3f1c2d98f0e02816a1dc9e6eaddcabb84c3a699a9332218c532017a3707ec57f4f99066bc671708bde4ec84dd873b8403422d7e9 jetty-util-9.4.54.v20240208.jar +1aa9024f49f74b44252f7c90d00bbfdd6aae4e96866708a0c2325def0314c8b7e5ad2fd17bb6b4b135eb2c513fe74b5b591d4b0fe3d1921192cfecadf140b7fa tomcat-api-9.0.86.jar +60a9991ff7b95ef4edfac57cd7c18b6c5177d9aee4f775b5794b5833246b928e1a685b80785babd2f450e3cd18383c58b843b0b5e742252a37044494bc90d608 tomcat-el-api-9.0.86.jar +b09cbfb834564cc7025ffad7bf069569989d3efa3bd176696045aea08bfb53622aa1aece5c84ea4371f0193d4fd477b9179999399e75d04205b219a3ab19bb66 tomcat-jasper-9.0.86.jar +1431469e91debc0ffcf820df2973782221be955dac0739a77d9030ac619cde96320970cb27eb2ff9de1e6bde3227a70b1645d1934da8e10fe2b32c069d33afec tomcat-jasper-el-9.0.86.jar +1ad9ebc1c49beb243c18ab2c459dbd54cab9514223c44b5c7e05d53d290c64c49990fc0fe276c66b1f6f6625acca651fdcb4b7df9e23fb0cc43bc05ad3900798 tomcat-jsp-api-9.0.86.jar +d30055aabf5d45ad350e01702ed0ff4bfbcdd14bee40e1e8a9a7690719816aff019ca961b7970234eaba673c3c13f5cea5dbf1bc0612ce4e8f7de795af2f170d tomcat-juli-9.0.86.jar +d7fa7d7bf35b35b7bb925cce6284e2f750e8e94ee54057daff4e369a32b361e6044e9011048a9dff54b12371ee785d34e82306b60ffae8add76602071e5fc9c5 tomcat-servlet-api-9.0.86.jar +b4a268b79fbfcd610ea5d14446ef71ad5f2ad3da247ae148669e3082ff5fd7f7256a2ecdf2529e4280ed393f53c3a7f6d09a5c38d5653b30b25ab3c4b74ed732 tomcat-util-9.0.86.jar +f2086356c8eca5cfe890232056ce30378422d3994c499845f52ec8641453af02041ae31ffdcb567bb998f0f24465d1ab65456b23e8f781058efdc01658c7252d tomcat-util-scan-9.0.86.jar diff --git a/auto/modules/nodejs b/auto/modules/nodejs index 968f3fdf..472fb3be 100644 --- a/auto/modules/nodejs +++ b/auto/modules/nodejs @@ -123,8 +123,8 @@ fi NXT_NODE_TMP=${NXT_BUILD_DIR}/src/${NXT_NODE}/unit-http NXT_NODE_TMP_G=${NXT_BUILD_DIR}/src/${NXT_NODE}/unit-http-g -NXT_NODE_TARBALL=${NXT_BUILD_DIR}/${NXT_NODE}-unit-http.tar.gz -NXT_NODE_TARBALL_G=${NXT_BUILD_DIR}/${NXT_NODE}-unit-http-g.tar.gz +NXT_NODE_TARBALL=${NXT_BUILD_DIR}/src/${NXT_NODE}-unit-http.tar.gz +NXT_NODE_TARBALL_G=${NXT_BUILD_DIR}/src/${NXT_NODE}-unit-http-g.tar.gz NXT_NODE_VERSION_FILE=${NXT_BUILD_DIR}/src/${NXT_NODE}/version.h NXT_NODE_PACKAGE_FILE=${NXT_BUILD_DIR}/src/${NXT_NODE}/package.json NXT_NODE_EXPORTS="export UNIT_SRC_PATH=${PWD}/src \ diff --git a/auto/modules/wasm-wasi-component b/auto/modules/wasm-wasi-component new file mode 100644 index 00000000..bfb6ffcb --- /dev/null +++ b/auto/modules/wasm-wasi-component @@ -0,0 +1,120 @@ +# Copyright (C) Andrew Clayton +# Copyright (C) F5, Inc. + + +NXT_WCM_MODULE=wasm-wasi-component +NXT_WCM_MOD_NAME=`echo $NXT_WCM_MODULE | tr '-' '_'`.unit.so + +NXT_WCM_MOD_CARGO="src/wasm-wasi-component/target/release/libwasm_wasi_component.so" + + +shift + +for nxt_option; do + + case "$nxt_option" in + -*=*) value=`echo "$nxt_option" | sed -e 's/[-_a-zA-Z0-9]*=//'` ;; + *) value="" ;; + esac + + case "$nxt_option" in + + --help) + cat << END + +END + exit 0 + ;; + + *) + echo + echo $0: error: invalid $NXT_WCM_MODULE option \"$nxt_option\" + echo + exit 1 + ;; + esac + +done + + +if [ ! -f $NXT_AUTOCONF_DATA ]; then + echo + echo Please run common $0 before configuring module \"$nxt_module\". + echo + exit 1 +fi + +. $NXT_AUTOCONF_DATA + +NXT_WCM_WASM_TOOLS_BIN=${NXT_WCM_WASM_TOOLS_BIN=} + + +$echo "configuring $NXT_WCM_MODULE module" +$echo "configuring $NXT_WCM_MODULE module ..." >> $NXT_AUTOCONF_ERR + +$echo -n "looking for rust compiler ... " + +if [ -z `which rustc 2>/dev/null` ]; then + $echo "not found." + exit 1; +fi + +$echo "found." + +$echo -n "looking for cargo ... " + +if [ -z `which cargo 2>/dev/null` ]; then + $echo "not found." + exit 1; +fi + +$echo "found." + + +if grep ^$NXT_WCM_MODULE: $NXT_MAKEFILE 2>&1 > /dev/null; then + $echo + $echo $0: error: duplicate \"$NXT_WCM_MODULE\" module configured. + $echo + exit 1; +fi + + +$echo " + $NXT_WCM_MODULE module: $NXT_WCM_MOD_NAME" + + +NXT_OS=$(uname -o) + +if [ $NXT_OS = "Darwin" ]; then + NXT_CARGO_CMD="cargo rustc --release --manifest-path src/wasm-wasi-component/Cargo.toml -- --emit link=target/release/libwasm_wasi_component.so -C link-args='-undefined dynamic_lookup'" +else + NXT_CARGO_CMD="cargo build --release --manifest-path src/wasm-wasi-component/Cargo.toml" +fi + + +cat << END >> $NXT_MAKEFILE + +.PHONY: ${NXT_WCM_MODULE} +.PHONY: ${NXT_WCM_MODULE}-install +.PHONY: ${NXT_WCM_MODULE}-uninstall + +all: ${NXT_WCM_MODULE} + +${NXT_WCM_MODULE}: ${NXT_WCM_MOD_CARGO} + +${NXT_WCM_MOD_CARGO}: build/src/nxt_unit.o + $NXT_CARGO_CMD + +install: ${NXT_WCM_MODULE}-install + +${NXT_WCM_MODULE}-install: ${NXT_WCM_MODULE} install-check + install -d \$(DESTDIR)$NXT_MODULESDIR + install -p ${NXT_WCM_MOD_CARGO} \\ + \$(DESTDIR)$NXT_MODULESDIR/$NXT_WCM_MOD_NAME + +uninstall: ${NXT_WCM_MODULE}-uninstall + +${NXT_WCM_MODULE}-uninstall: + rm -f \$(DESTDIR)$NXT_MODULESDIR/$NXT_WCM_MOD_NAME + @rmdir -p \$(DESTDIR)$NXT_MODULESDIR 2>/dev/null || true + +END diff --git a/docs/changes.xml b/docs/changes.xml index 60f259ad..ad7db70e 100644 --- a/docs/changes.xml +++ b/docs/changes.xml @@ -5,6 +5,178 @@ <change_log title="unit"> +<changes apply="unit-jsc21" ver="1.32.0" rev="1" + date="2023-10-17" time="16:00:00 -0700" + packager="Nginx Packaging <nginx-packaging@f5.com>"> + +<change> +<para> +Initial release of Java 21 module for NGINX Unit. +</para> +</change> + +</changes> + + +<changes apply="unit-python3.12" ver="1.32.0" rev="1" + date="2023-10-17" time="16:00:00 -0700" + packager="Nginx Packaging <nginx-packaging@f5.com>"> + +<change> +<para> +Initial release of Python 3.12 module for NGINX Unit. +</para> +</change> + +</changes> + + +<changes apply="unit-php + unit-python unit-python2.7 + unit-python3.4 unit-python3.5 unit-python3.6 unit-python3.7 + unit-python3.8 unit-python3.9 unit-python3.10 unit-python3.11 + unit-python3.12 + unit-go + unit-perl + unit-ruby + unit-jsc-common unit-jsc8 unit-jsc10 unit-jsc11 unit-jsc13 + unit-jsc14 unit-jsc15 unit-jsc16 unit-jsc17 unit-jsc18 + unit-jsc19 unit-jsc20 unit-jsc21 + unit-wasm" + ver="1.32.0" rev="1" + date="2024-02-27" time="18:00:00 +0000" + packager="Nginx Packaging <nginx-packaging@f5.com>"> + +<change> +<para> +NGINX Unit updated to 1.32.0. +</para> +</change> + + +</changes> + + +<changes apply="unit" ver="1.32.0" rev="1" + date="2024-02-27" time="18:00:00 +0000" + packager="Nginx Packaging <nginx-packaging@f5.com>"> + +<change type="feature"> +<para> +WebAssembly Components using WASI interfaces defined in wasi:http/proxy@0.2.0. +</para> +</change> + +<change type="feature"> +<para> +conditional access logging. +</para> +</change> + +<change type="feature"> +<para> +NJS variables access. +</para> +</change> + +<change type="feature"> +<para> +$request_id variable contains a string that is formed using random data and +can be used as a unique request identifier. +</para> +</change> + +<change type="feature"> +<para> +options to set control socket permissions. +</para> +</change> + +<change type="feature"> +<para> +Ruby arrays in response headers, improving compatibility with Rack v3.0. +</para> +</change> + +<change type="feature"> +<para> +Python bytearray response bodies for ASGI applications. +</para> +</change> + +<change type="bugfix"> +<para> +router could crash while sending large files. Thanks to rustedsword. +</para> +</change> + +<change type="bugfix"> +<para> +serving static files from a network filesystem could lead to error. +</para> +</change> + +<change type="bugfix"> +<para> +"uidmap" and "gidmap" isolation options validation. +</para> +</change> + +<change type="bugfix"> +<para> +abstract UNIX socket name could be corrupted during configuration validation. +Thanks to Alejandro Colomar. +</para> +</change> + +<change type="bugfix"> +<para> +HTTP header field value encoding could be misinterpreted in Python module. +</para> +</change> + +<change type="bugfix"> +<para> +Node.js http.createServer() accepts and ignores the "options" argument, +improving compatibility with strapi applications, among others. +</para> +</change> + +<change type="bugfix"> +<para> +ServerRequest.flushHeaders() implemented in Node.js module to make it compatible +with Next.js. +</para> +</change> + +<change type="bugfix"> +<para> +ServerRequest.httpVersion variable format in Node.js module. +</para> +</change> + +<change type="bugfix"> +<para> +Node.js module handles standard library imports prefixed with "node:", making it +possible to run newer Nuxt applications, among others. +</para> +</change> + +<change type="bugfix"> +<para> +Node.js tarball location changed to avoid build/install errors. +</para> +</change> + +<change type="bugfix"> +<para> +Go module sets environment variables necessary for building on macOS/arm64 +systems. +</para> +</change> + +</changes> + <changes apply="unit-php unit-python unit-python2.7 unit-python3.4 unit-python3.5 unit-python3.6 unit-python3.7 @@ -80,8 +252,8 @@ compatibility with Node.js 15.0.0 and above. <change type="bugfix"> <para> -Node.JS unit-http NPM module now has appropriate default paths for -macOS/arm64 systems. +Node.JS unit-http NPM module now has appropriate default paths for macOS/arm64 +systems. </para> </change> diff --git a/docs/man/man8/unitd.8.in b/docs/man/man8/unitd.8.in index 1c2093da..70371cdf 100644 --- a/docs/man/man8/unitd.8.in +++ b/docs/man/man8/unitd.8.in @@ -1,10 +1,16 @@ -.\" (C) 2017-2023, NGINX, Inc. -.\" (C) 2017-2023 Andrei Zeliankou -.\" (C) 2018-2023 Konstantin Pavlov -.\" (C) 2021-2023 Zhidao Hong +.\" (C) 2017-2024 NGINX, Inc. +.\" (C) 2017-2024 Andrei Zeliankou +.\" (C) 2018-2024 Konstantin Pavlov +.\" (C) 2021-2024 Zhidao Hong +.\" (C) 2022-2024 Andrew Clayton +.\" (C) 2022-2024 Liam Crilly +.\" (C) 2023-2024 Dan Callahan +.\" (C) 2023-2024 Danielle De Leo +.\" (C) 2023-2024 Dylan Arbour +.\" (C) 2023-2024 Gabor Javorszky +.\" (C) 2023-2024 Igor Ippolitov +.\" (C) 2023-2024 Taryn Musgrave .\" (C) 2021-2023 Alejandro Colomar -.\" (C) 2022-2023 Andrew Clayton -.\" (C) 2022-2023 Liam Crilly .\" (C) 2017-2022 Valentin V. Bartenev .\" (C) 2017-2022 Max Romanov .\" (C) 2021-2022 Oisín Canty @@ -24,6 +30,9 @@ .Nm .Op Fl Fl no-daemon .Op Fl Fl control Ar socket +.Op Fl Fl control-mode Ar mode +.Op Fl Fl control-user Ar user +.Op Fl Fl control-group Ar group .Op Fl Fl group Ar name .Op Fl Fl user Ar name .Op Fl Fl log Ar file @@ -53,6 +62,12 @@ Runs Unit in non-daemon mode. .It Fl Fl control Ar socket Overrides the control API's socket address in IPv4, IPv6, or UNIX-domain format. +.It Fl Fl control-mode Ar mode +Sets the permission of the UNIX-domain control socket. +.It Fl Fl control-user Ar user +Sets the owner of the UNIX-domain control socket. +.It Fl Fl control-group Ar group +Sets the group of the UNIX-domain control socket. .It Fl Fl group Ar name , Fl Fl user Ar name Override group name and user name used to run Unit's non-privileged processes. .It Fl Fl log Ar file @@ -82,13 +97,19 @@ The socket address of Unit's control API. .El .Sh Copyright .nf -(C) 2017-2023, NGINX, Inc. -(C) 2017-2023 Andrei Zeliankou -(C) 2018-2023 Konstantin Pavlov -(C) 2021-2023 Zhidao Hong +(C) 2017-2024 NGINX, Inc. +(C) 2017-2024 Andrei Zeliankou +(C) 2018-2024 Konstantin Pavlov +(C) 2021-2024 Zhidao Hong +(C) 2022-2024 Andrew Clayton +(C) 2022-2024 Liam Crilly +(C) 2023-2024 Dan Callahan +(C) 2023-2024 Danielle De Leo +(C) 2023-2024 Dylan Arbour +(C) 2023-2024 Gabor Javorszky +(C) 2023-2024 Igor Ippolitov +(C) 2023-2024 Taryn Musgrave (C) 2021-2023 Alejandro Colomar -(C) 2022-2023 Andrew Clayton -(C) 2022-2023 Liam Crilly (C) 2017-2022 Valentin V. Bartenev (C) 2017-2022 Max Romanov (C) 2021-2022 Oisín Canty diff --git a/docs/unit-openapi.yaml b/docs/unit-openapi.yaml index 4ce26fa0..b2e02e89 100644 --- a/docs/unit-openapi.yaml +++ b/docs/unit-openapi.yaml @@ -1,6 +1,6 @@ openapi: 3.0.0 info: - title: "NGINX Unit 1.31.1" + title: "NGINX Unit 1.32.0" description: "NGINX Unit is a lightweight and versatile application runtime that provides the essential components for your web application as a single open-source server: running application code, serving static assets, diff --git a/go/ldflags-darwin.go b/go/ldflags-darwin.go new file mode 100644 index 00000000..77114ee4 --- /dev/null +++ b/go/ldflags-darwin.go @@ -0,0 +1,15 @@ +//go:build darwin +// +build darwin + +/* + * Copyright (C) Danielle De Leo + * Copyright (C) NGINX, Inc. + */ + +package unit + +/* +#cgo LDFLAGS: -L/opt/homebrew/lib +#cgo CFLAGS: -I/opt/homebrew/include +*/ +import "C" diff --git a/go/ldflags-lrt.go b/go/ldflags-lrt.go index f5a63508..68a29145 100644 --- a/go/ldflags-lrt.go +++ b/go/ldflags-lrt.go @@ -1,3 +1,4 @@ +//go:build linux || netbsd // +build linux netbsd /* diff --git a/pkg/Makefile b/pkg/Makefile index c252969b..ad12efb7 100644 --- a/pkg/Makefile +++ b/pkg/Makefile @@ -11,10 +11,11 @@ default: dist: rm -f unit-$(VERSION).tar.gz - hg archive unit-$(VERSION).tar.gz \ - -r $(VERSION) \ - -p unit-$(VERSION) \ - -X "../.hg*" -X "../pkg/" -X "../docs/*.*" -X "../docs/Makefile" + cd .. && git archive \ + --output pkg/unit-$(VERSION).tar.gz \ + --prefix unit-$(VERSION)/ \ + --worktree-attributes \ + $(VERSION) ./ $(SHA512SUM) unit-$(VERSION).tar.gz > unit-$(VERSION).tar.gz.sha512 rpm: diff --git a/pkg/contrib/src/libunit-wasm/version b/pkg/contrib/src/libunit-wasm/version index 7ca15f98..60577d0e 100644 --- a/pkg/contrib/src/libunit-wasm/version +++ b/pkg/contrib/src/libunit-wasm/version @@ -1,2 +1,2 @@ -LIBUNIT_WASM_VERSION := 0.1.0 -LIBUNIT_WASM_GITHASH := d6ed6a219b31a58526721f96195c80061d41ce54 +LIBUNIT_WASM_VERSION := 0.3.0 +LIBUNIT_WASM_GITHASH := 01c43784ec53aa1ff22aca7e7ae6f18b4591b514 diff --git a/pkg/contrib/src/njs/SHA512SUMS b/pkg/contrib/src/njs/SHA512SUMS index 3c3ce210..43766487 100644 --- a/pkg/contrib/src/njs/SHA512SUMS +++ b/pkg/contrib/src/njs/SHA512SUMS @@ -1 +1 @@ -5038b4cd9e18de89c9cf7fe7b25a0a8a03c51cfb20b6ee5085e68f885113b104092baf5ac8fe80e9d1611b2f75e47448753e6b327bef2e706ea46f2d6299f927 njs-0.8.1.tar.gz +cc3110a0c6866dfc03d19c58745e5b75aa9792999db45bc55a752f7b04db8ae51322bfe0156b873109c8477c6c1a030c851c770697cf6791c6e89fb2fed0a2c5 njs-0.8.2.tar.gz diff --git a/pkg/contrib/src/njs/version b/pkg/contrib/src/njs/version index 73c524fb..00453419 100644 --- a/pkg/contrib/src/njs/version +++ b/pkg/contrib/src/njs/version @@ -1 +1 @@ -NJS_VERSION := 0.8.1 +NJS_VERSION := 0.8.2 diff --git a/pkg/deb/Makefile b/pkg/deb/Makefile index f82441c6..e48de155 100644 --- a/pkg/deb/Makefile +++ b/pkg/deb/Makefile @@ -19,6 +19,23 @@ BUILD_DEPENDS = $(BUILD_DEPENDS_unit) MODULES= +# Ubuntu 23.10 +ifeq ($(CODENAME),mantic) +include Makefile.php +include Makefile.python311 +include Makefile.python312 +include Makefile.go +include Makefile.perl +include Makefile.ruby +include Makefile.jsc-common +include Makefile.jsc11 +include Makefile.jsc17 +include Makefile.jsc19 +include Makefile.jsc20 +include Makefile.jsc21 +include Makefile.wasm +endif + # Ubuntu 23.04 ifeq ($(CODENAME),lunar) include Makefile.php diff --git a/pkg/deb/Makefile.jsc21 b/pkg/deb/Makefile.jsc21 new file mode 100644 index 00000000..82e7f9a2 --- /dev/null +++ b/pkg/deb/Makefile.jsc21 @@ -0,0 +1,71 @@ +MODULES+= jsc21 +MODULE_SUFFIX_jsc21= jsc21 + +MODULE_SUMMARY_jsc21= Java 21 module for NGINX Unit + +MODULE_VERSION_jsc21= $(VERSION) +MODULE_RELEASE_jsc21= 1 + +MODULE_CONFARGS_jsc21= java --module=java21 --home=/usr/lib/jvm/java-21-openjdk-$$\(DEB_HOST_ARCH\) --jars=/usr/share/unit-jsc-common/ +MODULE_MAKEARGS_jsc21= java21 +MODULE_INSTARGS_jsc21= java21-install + +MODULE_SOURCES_jsc21= unit.example-jsc-app \ + unit.example-jsc21-config + +BUILD_DEPENDS_jsc21= openjdk-21-jdk-headless openjdk-21-jre-headless +BUILD_DEPENDS+= $(BUILD_DEPENDS_jsc21) + +MODULE_BUILD_DEPENDS_jsc21=,openjdk-21-jdk-headless +MODULE_DEPENDS_jsc21=,openjdk-21-jre-headless,unit-jsc-common (= $(MODULE_VERSION_jsc_common)-$(MODULE_RELEASE_jsc_common)~$(CODENAME)) + +define MODULE_PREINSTALL_jsc21 + mkdir -p debian/unit-jsc21/usr/share/doc/unit-jsc21/examples/jsc-app + install -m 644 -p debian/unit.example-jsc-app debian/unit-jsc21/usr/share/doc/unit-jsc21/examples/jsc-app/index.jsp + install -m 644 -p debian/unit.example-jsc21-config debian/unit-jsc21/usr/share/doc/unit-jsc21/examples/unit.config + install -m 644 -p src/java/README.JSR-340 debian/unit-jsc21/usr/share/doc/unit-jsc21/ +endef +export MODULE_PREINSTALL_jsc21 + +define MODULE_POSTINSTALL_jsc21 + cd $$\(BUILDDIR_unit\) \&\& \ + DESTDIR=$$\(INSTALLDIR\) make java-shared-uninstall +endef +export MODULE_POSTINSTALL_jsc21 + +define MODULE_POST_jsc21 +cat <<BANNER +---------------------------------------------------------------------- + +The $(MODULE_SUMMARY_jsc21) has been installed. + +To check out the sample app, run these commands: + + sudo service unit restart + cd /usr/share/doc/unit-$(MODULE_SUFFIX_jsc21)/examples + sudo curl -X PUT --data-binary @unit.config --unix-socket /var/run/control.unit.sock http://localhost/config + curl http://localhost:8800/ + +Online documentation is available at https://unit.nginx.org + +NOTICE: + +This version of Unit code is made available in support of the open source +development process. This is an intermediate build made available for +testing purposes only. This Unit code is untested and presumed incompatible +with the JSR 340 Java Servlet 3.1 specification. You should not deploy or +write to this code. You should instead deploy and write production +applications on pre-built binaries that have been tested and certified +to meet the JSR-340 compatibility requirements such as certified binaries +published for the JSR-340 reference implementation available at +https://javaee.github.io/glassfish/. + +Redistribution of any Intermediate Build must retain this notice. + +Oracle and Java are registered trademarks of Oracle and/or its affiliates. +Other names may be trademarks of their respective owners. + +---------------------------------------------------------------------- +BANNER +endef +export MODULE_POST_jsc21 diff --git a/pkg/deb/Makefile.python312 b/pkg/deb/Makefile.python312 new file mode 100644 index 00000000..9ece7877 --- /dev/null +++ b/pkg/deb/Makefile.python312 @@ -0,0 +1,46 @@ +MODULES+= python312 +MODULE_SUFFIX_python312= python3.12 + +MODULE_SUMMARY_python312= Python 3.12 module for NGINX Unit + +MODULE_VERSION_python312= $(VERSION) +MODULE_RELEASE_python312= 1 + +MODULE_CONFARGS_python312= python --config=python3.12-config +MODULE_MAKEARGS_python312= python3.12 +MODULE_INSTARGS_python312= python3.12-install + +MODULE_SOURCES_python312= unit.example-python-app \ + unit.example-python3.12-config + +BUILD_DEPENDS_python312= python3.12-dev +BUILD_DEPENDS+= $(BUILD_DEPENDS_python312) + +MODULE_BUILD_DEPENDS_python312=,python3.12-dev + +define MODULE_PREINSTALL_python312 + mkdir -p debian/unit-python3.12/usr/share/doc/unit-python3.12/examples/python-app + install -m 644 -p debian/unit.example-python-app debian/unit-python3.12/usr/share/doc/unit-python3.12/examples/python-app/wsgi.py + install -m 644 -p debian/unit.example-python3.12-config debian/unit-python3.12/usr/share/doc/unit-python3.12/examples/unit.config +endef +export MODULE_PREINSTALL_python312 + +define MODULE_POST_python312 +cat <<BANNER +---------------------------------------------------------------------- + +The $(MODULE_SUMMARY_python312) has been installed. + +To check out the sample app, run these commands: + + sudo service unit restart + cd /usr/share/doc/unit-$(MODULE_SUFFIX_python312)/examples + sudo curl -X PUT --data-binary @unit.config --unix-socket /var/run/control.unit.sock http://localhost/config + curl http://localhost:8400/ + +Online documentation is available at https://unit.nginx.org + +---------------------------------------------------------------------- +BANNER +endef +export MODULE_POST_python312 diff --git a/pkg/deb/Makefile.wasm b/pkg/deb/Makefile.wasm index da028f19..8f3fdc67 100644 --- a/pkg/deb/Makefile.wasm +++ b/pkg/deb/Makefile.wasm @@ -6,9 +6,9 @@ MODULE_SUMMARY_wasm= WASM module for NGINX Unit MODULE_VERSION_wasm= $(VERSION) MODULE_RELEASE_wasm= 1 -MODULE_CONFARGS_wasm= wasm --include-path=\$$(CURDIR)/pkg/contrib/wasmtime/crates/c-api/include --lib-path=\$$(CURDIR)/pkg/contrib/wasmtime/target/release -MODULE_MAKEARGS_wasm= wasm -MODULE_INSTARGS_wasm= wasm-install +MODULE_CONFARGS_wasm= wasm --include-path=\$$(CURDIR)/pkg/contrib/wasmtime/crates/c-api/include --lib-path=\$$(CURDIR)/pkg/contrib/wasmtime/target/release \&\& ./configure wasm-wasi-component +MODULE_MAKEARGS_wasm= wasm wasm-wasi-component CFLAGS=\"\$$(shell grep ^CFLAGS \$$(BUILDDIR_\$$*)/build/Makefile | cut -d' ' -f 3-) -Wno-missing-prototypes\" +MODULE_INSTARGS_wasm= wasm-install wasm-wasi-component-install MODULE_SOURCES_wasm= diff --git a/pkg/deb/debian.module/copyright.unit-jsc11 b/pkg/deb/debian.module/copyright.unit-jsc11 index e11b64d3..6e512e86 100644 --- a/pkg/deb/debian.module/copyright.unit-jsc11 +++ b/pkg/deb/debian.module/copyright.unit-jsc11 @@ -1,13 +1,19 @@ NGINX Unit. - Copyright 2017-2023 NGINX, Inc. - Copyright 2017-2023 Andrei Zeliankou - Copyright 2018-2023 Konstantin Pavlov - Copyright 2021-2023 Zhidao Hong + Copyright 2017-2024 NGINX, Inc. + Copyright 2017-2024 Andrei Zeliankou + Copyright 2018-2024 Konstantin Pavlov + Copyright 2021-2024 Zhidao Hong + Copyright 2022-2024 Andrew Clayton + Copyright 2022-2024 Liam Crilly + Copyright 2023-2024 Dan Callahan + Copyright 2023-2024 Danielle De Leo + Copyright 2023-2024 Dylan Arbour + Copyright 2023-2024 Gabor Javorszky + Copyright 2023-2024 Igor Ippolitov + Copyright 2023-2024 Taryn Musgrave Copyright 2021-2023 Alejandro Colomar - Copyright 2022-2023 Andrew Clayton - Copyright 2022-2023 Liam Crilly Copyright 2017-2022 Valentin V. Bartenev Copyright 2017-2022 Max Romanov Copyright 2021-2022 Oisín Canty diff --git a/pkg/deb/debian.module/copyright.unit-jsc8 b/pkg/deb/debian.module/copyright.unit-jsc8 index 1d267021..60da2dfa 100644 --- a/pkg/deb/debian.module/copyright.unit-jsc8 +++ b/pkg/deb/debian.module/copyright.unit-jsc8 @@ -1,13 +1,19 @@ NGINX Unit. - Copyright 2017-2023 NGINX, Inc. - Copyright 2017-2023 Andrei Zeliankou - Copyright 2018-2023 Konstantin Pavlov - Copyright 2021-2023 Zhidao Hong + Copyright 2017-2024 NGINX, Inc. + Copyright 2017-2024 Andrei Zeliankou + Copyright 2018-2024 Konstantin Pavlov + Copyright 2021-2024 Zhidao Hong + Copyright 2022-2024 Andrew Clayton + Copyright 2022-2024 Liam Crilly + Copyright 2023-2024 Dan Callahan + Copyright 2023-2024 Danielle De Leo + Copyright 2023-2024 Dylan Arbour + Copyright 2023-2024 Gabor Javorszky + Copyright 2023-2024 Igor Ippolitov + Copyright 2023-2024 Taryn Musgrave Copyright 2021-2023 Alejandro Colomar - Copyright 2022-2023 Andrew Clayton - Copyright 2022-2023 Liam Crilly Copyright 2017-2022 Valentin V. Bartenev Copyright 2017-2022 Max Romanov Copyright 2021-2022 Oisín Canty diff --git a/pkg/deb/debian.module/unit.example-jsc21-config b/pkg/deb/debian.module/unit.example-jsc21-config new file mode 100644 index 00000000..a20cff8f --- /dev/null +++ b/pkg/deb/debian.module/unit.example-jsc21-config @@ -0,0 +1,15 @@ +{ + "applications": { + "example_java21": { + "processes": 1, + "type": "java 21", + "webapp": "/usr/share/doc/unit-jsc21/examples/jsc-app" + } + }, + + "listeners": { + "*:8800": { + "pass": "applications/example_java21" + } + } +} diff --git a/pkg/deb/debian.module/unit.example-python3.12-config b/pkg/deb/debian.module/unit.example-python3.12-config new file mode 100644 index 00000000..37b65f1f --- /dev/null +++ b/pkg/deb/debian.module/unit.example-python3.12-config @@ -0,0 +1,16 @@ +{ + "applications": { + "example_python": { + "type": "python 3.12", + "processes": 2, + "path": "/usr/share/doc/unit-python3.12/examples/python-app", + "module": "wsgi" + } + }, + + "listeners": { + "*:8400": { + "pass": "applications/example_python" + } + } +} diff --git a/pkg/deb/debian/copyright b/pkg/deb/debian/copyright index 692ae2e0..dbc37146 100644 --- a/pkg/deb/debian/copyright +++ b/pkg/deb/debian/copyright @@ -1,13 +1,19 @@ NGINX Unit. - Copyright 2017-2023 NGINX, Inc. - Copyright 2017-2023 Andrei Zeliankou - Copyright 2018-2023 Konstantin Pavlov - Copyright 2021-2023 Zhidao Hong + Copyright 2017-2024 NGINX, Inc. + Copyright 2017-2024 Andrei Zeliankou + Copyright 2018-2024 Konstantin Pavlov + Copyright 2021-2024 Zhidao Hong + Copyright 2022-2024 Andrew Clayton + Copyright 2022-2024 Liam Crilly + Copyright 2023-2024 Dan Callahan + Copyright 2023-2024 Danielle De Leo + Copyright 2023-2024 Dylan Arbour + Copyright 2023-2024 Gabor Javorszky + Copyright 2023-2024 Igor Ippolitov + Copyright 2023-2024 Taryn Musgrave Copyright 2021-2023 Alejandro Colomar - Copyright 2022-2023 Andrew Clayton - Copyright 2022-2023 Liam Crilly Copyright 2017-2022 Valentin V. Bartenev Copyright 2017-2022 Max Romanov Copyright 2021-2022 Oisín Canty diff --git a/pkg/docker/Dockerfile.go1.21 b/pkg/docker/Dockerfile.go1.21 index a90dc115..f0caf402 100644 --- a/pkg/docker/Dockerfile.go1.21 +++ b/pkg/docker/Dockerfile.go1.21 @@ -6,16 +6,16 @@ LABEL org.opencontainers.image.url="https://unit.nginx.org" LABEL org.opencontainers.image.source="https://github.com/nginx/unit" LABEL org.opencontainers.image.documentation="https://unit.nginx.org/installation/#docker-images" LABEL org.opencontainers.image.vendor="NGINX Docker Maintainers <docker-maint@nginx.com>" -LABEL org.opencontainers.image.version="1.31.1" +LABEL org.opencontainers.image.version="1.32.0" RUN set -ex \ && savedAptMark="$(apt-mark showmanual)" \ && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev curl pkg-config \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates git build-essential libssl-dev libpcre2-dev curl pkg-config \ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ && mkdir -p /usr/src/unit \ && cd /usr/src/unit \ - && hg clone -u 1.31.1-1 https://hg.nginx.org/unit \ + && git clone --depth 1 -b 1.32.0-1 https://github.com/nginx/unit \ && cd unit \ && NCPU="$(getconf _NPROCESSORS_ONLN)" \ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ @@ -77,7 +77,7 @@ RUN set -ex \ && apt-get purge -y --auto-remove build-essential \ && rm -rf /var/lib/apt/lists/* \ && rm -f /requirements.apt \ - && ln -sf /dev/stdout /var/log/unit.log + && ln -sf /dev/stderr /var/log/unit.log COPY docker-entrypoint.sh /usr/local/bin/ COPY welcome.* /usr/share/unit/welcome/ diff --git a/pkg/docker/Dockerfile.go1.20 b/pkg/docker/Dockerfile.go1.22 index 53379dd1..ccd19bda 100644 --- a/pkg/docker/Dockerfile.go1.20 +++ b/pkg/docker/Dockerfile.go1.22 @@ -1,21 +1,21 @@ -FROM golang:1.20-bullseye +FROM golang:1.22-bullseye -LABEL org.opencontainers.image.title="Unit (go1.20)" +LABEL org.opencontainers.image.title="Unit (go1.22)" LABEL org.opencontainers.image.description="Official build of Unit for Docker." LABEL org.opencontainers.image.url="https://unit.nginx.org" LABEL org.opencontainers.image.source="https://github.com/nginx/unit" LABEL org.opencontainers.image.documentation="https://unit.nginx.org/installation/#docker-images" LABEL org.opencontainers.image.vendor="NGINX Docker Maintainers <docker-maint@nginx.com>" -LABEL org.opencontainers.image.version="1.31.1" +LABEL org.opencontainers.image.version="1.32.0" RUN set -ex \ && savedAptMark="$(apt-mark showmanual)" \ && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev curl pkg-config \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates git build-essential libssl-dev libpcre2-dev curl pkg-config \ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ && mkdir -p /usr/src/unit \ && cd /usr/src/unit \ - && hg clone -u 1.31.1-1 https://hg.nginx.org/unit \ + && git clone --depth 1 -b 1.32.0-1 https://github.com/nginx/unit \ && cd unit \ && NCPU="$(getconf _NPROCESSORS_ONLN)" \ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ @@ -77,7 +77,7 @@ RUN set -ex \ && apt-get purge -y --auto-remove build-essential \ && rm -rf /var/lib/apt/lists/* \ && rm -f /requirements.apt \ - && ln -sf /dev/stdout /var/log/unit.log + && ln -sf /dev/stderr /var/log/unit.log COPY docker-entrypoint.sh /usr/local/bin/ COPY welcome.* /usr/share/unit/welcome/ diff --git a/pkg/docker/Dockerfile.jsc11 b/pkg/docker/Dockerfile.jsc11 index 2844c813..b056c0d9 100644 --- a/pkg/docker/Dockerfile.jsc11 +++ b/pkg/docker/Dockerfile.jsc11 @@ -6,16 +6,16 @@ LABEL org.opencontainers.image.url="https://unit.nginx.org" LABEL org.opencontainers.image.source="https://github.com/nginx/unit" LABEL org.opencontainers.image.documentation="https://unit.nginx.org/installation/#docker-images" LABEL org.opencontainers.image.vendor="NGINX Docker Maintainers <docker-maint@nginx.com>" -LABEL org.opencontainers.image.version="1.31.1" +LABEL org.opencontainers.image.version="1.32.0" RUN set -ex \ && savedAptMark="$(apt-mark showmanual)" \ && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev curl pkg-config \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates git build-essential libssl-dev libpcre2-dev curl pkg-config \ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ && mkdir -p /usr/src/unit \ && cd /usr/src/unit \ - && hg clone -u 1.31.1-1 https://hg.nginx.org/unit \ + && git clone --depth 1 -b 1.32.0-1 https://github.com/nginx/unit \ && cd unit \ && NCPU="$(getconf _NPROCESSORS_ONLN)" \ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ @@ -77,7 +77,7 @@ RUN set -ex \ && apt-get purge -y --auto-remove build-essential \ && rm -rf /var/lib/apt/lists/* \ && rm -f /requirements.apt \ - && ln -sf /dev/stdout /var/log/unit.log + && ln -sf /dev/stderr /var/log/unit.log COPY docker-entrypoint.sh /usr/local/bin/ COPY welcome.* /usr/share/unit/welcome/ diff --git a/pkg/docker/Dockerfile.minimal b/pkg/docker/Dockerfile.minimal index 4b585480..59849ed0 100644 --- a/pkg/docker/Dockerfile.minimal +++ b/pkg/docker/Dockerfile.minimal @@ -6,16 +6,16 @@ LABEL org.opencontainers.image.url="https://unit.nginx.org" LABEL org.opencontainers.image.source="https://github.com/nginx/unit" LABEL org.opencontainers.image.documentation="https://unit.nginx.org/installation/#docker-images" LABEL org.opencontainers.image.vendor="NGINX Docker Maintainers <docker-maint@nginx.com>" -LABEL org.opencontainers.image.version="1.31.1" +LABEL org.opencontainers.image.version="1.32.0" RUN set -ex \ && savedAptMark="$(apt-mark showmanual)" \ && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev curl pkg-config \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates git build-essential libssl-dev libpcre2-dev curl pkg-config \ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ && mkdir -p /usr/src/unit \ && cd /usr/src/unit \ - && hg clone -u 1.31.1-1 https://hg.nginx.org/unit \ + && git clone --depth 1 -b 1.32.0-1 https://github.com/nginx/unit \ && cd unit \ && NCPU="$(getconf _NPROCESSORS_ONLN)" \ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ @@ -77,7 +77,7 @@ RUN set -ex \ && apt-get purge -y --auto-remove build-essential \ && rm -rf /var/lib/apt/lists/* \ && rm -f /requirements.apt \ - && ln -sf /dev/stdout /var/log/unit.log + && ln -sf /dev/stderr /var/log/unit.log COPY docker-entrypoint.sh /usr/local/bin/ COPY welcome.* /usr/share/unit/welcome/ diff --git a/pkg/docker/Dockerfile.node20 b/pkg/docker/Dockerfile.node20 index f783ba72..6174fd62 100644 --- a/pkg/docker/Dockerfile.node20 +++ b/pkg/docker/Dockerfile.node20 @@ -6,16 +6,16 @@ LABEL org.opencontainers.image.url="https://unit.nginx.org" LABEL org.opencontainers.image.source="https://github.com/nginx/unit" LABEL org.opencontainers.image.documentation="https://unit.nginx.org/installation/#docker-images" LABEL org.opencontainers.image.vendor="NGINX Docker Maintainers <docker-maint@nginx.com>" -LABEL org.opencontainers.image.version="1.31.1" +LABEL org.opencontainers.image.version="1.32.0" RUN set -ex \ && savedAptMark="$(apt-mark showmanual)" \ && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev curl pkg-config \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates git build-essential libssl-dev libpcre2-dev curl pkg-config \ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ && mkdir -p /usr/src/unit \ && cd /usr/src/unit \ - && hg clone -u 1.31.1-1 https://hg.nginx.org/unit \ + && git clone --depth 1 -b 1.32.0-1 https://github.com/nginx/unit \ && cd unit \ && NCPU="$(getconf _NPROCESSORS_ONLN)" \ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ @@ -77,7 +77,7 @@ RUN set -ex \ && apt-get purge -y --auto-remove build-essential \ && rm -rf /var/lib/apt/lists/* \ && rm -f /requirements.apt \ - && ln -sf /dev/stdout /var/log/unit.log + && ln -sf /dev/stderr /var/log/unit.log COPY docker-entrypoint.sh /usr/local/bin/ COPY welcome.* /usr/share/unit/welcome/ diff --git a/pkg/docker/Dockerfile.node18 b/pkg/docker/Dockerfile.node21 index 4ac18847..a2c1e419 100644 --- a/pkg/docker/Dockerfile.node18 +++ b/pkg/docker/Dockerfile.node21 @@ -1,21 +1,21 @@ -FROM node:18-bullseye +FROM node:21-bullseye -LABEL org.opencontainers.image.title="Unit (node18)" +LABEL org.opencontainers.image.title="Unit (node21)" LABEL org.opencontainers.image.description="Official build of Unit for Docker." LABEL org.opencontainers.image.url="https://unit.nginx.org" LABEL org.opencontainers.image.source="https://github.com/nginx/unit" LABEL org.opencontainers.image.documentation="https://unit.nginx.org/installation/#docker-images" LABEL org.opencontainers.image.vendor="NGINX Docker Maintainers <docker-maint@nginx.com>" -LABEL org.opencontainers.image.version="1.31.1" +LABEL org.opencontainers.image.version="1.32.0" RUN set -ex \ && savedAptMark="$(apt-mark showmanual)" \ && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev curl pkg-config \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates git build-essential libssl-dev libpcre2-dev curl pkg-config \ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ && mkdir -p /usr/src/unit \ && cd /usr/src/unit \ - && hg clone -u 1.31.1-1 https://hg.nginx.org/unit \ + && git clone --depth 1 -b 1.32.0-1 https://github.com/nginx/unit \ && cd unit \ && NCPU="$(getconf _NPROCESSORS_ONLN)" \ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ @@ -77,7 +77,7 @@ RUN set -ex \ && apt-get purge -y --auto-remove build-essential \ && rm -rf /var/lib/apt/lists/* \ && rm -f /requirements.apt \ - && ln -sf /dev/stdout /var/log/unit.log + && ln -sf /dev/stderr /var/log/unit.log COPY docker-entrypoint.sh /usr/local/bin/ COPY welcome.* /usr/share/unit/welcome/ diff --git a/pkg/docker/Dockerfile.perl5.36 b/pkg/docker/Dockerfile.perl5.36 index 8cc5d9e2..5d7564c8 100644 --- a/pkg/docker/Dockerfile.perl5.36 +++ b/pkg/docker/Dockerfile.perl5.36 @@ -6,16 +6,16 @@ LABEL org.opencontainers.image.url="https://unit.nginx.org" LABEL org.opencontainers.image.source="https://github.com/nginx/unit" LABEL org.opencontainers.image.documentation="https://unit.nginx.org/installation/#docker-images" LABEL org.opencontainers.image.vendor="NGINX Docker Maintainers <docker-maint@nginx.com>" -LABEL org.opencontainers.image.version="1.31.1" +LABEL org.opencontainers.image.version="1.32.0" RUN set -ex \ && savedAptMark="$(apt-mark showmanual)" \ && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev curl pkg-config \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates git build-essential libssl-dev libpcre2-dev curl pkg-config \ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ && mkdir -p /usr/src/unit \ && cd /usr/src/unit \ - && hg clone -u 1.31.1-1 https://hg.nginx.org/unit \ + && git clone --depth 1 -b 1.32.0-1 https://github.com/nginx/unit \ && cd unit \ && NCPU="$(getconf _NPROCESSORS_ONLN)" \ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ @@ -77,7 +77,7 @@ RUN set -ex \ && apt-get purge -y --auto-remove build-essential \ && rm -rf /var/lib/apt/lists/* \ && rm -f /requirements.apt \ - && ln -sf /dev/stdout /var/log/unit.log + && ln -sf /dev/stderr /var/log/unit.log COPY docker-entrypoint.sh /usr/local/bin/ COPY welcome.* /usr/share/unit/welcome/ diff --git a/pkg/docker/Dockerfile.perl5.38 b/pkg/docker/Dockerfile.perl5.38 index 531188fe..6af576d1 100644 --- a/pkg/docker/Dockerfile.perl5.38 +++ b/pkg/docker/Dockerfile.perl5.38 @@ -6,16 +6,16 @@ LABEL org.opencontainers.image.url="https://unit.nginx.org" LABEL org.opencontainers.image.source="https://github.com/nginx/unit" LABEL org.opencontainers.image.documentation="https://unit.nginx.org/installation/#docker-images" LABEL org.opencontainers.image.vendor="NGINX Docker Maintainers <docker-maint@nginx.com>" -LABEL org.opencontainers.image.version="1.31.1" +LABEL org.opencontainers.image.version="1.32.0" RUN set -ex \ && savedAptMark="$(apt-mark showmanual)" \ && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev curl pkg-config \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates git build-essential libssl-dev libpcre2-dev curl pkg-config \ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ && mkdir -p /usr/src/unit \ && cd /usr/src/unit \ - && hg clone -u 1.31.1-1 https://hg.nginx.org/unit \ + && git clone --depth 1 -b 1.32.0-1 https://github.com/nginx/unit \ && cd unit \ && NCPU="$(getconf _NPROCESSORS_ONLN)" \ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ @@ -77,7 +77,7 @@ RUN set -ex \ && apt-get purge -y --auto-remove build-essential \ && rm -rf /var/lib/apt/lists/* \ && rm -f /requirements.apt \ - && ln -sf /dev/stdout /var/log/unit.log + && ln -sf /dev/stderr /var/log/unit.log COPY docker-entrypoint.sh /usr/local/bin/ COPY welcome.* /usr/share/unit/welcome/ diff --git a/pkg/docker/Dockerfile.php8.2 b/pkg/docker/Dockerfile.php8.2 index 5783bf6c..4348cfed 100644 --- a/pkg/docker/Dockerfile.php8.2 +++ b/pkg/docker/Dockerfile.php8.2 @@ -6,16 +6,16 @@ LABEL org.opencontainers.image.url="https://unit.nginx.org" LABEL org.opencontainers.image.source="https://github.com/nginx/unit" LABEL org.opencontainers.image.documentation="https://unit.nginx.org/installation/#docker-images" LABEL org.opencontainers.image.vendor="NGINX Docker Maintainers <docker-maint@nginx.com>" -LABEL org.opencontainers.image.version="1.31.1" +LABEL org.opencontainers.image.version="1.32.0" RUN set -ex \ && savedAptMark="$(apt-mark showmanual)" \ && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev curl pkg-config \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates git build-essential libssl-dev libpcre2-dev curl pkg-config \ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ && mkdir -p /usr/src/unit \ && cd /usr/src/unit \ - && hg clone -u 1.31.1-1 https://hg.nginx.org/unit \ + && git clone --depth 1 -b 1.32.0-1 https://github.com/nginx/unit \ && cd unit \ && NCPU="$(getconf _NPROCESSORS_ONLN)" \ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ @@ -77,7 +77,7 @@ RUN set -ex \ && apt-get purge -y --auto-remove build-essential \ && rm -rf /var/lib/apt/lists/* \ && rm -f /requirements.apt \ - && ln -sf /dev/stdout /var/log/unit.log + && ln -sf /dev/stderr /var/log/unit.log COPY docker-entrypoint.sh /usr/local/bin/ COPY welcome.* /usr/share/unit/welcome/ diff --git a/pkg/docker/Dockerfile.php8.3 b/pkg/docker/Dockerfile.php8.3 new file mode 100644 index 00000000..31fe2335 --- /dev/null +++ b/pkg/docker/Dockerfile.php8.3 @@ -0,0 +1,89 @@ +FROM php:8.3-cli-bullseye + +LABEL org.opencontainers.image.title="Unit (php8.3)" +LABEL org.opencontainers.image.description="Official build of Unit for Docker." +LABEL org.opencontainers.image.url="https://unit.nginx.org" +LABEL org.opencontainers.image.source="https://github.com/nginx/unit" +LABEL org.opencontainers.image.documentation="https://unit.nginx.org/installation/#docker-images" +LABEL org.opencontainers.image.vendor="NGINX Docker Maintainers <docker-maint@nginx.com>" +LABEL org.opencontainers.image.version="1.32.0" + +RUN set -ex \ + && savedAptMark="$(apt-mark showmanual)" \ + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates git build-essential libssl-dev libpcre2-dev curl pkg-config \ + && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ + && mkdir -p /usr/src/unit \ + && cd /usr/src/unit \ + && git clone --depth 1 -b 1.32.0-1 https://github.com/nginx/unit \ + && cd unit \ + && NCPU="$(getconf _NPROCESSORS_ONLN)" \ + && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ + && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \ + && LD_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_LDFLAGS_MAINT_APPEND="-Wl,--as-needed -pie" dpkg-buildflags --get LDFLAGS)" \ + && CONFIGURE_ARGS_MODULES="--prefix=/usr \ + --statedir=/var/lib/unit \ + --control=unix:/var/run/control.unit.sock \ + --runstatedir=/var/run \ + --pid=/var/run/unit.pid \ + --logdir=/var/log \ + --log=/var/log/unit.log \ + --tmpdir=/var/tmp \ + --user=unit \ + --group=unit \ + --openssl \ + --libdir=/usr/lib/$DEB_HOST_MULTIARCH" \ + && CONFIGURE_ARGS="$CONFIGURE_ARGS_MODULES \ + --njs" \ + && make -j $NCPU -C pkg/contrib .njs \ + && export PKG_CONFIG_PATH=$(pwd)/pkg/contrib/njs/build \ + && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modulesdir=/usr/lib/unit/debug-modules --debug \ + && make -j $NCPU unitd \ + && install -pm755 build/sbin/unitd /usr/sbin/unitd-debug \ + && make clean \ + && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modulesdir=/usr/lib/unit/modules \ + && make -j $NCPU unitd \ + && install -pm755 build/sbin/unitd /usr/sbin/unitd \ + && make clean \ + && /bin/true \ + && ./configure $CONFIGURE_ARGS_MODULES --cc-opt="$CC_OPT" --modulesdir=/usr/lib/unit/debug-modules --debug \ + && ./configure php \ + && make -j $NCPU php-install \ + && make clean \ + && ./configure $CONFIGURE_ARGS_MODULES --cc-opt="$CC_OPT" --modulesdir=/usr/lib/unit/modules \ + && ./configure php \ + && make -j $NCPU php-install \ + && cd \ + && rm -rf /usr/src/unit \ + && for f in /usr/sbin/unitd /usr/lib/unit/modules/*.unit.so; do \ + ldd $f | awk '/=>/{print $(NF-1)}' | while read n; do dpkg-query -S $n; done | sed 's/^\([^:]\+\):.*$/\1/' | sort | uniq >> /requirements.apt; \ + done \ + && apt-mark showmanual | xargs apt-mark auto > /dev/null \ + && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \ + && ldconfig \ + && mkdir -p /var/lib/unit/ \ + && mkdir -p /docker-entrypoint.d/ \ + && groupadd --gid 999 unit \ + && useradd \ + --uid 999 \ + --gid unit \ + --no-create-home \ + --home /nonexistent \ + --comment "unit user" \ + --shell /bin/false \ + unit \ + && apt-get update \ + && apt-get --no-install-recommends --no-install-suggests -y install curl $(cat /requirements.apt) \ + && apt-get purge -y --auto-remove build-essential \ + && rm -rf /var/lib/apt/lists/* \ + && rm -f /requirements.apt \ + && ln -sf /dev/stderr /var/log/unit.log + +COPY docker-entrypoint.sh /usr/local/bin/ +COPY welcome.* /usr/share/unit/welcome/ + +STOPSIGNAL SIGTERM + +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] +EXPOSE 80 +CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"] diff --git a/pkg/docker/Dockerfile.python3.11 b/pkg/docker/Dockerfile.python3.11 index b5e81b6c..699fa426 100644 --- a/pkg/docker/Dockerfile.python3.11 +++ b/pkg/docker/Dockerfile.python3.11 @@ -6,16 +6,16 @@ LABEL org.opencontainers.image.url="https://unit.nginx.org" LABEL org.opencontainers.image.source="https://github.com/nginx/unit" LABEL org.opencontainers.image.documentation="https://unit.nginx.org/installation/#docker-images" LABEL org.opencontainers.image.vendor="NGINX Docker Maintainers <docker-maint@nginx.com>" -LABEL org.opencontainers.image.version="1.31.1" +LABEL org.opencontainers.image.version="1.32.0" RUN set -ex \ && savedAptMark="$(apt-mark showmanual)" \ && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev curl pkg-config \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates git build-essential libssl-dev libpcre2-dev curl pkg-config \ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ && mkdir -p /usr/src/unit \ && cd /usr/src/unit \ - && hg clone -u 1.31.1-1 https://hg.nginx.org/unit \ + && git clone --depth 1 -b 1.32.0-1 https://github.com/nginx/unit \ && cd unit \ && NCPU="$(getconf _NPROCESSORS_ONLN)" \ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ @@ -77,7 +77,7 @@ RUN set -ex \ && apt-get purge -y --auto-remove build-essential \ && rm -rf /var/lib/apt/lists/* \ && rm -f /requirements.apt \ - && ln -sf /dev/stdout /var/log/unit.log + && ln -sf /dev/stderr /var/log/unit.log COPY docker-entrypoint.sh /usr/local/bin/ COPY welcome.* /usr/share/unit/welcome/ diff --git a/pkg/docker/Dockerfile.python3.12 b/pkg/docker/Dockerfile.python3.12 new file mode 100644 index 00000000..d3fb3c01 --- /dev/null +++ b/pkg/docker/Dockerfile.python3.12 @@ -0,0 +1,89 @@ +FROM python:3.12-bullseye + +LABEL org.opencontainers.image.title="Unit (python3.12)" +LABEL org.opencontainers.image.description="Official build of Unit for Docker." +LABEL org.opencontainers.image.url="https://unit.nginx.org" +LABEL org.opencontainers.image.source="https://github.com/nginx/unit" +LABEL org.opencontainers.image.documentation="https://unit.nginx.org/installation/#docker-images" +LABEL org.opencontainers.image.vendor="NGINX Docker Maintainers <docker-maint@nginx.com>" +LABEL org.opencontainers.image.version="1.32.0" + +RUN set -ex \ + && savedAptMark="$(apt-mark showmanual)" \ + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates git build-essential libssl-dev libpcre2-dev curl pkg-config \ + && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ + && mkdir -p /usr/src/unit \ + && cd /usr/src/unit \ + && git clone --depth 1 -b 1.32.0-1 https://github.com/nginx/unit \ + && cd unit \ + && NCPU="$(getconf _NPROCESSORS_ONLN)" \ + && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ + && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \ + && LD_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_LDFLAGS_MAINT_APPEND="-Wl,--as-needed -pie" dpkg-buildflags --get LDFLAGS)" \ + && CONFIGURE_ARGS_MODULES="--prefix=/usr \ + --statedir=/var/lib/unit \ + --control=unix:/var/run/control.unit.sock \ + --runstatedir=/var/run \ + --pid=/var/run/unit.pid \ + --logdir=/var/log \ + --log=/var/log/unit.log \ + --tmpdir=/var/tmp \ + --user=unit \ + --group=unit \ + --openssl \ + --libdir=/usr/lib/$DEB_HOST_MULTIARCH" \ + && CONFIGURE_ARGS="$CONFIGURE_ARGS_MODULES \ + --njs" \ + && make -j $NCPU -C pkg/contrib .njs \ + && export PKG_CONFIG_PATH=$(pwd)/pkg/contrib/njs/build \ + && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modulesdir=/usr/lib/unit/debug-modules --debug \ + && make -j $NCPU unitd \ + && install -pm755 build/sbin/unitd /usr/sbin/unitd-debug \ + && make clean \ + && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modulesdir=/usr/lib/unit/modules \ + && make -j $NCPU unitd \ + && install -pm755 build/sbin/unitd /usr/sbin/unitd \ + && make clean \ + && /bin/true \ + && ./configure $CONFIGURE_ARGS_MODULES --cc-opt="$CC_OPT" --modulesdir=/usr/lib/unit/debug-modules --debug \ + && ./configure python --config=/usr/local/bin/python3-config \ + && make -j $NCPU python3-install \ + && make clean \ + && ./configure $CONFIGURE_ARGS_MODULES --cc-opt="$CC_OPT" --modulesdir=/usr/lib/unit/modules \ + && ./configure python --config=/usr/local/bin/python3-config \ + && make -j $NCPU python3-install \ + && cd \ + && rm -rf /usr/src/unit \ + && for f in /usr/sbin/unitd /usr/lib/unit/modules/*.unit.so; do \ + ldd $f | awk '/=>/{print $(NF-1)}' | while read n; do dpkg-query -S $n; done | sed 's/^\([^:]\+\):.*$/\1/' | sort | uniq >> /requirements.apt; \ + done \ + && apt-mark showmanual | xargs apt-mark auto > /dev/null \ + && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \ + && /bin/true \ + && mkdir -p /var/lib/unit/ \ + && mkdir -p /docker-entrypoint.d/ \ + && groupadd --gid 999 unit \ + && useradd \ + --uid 999 \ + --gid unit \ + --no-create-home \ + --home /nonexistent \ + --comment "unit user" \ + --shell /bin/false \ + unit \ + && apt-get update \ + && apt-get --no-install-recommends --no-install-suggests -y install curl $(cat /requirements.apt) \ + && apt-get purge -y --auto-remove build-essential \ + && rm -rf /var/lib/apt/lists/* \ + && rm -f /requirements.apt \ + && ln -sf /dev/stderr /var/log/unit.log + +COPY docker-entrypoint.sh /usr/local/bin/ +COPY welcome.* /usr/share/unit/welcome/ + +STOPSIGNAL SIGTERM + +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] +EXPOSE 80 +CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"] diff --git a/pkg/docker/Dockerfile.ruby3.2 b/pkg/docker/Dockerfile.ruby3.2 index c417a327..b6263e5d 100644 --- a/pkg/docker/Dockerfile.ruby3.2 +++ b/pkg/docker/Dockerfile.ruby3.2 @@ -6,16 +6,16 @@ LABEL org.opencontainers.image.url="https://unit.nginx.org" LABEL org.opencontainers.image.source="https://github.com/nginx/unit" LABEL org.opencontainers.image.documentation="https://unit.nginx.org/installation/#docker-images" LABEL org.opencontainers.image.vendor="NGINX Docker Maintainers <docker-maint@nginx.com>" -LABEL org.opencontainers.image.version="1.31.1" +LABEL org.opencontainers.image.version="1.32.0" RUN set -ex \ && savedAptMark="$(apt-mark showmanual)" \ && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev curl pkg-config \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates git build-essential libssl-dev libpcre2-dev curl pkg-config \ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ && mkdir -p /usr/src/unit \ && cd /usr/src/unit \ - && hg clone -u 1.31.1-1 https://hg.nginx.org/unit \ + && git clone --depth 1 -b 1.32.0-1 https://github.com/nginx/unit \ && cd unit \ && NCPU="$(getconf _NPROCESSORS_ONLN)" \ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ @@ -77,7 +77,7 @@ RUN set -ex \ && apt-get purge -y --auto-remove build-essential \ && rm -rf /var/lib/apt/lists/* \ && rm -f /requirements.apt \ - && ln -sf /dev/stdout /var/log/unit.log + && ln -sf /dev/stderr /var/log/unit.log COPY docker-entrypoint.sh /usr/local/bin/ COPY welcome.* /usr/share/unit/welcome/ diff --git a/pkg/docker/Dockerfile.ruby3.3 b/pkg/docker/Dockerfile.ruby3.3 new file mode 100644 index 00000000..132bbb9c --- /dev/null +++ b/pkg/docker/Dockerfile.ruby3.3 @@ -0,0 +1,89 @@ +FROM ruby:3.3-bullseye + +LABEL org.opencontainers.image.title="Unit (ruby3.3)" +LABEL org.opencontainers.image.description="Official build of Unit for Docker." +LABEL org.opencontainers.image.url="https://unit.nginx.org" +LABEL org.opencontainers.image.source="https://github.com/nginx/unit" +LABEL org.opencontainers.image.documentation="https://unit.nginx.org/installation/#docker-images" +LABEL org.opencontainers.image.vendor="NGINX Docker Maintainers <docker-maint@nginx.com>" +LABEL org.opencontainers.image.version="1.32.0" + +RUN set -ex \ + && savedAptMark="$(apt-mark showmanual)" \ + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates git build-essential libssl-dev libpcre2-dev curl pkg-config \ + && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ + && mkdir -p /usr/src/unit \ + && cd /usr/src/unit \ + && git clone --depth 1 -b 1.32.0-1 https://github.com/nginx/unit \ + && cd unit \ + && NCPU="$(getconf _NPROCESSORS_ONLN)" \ + && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ + && CC_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_CFLAGS_MAINT_APPEND="-Wp,-D_FORTIFY_SOURCE=2 -fPIC" dpkg-buildflags --get CFLAGS)" \ + && LD_OPT="$(DEB_BUILD_MAINT_OPTIONS="hardening=+all,-pie" DEB_LDFLAGS_MAINT_APPEND="-Wl,--as-needed -pie" dpkg-buildflags --get LDFLAGS)" \ + && CONFIGURE_ARGS_MODULES="--prefix=/usr \ + --statedir=/var/lib/unit \ + --control=unix:/var/run/control.unit.sock \ + --runstatedir=/var/run \ + --pid=/var/run/unit.pid \ + --logdir=/var/log \ + --log=/var/log/unit.log \ + --tmpdir=/var/tmp \ + --user=unit \ + --group=unit \ + --openssl \ + --libdir=/usr/lib/$DEB_HOST_MULTIARCH" \ + && CONFIGURE_ARGS="$CONFIGURE_ARGS_MODULES \ + --njs" \ + && make -j $NCPU -C pkg/contrib .njs \ + && export PKG_CONFIG_PATH=$(pwd)/pkg/contrib/njs/build \ + && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modulesdir=/usr/lib/unit/debug-modules --debug \ + && make -j $NCPU unitd \ + && install -pm755 build/sbin/unitd /usr/sbin/unitd-debug \ + && make clean \ + && ./configure $CONFIGURE_ARGS --cc-opt="$CC_OPT" --ld-opt="$LD_OPT" --modulesdir=/usr/lib/unit/modules \ + && make -j $NCPU unitd \ + && install -pm755 build/sbin/unitd /usr/sbin/unitd \ + && make clean \ + && /bin/true \ + && ./configure $CONFIGURE_ARGS_MODULES --cc-opt="$CC_OPT" --modulesdir=/usr/lib/unit/debug-modules --debug \ + && ./configure ruby \ + && make -j $NCPU ruby-install \ + && make clean \ + && ./configure $CONFIGURE_ARGS_MODULES --cc-opt="$CC_OPT" --modulesdir=/usr/lib/unit/modules \ + && ./configure ruby \ + && make -j $NCPU ruby-install \ + && cd \ + && rm -rf /usr/src/unit \ + && for f in /usr/sbin/unitd /usr/lib/unit/modules/*.unit.so; do \ + ldd $f | awk '/=>/{print $(NF-1)}' | while read n; do dpkg-query -S $n; done | sed 's/^\([^:]\+\):.*$/\1/' | sort | uniq >> /requirements.apt; \ + done \ + && apt-mark showmanual | xargs apt-mark auto > /dev/null \ + && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \ + && gem install rack && rm -rf /root/.local \ + && mkdir -p /var/lib/unit/ \ + && mkdir -p /docker-entrypoint.d/ \ + && groupadd --gid 999 unit \ + && useradd \ + --uid 999 \ + --gid unit \ + --no-create-home \ + --home /nonexistent \ + --comment "unit user" \ + --shell /bin/false \ + unit \ + && apt-get update \ + && apt-get --no-install-recommends --no-install-suggests -y install curl $(cat /requirements.apt) \ + && apt-get purge -y --auto-remove build-essential \ + && rm -rf /var/lib/apt/lists/* \ + && rm -f /requirements.apt \ + && ln -sf /dev/stderr /var/log/unit.log + +COPY docker-entrypoint.sh /usr/local/bin/ +COPY welcome.* /usr/share/unit/welcome/ + +STOPSIGNAL SIGTERM + +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] +EXPOSE 80 +CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"] diff --git a/pkg/docker/Dockerfile.wasm b/pkg/docker/Dockerfile.wasm index e45f020f..7c107b4c 100644 --- a/pkg/docker/Dockerfile.wasm +++ b/pkg/docker/Dockerfile.wasm @@ -6,16 +6,16 @@ LABEL org.opencontainers.image.url="https://unit.nginx.org" LABEL org.opencontainers.image.source="https://github.com/nginx/unit" LABEL org.opencontainers.image.documentation="https://unit.nginx.org/installation/#docker-images" LABEL org.opencontainers.image.vendor="NGINX Docker Maintainers <docker-maint@nginx.com>" -LABEL org.opencontainers.image.version="1.31.1" +LABEL org.opencontainers.image.version="1.32.0" RUN set -ex \ && savedAptMark="$(apt-mark showmanual)" \ && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev curl pkg-config \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates git build-essential libssl-dev libpcre2-dev curl pkg-config \ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ && mkdir -p /usr/src/unit \ && cd /usr/src/unit \ - && hg clone -u 1.31.1-1 https://hg.nginx.org/unit \ + && git clone --depth 1 -b 1.32.0-1 https://github.com/nginx/unit \ && cd unit \ && NCPU="$(getconf _NPROCESSORS_ONLN)" \ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ @@ -45,7 +45,8 @@ RUN set -ex \ && make -j $NCPU unitd \ && install -pm755 build/sbin/unitd /usr/sbin/unitd \ && make clean \ - && export RUST_VERSION=1.71.0 \ + && apt-get install --no-install-recommends --no-install-suggests -y libclang-dev \ + && export RUST_VERSION=1.76.0 \ && export RUSTUP_HOME=/usr/src/unit/rustup \ && export CARGO_HOME=/usr/src/unit/cargo \ && export PATH=/usr/src/unit/cargo/bin:$PATH \ @@ -67,12 +68,12 @@ RUN set -ex \ && make -C pkg/contrib .wasmtime \ && install -pm 755 pkg/contrib/wasmtime/target/release/libwasmtime.so /usr/lib/$(dpkg-architecture -q DEB_HOST_MULTIARCH)/ \ && ./configure $CONFIGURE_ARGS_MODULES --cc-opt="$CC_OPT" --modulesdir=/usr/lib/unit/debug-modules --debug \ - && ./configure wasm --include-path=`pwd`/pkg/contrib/wasmtime/crates/c-api/include --lib-path=/usr/lib/$(dpkg-architecture -q DEB_HOST_MULTIARCH)/ \ - && make -j $NCPU wasm-install \ + && ./configure wasm --include-path=`pwd`/pkg/contrib/wasmtime/crates/c-api/include --lib-path=/usr/lib/$(dpkg-architecture -q DEB_HOST_MULTIARCH)/ && ./configure wasm-wasi-component \ + && make -j $NCPU wasm-install wasm-wasi-component-install \ && make clean \ && ./configure $CONFIGURE_ARGS_MODULES --cc-opt="$CC_OPT" --modulesdir=/usr/lib/unit/modules \ - && ./configure wasm --include-path=`pwd`/pkg/contrib/wasmtime/crates/c-api/include --lib-path=/usr/lib/$(dpkg-architecture -q DEB_HOST_MULTIARCH)/ \ - && make -j $NCPU wasm-install \ + && ./configure wasm --include-path=`pwd`/pkg/contrib/wasmtime/crates/c-api/include --lib-path=/usr/lib/$(dpkg-architecture -q DEB_HOST_MULTIARCH)/ && ./configure wasm-wasi-component \ + && make -j $NCPU wasm-install wasm-wasi-component-install \ && cd \ && rm -rf /usr/src/unit \ && for f in /usr/sbin/unitd /usr/lib/unit/modules/*.unit.so; do \ @@ -97,7 +98,7 @@ RUN set -ex \ && apt-get purge -y --auto-remove build-essential \ && rm -rf /var/lib/apt/lists/* \ && rm -f /requirements.apt \ - && ln -sf /dev/stdout /var/log/unit.log + && ln -sf /dev/stderr /var/log/unit.log COPY docker-entrypoint.sh /usr/local/bin/ COPY welcome.* /usr/share/unit/welcome/ diff --git a/pkg/docker/Makefile b/pkg/docker/Makefile index 237228a9..d8c53021 100644 --- a/pkg/docker/Makefile +++ b/pkg/docker/Makefile @@ -19,7 +19,7 @@ INSTALL_minimal ?= version RUN_minimal ?= /bin/true MODULE_PREBUILD_minimal ?= /bin/true -VERSIONS_go ?= 1.20 1.21 +VERSIONS_go ?= 1.21 1.22 VARIANT_go ?= $(VARIANT) $(foreach goversion, $(VERSIONS_go), $(eval CONTAINER_go$(goversion) = golang:$(goversion)-$(VARIANT_go))) CONFIGURE_go ?= go --go-path=$$GOPATH @@ -35,7 +35,7 @@ INSTALL_jsc ?= java-shared-install java-install RUN_jsc ?= rm -rf /root/.m2 MODULE_PREBUILD_jsc ?= /bin/true -VERSIONS_node ?= 18 20 +VERSIONS_node ?= 20 21 VARIANT_node ?= $(VARIANT) $(foreach nodeversion, $(VERSIONS_node), $(eval CONTAINER_node$(nodeversion) = node:$(nodeversion)-$(VARIANT_node))) CONFIGURE_node ?= nodejs --node-gyp=/usr/local/bin/node-gyp @@ -51,7 +51,7 @@ INSTALL_perl ?= perl-install RUN_perl ?= /bin/true MODULE_PREBUILD_perl ?= /bin/true -VERSIONS_php ?= 8.2 +VERSIONS_php ?= 8.2 8.3 VARIANT_php ?= cli-$(VARIANT) $(foreach phpversion, $(VERSIONS_php), $(eval CONTAINER_php$(phpversion) = php:$(phpversion)-$(VARIANT_php))) CONFIGURE_php ?= php @@ -59,7 +59,7 @@ INSTALL_php ?= php-install RUN_php ?= ldconfig MODULE_PREBUILD_php ?= /bin/true -VERSIONS_python ?= 3.11 +VERSIONS_python ?= 3.11 3.12 VARIANT_python ?= $(VARIANT) $(foreach pythonversion, $(VERSIONS_python), $(eval CONTAINER_python$(pythonversion) = python:$(pythonversion)-$(VARIANT_python))) CONFIGURE_python ?= python --config=/usr/local/bin/python3-config @@ -67,7 +67,7 @@ INSTALL_python ?= python3-install RUN_python ?= /bin/true MODULE_PREBUILD_python ?= /bin/true -VERSIONS_ruby ?= 3.2 +VERSIONS_ruby ?= 3.2 3.3 VARIANT_ruby ?= $(VARIANT) $(foreach rubyversion, $(VERSIONS_ruby), $(eval CONTAINER_ruby$(rubyversion) = ruby:$(rubyversion)-$(VARIANT_ruby))) CONFIGURE_ruby ?= ruby @@ -77,11 +77,13 @@ MODULE_PREBUILD_ruby ?= /bin/true VERSIONS_wasm ?= CONTAINER_wasm ?= debian:$(VARIANT)-slim -CONFIGURE_wasm ?= wasm --include-path=\`pwd\`/pkg/contrib/wasmtime/crates/c-api/include --lib-path=/usr/lib/\$$(dpkg-architecture -q DEB_HOST_MULTIARCH)/ -INSTALL_wasm ?= wasm-install -RUN_wasm ?= /bin/true +CONFIGURE_wasm ?= wasm --include-path=\`pwd\`/pkg/contrib/wasmtime/crates/c-api/include --lib-path=/usr/lib/\$$(dpkg-architecture -q DEB_HOST_MULTIARCH)/ \&\& ./configure wasm-wasi-component +INSTALL_wasm ?= wasm-install wasm-wasi-component-install +RUN_wasm ?= /bin/true + define MODULE_PREBUILD_wasm -export RUST_VERSION=1.71.0 \\\n \ +apt-get install --no-install-recommends --no-install-suggests -y libclang-dev \\\n \ +\ \ \ \&\& export RUST_VERSION=1.76.0 \\\n \ \ \ \ \&\& export RUSTUP_HOME=/usr/src/unit/rustup \\\n \ \ \ \ \&\& export CARGO_HOME=/usr/src/unit/cargo \\\n \ \ \ \ \&\& export PATH=/usr/src/unit/cargo/bin:\$$PATH \\\n \ diff --git a/pkg/docker/template.Dockerfile b/pkg/docker/template.Dockerfile index 4d5cc101..edf9ba75 100644 --- a/pkg/docker/template.Dockerfile +++ b/pkg/docker/template.Dockerfile @@ -11,11 +11,11 @@ LABEL org.opencontainers.image.version="@@VERSION@@" RUN set -ex \ && savedAptMark="$(apt-mark showmanual)" \ && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates mercurial build-essential libssl-dev libpcre2-dev curl pkg-config \ + && apt-get install --no-install-recommends --no-install-suggests -y ca-certificates git build-essential libssl-dev libpcre2-dev curl pkg-config \ && mkdir -p /usr/lib/unit/modules /usr/lib/unit/debug-modules \ && mkdir -p /usr/src/unit \ && cd /usr/src/unit \ - && hg clone -u @@VERSION@@-@@PATCHLEVEL@@ https://hg.nginx.org/unit \ + && git clone --depth 1 -b @@VERSION@@-@@PATCHLEVEL@@ https://github.com/nginx/unit \ && cd unit \ && NCPU="$(getconf _NPROCESSORS_ONLN)" \ && DEB_HOST_MULTIARCH="$(dpkg-architecture -q DEB_HOST_MULTIARCH)" \ @@ -77,7 +77,7 @@ RUN set -ex \ && apt-get purge -y --auto-remove build-essential \ && rm -rf /var/lib/apt/lists/* \ && rm -f /requirements.apt \ - && ln -sf /dev/stdout /var/log/unit.log + && ln -sf /dev/stderr /var/log/unit.log COPY docker-entrypoint.sh /usr/local/bin/ COPY welcome.* /usr/share/unit/welcome/ diff --git a/pkg/docker/welcome.html b/pkg/docker/welcome.html index 89de39e1..7167ddb7 100644 --- a/pkg/docker/welcome.html +++ b/pkg/docker/welcome.html @@ -40,6 +40,6 @@ <hr> <p><a href="https://unit.nginx.org/?referer=welcome&platform=docker">NGINX Unit — the universal web app server</a><br> - NGINX, Inc. © 2023</p> + NGINX, Inc. © 2024</p> </body> </html> diff --git a/pkg/rpm/Makefile b/pkg/rpm/Makefile index 355f8a59..1f3bbd58 100644 --- a/pkg/rpm/Makefile +++ b/pkg/rpm/Makefile @@ -22,8 +22,10 @@ else ifeq ($(shell rpm --eval "%{?amzn}"), 2023) OSVER = amazonlinux2023 else ifeq ($(shell test `rpm --eval '0%{?fedora} -ge 35 -a 0%{?fedora} -le 36'`; echo $$?),0) OSVER = fedora -else ifeq ($(shell test `rpm --eval '0%{?fedora} -ge 37'`; echo $$?),0) +else ifeq ($(shell test `rpm --eval '0%{?fedora} -ge 37 -a 0%{?fedora} -le 38'`; echo $$?),0) OSVER = fedora37 +else ifeq ($(shell test `rpm --eval '0%{?fedora} -ge 39'`; echo $$?),0) +OSVER = fedora39 endif BUILD_DEPENDS_unit = gcc rpm-build rpmlint @@ -124,6 +126,18 @@ include Makefile.jsc11 include Makefile.wasm endif +ifeq ($(OSVER), fedora39) +include Makefile.php +include Makefile.python312 +include Makefile.go +include Makefile.perl +include Makefile.ruby +include Makefile.jsc-common +include Makefile.jsc17 +include Makefile.wasm +endif + + CONFIGURE_ARGS_COMMON=\ --prefix=/usr \ --statedir=%{_sharedstatedir}/unit \ @@ -254,7 +268,7 @@ rpmbuild/SPECS/unit-%.spec: unit.module.spec.in ../../docs/changes.xml | rpmbuil cat ../../build/unit-$(MODULE_SUFFIX_$*).rpm-changelog | sed -e \ "s/> - $(DEFAULT_VERSION)-$(DEFAULT_RELEASE)/> - $(MODULE_VERSION_$*)-$(MODULE_RELEASE_$*)/" \ >> $@.tmp - mv $@.tmp $@ + mv $@.tmp $@ unit-%: check-build-depends-% rpmbuild/SPECS/unit-%.spec rpmbuild/SOURCES/unit-$(VERSION).tar.gz @echo "===> Building $(subst _,-,$@) package" ; \ diff --git a/pkg/rpm/Makefile.jsc-common b/pkg/rpm/Makefile.jsc-common index a3c3a3da..f77ca1e9 100644 --- a/pkg/rpm/Makefile.jsc-common +++ b/pkg/rpm/Makefile.jsc-common @@ -10,16 +10,19 @@ JAVA_ARCH_jsc_common= $(shell /usr/lib/jvm/java-1.8.0/bin/java -XshowSettings 2> ifeq ($(OSVER),amazonlinux2023) MODULE_CONFARGS_jsc_common= java --home=/usr/lib/jvm/java-17-amazon-corretto --lib-path=/usr/lib/jvm/java-17-amazon-corretto/lib --jars=/usr/share/unit-jsc-common/ +else ifeq ($(OSVER),fedora39) +MODULE_CONFARGS_jsc_common= java --home=/usr/lib/jvm/java-17-openjdk --lib-path=/usr/lib/jvm/java-17-openjdk/lib --jars=/usr/share/unit-jsc-common/ else MODULE_CONFARGS_jsc_common= java --home=/usr/lib/jvm/java-1.8.0 --lib-path=/usr/lib/jvm/jre-1.8.0/lib/$(JAVA_ARCH_jsc_common) --jars=/usr/share/unit-jsc-common/ endif -MODULE_MAKEARGS_jsc_common= java MODULE_INSTARGS_jsc_common= java-shared-install MODULE_SOURCES_jsc_common= COPYRIGHT.unit-jsc-common ifeq ($(OSVER),amazonlinux2023) BUILD_DEPENDS_jsc_common= java-17-amazon-corretto-devel curl +else ifeq ($(OSVER),fedora39) +BUILD_DEPENDS_jsc_common= java-17-openjdk-devel curl else BUILD_DEPENDS_jsc_common= java-1.8.0-openjdk-devel curl endif diff --git a/pkg/rpm/Makefile.jsc17 b/pkg/rpm/Makefile.jsc17 index 7efdafaa..9a42c5a1 100644 --- a/pkg/rpm/Makefile.jsc17 +++ b/pkg/rpm/Makefile.jsc17 @@ -6,19 +6,32 @@ MODULE_SUMMARY_jsc17= Java 17 module for NGINX Unit MODULE_VERSION_jsc17= $(VERSION) MODULE_RELEASE_jsc17= 1 +ifeq ($(OSVER),amazonlinux2023) MODULE_CONFARGS_jsc17= java --module=java17 --home=/usr/lib/jvm/java-17-amazon-corretto --lib-path=/usr/lib/jvm/java-17-amazon-corretto/lib --jars=/usr/share/unit-jsc-common/ +else ifeq ($(OSVER),fedora39) +MODULE_CONFARGS_jsc17= java --module=java17 --home=/usr/lib/jvm/java-17-openjdk --lib-path=/usr/lib/jvm/java-17-openjdk/lib --jars=/usr/share/unit-jsc-common/ +endif MODULE_MAKEARGS_jsc17= java17 MODULE_INSTARGS_jsc17= java17-install MODULE_SOURCES_jsc17= unit.example-jsc-app \ unit.example-jsc17-config +ifeq ($(OSVER),amazonlinux2023) BUILD_DEPENDS_jsc17= java-17-amazon-corretto-devel +else ifeq ($(OSVER),fedora39) +BUILD_DEPENDS_jsc17= java-17-openjdk-devel BUILD_DEPENDS+= $(BUILD_DEPENDS_jsc17) +endif define MODULE_DEFINITIONS_jsc17 Requires: unit-jsc-common == $(MODULE_VERSION_jsc_common)-$(MODULE_RELEASE_jsc_common)%{?dist}.ngx +%if (0%{?amzn} == 2023) Requires: java-17-amazon-corretto-headless +%endif +%if (0%{?fedora} >= 39) +Requires: java-17-openjdk-headless +%endif endef export MODULE_DEFINITIONS_jsc17 diff --git a/pkg/rpm/Makefile.python312 b/pkg/rpm/Makefile.python312 new file mode 100644 index 00000000..c37069eb --- /dev/null +++ b/pkg/rpm/Makefile.python312 @@ -0,0 +1,53 @@ +MODULES+= python312 +MODULE_SUFFIX_python312= python3.12 + +MODULE_SUMMARY_python312= Python 3.12 module for NGINX Unit + +MODULE_VERSION_python312= $(VERSION) +MODULE_RELEASE_python312= 1 + +MODULE_CONFARGS_python312= python --config=python3.12-config +MODULE_MAKEARGS_python312= python3.12 +MODULE_INSTARGS_python312= python3.12-install + +MODULE_SOURCES_python312= unit.example-python-app \ + unit.example-python312-config + +BUILD_DEPENDS_python312= python3-devel + +BUILD_DEPENDS+= $(BUILD_DEPENDS_python312) + +define MODULE_PREINSTALL_python312 +%{__mkdir} -p %{buildroot}%{_datadir}/doc/unit-python312/examples/python-app +%{__install} -m 644 -p %{SOURCE100} \ + %{buildroot}%{_datadir}/doc/unit-python312/examples/python-app/wsgi.py +%{__install} -m 644 -p %{SOURCE101} \ + %{buildroot}%{_datadir}/doc/unit-python312/examples/unit.config +endef +export MODULE_PREINSTALL_python312 + +define MODULE_FILES_python312 +%{_libdir}/unit/modules/* +%{_libdir}/unit/debug-modules/* +endef +export MODULE_FILES_python312 + +define MODULE_POST_python312 +cat <<BANNER +---------------------------------------------------------------------- + +The $(MODULE_SUMMARY_python312) has been installed. + +To check the sample app, run these commands: + + sudo service unit start + cd /usr/share/doc/%{name}/examples + sudo curl -X PUT --data-binary @unit.config --unix-socket /var/run/unit/control.sock http://localhost/config + curl http://localhost:8400/ + +Online documentation is available at https://unit.nginx.org + +---------------------------------------------------------------------- +BANNER +endef +export MODULE_POST_python312 diff --git a/pkg/rpm/Makefile.wasm b/pkg/rpm/Makefile.wasm index c638071b..cb2ad35a 100644 --- a/pkg/rpm/Makefile.wasm +++ b/pkg/rpm/Makefile.wasm @@ -6,9 +6,9 @@ MODULE_SUMMARY_wasm= WASM module for NGINX Unit MODULE_VERSION_wasm= $(VERSION) MODULE_RELEASE_wasm= 1 -MODULE_CONFARGS_wasm= wasm --include-path=\`pwd\`/pkg/contrib/wasmtime/crates/c-api/include --lib-path=\`pwd\`/pkg/contrib/wasmtime/target/release -MODULE_MAKEARGS_wasm= wasm -MODULE_INSTARGS_wasm= wasm-install +MODULE_CONFARGS_wasm= wasm --include-path=\`pwd\`/pkg/contrib/wasmtime/crates/c-api/include --lib-path=\`pwd\`/pkg/contrib/wasmtime/target/release \&\& ./configure wasm-wasi-component +MODULE_MAKEARGS_wasm= wasm wasm-wasi-component CFLAGS=\"\$$(grep ^CFLAGS build/Makefile | cut -d' ' -f 3-) -Wno-missing-prototypes\" +MODULE_INSTARGS_wasm= wasm-install wasm-wasi-component-install MODULE_SOURCES_wasm= diff --git a/pkg/rpm/rpmbuild/SOURCES/COPYRIGHT.unit-jsc11 b/pkg/rpm/rpmbuild/SOURCES/COPYRIGHT.unit-jsc11 index 4505b5b5..e6372b97 100644 --- a/pkg/rpm/rpmbuild/SOURCES/COPYRIGHT.unit-jsc11 +++ b/pkg/rpm/rpmbuild/SOURCES/COPYRIGHT.unit-jsc11 @@ -1,13 +1,19 @@ NGINX Unit. - Copyright 2017-2023 NGINX, Inc. - Copyright 2017-2023 Andrei Zeliankou - Copyright 2018-2023 Konstantin Pavlov - Copyright 2021-2023 Zhidao Hong + Copyright 2017-2024 NGINX, Inc. + Copyright 2017-2024 Andrei Zeliankou + Copyright 2018-2024 Konstantin Pavlov + Copyright 2021-2024 Zhidao Hong + Copyright 2022-2024 Andrew Clayton + Copyright 2022-2024 Liam Crilly + Copyright 2023-2024 Dan Callahan + Copyright 2023-2024 Danielle De Leo + Copyright 2023-2024 Dylan Arbour + Copyright 2023-2024 Gabor Javorszky + Copyright 2023-2024 Igor Ippolitov + Copyright 2023-2024 Taryn Musgrave Copyright 2021-2023 Alejandro Colomar - Copyright 2022-2023 Andrew Clayton - Copyright 2022-2023 Liam Crilly Copyright 2017-2022 Valentin V. Bartenev Copyright 2017-2022 Max Romanov Copyright 2021-2022 Oisín Canty diff --git a/pkg/rpm/rpmbuild/SOURCES/COPYRIGHT.unit-jsc8 b/pkg/rpm/rpmbuild/SOURCES/COPYRIGHT.unit-jsc8 index 59891951..174f2309 100644 --- a/pkg/rpm/rpmbuild/SOURCES/COPYRIGHT.unit-jsc8 +++ b/pkg/rpm/rpmbuild/SOURCES/COPYRIGHT.unit-jsc8 @@ -1,13 +1,19 @@ NGINX Unit. - Copyright 2017-2023 NGINX, Inc. - Copyright 2017-2023 Andrei Zeliankou - Copyright 2018-2023 Konstantin Pavlov - Copyright 2021-2023 Zhidao Hong + Copyright 2017-2024 NGINX, Inc. + Copyright 2017-2024 Andrei Zeliankou + Copyright 2018-2024 Konstantin Pavlov + Copyright 2021-2024 Zhidao Hong + Copyright 2022-2024 Andrew Clayton + Copyright 2022-2024 Liam Crilly + Copyright 2023-2024 Dan Callahan + Copyright 2023-2024 Danielle De Leo + Copyright 2023-2024 Dylan Arbour + Copyright 2023-2024 Gabor Javorszky + Copyright 2023-2024 Igor Ippolitov + Copyright 2023-2024 Taryn Musgrave Copyright 2021-2023 Alejandro Colomar - Copyright 2022-2023 Andrew Clayton - Copyright 2022-2023 Liam Crilly Copyright 2017-2022 Valentin V. Bartenev Copyright 2017-2022 Max Romanov Copyright 2021-2022 Oisín Canty diff --git a/pkg/rpm/rpmbuild/SOURCES/unit.example-python312-config b/pkg/rpm/rpmbuild/SOURCES/unit.example-python312-config new file mode 100644 index 00000000..1de2eba6 --- /dev/null +++ b/pkg/rpm/rpmbuild/SOURCES/unit.example-python312-config @@ -0,0 +1,16 @@ +{ + "applications": { + "example_python": { + "type": "python 3.12", + "processes": 2, + "path": "/usr/share/doc/unit-python312/examples/python-app", + "module": "wsgi" + } + }, + + "listeners": { + "*:8400": { + "pass": "applications/example_python" + } + } +} diff --git a/src/java/nginx/unit/websocket/DigestAuthenticator.java b/src/java/nginx/unit/websocket/DigestAuthenticator.java index 9530c303..eb91a9b4 100644 --- a/src/java/nginx/unit/websocket/DigestAuthenticator.java +++ b/src/java/nginx/unit/websocket/DigestAuthenticator.java @@ -22,7 +22,7 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Map; -import org.apache.tomcat.util.security.MD5Encoder; +import org.apache.tomcat.util.buf.HexUtils; /** * Authenticator supporting the DIGEST auth method. @@ -140,7 +140,7 @@ public class DigestAuthenticator extends Authenticator { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] thedigest = md.digest(bytesOfMessage); - return MD5Encoder.encode(thedigest); + return HexUtils.toHexString(thedigest); } @Override diff --git a/src/java/nginx/unit/websocket/Util.java b/src/java/nginx/unit/websocket/Util.java index 6acf3ade..5388431f 100644 --- a/src/java/nginx/unit/websocket/Util.java +++ b/src/java/nginx/unit/websocket/Util.java @@ -332,7 +332,7 @@ public class Util { public static List<DecoderEntry> getDecoders( List<Class<? extends Decoder>> decoderClazzes) - throws DeploymentException{ + throws DeploymentException { List<DecoderEntry> result = new ArrayList<>(); if (decoderClazzes != null) { diff --git a/src/java/nginx/unit/websocket/server/WsSessionListener.java b/src/java/nginx/unit/websocket/server/WsSessionListener.java index fc2bc9c5..2921bd45 100644 --- a/src/java/nginx/unit/websocket/server/WsSessionListener.java +++ b/src/java/nginx/unit/websocket/server/WsSessionListener.java @@ -19,7 +19,7 @@ package nginx.unit.websocket.server; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; -public class WsSessionListener implements HttpSessionListener{ +public class WsSessionListener implements HttpSessionListener { private final WsServerContainer wsServerContainer; diff --git a/src/nodejs/unit-http/http.js b/src/nodejs/unit-http/http.js index d298a35f..60b8004f 100644 --- a/src/nodejs/unit-http/http.js +++ b/src/nodejs/unit-http/http.js @@ -11,8 +11,8 @@ const { ServerResponse, } = require('./http_server'); -function createServer (requestHandler) { - return new Server(requestHandler); +function createServer (options, requestHandler) { + return new Server(options, requestHandler); } const http = require("http") diff --git a/src/nodejs/unit-http/http_server.js b/src/nodejs/unit-http/http_server.js index 0f00b47f..4e1c190e 100644 --- a/src/nodejs/unit-http/http_server.js +++ b/src/nodejs/unit-http/http_server.js @@ -138,6 +138,10 @@ ServerResponse.prototype.removeHeader = function removeHeader(name) { } }; +ServerResponse.prototype.flushHeaders = function flushHeaders() { + this._sendHeaders(); +}; + ServerResponse.prototype._removeHeader = function _removeHeader(lc_name) { let entry = this.headers[lc_name]; let name_len = Buffer.byteLength(entry[0] + "", 'latin1'); @@ -409,7 +413,14 @@ ServerRequest.prototype._read = function _read(n) { }; -function Server(requestListener) { +function Server(options, requestListener) { + if (typeof options === 'function') { + requestListener = options; + options = {}; + } else { + console.warn("http.Server constructor was called with unsupported options, using default settings"); + } + EventEmitter.call(this); this.unit = new unit_lib.Unit(); diff --git a/src/nodejs/unit-http/loader.js b/src/nodejs/unit-http/loader.js index e5aa3558..849df3d1 100644 --- a/src/nodejs/unit-http/loader.js +++ b/src/nodejs/unit-http/loader.js @@ -11,10 +11,12 @@ if (module.parent && module.parent.id === "internal/preload") { Module.prototype.require = function (id) { switch(id) { case "http": + case "node:http": case "unit-http": return http case "websocket": + case "node:websocket": case "unit-http/websocket": return websocket } diff --git a/src/nodejs/unit-http/loader.mjs b/src/nodejs/unit-http/loader.mjs index 83985b0f..01fa2920 100644 --- a/src/nodejs/unit-http/loader.mjs +++ b/src/nodejs/unit-http/loader.mjs @@ -2,6 +2,7 @@ export async function resolve(specifier, context, defaultResolver) { switch (specifier) { case "websocket": + case "node:websocket": return { url: new URL("./websocket.js", import.meta.url).href, format: "commonjs", @@ -9,6 +10,7 @@ export async function resolve(specifier, context, defaultResolver) { } case "http": + case "node:http": return { url: new URL("./http.js", import.meta.url).href, format: "commonjs", diff --git a/src/nodejs/unit-http/unit.cpp b/src/nodejs/unit-http/unit.cpp index 7912d0ac..7d9395bb 100644 --- a/src/nodejs/unit-http/unit.cpp +++ b/src/nodejs/unit-http/unit.cpp @@ -581,6 +581,7 @@ Unit::get_server_object() void Unit::create_headers(nxt_unit_request_info_t *req, napi_value request) { + char *p; uint32_t i; napi_value headers, raw_headers; napi_status status; @@ -602,7 +603,12 @@ Unit::create_headers(nxt_unit_request_info_t *req, napi_value request) set_named_property(request, "headers", headers); set_named_property(request, "rawHeaders", raw_headers); - set_named_property(request, "httpVersion", r->version, r->version_length); + + // trim the "HTTP/" protocol prefix + p = (char *) nxt_unit_sptr_get(&r->version); + p += 5; + + set_named_property(request, "httpVersion", create_string_latin1(p, 3)); set_named_property(request, "method", r->method, r->method_length); set_named_property(request, "url", r->target, r->target_length); diff --git a/src/nodejs/unit-http/websocket_connection.js b/src/nodejs/unit-http/websocket_connection.js index 4eccf6bf..c04075d7 100644 --- a/src/nodejs/unit-http/websocket_connection.js +++ b/src/nodejs/unit-http/websocket_connection.js @@ -36,11 +36,11 @@ var idCounter = 0; function WebSocketConnection(socket, extensions, protocol, maskOutgoingPackets, config) { this._debug = utils.BufferingLogger('websocket:connection', ++idCounter); this._debug('constructor'); - + if (this._debug.enabled) { instrumentSocketForDebugging(this, socket); } - + // Superclass Constructor EventEmitter.call(this); @@ -432,8 +432,8 @@ WebSocketConnection.prototype.processFrame = function(frame) { // logic to emit the ping frame: this is only done when a listener is known to exist // Expose a function allowing the user to override the default ping() behavior var cancelled = false; - var cancel = function() { - cancelled = true; + var cancel = function() { + cancelled = true; }; this.emit('ping', cancel, frame.binaryPayload); diff --git a/src/nodejs/unit-http/websocket_request.js b/src/nodejs/unit-http/websocket_request.js index d84e428b..450ab629 100644 --- a/src/nodejs/unit-http/websocket_request.js +++ b/src/nodejs/unit-http/websocket_request.js @@ -247,7 +247,7 @@ WebSocketRequest.prototype.parseCookies = function(str) { WebSocketRequest.prototype.accept = function(acceptedProtocol, allowedOrigin, cookies) { this._verifyResolution(); - + // TODO: Handle extensions var protocolFullCase; @@ -418,7 +418,7 @@ WebSocketRequest.prototype.accept = function(acceptedProtocol, allowedOrigin, co // if (negotiatedExtensions) { // response += 'Sec-WebSocket-Extensions: ' + negotiatedExtensions.join(', ') + '\r\n'; // } - + // Mark the request resolved now so that the user can't call accept or // reject a second time. this._resolved = true; @@ -447,12 +447,12 @@ WebSocketRequest.prototype.accept = function(acceptedProtocol, allowedOrigin, co WebSocketRequest.prototype.reject = function(status, reason, extraHeaders) { this._verifyResolution(); - + // Mark the request resolved now so that the user can't call accept or // reject a second time. this._resolved = true; this.emit('requestResolved', this); - + if (typeof(status) !== 'number') { status = 403; } diff --git a/src/nxt_application.c b/src/nxt_application.c index 872e387a..e0247bf0 100644 --- a/src/nxt_application.c +++ b/src/nxt_application.c @@ -1100,6 +1100,9 @@ nxt_app_parse_type(u_char *p, size_t length) } else if (nxt_str_eq(&str, "java", 4)) { return NXT_APP_JAVA; + } else if (nxt_str_eq(&str, "wasm-wasi-component", 19)) { + return NXT_APP_WASM_WC; + } else if (nxt_str_eq(&str, "wasm", 4)) { return NXT_APP_WASM; } diff --git a/src/nxt_application.h b/src/nxt_application.h index 64866db6..f5d7a9df 100644 --- a/src/nxt_application.h +++ b/src/nxt_application.h @@ -22,6 +22,7 @@ typedef enum { NXT_APP_RUBY, NXT_APP_JAVA, NXT_APP_WASM, + NXT_APP_WASM_WC, NXT_APP_UNKNOWN, } nxt_app_type_t; @@ -104,6 +105,13 @@ typedef struct { } nxt_wasm_app_conf_t; +typedef struct { + const char *component; + + nxt_conf_value_t *access; +} nxt_wasm_wc_app_conf_t; + + struct nxt_common_app_conf_s { nxt_str_t name; nxt_str_t type; @@ -133,6 +141,7 @@ struct nxt_common_app_conf_s { nxt_ruby_app_conf_t ruby; nxt_java_app_conf_t java; nxt_wasm_app_conf_t wasm; + nxt_wasm_wc_app_conf_t wasm_wc; } u; nxt_conf_value_t *self; diff --git a/src/nxt_atomic.h b/src/nxt_atomic.h index cd2e7253..dae999a9 100644 --- a/src/nxt_atomic.h +++ b/src/nxt_atomic.h @@ -58,6 +58,10 @@ typedef volatile nxt_atomic_uint_t nxt_atomic_t; #define nxt_cpu_pause() \ __asm__ ("pause") +#elif (__aarch64__ || __arm64__) +#define nxt_cpu_pause() \ + __asm__ ("isb") + #else #define nxt_cpu_pause() #endif diff --git a/src/nxt_clone.c b/src/nxt_clone.c index 1cd70f6c..e78a7822 100644 --- a/src/nxt_clone.c +++ b/src/nxt_clone.c @@ -143,7 +143,7 @@ nxt_clone_credential_map_set(nxt_task_t *task, const char* mapfile, pid_t pid, end = mapinfo + len; for (i = 0; i < map->size; i++) { - p = nxt_sprintf(p, end, "%d %d %d", map->map[i].container, + p = nxt_sprintf(p, end, "%L %L %L", map->map[i].container, map->map[i].host, map->map[i].size); if (nxt_slow_path(p == end)) { @@ -152,7 +152,7 @@ nxt_clone_credential_map_set(nxt_task_t *task, const char* mapfile, pid_t pid, return NXT_ERROR; } - if (i+1 < map->size) { + if (i + 1 < map->size) { *p++ = '\n'; } else { @@ -332,7 +332,7 @@ nxt_clone_vldt_credential_gidmap(nxt_task_t *task, if (nxt_slow_path((nxt_gid_t) m.host != nxt_egid)) { nxt_log(task, NXT_LOG_ERR, "\"gidmap\" field has an entry for " - "host gid %d but unprivileged unit can only map itself " + "host gid %L but unprivileged unit can only map itself " "(gid %d) into child namespaces.", m.host, nxt_egid); return NXT_ERROR; @@ -340,7 +340,7 @@ nxt_clone_vldt_credential_gidmap(nxt_task_t *task, if (nxt_slow_path(m.size > 1)) { nxt_log(task, NXT_LOG_ERR, "\"gidmap\" field has an entry with " - "\"size\": %d, but for unprivileged unit it must be 1.", + "\"size\": %L, but for unprivileged unit it must be 1.", m.size); return NXT_ERROR; @@ -382,13 +382,13 @@ nxt_clone_vldt_credential_gidmap(nxt_task_t *task, m = map->map[j]; if (!base_ok && creds->base_gid >= (nxt_gid_t) m.container - && creds->base_gid < (nxt_gid_t) (m.container+m.size)) + && creds->base_gid < (nxt_gid_t) (m.container + m.size)) { base_ok = 1; } if (creds->gids[i] >= (nxt_gid_t) m.container - && creds->gids[i] < (nxt_gid_t) (m.container+m.size)) + && creds->gids[i] < (nxt_gid_t) (m.container + m.size)) { gid_ok = 1; break; @@ -405,7 +405,7 @@ nxt_clone_vldt_credential_gidmap(nxt_task_t *task, m = map->map[i]; if (creds->base_gid >= (nxt_gid_t) m.container - && creds->base_gid < (nxt_gid_t) (m.container+m.size)) + && creds->base_gid < (nxt_gid_t) (m.container + m.size)) { base_ok = 1; break; diff --git a/src/nxt_clone.h b/src/nxt_clone.h index 6cea1bd7..bf28322f 100644 --- a/src/nxt_clone.h +++ b/src/nxt_clone.h @@ -9,10 +9,12 @@ #if (NXT_HAVE_CLONE_NEWUSER) +typedef int64_t nxt_cred_t; + typedef struct { - nxt_int_t container; - nxt_int_t host; - nxt_int_t size; + nxt_cred_t container; + nxt_cred_t host; + nxt_cred_t size; } nxt_clone_map_entry_t; typedef struct { diff --git a/src/nxt_conf.c b/src/nxt_conf.c index 664b5468..008cb968 100644 --- a/src/nxt_conf.c +++ b/src/nxt_conf.c @@ -3,6 +3,7 @@ * Copyright (C) Igor Sysoev * Copyright (C) Valentin V. Bartenev * Copyright (C) NGINX, Inc. + * Copyright 2024, Alejandro Colomar <alx@kernel.org> */ #include <nxt_main.h> @@ -174,6 +175,16 @@ nxt_conf_get_string(nxt_conf_value_t *value, nxt_str_t *str) } +nxt_str_t * +nxt_conf_get_string_dup(nxt_conf_value_t *value, nxt_mp_t *mp, nxt_str_t *str) +{ + nxt_str_t s; + + nxt_conf_get_string(value, &s); + return nxt_str_dup(mp, str, &s); +} + + void nxt_conf_set_string(nxt_conf_value_t *value, nxt_str_t *str) { diff --git a/src/nxt_conf.h b/src/nxt_conf.h index 1b13f5ae..626b6d4d 100644 --- a/src/nxt_conf.h +++ b/src/nxt_conf.h @@ -3,6 +3,7 @@ * Copyright (C) Igor Sysoev * Copyright (C) Valentin V. Bartenev * Copyright (C) NGINX, Inc. + * Copyright 2024, Alejandro Colomar <alx@kernel.org> */ #ifndef _NXT_CONF_INCLUDED_ @@ -116,6 +117,8 @@ void nxt_conf_json_position(u_char *start, const u_char *pos, nxt_uint_t *line, nxt_int_t nxt_conf_validate(nxt_conf_validation_t *vldt); NXT_EXPORT void nxt_conf_get_string(nxt_conf_value_t *value, nxt_str_t *str); +NXT_EXPORT nxt_str_t *nxt_conf_get_string_dup(nxt_conf_value_t *value, + nxt_mp_t *mp, nxt_str_t *str); NXT_EXPORT void nxt_conf_set_string(nxt_conf_value_t *value, nxt_str_t *str); NXT_EXPORT nxt_int_t nxt_conf_set_string_dup(nxt_conf_value_t *value, nxt_mp_t *mp, const nxt_str_t *str); diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index f00b28b8..2099f887 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -2,6 +2,7 @@ /* * Copyright (C) Valentin V. Bartenev * Copyright (C) NGINX, Inc. + * Copyright 2024, Alejandro Colomar <alx@kernel.org> */ #include <nxt_main.h> @@ -77,6 +78,8 @@ static nxt_int_t nxt_conf_vldt_error(nxt_conf_validation_t *vldt, const char *fmt, ...); static nxt_int_t nxt_conf_vldt_var(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_str_t *value); +static nxt_int_t nxt_conf_vldt_if(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); nxt_inline nxt_int_t nxt_conf_vldt_unsupported(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) NXT_MAYBE_UNUSED; @@ -216,8 +219,6 @@ static nxt_int_t nxt_conf_vldt_clone_namespaces(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); #if (NXT_HAVE_CLONE_NEWUSER) -static nxt_int_t nxt_conf_vldt_clone_procmap(nxt_conf_validation_t *vldt, - const char* mapfile, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_clone_uidmap(nxt_conf_validation_t *vldt, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_clone_gidmap(nxt_conf_validation_t *vldt, @@ -690,6 +691,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_action_common_members[] = { { .name = nxt_string("rewrite"), .type = NXT_CONF_VLDT_STRING, + .flags = NXT_CONF_VLDT_TSTR, }, { .name = nxt_string("response_headers"), @@ -1093,6 +1095,22 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_wasm_members[] = { }; +static nxt_conf_vldt_object_t nxt_conf_vldt_wasm_wc_members[] = { + { + .name = nxt_string("component"), + .type = NXT_CONF_VLDT_STRING, + .flags = NXT_CONF_VLDT_REQUIRED, + }, { + .name = nxt_string("access"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = nxt_conf_vldt_object, + .u.members = nxt_conf_vldt_wasm_access_members, + }, + + NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members) +}; + + static nxt_conf_vldt_object_t nxt_conf_vldt_wasm_access_members[] = { { .name = nxt_string("filesystem"), @@ -1324,12 +1342,15 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_procmap_members[] = { { .name = nxt_string("container"), .type = NXT_CONF_VLDT_INTEGER, + .flags = NXT_CONF_VLDT_REQUIRED, }, { .name = nxt_string("host"), .type = NXT_CONF_VLDT_INTEGER, + .flags = NXT_CONF_VLDT_REQUIRED, }, { .name = nxt_string("size"), .type = NXT_CONF_VLDT_INTEGER, + .flags = NXT_CONF_VLDT_REQUIRED, }, NXT_CONF_VLDT_END @@ -1368,6 +1389,10 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_access_log_members[] = { }, { .name = nxt_string("format"), .type = NXT_CONF_VLDT_STRING, + }, { + .name = nxt_string("if"), + .type = NXT_CONF_VLDT_STRING, + .validator = nxt_conf_vldt_if, }, NXT_CONF_VLDT_END @@ -1537,6 +1562,37 @@ nxt_conf_vldt_var(nxt_conf_validation_t *vldt, nxt_str_t *name, } +static nxt_int_t +nxt_conf_vldt_if(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, + void *data) +{ + nxt_str_t str; + + static nxt_str_t if_str = nxt_string("if"); + + if (nxt_conf_type(value) != NXT_CONF_STRING) { + return nxt_conf_vldt_error(vldt, "The \"if\" must be a string"); + } + + nxt_conf_get_string(value, &str); + + if (str.length == 0) { + return NXT_OK; + } + + if (str.start[0] == '!') { + str.start++; + str.length--; + } + + if (nxt_is_tstr(&str)) { + return nxt_conf_vldt_var(vldt, &if_str, &str); + } + + return NXT_OK; +} + + typedef struct { nxt_mp_t *pool; nxt_str_t *type; @@ -1897,10 +1953,13 @@ static nxt_int_t nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) { - nxt_str_t name; + nxt_str_t name, *ret; nxt_sockaddr_t *sa; - nxt_conf_get_string(value, &name); + ret = nxt_conf_get_string_dup(value, vldt->pool, &name); + if (nxt_slow_path(ret == NULL)) { + return NXT_ERROR; + } if (nxt_str_start(&name, "http://", 7)) { name.length -= 7; @@ -2617,6 +2676,7 @@ nxt_conf_vldt_app(nxt_conf_validation_t *vldt, nxt_str_t *name, { nxt_conf_vldt_object, nxt_conf_vldt_ruby_members }, { nxt_conf_vldt_object, nxt_conf_vldt_java_members }, { nxt_conf_vldt_object, nxt_conf_vldt_wasm_members }, + { nxt_conf_vldt_object, nxt_conf_vldt_wasm_wc_members }, }; ret = nxt_conf_vldt_type(vldt, name, value, NXT_CONF_VLDT_OBJECT); @@ -2787,7 +2847,7 @@ nxt_conf_vldt_processes(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, nxt_int_t ret; nxt_conf_vldt_processes_conf_t proc; - if (nxt_conf_type(value) == NXT_CONF_NUMBER) { + if (nxt_conf_type(value) == NXT_CONF_INTEGER) { int_value = nxt_conf_get_number(value); if (int_value < 1) { @@ -2874,13 +2934,11 @@ nxt_conf_vldt_object_iterator(nxt_conf_validation_t *vldt, for ( ;; ) { member = nxt_conf_next_object_member(value, &name, &index); - if (member == NULL) { return NXT_OK; } ret = validator(vldt, &name, member); - if (ret != NXT_OK) { return ret; } @@ -3050,73 +3108,6 @@ nxt_conf_vldt_isolation(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, #if (NXT_HAVE_CLONE_NEWUSER) -typedef struct { - nxt_int_t container; - nxt_int_t host; - nxt_int_t size; -} nxt_conf_vldt_clone_procmap_conf_t; - - -static nxt_conf_map_t nxt_conf_vldt_clone_procmap_conf_map[] = { - { - nxt_string("container"), - NXT_CONF_MAP_INT32, - offsetof(nxt_conf_vldt_clone_procmap_conf_t, container), - }, - - { - nxt_string("host"), - NXT_CONF_MAP_INT32, - offsetof(nxt_conf_vldt_clone_procmap_conf_t, host), - }, - - { - nxt_string("size"), - NXT_CONF_MAP_INT32, - offsetof(nxt_conf_vldt_clone_procmap_conf_t, size), - }, - -}; - - -static nxt_int_t -nxt_conf_vldt_clone_procmap(nxt_conf_validation_t *vldt, const char *mapfile, - nxt_conf_value_t *value) -{ - nxt_int_t ret; - nxt_conf_vldt_clone_procmap_conf_t procmap; - - procmap.container = -1; - procmap.host = -1; - procmap.size = -1; - - ret = nxt_conf_map_object(vldt->pool, value, - nxt_conf_vldt_clone_procmap_conf_map, - nxt_nitems(nxt_conf_vldt_clone_procmap_conf_map), - &procmap); - if (ret != NXT_OK) { - return ret; - } - - if (procmap.container == -1) { - return nxt_conf_vldt_error(vldt, "The %s requires the " - "\"container\" field set.", mapfile); - } - - if (procmap.host == -1) { - return nxt_conf_vldt_error(vldt, "The %s requires the " - "\"host\" field set.", mapfile); - } - - if (procmap.size == -1) { - return nxt_conf_vldt_error(vldt, "The %s requires the " - "\"size\" field set.", mapfile); - } - - return NXT_OK; -} - - static nxt_int_t nxt_conf_vldt_clone_uidmap(nxt_conf_validation_t *vldt, nxt_conf_value_t *value) { @@ -3133,7 +3124,7 @@ nxt_conf_vldt_clone_uidmap(nxt_conf_validation_t *vldt, nxt_conf_value_t *value) return ret; } - return nxt_conf_vldt_clone_procmap(vldt, "uid_map", value); + return NXT_OK; } @@ -3153,7 +3144,7 @@ nxt_conf_vldt_clone_gidmap(nxt_conf_validation_t *vldt, nxt_conf_value_t *value) return ret; } - return nxt_conf_vldt_clone_procmap(vldt, "gid_map", value); + return NXT_OK; } #endif @@ -3296,16 +3287,19 @@ nxt_conf_vldt_server(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value) { nxt_int_t ret; + nxt_str_t str; nxt_sockaddr_t *sa; ret = nxt_conf_vldt_type(vldt, name, value, NXT_CONF_VLDT_OBJECT); - if (ret != NXT_OK) { return ret; } - sa = nxt_sockaddr_parse(vldt->pool, name); + if (nxt_slow_path(nxt_str_dup(vldt->pool, &str, name) == NULL)) { + return NXT_ERROR; + } + sa = nxt_sockaddr_parse(vldt->pool, &str); if (sa == NULL) { return nxt_conf_vldt_error(vldt, "The \"%V\" is not valid " "server address.", name); diff --git a/src/nxt_conn_write.c b/src/nxt_conn_write.c index 714a3e15..7d0a579f 100644 --- a/src/nxt_conn_write.c +++ b/src/nxt_conn_write.c @@ -172,6 +172,13 @@ nxt_conn_io_sendbuf(nxt_task_t *task, nxt_sendbuf_t *sb) return 0; } + /* + * XXX Temporary fix for <https://github.com/nginx/unit/issues/1125> + */ + if (niov == 0 && sb->buf == NULL) { + return 0; + } + if (niov == 0 && nxt_buf_is_file(sb->buf)) { return nxt_conn_io_sendfile(task, sb); } diff --git a/src/nxt_credential.c b/src/nxt_credential.c index bda97024..1c9fa9a7 100644 --- a/src/nxt_credential.c +++ b/src/nxt_credential.c @@ -74,8 +74,11 @@ nxt_credential_get(nxt_task_t *task, nxt_mp_t *mp, nxt_credential_t *uc, end = msg + NXT_MAX_ERROR_STR; for (i = 0; i < uc->ngroups; i++) { - p = nxt_sprintf(p, end, "%d%c", uc->gids[i], - i+1 < uc->ngroups ? ',' : '\0'); + p = nxt_sprintf(p, end, "%d,", uc->gids[i]); + } + + if (uc->ngroups > 0) { + p--; } nxt_debug(task, "user \"%s\" has gids:%*s", uc->user, p - msg, msg); diff --git a/src/nxt_file.c b/src/nxt_file.c index 6f1a93e4..4047d9d7 100644 --- a/src/nxt_file.c +++ b/src/nxt_file.c @@ -326,6 +326,88 @@ nxt_file_set_access(nxt_file_name_t *name, nxt_file_access_t access) nxt_int_t +nxt_file_chown(nxt_file_name_t *name, const char *owner, const char *group) +{ + int err; + char *buf; + long bufsize; + gid_t gid = ~0; + uid_t uid = ~0; + + if (owner == NULL && group == NULL) { + return NXT_OK; + } + + if (owner != NULL) { + struct passwd pwd, *result; + + bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); + if (bufsize == -1) { + bufsize = 32768; + } + + buf = nxt_malloc(bufsize); + if (buf == NULL) { + return NXT_ERROR; + } + + err = getpwnam_r(owner, &pwd, buf, bufsize, &result); + if (result == NULL) { + nxt_thread_log_alert("getpwnam_r(\"%s\", ...) failed %E %s", + owner, nxt_errno, + err == 0 ? "(User not found)" : ""); + goto out_err_free; + } + + uid = pwd.pw_uid; + + nxt_free(buf); + } + + if (group != NULL) { + struct group grp, *result; + + bufsize = sysconf(_SC_GETGR_R_SIZE_MAX); + if (bufsize == -1) { + bufsize = 32768; + } + + buf = nxt_malloc(bufsize); + if (buf == NULL) { + return NXT_ERROR; + } + + err = getgrnam_r(group, &grp, buf, bufsize, &result); + if (result == NULL) { + nxt_thread_log_alert("getgrnam_r(\"%s\", ...) failed %E %s", + group, nxt_errno, + err == 0 ? "(Group not found)" : ""); + goto out_err_free; + } + + gid = grp.gr_gid; + + nxt_free(buf); + } + + if (nxt_fast_path(chown((const char *) name, uid, gid) == 0)) { + return NXT_OK; + } + + nxt_thread_log_alert("chown(\"%FN\", %l, %l) failed %E", name, + owner != NULL ? (long) uid : -1, + group != NULL ? (long) gid : -1, nxt_errno); + + return NXT_ERROR; + +out_err_free: + nxt_free(buf); + + return NXT_ERROR; +} + + +nxt_int_t nxt_file_rename(nxt_file_name_t *old_name, nxt_file_name_t *new_name) { int ret; diff --git a/src/nxt_file.h b/src/nxt_file.h index 97636db6..0c03a5f9 100644 --- a/src/nxt_file.h +++ b/src/nxt_file.h @@ -177,6 +177,8 @@ NXT_EXPORT nxt_int_t nxt_file_info(nxt_file_t *file, nxt_file_info_t *fi); NXT_EXPORT nxt_int_t nxt_file_delete(nxt_file_name_t *name); NXT_EXPORT nxt_int_t nxt_file_set_access(nxt_file_name_t *name, nxt_file_access_t access); +NXT_EXPORT nxt_int_t nxt_file_chown(nxt_file_name_t *name, const char *owner, + const char *group); NXT_EXPORT nxt_int_t nxt_file_rename(nxt_file_name_t *old_name, nxt_file_name_t *new_name); diff --git a/src/nxt_http_js.c b/src/nxt_http_js.c index 72ba761f..e3beb8b4 100644 --- a/src/nxt_http_js.c +++ b/src/nxt_http_js.c @@ -28,6 +28,8 @@ static njs_int_t nxt_http_js_ext_get_cookie(njs_vm_t *vm, njs_value_t *retval); static njs_int_t nxt_http_js_ext_keys_cookie(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys); +static njs_int_t nxt_http_js_ext_get_var(njs_vm_t *vm, njs_object_prop_t *prop, + njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_external_t nxt_http_js_proto[] = { @@ -88,6 +90,14 @@ static njs_external_t nxt_http_js_proto[] = { .keys = nxt_http_js_ext_keys_cookie, } }, + + { + .flags = NJS_EXTERN_OBJECT, + .name.string = njs_str("vars"), + .u.object = { + .prop_handler = nxt_http_js_ext_get_var, + } + }, }; @@ -338,3 +348,42 @@ nxt_http_js_ext_keys_cookie(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys) return NJS_OK; } + + +static njs_int_t +nxt_http_js_ext_get_var(njs_vm_t *vm, njs_object_prop_t *prop, + njs_value_t *value, njs_value_t *setval, njs_value_t *retval) +{ + njs_int_t rc; + njs_str_t key; + nxt_str_t name, *vv; + nxt_router_conf_t *rtcf; + nxt_http_request_t *r; + + r = njs_vm_external(vm, nxt_js_proto_id, value); + if (r == NULL) { + njs_value_undefined_set(retval); + return NJS_DECLINED; + } + + rc = njs_vm_prop_name(vm, prop, &key); + if (rc != NJS_OK) { + njs_value_undefined_set(retval); + return NJS_DECLINED; + } + + rtcf = r->conf->socket_conf->router_conf; + + name.start = key.start; + name.length = key.length; + + vv = nxt_var_get(&r->task, rtcf->tstr_state, &r->tstr_cache.var, &name, r); + + if (vv != NULL) { + return njs_vm_value_string_set(vm, retval, vv->start, vv->length); + } + + njs_value_undefined_set(retval); + + return NJS_DECLINED; +} diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c index e532baff..f8d8d887 100644 --- a/src/nxt_http_request.c +++ b/src/nxt_http_request.c @@ -24,6 +24,8 @@ static void nxt_http_request_proto_info(nxt_task_t *task, static void nxt_http_request_mem_buf_completion(nxt_task_t *task, void *obj, void *data); static void nxt_http_request_done(nxt_task_t *task, void *obj, void *data); +static nxt_int_t nxt_http_request_access_log(nxt_task_t *task, + nxt_http_request_t *r, nxt_router_conf_t *rtcf); static u_char *nxt_http_date_cache_handler(u_char *buf, nxt_realtime_t *now, struct tm *tm, size_t size, const char *format); @@ -816,27 +818,27 @@ nxt_http_request_error_handler(nxt_task_t *task, void *obj, void *data) void nxt_http_request_close_handler(nxt_task_t *task, void *obj, void *data) { - nxt_tstr_t *log_format; + nxt_int_t ret; nxt_http_proto_t proto; + nxt_router_conf_t *rtcf; nxt_http_request_t *r; nxt_http_protocol_t protocol; nxt_socket_conf_joint_t *conf; - nxt_router_access_log_t *access_log; r = obj; proto.any = data; conf = r->conf; + rtcf = conf->socket_conf->router_conf; if (!r->logged) { r->logged = 1; - access_log = conf->socket_conf->router_conf->access_log; - log_format = conf->socket_conf->router_conf->log_format; - - if (access_log != NULL) { - access_log->handler(task, r, access_log, log_format); - return; + if (rtcf->access_log != NULL) { + ret = nxt_http_request_access_log(task, r, rtcf); + if (ret == NXT_OK) { + return; + } } } @@ -866,6 +868,57 @@ nxt_http_request_close_handler(nxt_task_t *task, void *obj, void *data) } +static nxt_int_t +nxt_http_request_access_log(nxt_task_t *task, nxt_http_request_t *r, + nxt_router_conf_t *rtcf) +{ + nxt_int_t ret; + nxt_str_t str; + nxt_bool_t expr; + nxt_router_access_log_t *access_log; + + access_log = rtcf->access_log; + + expr = 1; + + if (rtcf->log_expr != NULL) { + + if (nxt_tstr_is_const(rtcf->log_expr)) { + nxt_tstr_str(rtcf->log_expr, &str); + + } else { + ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state, + &r->tstr_cache, r, r->mem_pool); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_DECLINED; + } + + nxt_tstr_query(task, r->tstr_query, rtcf->log_expr, &str); + + if (nxt_slow_path(nxt_tstr_query_failed(r->tstr_query))) { + return NXT_DECLINED; + } + } + + if (str.length == 0 + || nxt_str_eq(&str, "0", 1) + || nxt_str_eq(&str, "false", 5) + || nxt_str_eq(&str, "null", 4) + || nxt_str_eq(&str, "undefined", 9)) + { + expr = 0; + } + } + + if (rtcf->log_negate ^ expr) { + access_log->handler(task, r, access_log, rtcf->log_format); + return NXT_OK; + } + + return NXT_DECLINED; +} + + static u_char * nxt_http_date_cache_handler(u_char *buf, nxt_realtime_t *now, struct tm *tm, size_t size, const char *format) diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c index e51ba6b0..ee25015e 100644 --- a/src/nxt_http_static.c +++ b/src/nxt_http_static.c @@ -117,9 +117,7 @@ nxt_http_static_init(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_str_set(&conf->index, "index.html"); } else { - nxt_conf_get_string(acf->index, &str); - - ret = nxt_str_dup(mp, &conf->index, &str); + ret = nxt_conf_get_string_dup(acf->index, mp, &conf->index); if (nxt_slow_path(ret == NULL)) { return NXT_ERROR; } @@ -879,12 +877,7 @@ complete_buf: n = nxt_file_read(fb->file, b->mem.start, size, fb->file_pos); - if (n != size) { - if (n >= 0) { - nxt_log(task, NXT_LOG_ERR, "file \"%FN\" has changed " - "while sending response to a client", fb->file->name); - } - + if (nxt_slow_path(n == NXT_ERROR)) { nxt_http_request_error_handler(task, r, r->proto.any); goto clean; } diff --git a/src/nxt_http_variables.c b/src/nxt_http_variables.c index 46594a6b..85ae6004 100644 --- a/src/nxt_http_variables.c +++ b/src/nxt_http_variables.c @@ -28,6 +28,8 @@ static u_char *nxt_http_log_date(u_char *buf, nxt_realtime_t *now, struct tm *tm, size_t size, const char *format); static nxt_int_t nxt_http_var_request_line(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data); +static nxt_int_t nxt_http_var_request_id(nxt_task_t *task, nxt_str_t *str, + void *ctx, void *data); static nxt_int_t nxt_http_var_status(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data); static nxt_int_t nxt_http_var_body_bytes_sent(nxt_task_t *task, nxt_str_t *str, @@ -90,6 +92,10 @@ static nxt_var_decl_t nxt_http_vars[] = { .handler = nxt_http_var_request_line, .cacheable = 1, }, { + .name = nxt_string("request_id"), + .handler = nxt_http_var_request_id, + .cacheable = 1, + }, { .name = nxt_string("status"), .handler = nxt_http_var_status, .cacheable = 1, @@ -129,8 +135,7 @@ nxt_http_register_variables(void) nxt_int_t -nxt_http_unknown_var_ref(nxt_tstr_state_t *state, nxt_var_ref_t *ref, - nxt_str_t *name) +nxt_http_unknown_var_ref(nxt_mp_t *mp, nxt_var_ref_t *ref, nxt_str_t *name) { int64_t hash; nxt_str_t str, *lower; @@ -146,7 +151,7 @@ nxt_http_unknown_var_ref(nxt_tstr_state_t *state, nxt_var_ref_t *ref, return NXT_ERROR; } - lower = nxt_str_alloc(state->pool, str.length); + lower = nxt_str_alloc(mp, str.length); if (nxt_slow_path(lower == NULL)) { return NXT_ERROR; } @@ -169,7 +174,7 @@ nxt_http_unknown_var_ref(nxt_tstr_state_t *state, nxt_var_ref_t *ref, return NXT_ERROR; } - hash = nxt_http_header_hash(state->pool, &str); + hash = nxt_http_header_hash(mp, &str); if (nxt_slow_path(hash == -1)) { return NXT_ERROR; } @@ -185,7 +190,7 @@ nxt_http_unknown_var_ref(nxt_tstr_state_t *state, nxt_var_ref_t *ref, return NXT_ERROR; } - hash = nxt_http_argument_hash(state->pool, &str); + hash = nxt_http_argument_hash(mp, &str); if (nxt_slow_path(hash == -1)) { return NXT_ERROR; } @@ -201,7 +206,7 @@ nxt_http_unknown_var_ref(nxt_tstr_state_t *state, nxt_var_ref_t *ref, return NXT_ERROR; } - hash = nxt_http_cookie_hash(state->pool, &str); + hash = nxt_http_cookie_hash(mp, &str); if (nxt_slow_path(hash == -1)) { return NXT_ERROR; } @@ -210,7 +215,7 @@ nxt_http_unknown_var_ref(nxt_tstr_state_t *state, nxt_var_ref_t *ref, return NXT_ERROR; } - ref->data = nxt_var_field_new(state->pool, &str, (uint32_t) hash); + ref->data = nxt_var_field_new(mp, &str, (uint32_t) hash); if (nxt_slow_path(ref->data == NULL)) { return NXT_ERROR; } @@ -396,6 +401,32 @@ nxt_http_var_request_line(nxt_task_t *task, nxt_str_t *str, void *ctx, static nxt_int_t +nxt_http_var_request_id(nxt_task_t *task, nxt_str_t *str, void *ctx, + void *data) +{ + nxt_random_t *rand; + nxt_http_request_t *r; + + r = ctx; + + str->start = nxt_mp_nget(r->mem_pool, 32); + if (nxt_slow_path(str->start == NULL)) { + return NXT_ERROR; + } + + str->length = 32; + + rand = &task->thread->random; + + (void) nxt_sprintf(str->start, str->start + 32, "%08xD%08xD%08xD%08xD", + nxt_random(rand), nxt_random(rand), + nxt_random(rand), nxt_random(rand)); + + return NXT_OK; +} + + +static nxt_int_t nxt_http_var_body_bytes_sent(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data) { @@ -423,7 +454,6 @@ nxt_http_var_body_bytes_sent(nxt_task_t *task, nxt_str_t *str, void *ctx, static nxt_int_t nxt_http_var_status(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data) { - u_char *p; nxt_http_request_t *r; r = ctx; @@ -433,9 +463,9 @@ nxt_http_var_status(nxt_task_t *task, nxt_str_t *str, void *ctx, void *data) return NXT_ERROR; } - p = nxt_sprintf(str->start, str->start + 3, "%03d", r->status); + (void) nxt_sprintf(str->start, str->start + 3, "%03d", r->status); - str->length = p - str->start; + str->length = 3; return NXT_OK; } diff --git a/src/nxt_isolation.c b/src/nxt_isolation.c index cfa494a8..ed5e0d76 100644 --- a/src/nxt_isolation.c +++ b/src/nxt_isolation.c @@ -326,19 +326,19 @@ nxt_isolation_credential_map(nxt_task_t *task, nxt_mp_t *mp, static nxt_conf_map_t nxt_clone_map_entry_conf[] = { { nxt_string("container"), - NXT_CONF_MAP_INT, + NXT_CONF_MAP_INT64, offsetof(nxt_clone_map_entry_t, container), }, { nxt_string("host"), - NXT_CONF_MAP_INT, + NXT_CONF_MAP_INT64, offsetof(nxt_clone_map_entry_t, host), }, { nxt_string("size"), - NXT_CONF_MAP_INT, + NXT_CONF_MAP_INT64, offsetof(nxt_clone_map_entry_t, size), }, }; diff --git a/src/nxt_js.c b/src/nxt_js.c index 74663660..6885afb7 100644 --- a/src/nxt_js.c +++ b/src/nxt_js.c @@ -240,7 +240,7 @@ nxt_js_add_tpl(nxt_js_conf_t *jcf, nxt_str_t *str, nxt_bool_t strz) nxt_str_t *func; static nxt_str_t func_str = nxt_string("function(uri, host, remoteAddr, " - "args, headers, cookies) {" + "args, headers, cookies, vars) {" " return "); /* @@ -388,16 +388,20 @@ nxt_js_call(nxt_task_t *task, nxt_js_conf_t *jcf, nxt_js_cache_t *cache, njs_vm_t *vm; njs_int_t ret; njs_str_t res; + njs_uint_t i, n; njs_value_t *value; njs_function_t *func; - njs_opaque_value_t retval, opaque_value, arguments[6]; - - static const njs_str_t uri_str = njs_str("uri"); - static const njs_str_t host_str = njs_str("host"); - static const njs_str_t remote_addr_str = njs_str("remoteAddr"); - static const njs_str_t args_str = njs_str("args"); - static const njs_str_t headers_str = njs_str("headers"); - static const njs_str_t cookies_str = njs_str("cookies"); + njs_opaque_value_t retval, opaque_value, arguments[7]; + + static const njs_str_t js_args[] = { + njs_str("uri"), + njs_str("host"), + njs_str("remoteAddr"), + njs_str("args"), + njs_str("headers"), + njs_str("cookies"), + njs_str("vars"), + }; vm = cache->vm; @@ -424,43 +428,17 @@ nxt_js_call(nxt_task_t *task, nxt_js_conf_t *jcf, nxt_js_cache_t *cache, return NXT_ERROR; } - value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &uri_str, - &arguments[0]); - if (nxt_slow_path(value == NULL)) { - return NXT_ERROR; - } - - value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &host_str, - &arguments[1]); - if (nxt_slow_path(value == NULL)) { - return NXT_ERROR; - } - - value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), - &remote_addr_str, &arguments[2]); - if (nxt_slow_path(value == NULL)) { - return NXT_ERROR; - } + n = nxt_nitems(js_args); - value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &args_str, - &arguments[3]); - if (nxt_slow_path(value == NULL)) { - return NXT_ERROR; - } - - value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &headers_str, - &arguments[4]); - if (nxt_slow_path(value == NULL)) { - return NXT_ERROR; - } - - value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), &cookies_str, - &arguments[5]); - if (nxt_slow_path(value == NULL)) { - return NXT_ERROR; + for (i = 0; i < n; i++) { + value = njs_vm_object_prop(vm, njs_value_arg(&opaque_value), + &js_args[i], &arguments[i]); + if (nxt_slow_path(value == NULL)) { + return NXT_ERROR; + } } - ret = njs_vm_invoke(vm, func, njs_value_arg(&arguments), 6, + ret = njs_vm_invoke(vm, func, njs_value_arg(&arguments), n, njs_value_arg(&retval)); if (ret != NJS_OK) { diff --git a/src/nxt_listen_socket.c b/src/nxt_listen_socket.c index d477eef1..047c1ef9 100644 --- a/src/nxt_listen_socket.c +++ b/src/nxt_listen_socket.c @@ -127,13 +127,23 @@ nxt_listen_socket_create(nxt_task_t *task, nxt_mp_t *mp, #if (NXT_HAVE_UNIX_DOMAIN) if (family == AF_UNIX) { - name = (nxt_file_name_t *) sa->u.sockaddr_un.sun_path; + const char *user; + const char *group; + nxt_runtime_t *rt = thr->runtime; - access = (S_IRUSR | S_IWUSR); + name = (nxt_file_name_t *) sa->u.sockaddr_un.sun_path; + access = rt->control_mode > 0 ? rt->control_mode : S_IRUSR | S_IWUSR; if (nxt_file_set_access(name, access) != NXT_OK) { goto listen_fail; } + + user = rt->control_user; + group = rt->control_group; + + if (nxt_file_chown(name, user, group) != NXT_OK) { + goto listen_fail; + } } #endif diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index 3f317d5e..060ead41 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -377,6 +377,20 @@ static nxt_conf_map_t nxt_wasm_app_conf[] = { }; +static nxt_conf_map_t nxt_wasm_wc_app_conf[] = { + { + nxt_string("component"), + NXT_CONF_MAP_CSTRZ, + offsetof(nxt_common_app_conf_t, u.wasm_wc.component), + }, + { + nxt_string("access"), + NXT_CONF_MAP_PTR, + offsetof(nxt_common_app_conf_t, u.wasm_wc.access), + }, +}; + + static nxt_conf_app_map_t nxt_app_maps[] = { { nxt_nitems(nxt_external_app_conf), nxt_external_app_conf }, { nxt_nitems(nxt_python_app_conf), nxt_python_app_conf }, @@ -385,6 +399,7 @@ static nxt_conf_app_map_t nxt_app_maps[] = { { nxt_nitems(nxt_ruby_app_conf), nxt_ruby_app_conf }, { nxt_nitems(nxt_java_app_conf), nxt_java_app_conf }, { nxt_nitems(nxt_wasm_app_conf), nxt_wasm_app_conf }, + { nxt_nitems(nxt_wasm_wc_app_conf), nxt_wasm_wc_app_conf }, }; diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c index f56135f3..8f66f45b 100644 --- a/src/nxt_openssl.c +++ b/src/nxt_openssl.c @@ -73,7 +73,7 @@ static nxt_int_t nxt_ssl_conf_commands(nxt_task_t *task, SSL_CTX *ctx, static nxt_int_t nxt_tls_ticket_keys(nxt_task_t *task, SSL_CTX *ctx, nxt_tls_init_t *tls_init, nxt_mp_t *mp); static int nxt_tls_ticket_key_callback(SSL *s, unsigned char *name, - unsigned char *iv, EVP_CIPHER_CTX *ectx,HMAC_CTX *hctx, int enc); + unsigned char *iv, EVP_CIPHER_CTX *ectx, HMAC_CTX *hctx, int enc); #endif static void nxt_ssl_session_cache(SSL_CTX *ctx, size_t cache_size, time_t timeout); diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index ba000fc0..77117533 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -1225,6 +1225,8 @@ nxt_php_execute(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) nxt_unit_req_debug(ctx->req, "php_request_startup() failed"); nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR); + fclose(fp); + return; } diff --git a/src/nxt_router.c b/src/nxt_router.c index 4e3cb303..1a1aca2b 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -281,6 +281,7 @@ static const nxt_str_t *nxt_app_msg_prefix[] = { [NXT_APP_RUBY] = &http_prefix, [NXT_APP_JAVA] = &empty_prefix, [NXT_APP_WASM] = &empty_prefix, + [NXT_APP_WASM_WC] = &empty_prefix, }; @@ -2275,7 +2276,7 @@ nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf, { uint32_t next, i; nxt_mp_t *mp; - nxt_str_t *type, exten, str; + nxt_str_t *type, exten, str, *s; nxt_int_t ret; nxt_uint_t exts; nxt_conf_value_t *mtypes_conf, *ext_conf, *value; @@ -2311,9 +2312,8 @@ nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf, } if (nxt_conf_type(ext_conf) == NXT_CONF_STRING) { - nxt_conf_get_string(ext_conf, &str); - - if (nxt_slow_path(nxt_str_dup(mp, &exten, &str) == NULL)) { + s = nxt_conf_get_string_dup(ext_conf, mp, &exten); + if (nxt_slow_path(s == NULL)) { return NXT_ERROR; } @@ -2331,9 +2331,8 @@ nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf, for (i = 0; i < exts; i++) { value = nxt_conf_get_array_element(ext_conf, i); - nxt_conf_get_string(value, &str); - - if (nxt_slow_path(nxt_str_dup(mp, &exten, &str) == NULL)) { + s = nxt_conf_get_string_dup(value, mp, &exten); + if (nxt_slow_path(s == NULL)) { return NXT_ERROR; } @@ -2425,14 +2424,11 @@ static nxt_int_t nxt_router_conf_forward_header(nxt_mp_t *mp, nxt_conf_value_t *conf, nxt_http_forward_header_t *fh) { - char c; - size_t i; - uint32_t hash; - nxt_str_t header; - - nxt_conf_get_string(conf, &header); + char c; + size_t i; + uint32_t hash; - fh->header = nxt_str_dup(mp, NULL, &header); + fh->header = nxt_conf_get_string_dup(conf, mp, NULL); if (nxt_slow_path(fh->header == NULL)) { return NXT_ERROR; } @@ -2971,7 +2967,7 @@ nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, mp = tmcf->router_conf->mem_pool; - if (tls->socket_conf->tls == NULL){ + if (tls->socket_conf->tls == NULL) { tlscf = nxt_mp_zget(mp, sizeof(nxt_tls_conf_t)); if (nxt_slow_path(tlscf == NULL)) { goto fail; diff --git a/src/nxt_router.h b/src/nxt_router.h index b14f8410..3e523001 100644 --- a/src/nxt_router.h +++ b/src/nxt_router.h @@ -54,6 +54,8 @@ typedef struct { nxt_router_access_log_t *access_log; nxt_tstr_t *log_format; + nxt_tstr_t *log_expr; + uint8_t log_negate; /* 1 bit */ } nxt_router_conf_t; diff --git a/src/nxt_router_access_log.c b/src/nxt_router_access_log.c index ccbddb96..7fc59972 100644 --- a/src/nxt_router_access_log.c +++ b/src/nxt_router_access_log.c @@ -13,6 +13,7 @@ typedef struct { nxt_str_t path; nxt_str_t format; + nxt_conf_value_t *expr; } nxt_router_access_log_conf_t; @@ -53,6 +54,12 @@ static nxt_conf_map_t nxt_router_access_log_conf[] = { NXT_CONF_MAP_STR, offsetof(nxt_router_access_log_conf_t, format), }, + + { + nxt_string("if"), + NXT_CONF_MAP_PTR, + offsetof(nxt_router_access_log_conf_t, expr), + }, }; @@ -72,6 +79,8 @@ nxt_router_access_log_create(nxt_task_t *task, nxt_router_conf_t *rtcf, "[$time_local] \"$request_line\" $status $body_bytes_sent " "\"$header_referer\" \"$header_user_agent\""); + nxt_memzero(&alcf, sizeof(nxt_router_access_log_conf_t)); + alcf.format = log_format_str; if (nxt_conf_type(value) == NXT_CONF_STRING) { @@ -133,6 +142,22 @@ nxt_router_access_log_create(nxt_task_t *task, nxt_router_conf_t *rtcf, rtcf->access_log = access_log; rtcf->log_format = format; + if (alcf.expr != NULL) { + nxt_conf_get_string(alcf.expr, &str); + + if (str.length > 0 && str.start[0] == '!') { + rtcf->log_negate = 1; + + str.start++; + str.length--; + } + + rtcf->log_expr = nxt_tstr_compile(rtcf->tstr_state, &str, 0); + if (nxt_slow_path(rtcf->log_expr == NULL)) { + return NXT_ERROR; + } + } + return NXT_OK; } diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c index 9bfabc75..0e7f879e 100644 --- a/src/nxt_runtime.c +++ b/src/nxt_runtime.c @@ -956,6 +956,12 @@ nxt_runtime_conf_read_cmd(nxt_task_t *task, nxt_runtime_t *rt) static const char no_control[] = "option \"--control\" requires socket address\n"; + static const char no_control_mode[] = + "option \"--control-mode\" requires a mode\n"; + static const char no_control_user[] = + "option \"--control-user\" requires a username\n"; + static const char no_control_group[] = + "option \"--control-group\" requires a group name\n"; static const char no_user[] = "option \"--user\" requires username\n"; static const char no_group[] = "option \"--group\" requires group name\n"; static const char no_pid[] = "option \"--pid\" requires filename\n"; @@ -984,6 +990,13 @@ nxt_runtime_conf_read_cmd(nxt_task_t *task, nxt_runtime_t *rt) " --control ADDRESS set address of control API socket\n" " default: \"" NXT_CONTROL_SOCK "\"\n" "\n" + " --control-mode MODE set mode of the control API socket\n" + " default: 0600\n" + "\n" + " --control-user USER set the owner of the control API socket\n" + "\n" + " --control-group GROUP set the group of the control API socket\n" + "\n" " --pid FILE set pid filename\n" " default: \"" NXT_PID "\"\n" "\n" @@ -1032,6 +1045,48 @@ nxt_runtime_conf_read_cmd(nxt_task_t *task, nxt_runtime_t *rt) continue; } + if (nxt_strcmp(p, "--control-mode") == 0) { + if (*argv == NULL) { + write(STDERR_FILENO, no_control_mode, + nxt_length(no_control_mode)); + return NXT_ERROR; + } + + p = *argv++; + + rt->control_mode = strtoul(p, NULL, 8); + + continue; + } + + if (nxt_strcmp(p, "--control-user") == 0) { + if (*argv == NULL) { + write(STDERR_FILENO, no_control_user, + nxt_length(no_control_user)); + return NXT_ERROR; + } + + p = *argv++; + + rt->control_user = p; + + continue; + } + + if (nxt_strcmp(p, "--control-group") == 0) { + if (*argv == NULL) { + write(STDERR_FILENO, no_control_group, + nxt_length(no_control_group)); + return NXT_ERROR; + } + + p = *argv++; + + rt->control_group = p; + + continue; + } + if (nxt_strcmp(p, "--user") == 0) { if (*argv == NULL) { write(STDERR_FILENO, no_user, nxt_length(no_user)); diff --git a/src/nxt_runtime.h b/src/nxt_runtime.h index 66ec0106..7bd490d7 100644 --- a/src/nxt_runtime.h +++ b/src/nxt_runtime.h @@ -70,8 +70,12 @@ struct nxt_runtime_s { const char *ver_tmp; const char *conf; const char *conf_tmp; - const char *control; const char *tmp; + const char *control; + + mode_t control_mode; + const char *control_user; + const char *control_group; nxt_str_t certs; nxt_str_t scripts; diff --git a/src/nxt_socket.c b/src/nxt_socket.c index a8e0d514..9ac8ecd2 100644 --- a/src/nxt_socket.c +++ b/src/nxt_socket.c @@ -322,15 +322,15 @@ nxt_socket_error(nxt_socket_t s) err = 0; len = sizeof(int); - /* - * Linux and BSDs return 0 and store a pending error in the err argument; + /* + * Linux and BSDs return 0 and store a pending error in the err argument; * Solaris returns -1 and sets the errno. - */ + */ ret = getsockopt(s, SOL_SOCKET, SO_ERROR, (void *) &err, &len); if (nxt_slow_path(ret == -1)) { err = nxt_errno; - } + } return err; } diff --git a/src/nxt_unit.h b/src/nxt_unit.h index 35f9fa55..a50046a5 100644 --- a/src/nxt_unit.h +++ b/src/nxt_unit.h @@ -40,7 +40,7 @@ enum { /* * Mostly opaque structure with library state. * - * Only user defined 'data' pointer exposed here. The rest is unit + * Only the user defined 'data' pointer is exposed here. The rest is unit * implementation specific and hidden. */ struct nxt_unit_s { @@ -51,8 +51,8 @@ struct nxt_unit_s { * Thread context. * * First (main) context is provided 'for free'. To receive and process - * requests in other thread, one need to allocate context and use it - * further in this thread. + * requests in other threads, one needs to allocate a new context and use it + * further in that thread. */ struct nxt_unit_ctx_s { void *data; /* User context-specific data. */ @@ -72,7 +72,7 @@ struct nxt_unit_port_id_s { }; /* - * unit provides port storage which is able to store and find the following + * Unit provides port storage which is able to store and find the following * data structures. */ struct nxt_unit_port_s { @@ -114,13 +114,13 @@ struct nxt_unit_request_info_s { /* - * Set of application-specific callbacks. Application may leave all optional - * callbacks as NULL. + * Set of application-specific callbacks. The application may leave all + * optional callbacks as NULL. */ struct nxt_unit_callbacks_s { /* - * Process request. Unlike all other callback, this callback - * need to be defined by application. + * Process request. Unlike all other callbacks, this callback is required + * and needs to be defined by the application. */ void (*request_handler)(nxt_unit_request_info_t *req); @@ -201,15 +201,15 @@ struct nxt_unit_read_info_s { nxt_unit_ctx_t *nxt_unit_init(nxt_unit_init_t *); /* - * Main function useful in case when application does not have it's own - * event loop. nxt_unit_run() starts infinite message wait and process loop. + * Main function, useful in case the application does not have its own event + * loop. nxt_unit_run() starts an infinite message wait and process loop. * * for (;;) { * app_lib->port_recv(...); * nxt_unit_process_msg(...); * } * - * The normally function returns when QUIT message received from Unit. + * The function returns normally when a QUIT message is received from Unit. */ int nxt_unit_run(nxt_unit_ctx_t *); @@ -220,10 +220,10 @@ int nxt_unit_run_shared(nxt_unit_ctx_t *ctx); nxt_unit_request_info_t *nxt_unit_dequeue_request(nxt_unit_ctx_t *ctx); /* - * Receive and process one message, invoke configured callbacks. + * Receive and process one message, and invoke configured callbacks. * - * If application implements it's own event loop, each datagram received - * from port socket should be initially processed by unit. This function + * If the application implements its own event loop, each datagram received + * from the port socket should be initially processed by unit. This function * may invoke other application-defined callback for message processing. */ int nxt_unit_run_once(nxt_unit_ctx_t *ctx); @@ -234,8 +234,8 @@ int nxt_unit_process_port_msg(nxt_unit_ctx_t *ctx, nxt_unit_port_t *port); void nxt_unit_done(nxt_unit_ctx_t *); /* - * Allocate and initialize new execution context with new listen port to - * process requests in other thread. + * Allocate and initialize a new execution context with a new listen port to + * process requests in another thread. */ nxt_unit_ctx_t *nxt_unit_ctx_alloc(nxt_unit_ctx_t *, void *); @@ -253,7 +253,7 @@ void nxt_unit_split_host(char *host_start, uint32_t host_length, void nxt_unit_request_group_dup_fields(nxt_unit_request_info_t *req); /* - * Allocate response structure capable to store limited numer of fields. + * Allocate response structure capable of storing a limited number of fields. * The structure may be accessed directly via req->response pointer or * filled step-by-step using functions add_field and add_content. */ @@ -273,8 +273,8 @@ int nxt_unit_response_add_content(nxt_unit_request_info_t *req, const void* src, uint32_t size); /* - * Send prepared response to Unit server. Response structure destroyed during - * this call. + * Send the prepared response to the Unit server. The Response structure is + * destroyed during this call. */ int nxt_unit_response_send(nxt_unit_request_info_t *req); diff --git a/src/nxt_var.c b/src/nxt_var.c index 729de788..2600371b 100644 --- a/src/nxt_var.c +++ b/src/nxt_var.c @@ -50,11 +50,12 @@ struct nxt_var_query_s { static nxt_int_t nxt_var_hash_test(nxt_lvlhsh_query_t *lhq, void *data); static nxt_var_decl_t *nxt_var_hash_find(nxt_str_t *name); -static nxt_var_ref_t *nxt_var_ref_get(nxt_tstr_state_t *state, nxt_str_t *name); +static nxt_var_ref_t *nxt_var_ref_get(nxt_tstr_state_t *state, nxt_str_t *name, + nxt_mp_t *mp); static nxt_int_t nxt_var_cache_test(nxt_lvlhsh_query_t *lhq, void *data); static nxt_str_t *nxt_var_cache_value(nxt_task_t *task, nxt_tstr_state_t *state, - nxt_var_cache_t *cache, uint32_t index, void *ctx); + nxt_var_cache_t *cache, nxt_var_ref_t *ref, void *ctx); static u_char *nxt_var_next_part(u_char *start, u_char *end, nxt_str_t *part); @@ -109,7 +110,7 @@ nxt_var_hash_find(nxt_str_t *name) static nxt_var_ref_t * -nxt_var_ref_get(nxt_tstr_state_t *state, nxt_str_t *name) +nxt_var_ref_get(nxt_tstr_state_t *state, nxt_str_t *name, nxt_mp_t *mp) { nxt_int_t ret; nxt_uint_t i; @@ -125,16 +126,21 @@ nxt_var_ref_get(nxt_tstr_state_t *state, nxt_str_t *name) } } - ref = nxt_array_add(state->var_refs); - if (nxt_slow_path(ref == NULL)) { - return NULL; - } + if (mp != NULL) { + ref = nxt_mp_alloc(mp, sizeof(nxt_var_ref_t)); + if (nxt_slow_path(ref == NULL)) { + return NULL; + } - ref->index = state->var_refs->nelts - 1; + } else { + ref = nxt_array_add(state->var_refs); + if (nxt_slow_path(ref == NULL)) { + return NULL; + } - ref->name = nxt_str_dup(state->pool, NULL, name); - if (nxt_slow_path(ref->name == NULL)) { - return NULL; + ref->index = state->var_refs->nelts - 1; + + mp = state->pool; } decl = nxt_var_hash_find(name); @@ -143,14 +149,21 @@ nxt_var_ref_get(nxt_tstr_state_t *state, nxt_str_t *name) ref->handler = decl->handler; ref->cacheable = decl->cacheable; - return ref; + goto done; } - ret = nxt_http_unknown_var_ref(state, ref, name); + ret = nxt_http_unknown_var_ref(mp, ref, name); if (nxt_slow_path(ret != NXT_OK)) { return NULL; } +done: + + ref->name = nxt_str_dup(mp, NULL, name); + if (nxt_slow_path(ref->name == NULL)) { + return NULL; + } + return ref; } @@ -203,16 +216,12 @@ nxt_var_cache_test(nxt_lvlhsh_query_t *lhq, void *data) static nxt_str_t * nxt_var_cache_value(nxt_task_t *task, nxt_tstr_state_t *state, - nxt_var_cache_t *cache, uint32_t index, void *ctx) + nxt_var_cache_t *cache, nxt_var_ref_t *ref, void *ctx) { nxt_int_t ret; nxt_str_t *value; - nxt_var_ref_t *ref; nxt_lvlhsh_query_t lhq; - ref = state->var_refs->elts; - ref = &ref[index]; - value = cache->spare; if (value == NULL) { @@ -228,10 +237,10 @@ nxt_var_cache_value(nxt_task_t *task, nxt_tstr_state_t *state, goto not_cached; } - lhq.key_hash = nxt_murmur_hash2_uint32(&index); + lhq.key_hash = nxt_murmur_hash2_uint32(&ref->index); lhq.replace = 0; lhq.key.length = sizeof(uint32_t); - lhq.key.start = (u_char *) &index; + lhq.key.start = (u_char *) &ref->index; lhq.value = value; lhq.proto = &nxt_var_cache_proto; lhq.pool = cache->pool; @@ -357,7 +366,7 @@ nxt_var_compile(nxt_tstr_state_t *state, nxt_str_t *str) next = nxt_var_next_part(p, end, &part); if (part.start != NULL) { - ref = nxt_var_ref_get(state, &part); + ref = nxt_var_ref_get(state, &part, NULL); if (nxt_slow_path(ref == NULL)) { return NULL; } @@ -397,7 +406,7 @@ nxt_var_test(nxt_tstr_state_t *state, nxt_str_t *str, u_char *error) } if (part.start != NULL) { - ref = nxt_var_ref_get(state, &part); + ref = nxt_var_ref_get(state, &part, NULL); if (ref == NULL) { nxt_sprintf(error, error + NXT_MAX_ERROR_STR, @@ -491,20 +500,24 @@ nxt_var_interpreter(nxt_task_t *task, nxt_tstr_state_t *state, { u_char *p, *src; size_t length, last, next; + uint32_t index; nxt_str_t *value, **part; nxt_uint_t i; nxt_array_t parts; + nxt_var_ref_t *ref; nxt_var_sub_t *subs; nxt_memzero(&parts, sizeof(nxt_array_t)); nxt_array_init(&parts, cache->pool, sizeof(nxt_str_t *)); + ref = state->var_refs->elts; subs = nxt_var_subs(var); length = var->length; for (i = 0; i < var->vars; i++) { - value = nxt_var_cache_value(task, state, cache, subs[i].index, ctx); + index = subs[i].index; + value = nxt_var_cache_value(task, state, cache, &ref[index], ctx); if (nxt_slow_path(value == NULL)) { return NXT_ERROR; } @@ -558,3 +571,18 @@ nxt_var_interpreter(nxt_task_t *task, nxt_tstr_state_t *state, return NXT_OK; } + + +nxt_str_t * +nxt_var_get(nxt_task_t *task, nxt_tstr_state_t *state, nxt_var_cache_t *cache, + nxt_str_t *name, void *ctx) +{ + nxt_var_ref_t *ref; + + ref = nxt_var_ref_get(state, name, cache->pool); + if (nxt_slow_path(ref == NULL)) { + return NULL; + } + + return nxt_var_cache_value(task, state, cache, ref, ctx); +} diff --git a/src/nxt_var.h b/src/nxt_var.h index fde64f1e..08a92c08 100644 --- a/src/nxt_var.h +++ b/src/nxt_var.h @@ -59,10 +59,10 @@ nxt_int_t nxt_var_test(nxt_tstr_state_t *state, nxt_str_t *str, u_char *error); nxt_int_t nxt_var_interpreter(nxt_task_t *task, nxt_tstr_state_t *state, nxt_var_cache_t *cache, nxt_var_t *var, nxt_str_t *str, void *ctx, nxt_bool_t logging); -nxt_str_t *nxt_var_get(nxt_task_t *task, nxt_var_cache_t *cache, - nxt_str_t *name, void *ctx); +nxt_str_t *nxt_var_get(nxt_task_t *task, nxt_tstr_state_t *state, + nxt_var_cache_t *cache, nxt_str_t *name, void *ctx); -nxt_int_t nxt_http_unknown_var_ref(nxt_tstr_state_t *state, nxt_var_ref_t *ref, +nxt_int_t nxt_http_unknown_var_ref(nxt_mp_t *mp, nxt_var_ref_t *ref, nxt_str_t *name); diff --git a/src/python/nxt_python_asgi_http.c b/src/python/nxt_python_asgi_http.c index 05c0da4f..cdd6357e 100644 --- a/src/python/nxt_python_asgi_http.c +++ b/src/python/nxt_python_asgi_http.c @@ -362,16 +362,6 @@ nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict) Py_ssize_t body_len, body_off; nxt_py_asgi_ctx_data_t *ctx_data; - body = PyDict_GetItem(dict, nxt_py_body_str); - if (nxt_slow_path(body != NULL && !PyBytes_Check(body))) { - return PyErr_Format(PyExc_TypeError, "'body' is not a byte string"); - } - - more_body = PyDict_GetItem(dict, nxt_py_more_body_str); - if (nxt_slow_path(more_body != NULL && !PyBool_Check(more_body))) { - return PyErr_Format(PyExc_TypeError, "'more_body' is not a bool"); - } - if (nxt_slow_path(http->complete)) { return PyErr_Format(PyExc_RuntimeError, "Unexpected ASGI message 'http.response.body' " @@ -382,9 +372,26 @@ nxt_py_asgi_http_response_body(nxt_py_asgi_http_t *http, PyObject *dict) return PyErr_Format(PyExc_RuntimeError, "Concurrent send"); } + more_body = PyDict_GetItem(dict, nxt_py_more_body_str); + if (nxt_slow_path(more_body != NULL && !PyBool_Check(more_body))) { + return PyErr_Format(PyExc_TypeError, "'more_body' is not a bool"); + } + + body = PyDict_GetItem(dict, nxt_py_body_str); + if (body != NULL) { - body_str = PyBytes_AS_STRING(body); - body_len = PyBytes_GET_SIZE(body); + if (PyBytes_Check(body)) { + body_str = PyBytes_AS_STRING(body); + body_len = PyBytes_GET_SIZE(body); + + } else if (PyByteArray_Check(body)) { + body_str = PyByteArray_AS_STRING(body); + body_len = PyByteArray_GET_SIZE(body); + + } else { + return PyErr_Format(PyExc_TypeError, + "'body' is not a byte string or bytearray"); + } nxt_unit_req_debug(http->req, "asgi_http_response_body: %d, %d", (int) body_len, (more_body == Py_True) ); diff --git a/src/python/nxt_python_wsgi.c b/src/python/nxt_python_wsgi.c index dfb31509..c621097e 100644 --- a/src/python/nxt_python_wsgi.c +++ b/src/python/nxt_python_wsgi.c @@ -863,11 +863,38 @@ nxt_python_field_value(nxt_unit_field_t *f, int n, uint32_t vl) char *p, *src; PyObject *res; + src = nxt_unit_sptr_get(&f->value); + #if PY_MAJOR_VERSION == 3 - res = PyUnicode_New(vl, 255); + if (nxt_slow_path(n > 1)) { + char *ptr; + + p = nxt_unit_malloc(NULL, vl + 1); + if (nxt_slow_path(p == NULL)) { + return NULL; + } + + ptr = p; + p = nxt_cpymem(p, src, f->value_length); + + for (i = 1; i < n; i++) { + p = nxt_cpymem(p, ", ", 2); + + src = nxt_unit_sptr_get(&f[i].value); + p = nxt_cpymem(p, src, f[i].value_length); + } + *p = '\0'; + + src = ptr; + } + + res = PyUnicode_DecodeCharmap(src, vl, NULL, NULL); + + if (nxt_slow_path(n > 1)) { + nxt_unit_free(NULL, src); + } #else res = PyString_FromStringAndSize(NULL, vl); -#endif if (nxt_slow_path(res == NULL)) { return NULL; @@ -875,7 +902,6 @@ nxt_python_field_value(nxt_unit_field_t *f, int n, uint32_t vl) p = PyString_AS_STRING(res); - src = nxt_unit_sptr_get(&f->value); p = nxt_cpymem(p, src, f->value_length); for (i = 1; i < n; i++) { @@ -884,6 +910,7 @@ nxt_python_field_value(nxt_unit_field_t *f, int n, uint32_t vl) src = nxt_unit_sptr_get(&f[i].value); p = nxt_cpymem(p, src, f[i].value_length); } +#endif return res; } diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c index bcb48f6b..27b868fe 100644 --- a/src/ruby/nxt_ruby.c +++ b/src/ruby/nxt_ruby.c @@ -889,13 +889,41 @@ nxt_ruby_hash_info(VALUE r_key, VALUE r_value, VALUE arg) goto fail; } - if (nxt_slow_path(TYPE(r_value) != T_STRING)) { + if (nxt_slow_path(TYPE(r_value) != T_STRING && TYPE(r_value) != T_ARRAY)) { nxt_unit_req_error(headers_info->req, "Ruby: Wrong header entry 'value' from application"); goto fail; } + if (TYPE(r_value) == T_ARRAY) { + int i; + int arr_len = RARRAY_LEN(r_value); + VALUE item; + size_t len = 0; + + for (i = 0; i < arr_len; i++) { + item = rb_ary_entry(r_value, i); + if (TYPE(item) != T_STRING) { + nxt_unit_req_error(headers_info->req, + "Ruby: Wrong header entry in 'value' array " + "from application"); + goto fail; + } + + len += RSTRING_LEN(item) + 2; /* +2 for '; ' */ + } + + if (arr_len > 0) { + len -= 2; + } + + headers_info->fields++; + headers_info->size += RSTRING_LEN(r_key) + len; + + return ST_CONTINUE; + } + value = RSTRING_PTR(r_value); value_end = value + RSTRING_LEN(r_value); @@ -941,11 +969,54 @@ nxt_ruby_hash_add(VALUE r_key, VALUE r_value, VALUE arg) headers_info = (void *) (uintptr_t) arg; rc = &headers_info->rc; + key_len = RSTRING_LEN(r_key); + + if (TYPE(r_value) == T_ARRAY) { + int i; + int arr_len = RARRAY_LEN(r_value); + char *field, *p; + VALUE item; + size_t len = 0; + + for (i = 0; i < arr_len; i++) { + item = rb_ary_entry(r_value, i); + + len += RSTRING_LEN(item) + 2; /* +2 for '; ' */ + } + + field = nxt_unit_malloc(NULL, len); + if (field == NULL) { + goto fail; + } + + p = field; + + for (i = 0; i < arr_len; i++) { + item = rb_ary_entry(r_value, i); + + p = nxt_cpymem(p, RSTRING_PTR(item), RSTRING_LEN(item)); + p = nxt_cpymem(p, "; ", 2); + } + + if (arr_len > 0) { + len -= 2; + } + + *rc = nxt_unit_response_add_field(headers_info->req, + RSTRING_PTR(r_key), key_len, + field, len); + nxt_unit_free(NULL, field); + + if (nxt_slow_path(*rc != NXT_UNIT_OK)) { + goto fail; + } + + return ST_CONTINUE; + } + value = RSTRING_PTR(r_value); value_end = value + RSTRING_LEN(r_value); - key_len = RSTRING_LEN(r_key); - pos = value; for ( ;; ) { diff --git a/src/test/nxt_rbtree1.c b/src/test/nxt_rbtree1.c index 1ae059ab..ec024858 100644 --- a/src/test/nxt_rbtree1.c +++ b/src/test/nxt_rbtree1.c @@ -1,7 +1,7 @@ /* * Copyright (C) Igor Sysoev - * Copyright (C) Nginx, Inc. + * Copyright (C) NGINX, Inc. */ diff --git a/src/test/nxt_rbtree1.h b/src/test/nxt_rbtree1.h index 60048dad..d6230ab0 100644 --- a/src/test/nxt_rbtree1.h +++ b/src/test/nxt_rbtree1.h @@ -1,7 +1,7 @@ /* * Copyright (C) Igor Sysoev - * Copyright (C) Nginx, Inc. + * Copyright (C) NGINX, Inc. */ diff --git a/src/wasm-wasi-component/.gitignore b/src/wasm-wasi-component/.gitignore new file mode 100644 index 00000000..eb5a316c --- /dev/null +++ b/src/wasm-wasi-component/.gitignore @@ -0,0 +1 @@ +target diff --git a/src/wasm-wasi-component/Cargo.lock b/src/wasm-wasi-component/Cargo.lock new file mode 100644 index 00000000..bc09e96a --- /dev/null +++ b/src/wasm-wasi-component/Cargo.lock @@ -0,0 +1,2293 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "ambient-authority" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + +[[package]] +name = "async-trait" +version = "0.1.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bindgen" +version = "0.68.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" +dependencies = [ + "bitflags 2.4.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cap-fs-ext" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88e341d15ac1029aadce600be764a1a1edafe40e03cde23285bc1d261b3a4866" +dependencies = [ + "cap-primitives", + "cap-std", + "io-lifetimes", + "windows-sys 0.52.0", +] + +[[package]] +name = "cap-net-ext" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434168fe6533055f0f4204039abe3ff6d7db338ef46872a5fa39e9d5ad5ab7a9" +dependencies = [ + "cap-primitives", + "cap-std", + "rustix", + "smallvec", +] + +[[package]] +name = "cap-primitives" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe16767ed8eee6d3f1f00d6a7576b81c226ab917eb54b96e5f77a5216ef67abb" +dependencies = [ + "ambient-authority", + "fs-set-times", + "io-extras", + "io-lifetimes", + "ipnet", + "maybe-owned", + "rustix", + "windows-sys 0.52.0", + "winx", +] + +[[package]] +name = "cap-rand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20e5695565f0cd7106bc3c7170323597540e772bb73e0be2cd2c662a0f8fa4ca" +dependencies = [ + "ambient-authority", + "rand", +] + +[[package]] +name = "cap-std" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "593db20e4c51f62d3284bae7ee718849c3214f93a3b94ea1899ad85ba119d330" +dependencies = [ + "cap-primitives", + "io-extras", + "io-lifetimes", + "rustix", +] + +[[package]] +name = "cap-time-ext" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03261630f291f425430a36f38c847828265bc928f517cdd2004c56f4b02f002b" +dependencies = [ + "ambient-authority", + "cap-primitives", + "iana-time-zone", + "once_cell", + "rustix", + "winx", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cranelift-bforest" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d819feeda4c420a18f1e28236ca0ce1177b22bf7c8a44ddee92dfe40de15bcf0" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9b8d03d5bdbca7e5f72b0e0a0f69933ed1f09e24be6c075aa6fe3f802b0cc0c" +dependencies = [ + "bumpalo", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-control", + "cranelift-entity", + "cranelift-isle", + "gimli", + "hashbrown 0.14.3", + "log", + "regalloc2", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3fd3664e38e51649b17dc30cfdd561273fe2f590dcd013fb75d9eabc6272dfb" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b031ec5e605828975952622b5a77d49126f20ffe88d33719a0af66b23a0fc36" + +[[package]] +name = "cranelift-control" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fada054d017cf2ed8f7ed2336e0517fc1b19e6825be1790de9eb00c94788362b" +dependencies = [ + "arbitrary", +] + +[[package]] +name = "cranelift-entity" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177b6f94ae8de6348eb45bf977c79ab9e3c40fc3ac8cb7ed8109560ea39bee7d" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "cranelift-frontend" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebebd23a69a23e3ddea78e98ff3a2de222e88c8e045d81ef4a72f042e0d79dbd" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1571bfc14df8966d12c6121b5325026591a4b4009e22fea0fe3765ab7cd33b96" + +[[package]] +name = "cranelift-native" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35a69c37e0c10b46fe5527f2397ac821046efbf5f7ec112c8b84df25712f465b" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon", +] + +[[package]] +name = "cranelift-wasm" +version = "0.104.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3fef8bbceb8cb56d3f1778b0418d75c5cf12ec571a35fc01eb41abb0227a25" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "itertools", + "log", + "smallvec", + "wasmparser 0.118.1", + "wasmtime-types", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fd-lock" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" +dependencies = [ + "cfg-if", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs-set-times" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "033b337d725b97690d86893f9de22b67b80dcc4e9ad815f348254c38119db8fb" +dependencies = [ + "io-lifetimes", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "h2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d030e59af851932b72ceebadf4a2b5986dba4c3b99dd2493f8273a0f151943" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "http" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "tokio", + "want", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", + "serde", +] + +[[package]] +name = "io-extras" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c301e73fb90e8a29e600a9f402d095765f74310d582916a952f618836a1bd1ed" +dependencies = [ + "io-lifetimes", + "windows-sys 0.52.0", +] + +[[package]] +name = "io-lifetimes" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a611371471e98973dbcab4e0ec66c31a10bc356eeb4d54a0e05eac8158fe38c" + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libloading" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.2", + "libc", + "redox_syscall", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "maybe-owned" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "memfd" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" +dependencies = [ + "rustix", +] + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "crc32fast", + "hashbrown 0.14.3", + "indexmap", + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "prettyplease" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "regalloc2" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad156d539c879b7a24a363a2016d77961786e71f48f2e2fc8302a92abd2429a6" +dependencies = [ + "hashbrown 0.13.2", + "log", + "rustc-hash", + "slice-group-by", + "smallvec", +] + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "ring" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +dependencies = [ + "bitflags 2.4.2", + "errno", + "itoa", + "libc", + "linux-raw-sys", + "once_cell", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.21.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "semver" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" + +[[package]] +name = "serde" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.113" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shellexpand" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4" +dependencies = [ + "dirs", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slice-group-by" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "sptr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-interface" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0682e006dd35771e392a6623ac180999a9a854b1d4a6c12fb2e804941c2b1f58" +dependencies = [ + "bitflags 2.4.2", + "cap-fs-ext", + "cap-std", + "fd-lock", + "io-lifetimes", + "rustix", + "windows-sys 0.52.0", + "winx", +] + +[[package]] +name = "target-lexicon" +version = "0.12.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae" + +[[package]] +name = "thiserror" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi-cap-std-sync" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db014d2ced91f17d1f1a8f2b76d6ea8d731bc1dbc8c2bbaec689d6a242568e5d" +dependencies = [ + "anyhow", + "async-trait", + "cap-fs-ext", + "cap-rand", + "cap-std", + "cap-time-ext", + "fs-set-times", + "io-extras", + "io-lifetimes", + "once_cell", + "rustix", + "system-interface", + "tracing", + "wasi-common", + "windows-sys 0.52.0", +] + +[[package]] +name = "wasi-common" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "449d17849e3c83a931374442fe2deee4d6bd1ebf469719ef44192e9e82e19c89" +dependencies = [ + "anyhow", + "bitflags 2.4.2", + "cap-rand", + "cap-std", + "io-extras", + "log", + "rustix", + "thiserror", + "tracing", + "wasmtime", + "wiggle", + "windows-sys 0.52.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" + +[[package]] +name = "wasm-encoder" +version = "0.38.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-wasi-component" +version = "0.1.0" +dependencies = [ + "anyhow", + "bindgen", + "bytes", + "cc", + "futures-util", + "http", + "http-body", + "http-body-util", + "tokio", + "wasmtime", + "wasmtime-wasi", + "wasmtime-wasi-http", +] + +[[package]] +name = "wasmparser" +version = "0.118.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ee9723b928e735d53000dec9eae7b07a60e490c85ab54abb66659fc61bfcd9" +dependencies = [ + "indexmap", + "semver", +] + +[[package]] +name = "wasmparser" +version = "0.121.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953cf6a7606ab31382cb1caa5ae403e77ba70c7f8e12eeda167e7040d42bfda8" +dependencies = [ + "bitflags 2.4.2", + "indexmap", + "semver", +] + +[[package]] +name = "wasmprinter" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e32c13c59fdc64d3f6998a1d52eb1d362b6904a88b754190ccb85661ad577a" +dependencies = [ + "anyhow", + "wasmparser 0.121.0", +] + +[[package]] +name = "wasmtime" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "910fabce77e660f0e0e41cfd5f69fc8bf020a025f059718846e918db7177f469" +dependencies = [ + "anyhow", + "async-trait", + "bincode", + "bumpalo", + "cfg-if", + "encoding_rs", + "indexmap", + "libc", + "log", + "object", + "once_cell", + "paste", + "serde", + "serde_derive", + "serde_json", + "target-lexicon", + "wasmparser 0.118.1", + "wasmtime-component-macro", + "wasmtime-component-util", + "wasmtime-cranelift", + "wasmtime-environ", + "wasmtime-fiber", + "wasmtime-jit", + "wasmtime-runtime", + "wasmtime-winch", + "windows-sys 0.52.0", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37288142e9b4a61655a3bcbdc7316c2e4bb9e776b10ce3dd758f8186b4469572" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "wasmtime-component-macro" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad63de18eb42e586386b6091f787c82707cbd5ac5e9343216dba1976190cd03a" +dependencies = [ + "anyhow", + "proc-macro2", + "quote", + "syn", + "wasmtime-component-util", + "wasmtime-wit-bindgen", + "wit-parser", +] + +[[package]] +name = "wasmtime-component-util" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e0a160c0c44369aa4bee6d311a8e4366943bab1651040cc8b0fcec2c9eb8906" + +[[package]] +name = "wasmtime-cranelift" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3734cc01b7cd37bc62fdbcd9529ca9547440052d4b3886cfdec3b8081a5d3647" +dependencies = [ + "anyhow", + "cfg-if", + "cranelift-codegen", + "cranelift-control", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "gimli", + "log", + "object", + "target-lexicon", + "thiserror", + "wasmparser 0.118.1", + "wasmtime-cranelift-shared", + "wasmtime-environ", + "wasmtime-versioned-export-macros", +] + +[[package]] +name = "wasmtime-cranelift-shared" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0eb33cd30c47844aa228d4d0030587e65c1108343f311fe9f7248b5bd9cb65c" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-control", + "cranelift-native", + "gimli", + "object", + "target-lexicon", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-environ" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a056b041fdea604f0972e2fae97958e7748d629a55180228348baefdfc217" +dependencies = [ + "anyhow", + "cranelift-entity", + "gimli", + "indexmap", + "log", + "object", + "serde", + "serde_derive", + "target-lexicon", + "thiserror", + "wasm-encoder", + "wasmparser 0.118.1", + "wasmprinter", + "wasmtime-component-util", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-fiber" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43987d0977c07f15c3608c2f255870c127ffd19e35eeedb1ac1dccedf9932a42" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "rustix", + "wasmtime-asm-macros", + "wasmtime-versioned-export-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "wasmtime-jit" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3e48395ac672b386ed588d97a9612aa13a345008f26466f0dfb2a91628aa9f" +dependencies = [ + "anyhow", + "bincode", + "cfg-if", + "gimli", + "log", + "object", + "rustix", + "serde", + "serde_derive", + "target-lexicon", + "wasmtime-environ", + "wasmtime-jit-icache-coherence", + "wasmtime-runtime", + "windows-sys 0.52.0", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdc26415bb89e9ccd3bdc498fef63aabf665c4c0dd710c107691deb9694955da" +dependencies = [ + "cfg-if", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "wasmtime-runtime" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0abddaf17912aabaf39be0802d5eba9a002e956e902d1ebd438a2fe1c88769a2" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "encoding_rs", + "indexmap", + "libc", + "log", + "mach", + "memfd", + "memoffset", + "paste", + "psm", + "rustix", + "sptr", + "wasm-encoder", + "wasmtime-asm-macros", + "wasmtime-environ", + "wasmtime-fiber", + "wasmtime-versioned-export-macros", + "wasmtime-wmemcheck", + "windows-sys 0.52.0", +] + +[[package]] +name = "wasmtime-types" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35a95cdc1433729085beab42c0a5c742b431f25b17c40d7718e46df63d5ffc7" +dependencies = [ + "cranelift-entity", + "serde", + "serde_derive", + "thiserror", + "wasmparser 0.118.1", +] + +[[package]] +name = "wasmtime-versioned-export-macros" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad322733fe67e45743784d8b1df452bcb54f581572a4f1a646a4332deecbcc2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "wasmtime-wasi" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "902cc299b73655c36679b77efdfce4bb5971992f1a4a8a436dd3809a6848ff0e" +dependencies = [ + "anyhow", + "async-trait", + "bitflags 2.4.2", + "bytes", + "cap-fs-ext", + "cap-net-ext", + "cap-rand", + "cap-std", + "cap-time-ext", + "fs-set-times", + "futures", + "io-extras", + "io-lifetimes", + "libc", + "log", + "once_cell", + "rustix", + "system-interface", + "thiserror", + "tokio", + "tracing", + "url", + "wasi-cap-std-sync", + "wasi-common", + "wasmtime", + "wiggle", + "windows-sys 0.52.0", +] + +[[package]] +name = "wasmtime-wasi-http" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "151fc711fad35034b8a6df00a5bcd5a7b1acb89ca12c2407f564a36ebd382e26" +dependencies = [ + "anyhow", + "async-trait", + "bytes", + "futures", + "http", + "http-body", + "http-body-util", + "hyper", + "rustls", + "tokio", + "tokio-rustls", + "tracing", + "wasmtime", + "wasmtime-wasi", + "webpki-roots", +] + +[[package]] +name = "wasmtime-winch" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e63aeca929f84560eec52c5af43bf5d623b92683b0195d9fb06da8ed860e092" +dependencies = [ + "anyhow", + "cranelift-codegen", + "gimli", + "object", + "target-lexicon", + "wasmparser 0.118.1", + "wasmtime-cranelift-shared", + "wasmtime-environ", + "winch-codegen", +] + +[[package]] +name = "wasmtime-wit-bindgen" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41e5675998fdc74495afdd90ad2bd221206a258075b23048af0535a969b07893" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "wit-parser", +] + +[[package]] +name = "wasmtime-wmemcheck" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b20a19e10d8cb50b45412fb21192982b7ce85c0122dc33bb71f1813e25dc6e52" + +[[package]] +name = "wast" +version = "35.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ef140f1b49946586078353a453a1d28ba90adfc54dde75710bc1931de204d68" +dependencies = [ + "leb128", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "wiggle" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "737728db69a7657a5f6a7bac445c02d8564d603d62c46c95edf928554e67d072" +dependencies = [ + "anyhow", + "async-trait", + "bitflags 2.4.2", + "thiserror", + "tracing", + "wasmtime", + "wiggle-macro", +] + +[[package]] +name = "wiggle-generate" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2460c7163b79ffefd9a564eaeab0a5b0e84bb91afdfeeb84d36f304ddbe08982" +dependencies = [ + "anyhow", + "heck", + "proc-macro2", + "quote", + "shellexpand", + "syn", + "witx", +] + +[[package]] +name = "wiggle-macro" +version = "17.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa8d8412375ba8325d61fbae56dead51dabfaec85d620ce36427922fb9cece83" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wiggle-generate", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winch-codegen" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d2b346bad5397b219b4ff0a8fa7230936061ff07c61f05d589d8d81e06fb7b2" +dependencies = [ + "anyhow", + "cranelift-codegen", + "gimli", + "regalloc2", + "smallvec", + "target-lexicon", + "wasmparser 0.118.1", + "wasmtime-environ", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winx" +version = "0.36.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9643b83820c0cd246ecabe5fa454dd04ba4fa67996369466d0747472d337346" +dependencies = [ + "bitflags 2.4.2", + "windows-sys 0.52.0", +] + +[[package]] +name = "wit-parser" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df4913a2219096373fd6512adead1fb77ecdaa59d7fc517972a7d30b12f625be" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", +] + +[[package]] +name = "witx" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e366f27a5cabcddb2706a78296a40b8fcc451e1a6aba2fc1d94b4a01bdaaef4b" +dependencies = [ + "anyhow", + "log", + "thiserror", + "wast", +] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/src/wasm-wasi-component/Cargo.toml b/src/wasm-wasi-component/Cargo.toml new file mode 100644 index 00000000..feb7f53c --- /dev/null +++ b/src/wasm-wasi-component/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "wasm-wasi-component" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib"] + +[dependencies] +anyhow = "1.0.75" +bytes = "1.5.0" +futures-util = { version = "0.3.29", default-features = false } +http = "1.0.0" +http-body = { version = "1.0.0", default-features = false } +http-body-util = "0.1.0" +tokio = { version = "1.33.0", default-features = false } +wasmtime = { version = "17.0.0", default-features = false, features = ['component-model', 'cranelift'] } +wasmtime-wasi = "17.0.0" +wasmtime-wasi-http = "17.0.0" + +[build-dependencies] +bindgen = "0.68.1" +cc = "1.0.83" + +[profile.dev] +panic = 'abort' + +[profile.release] +panic = 'abort' diff --git a/src/wasm-wasi-component/build.rs b/src/wasm-wasi-component/build.rs new file mode 100644 index 00000000..5ea74f17 --- /dev/null +++ b/src/wasm-wasi-component/build.rs @@ -0,0 +1,33 @@ +use std::env; +use std::path::PathBuf; + +fn main() { + // Tell cargo to invalidate the built crate whenever the wrapper changes + println!("cargo:rerun-if-changed=wrapper.h"); + + let bindings = bindgen::Builder::default() + .clang_args(["-I", "../"]) + .clang_args(["-I", "../../build/include"]) + .header("./wrapper.h") + // only generate bindings for `nxt_*` header files + .allowlist_file(".*nxt_.*.h") + // generates an "improper_ctypes" warning and we don't need it anyway + .blocklist_function("nxt_vsprintf") + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + // disable some features which aren't necessary + .layout_tests(false) + .derive_debug(false) + .generate() + .expect("Unable to generate bindings"); + + cc::Build::new() + .object("../../build/src/nxt_unit.o") + .compile("nxt-unit"); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/src/wasm-wasi-component/src/lib.rs b/src/wasm-wasi-component/src/lib.rs new file mode 100644 index 00000000..3ee40c4f --- /dev/null +++ b/src/wasm-wasi-component/src/lib.rs @@ -0,0 +1,611 @@ +use anyhow::{bail, Context, Result}; +use bytes::{Bytes, BytesMut}; +use http_body_util::combinators::BoxBody; +use http_body_util::{BodyExt, Full}; +use std::ffi::{CStr, CString}; +use std::mem::MaybeUninit; +use std::ptr; +use std::sync::OnceLock; +use tokio::sync::mpsc; +use wasmtime::component::{Component, InstancePre, Linker, ResourceTable}; +use wasmtime::{Config, Engine, Store}; +use wasmtime_wasi::preview2::{ + DirPerms, FilePerms, WasiCtx, WasiCtxBuilder, WasiView, +}; +use wasmtime_wasi::{ambient_authority, Dir}; +use wasmtime_wasi_http::bindings::http::types::ErrorCode; +use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView}; + +#[allow( + non_camel_case_types, + non_upper_case_globals, + non_snake_case, + dead_code +)] +mod bindings { + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + + pub const fn nxt_string(s: &'static str) -> nxt_str_t { + nxt_str_t { + start: s.as_ptr().cast_mut(), + length: s.len(), + } + } + + pub unsafe fn nxt_unit_sptr_get(sptr: &nxt_unit_sptr_t) -> *const u8 { + sptr.base.as_ptr().offset(sptr.offset as isize) + } +} + +#[no_mangle] +pub static mut nxt_app_module: bindings::nxt_app_module_t = { + const COMPAT: [u32; 2] = [bindings::NXT_VERNUM, bindings::NXT_DEBUG]; + let version = "0.1\0"; + bindings::nxt_app_module_t { + compat: COMPAT.as_ptr().cast_mut(), + compat_length: COMPAT.len() * 4, + mounts: ptr::null(), + nmounts: 0, + type_: bindings::nxt_string("wasm-wasi-component"), + version: version.as_ptr().cast(), + setup: Some(setup), + start: Some(start), + } +}; + +static GLOBAL_CONFIG: OnceLock<GlobalConfig> = OnceLock::new(); +static GLOBAL_STATE: OnceLock<GlobalState> = OnceLock::new(); + +unsafe extern "C" fn setup( + task: *mut bindings::nxt_task_t, + // TODO: should this get used? + _process: *mut bindings::nxt_process_t, + conf: *mut bindings::nxt_common_app_conf_t, +) -> bindings::nxt_int_t { + handle_result(task, || { + let wasm_conf = &(*conf).u.wasm_wc; + let component = CStr::from_ptr(wasm_conf.component).to_str()?; + let mut dirs = Vec::new(); + if !wasm_conf.access.is_null() { + let dirs_ptr = bindings::nxt_conf_get_object_member( + wasm_conf.access, + &mut bindings::nxt_string("filesystem"), + ptr::null_mut(), + ); + for i in 0..bindings::nxt_conf_object_members_count(dirs_ptr) { + let value = bindings::nxt_conf_get_array_element( + dirs_ptr, + i.try_into().unwrap(), + ); + let mut s = bindings::nxt_string(""); + bindings::nxt_conf_get_string(value, &mut s); + dirs.push( + std::str::from_utf8(std::slice::from_raw_parts( + s.start, s.length, + ))? + .to_string(), + ); + } + } + + let result = GLOBAL_CONFIG.set(GlobalConfig { + component: component.to_string(), + dirs, + }); + assert!(result.is_ok()); + Ok(()) + }) +} + +unsafe extern "C" fn start( + task: *mut bindings::nxt_task_t, + data: *mut bindings::nxt_process_data_t, +) -> bindings::nxt_int_t { + handle_result(task, || { + let config = GLOBAL_CONFIG.get().unwrap(); + let state = GlobalState::new(&config) + .context("failed to create initial state")?; + let res = GLOBAL_STATE.set(state); + assert!(res.is_ok()); + + let conf = (*data).app; + let mut wasm_init = MaybeUninit::uninit(); + let ret = + bindings::nxt_unit_default_init(task, wasm_init.as_mut_ptr(), conf); + if ret != bindings::NXT_OK as bindings::nxt_int_t { + bail!("nxt_unit_default_init() failed"); + } + let mut wasm_init = wasm_init.assume_init(); + wasm_init.callbacks.request_handler = Some(request_handler); + + let unit_ctx = bindings::nxt_unit_init(&mut wasm_init); + if unit_ctx.is_null() { + bail!("nxt_unit_init() failed"); + } + + bindings::nxt_unit_run(unit_ctx); + bindings::nxt_unit_done(unit_ctx); + + Ok(()) + }) +} + +unsafe fn handle_result( + task: *mut bindings::nxt_task_t, + func: impl FnOnce() -> Result<()>, +) -> bindings::nxt_int_t { + let rc = match func() { + Ok(()) => bindings::NXT_OK as bindings::nxt_int_t, + Err(e) => { + alert(task, &format!("{e:?}")); + bindings::NXT_ERROR as bindings::nxt_int_t + } + }; + return rc; + + unsafe fn alert(task: *mut bindings::nxt_task_t, msg: &str) { + let log = (*task).log; + let msg = CString::new(msg).unwrap(); + ((*log).handler).unwrap()( + bindings::NXT_LOG_ALERT as bindings::nxt_uint_t, + log, + "%s\0".as_ptr().cast(), + msg.as_ptr(), + ); + } +} + +unsafe extern "C" fn request_handler( + info: *mut bindings::nxt_unit_request_info_t, +) { + // Enqueue this request to get processed by the Tokio event loop, and + // otherwise immediately return. + let state = GLOBAL_STATE.get().unwrap(); + state.sender.blocking_send(NxtRequestInfo { info }).unwrap(); +} + +struct GlobalConfig { + component: String, + dirs: Vec<String>, +} + +struct GlobalState { + engine: Engine, + component: InstancePre<StoreState>, + global_config: &'static GlobalConfig, + sender: mpsc::Sender<NxtRequestInfo>, +} + +impl GlobalState { + fn new(global_config: &'static GlobalConfig) -> Result<GlobalState> { + // Configure Wasmtime, e.g. the component model and async support are + // enabled here. Other configuration can include: + // + // * Epochs/fuel - enables async yielding to prevent any one request + // starving others. + // * Pooling allocator - accelerates instantiation at the cost of a + // large virtual memory reservation. + // * Memory limits/etc. + let mut config = Config::new(); + config.wasm_component_model(true); + config.async_support(true); + let engine = Engine::new(&config)?; + + // Compile the binary component on disk in Wasmtime. This is then + // pre-instantiated with host APIs defined by WASI. The result of + // this is a "pre-instantiated instance" which can be used to + // repeatedly instantiate later on. This will frontload + // compilation/linking/type-checking/etc to happen once rather than on + // each request. + let component = Component::from_file(&engine, &global_config.component) + .context("failed to compile component")?; + let mut linker = Linker::<StoreState>::new(&engine); + wasmtime_wasi::preview2::command::add_to_linker(&mut linker)?; + wasmtime_wasi_http::proxy::add_only_http_to_linker(&mut linker)?; + let component = linker + .instantiate_pre(&component) + .context("failed to pre-instantiate the provided component")?; + + // Spin up the Tokio async runtime in a separate thread with a + // communication channel into it. This thread will send requests to + // Tokio and the results will be calculated there. + let (sender, receiver) = mpsc::channel(10); + std::thread::spawn(|| GlobalState::run(receiver)); + + Ok(GlobalState { + engine, + component, + sender, + global_config, + }) + } + + /// Worker thread that executes the Tokio runtime, infinitely receiving + /// messages from the provided `receiver` and handling those requests. + /// + /// Each request is handled in a separate subtask so processing can all + /// happen concurrently. + fn run(mut receiver: mpsc::Receiver<NxtRequestInfo>) { + let rt = tokio::runtime::Runtime::new().unwrap(); + rt.block_on(async { + while let Some(msg) = receiver.recv().await { + let state = GLOBAL_STATE.get().unwrap(); + tokio::task::spawn(async move { + state.handle(msg).await.expect("failed to handle request") + }); + } + }); + } + + async fn handle(&'static self, mut info: NxtRequestInfo) -> Result<()> { + // Create a "Store" which is the unit of per-request isolation in + // Wasmtime. + let data = StoreState { + ctx: { + let mut cx = WasiCtxBuilder::new(); + // NB: while useful for debugging untrusted code probably + // shouldn't get raw access to stdout/stderr. + cx.inherit_stdout(); + cx.inherit_stderr(); + for dir in self.global_config.dirs.iter() { + let fd = Dir::open_ambient_dir(dir, ambient_authority()) + .with_context(|| { + format!("failed to open directory '{dir}'") + })?; + cx.preopened_dir( + fd, + DirPerms::all(), + FilePerms::all(), + dir, + ); + } + cx.build() + }, + table: ResourceTable::default(), + http: WasiHttpCtx, + }; + let mut store = Store::new(&self.engine, data); + + // Convert the `nxt_*` representation into the representation required + // by Wasmtime's `wasi-http` implementation using the Rust `http` + // crate. + let request = self.to_request_builder(&info)?; + let body = self.to_request_body(&mut info); + let request = request.body(body)?; + + let (sender, receiver) = tokio::sync::oneshot::channel(); + + // Instantiate the WebAssembly component and invoke its `handle` + // function which receives a request and where to put a response. + // + // Note that this is done in a sub-task to work concurrently with + // writing the response when it's available. This enables wasm to + // generate headers, write those below, and then compute the body + // afterwards. + let task = tokio::spawn(async move { + let (proxy, _) = wasmtime_wasi_http::proxy::Proxy::instantiate_pre( + &mut store, + &self.component, + ) + .await + .context("failed to instantiate")?; + let req = store.data_mut().new_incoming_request(request)?; + let out = store.data_mut().new_response_outparam(sender)?; + proxy + .wasi_http_incoming_handler() + .call_handle(&mut store, req, out) + .await + .context("failed to invoke wasm `handle`")?; + Ok::<_, anyhow::Error>(()) + }); + + // Wait for the wasm to produce the initial response. If this succeeds + // then propagate that failure. If this fails then wait for the above + // task to complete to see if it failed, otherwise panic since that's + // unexpected. + let response = match receiver.await { + Ok(response) => response.context("response generation failed")?, + Err(_) => { + task.await.unwrap()?; + panic!("sender of response disappeared"); + } + }; + + // Send the headers/status which will extract the body for the next + // phase. + let body = self.send_response(&mut info, response); + + // Send the body, a blocking operation, over time as it becomes + // available. + self.send_response_body(&mut info, body) + .await + .context("failed to write response body")?; + + // Join on completion of the wasm task which should be done by this + // point. + task.await.unwrap()?; + + // And finally signal that we're done. + info.request_done(); + + Ok(()) + } + + fn to_request_builder( + &self, + info: &NxtRequestInfo, + ) -> Result<http::request::Builder> { + let mut request = http::Request::builder(); + + request = request.method(info.method()); + request = match info.version() { + "HTTP/0.9" => request.version(http::Version::HTTP_09), + "HTTP/1.0" => request.version(http::Version::HTTP_10), + "HTTP/1.1" => request.version(http::Version::HTTP_11), + "HTTP/2.0" => request.version(http::Version::HTTP_2), + "HTTP/3.0" => request.version(http::Version::HTTP_3), + version => { + println!("unknown version: {version}"); + request + } + }; + + let uri = http::Uri::builder() + .scheme(if info.tls() { "https" } else { "http" }) + .authority(info.server_name()) + .path_and_query(info.target()) + .build() + .context("failed to build URI")?; + request = request.uri(uri); + + for (name, value) in info.fields() { + request = request.header(name, value); + } + Ok(request) + } + + fn to_request_body( + &self, + info: &mut NxtRequestInfo, + ) -> BoxBody<Bytes, ErrorCode> { + // TODO: should convert the body into a form of `Stream` to become an + // async stream of frames. The return value can represent that here + // but for now this slurps up the entire body into memory and puts it + // all in a single `BytesMut` which is then converted to `Bytes`. + let mut body = + BytesMut::with_capacity(info.content_length().try_into().unwrap()); + + // TODO: can this perform a partial read? + // TODO: how to make this async at the nxt level? + info.request_read(&mut body); + + Full::new(body.freeze()).map_err(|e| match e {}).boxed() + } + + fn send_response<T>( + &self, + info: &mut NxtRequestInfo, + response: http::Response<T>, + ) -> T { + info.init_response( + response.status().as_u16(), + response.headers().len().try_into().unwrap(), + response + .headers() + .iter() + .map(|(k, v)| k.as_str().len() + v.len()) + .sum::<usize>() + .try_into() + .unwrap(), + ); + for (k, v) in response.headers() { + info.add_field(k.as_str().as_bytes(), v.as_bytes()); + } + info.send_response(); + + response.into_body() + } + + async fn send_response_body( + &self, + info: &mut NxtRequestInfo, + mut body: BoxBody<Bytes, ErrorCode>, + ) -> Result<()> { + loop { + // Acquire the next frame, and because nothing is actually async + // at the moment this should never block meaning that the + // `Pending` case should not happen. + let frame = match body.frame().await { + Some(Ok(frame)) => frame, + Some(Err(e)) => break Err(e.into()), + None => break Ok(()), + }; + match frame.data_ref() { + Some(data) => { + info.response_write(&data); + } + None => { + // TODO: what to do with trailers? + } + } + } + } +} + +struct NxtRequestInfo { + info: *mut bindings::nxt_unit_request_info_t, +} + +// TODO: is this actually safe? +unsafe impl Send for NxtRequestInfo {} +unsafe impl Sync for NxtRequestInfo {} + +impl NxtRequestInfo { + fn method(&self) -> &str { + unsafe { + let raw = (*self.info).request; + self.get_str(&(*raw).method, (*raw).method_length.into()) + } + } + + fn tls(&self) -> bool { + unsafe { (*(*self.info).request).tls != 0 } + } + + fn version(&self) -> &str { + unsafe { + let raw = (*self.info).request; + self.get_str(&(*raw).version, (*raw).version_length.into()) + } + } + + fn server_name(&self) -> &str { + unsafe { + let raw = (*self.info).request; + self.get_str(&(*raw).server_name, (*raw).server_name_length.into()) + } + } + + fn target(&self) -> &str { + unsafe { + let raw = (*self.info).request; + self.get_str(&(*raw).target, (*raw).target_length.into()) + } + } + + fn content_length(&self) -> u64 { + unsafe { + let raw_request = (*self.info).request; + (*raw_request).content_length + } + } + + fn fields(&self) -> impl Iterator<Item = (&str, &str)> { + unsafe { + let raw = (*self.info).request; + (0..(*raw).fields_count).map(move |i| { + let field = (*raw).fields.as_ptr().add(i as usize); + let name = + self.get_str(&(*field).name, (*field).name_length.into()); + let value = + self.get_str(&(*field).value, (*field).value_length.into()); + (name, value) + }) + } + } + + fn request_read(&mut self, dst: &mut BytesMut) { + unsafe { + let rest = dst.spare_capacity_mut(); + let mut total_bytes_read = 0; + loop { + let amt = bindings::nxt_unit_request_read( + self.info, + rest.as_mut_ptr().wrapping_add(total_bytes_read).cast(), + 32 * 1024 * 1024, + ); + total_bytes_read += amt as usize; + if total_bytes_read >= rest.len() { + break; + } + } + // TODO: handle failure when `amt` is negative + let total_bytes_read: usize = total_bytes_read.try_into().unwrap(); + dst.set_len(dst.len() + total_bytes_read); + } + } + + fn response_write(&mut self, data: &[u8]) { + unsafe { + let rc = bindings::nxt_unit_response_write( + self.info, + data.as_ptr().cast(), + data.len(), + ); + assert_eq!(rc, 0); + } + } + + fn init_response(&mut self, status: u16, headers: u32, headers_size: u32) { + unsafe { + let rc = bindings::nxt_unit_response_init( + self.info, + status, + headers, + headers_size, + ); + assert_eq!(rc, 0); + } + } + + fn add_field(&mut self, key: &[u8], val: &[u8]) { + unsafe { + let rc = bindings::nxt_unit_response_add_field( + self.info, + key.as_ptr().cast(), + key.len().try_into().unwrap(), + val.as_ptr().cast(), + val.len().try_into().unwrap(), + ); + assert_eq!(rc, 0); + } + } + + fn send_response(&mut self) { + unsafe { + let rc = bindings::nxt_unit_response_send(self.info); + assert_eq!(rc, 0); + } + } + + fn request_done(self) { + unsafe { + bindings::nxt_unit_request_done( + self.info, + bindings::NXT_UNIT_OK as i32, + ); + } + } + + unsafe fn get_str( + &self, + ptr: &bindings::nxt_unit_sptr_t, + len: u32, + ) -> &str { + let ptr = bindings::nxt_unit_sptr_get(ptr); + let slice = std::slice::from_raw_parts(ptr, len.try_into().unwrap()); + std::str::from_utf8(slice).unwrap() + } +} + +struct StoreState { + ctx: WasiCtx, + http: WasiHttpCtx, + table: ResourceTable, +} + +impl WasiView for StoreState { + fn table(&self) -> &ResourceTable { + &self.table + } + fn table_mut(&mut self) -> &mut ResourceTable { + &mut self.table + } + fn ctx(&self) -> &WasiCtx { + &self.ctx + } + fn ctx_mut(&mut self) -> &mut WasiCtx { + &mut self.ctx + } +} + +impl WasiHttpView for StoreState { + fn ctx(&mut self) -> &mut WasiHttpCtx { + &mut self.http + } + fn table(&mut self) -> &mut ResourceTable { + &mut self.table + } +} + +impl StoreState {} diff --git a/src/wasm-wasi-component/wrapper.h b/src/wasm-wasi-component/wrapper.h new file mode 100644 index 00000000..93f3014a --- /dev/null +++ b/src/wasm-wasi-component/wrapper.h @@ -0,0 +1,5 @@ +#include <nxt_main.h> +#include <nxt_unit.h> +#include <nxt_unit_request.h> +#include <nxt_unit_typedefs.h> +#include <nxt_application.h> diff --git a/test/conftest.py b/test/conftest.py index 8d2850fd..2fe4d8dc 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -11,10 +11,12 @@ import sys import tempfile import time from multiprocessing import Process +from pathlib import Path import pytest -from unit.check.discover_available import discover_available + from unit.check.check_prerequisites import check_prerequisites +from unit.check.discover_available import discover_available from unit.http import HTTP1 from unit.log import Log from unit.log import print_log_on_assert @@ -265,27 +267,26 @@ def unit_run(state_dir=None): if not option.restart and 'unitd' in unit_instance: return unit_instance - builddir = f'{option.current_dir}/build' - libdir = f'{builddir}/lib' + builddir = f'{option.current_dir}/build' + libdir = f'{builddir}/lib' modulesdir = f'{libdir}/unit/modules' - sbindir = f'{builddir}/sbin' - unitd = f'{sbindir}/unitd' + sbindir = f'{builddir}/sbin' + unitd = f'{sbindir}/unitd' - if not os.path.isfile(unitd): - exit('Could not find unit') + if not Path(unitd).is_file(): + sys.exit('Could not find unit') - temp_dir = tempfile.mkdtemp(prefix='unit-test-') - option.temp_dir = temp_dir - public_dir(temp_dir) + temporary_dir = tempfile.mkdtemp(prefix='unit-test-') + option.temp_dir = temporary_dir + public_dir(temporary_dir) - if oct(stat.S_IMODE(os.stat(builddir).st_mode)) != '0o777': + if oct(stat.S_IMODE(Path(builddir).stat().st_mode)) != '0o777': public_dir(builddir) - statedir = f'{temp_dir}/state' if state_dir is None else state_dir - if not os.path.isdir(statedir): - os.mkdir(statedir) + statedir = f'{temporary_dir}/state' if state_dir is None else state_dir + Path(statedir).mkdir(exist_ok=True) - control_sock = f'{temp_dir}/control.unit.sock' + control_sock = f'{temporary_dir}/control.unit.sock' unitd_args = [ unitd, @@ -295,31 +296,32 @@ def unit_run(state_dir=None): '--statedir', statedir, '--pid', - f'{temp_dir}/unit.pid', + f'{temporary_dir}/unit.pid', '--log', - f'{temp_dir}/unit.log', + f'{temporary_dir}/unit.log', '--control', - f'unix:{temp_dir}/control.unit.sock', + f'unix:{temporary_dir}/control.unit.sock', '--tmpdir', - temp_dir, + temporary_dir, ] if option.user: unitd_args.extend(['--user', option.user]) - with open(f'{temp_dir}/unit.log', 'w') as log: + with open(f'{temporary_dir}/unit.log', 'w', encoding='utf-8') as log: unit_instance['process'] = subprocess.Popen(unitd_args, stderr=log) if not waitforfiles(control_sock): Log.print_log() - exit('Could not start unit') + sys.exit('Could not start unit') - unit_instance['temp_dir'] = temp_dir + unit_instance['temp_dir'] = temporary_dir unit_instance['control_sock'] = control_sock unit_instance['unitd'] = unitd - with open(f'{temp_dir}/unit.pid', 'r') as f: - unit_instance['pid'] = f.read().rstrip() + unit_instance['pid'] = ( + Path(f'{temporary_dir}/unit.pid').read_text(encoding='utf-8').rstrip() + ) if state_dir is None: _clear_conf() @@ -424,26 +426,27 @@ def _clear_conf(*, log=None): def _clear_temp_dir(): - temp_dir = unit_instance['temp_dir'] + temporary_dir = unit_instance['temp_dir'] - if is_findmnt and not waitforunmount(temp_dir, timeout=600): - exit('Could not unmount some filesystems in tmpdir ({temp_dir}).') + if is_findmnt and not waitforunmount(temporary_dir, timeout=600): + sys.exit('Could not unmount filesystems in tmpdir ({temporary_dir}).') - for item in os.listdir(temp_dir): - if item not in [ + for item in Path(temporary_dir).iterdir(): + if item.name not in [ 'control.unit.sock', 'state', 'unit.pid', 'unit.log', ]: - path = os.path.join(temp_dir, item) - public_dir(path) - if os.path.isfile(path) or stat.S_ISSOCK(os.stat(path).st_mode): - os.remove(path) + + public_dir(item) + + if item.is_file() or stat.S_ISSOCK(item.stat().st_mode): + item.unlink() else: for _ in range(10): try: - shutil.rmtree(path) + shutil.rmtree(item) break except OSError as err: # OSError: [Errno 16] Device or resource busy @@ -456,7 +459,7 @@ def _clear_temp_dir(): def _check_processes(): router_pid = _fds_info['router']['pid'] controller_pid = _fds_info['controller']['pid'] - unit_pid = unit_instance['pid'] + main_pid = unit_instance['pid'] for _ in range(600): out = ( @@ -466,7 +469,7 @@ def _check_processes(): .decode() .splitlines() ) - out = [l for l in out if unit_pid in l] + out = [l for l in out if main_pid in l] if len(out) <= 3: break @@ -485,14 +488,14 @@ def _check_processes(): out = [ l for l in out - if re.search(fr'{router_pid}\s+{unit_pid}.*unit: router', l) is None + if re.search(fr'{router_pid}\s+{main_pid}.*unit: router', l) is None ] assert len(out) == 1, 'one router' out = [ l for l in out - if re.search(fr'{controller_pid}\s+{unit_pid}.*unit: controller', l) + if re.search(fr'{controller_pid}\s+{main_pid}.*unit: controller', l) is None ] assert len(out) == 0, 'one controller' @@ -542,9 +545,9 @@ def _check_fds(*, log=None): def _count_fds(pid): - procfile = f'/proc/{pid}/fd' - if os.path.isdir(procfile): - return len(os.listdir(procfile)) + procfile = Path(f'/proc/{pid}/fd') + if procfile.is_dir(): + return len(list(procfile.iterdir())) try: out = subprocess.check_output( @@ -616,7 +619,7 @@ def pytest_sessionfinish(): public_dir(option.cache_dir) shutil.rmtree(option.cache_dir) - if not option.save_log and os.path.isdir(option.temp_dir): + if not option.save_log and Path(option.temp_dir).is_dir(): public_dir(option.temp_dir) shutil.rmtree(option.temp_dir) diff --git a/test/go/404/app.go b/test/go/404/app.go index 7eba2cf4..255f5dac 100644 --- a/test/go/404/app.go +++ b/test/go/404/app.go @@ -18,5 +18,5 @@ func handler(w http.ResponseWriter, r *http.Request) { func main() { http.HandleFunc("/", handler) - unit.ListenAndServe(":7080", nil) + unit.ListenAndServe(":8080", nil) } diff --git a/test/go/command_line_arguments/app.go b/test/go/command_line_arguments/app.go index 1101e1cf..5da12ffe 100644 --- a/test/go/command_line_arguments/app.go +++ b/test/go/command_line_arguments/app.go @@ -19,5 +19,5 @@ func handler(w http.ResponseWriter, r *http.Request) { func main() { http.HandleFunc("/", handler) - unit.ListenAndServe(":7080", nil) + unit.ListenAndServe(":8080", nil) } diff --git a/test/go/cookies/app.go b/test/go/cookies/app.go index 2216e153..49779d35 100644 --- a/test/go/cookies/app.go +++ b/test/go/cookies/app.go @@ -15,5 +15,5 @@ func handler(w http.ResponseWriter, r *http.Request) { func main() { http.HandleFunc("/", handler) - unit.ListenAndServe(":7080", nil) + unit.ListenAndServe(":8080", nil) } diff --git a/test/go/empty/app.go b/test/go/empty/app.go index 9326a19b..61e27f67 100644 --- a/test/go/empty/app.go +++ b/test/go/empty/app.go @@ -9,5 +9,5 @@ func handler(w http.ResponseWriter, r *http.Request) {} func main() { http.HandleFunc("/", handler) - unit.ListenAndServe(":7080", nil) + unit.ListenAndServe(":8080", nil) } diff --git a/test/go/get_variables/app.go b/test/go/get_variables/app.go index 1c0205a8..d70669f2 100644 --- a/test/go/get_variables/app.go +++ b/test/go/get_variables/app.go @@ -13,5 +13,5 @@ func handler(w http.ResponseWriter, r *http.Request) { func main() { http.HandleFunc("/", handler) - unit.ListenAndServe(":7080", nil) + unit.ListenAndServe(":8080", nil) } diff --git a/test/go/mirror/app.go b/test/go/mirror/app.go index 78f047c3..daf55df8 100644 --- a/test/go/mirror/app.go +++ b/test/go/mirror/app.go @@ -17,5 +17,5 @@ func handler(w http.ResponseWriter, r *http.Request) { func main() { http.HandleFunc("/", handler) - unit.ListenAndServe(":7080", nil) + unit.ListenAndServe(":8080", nil) } diff --git a/test/go/ns_inspect/app.go b/test/go/ns_inspect/app.go index 570580e6..977f0d9c 100644 --- a/test/go/ns_inspect/app.go +++ b/test/go/ns_inspect/app.go @@ -97,5 +97,5 @@ func handler(w http.ResponseWriter, r *http.Request) { func main() { http.HandleFunc("/", handler) - unit.ListenAndServe(":7080", nil) + unit.ListenAndServe(":8080", nil) } diff --git a/test/go/post_variables/app.go b/test/go/post_variables/app.go index e6279ac6..06900d4c 100644 --- a/test/go/post_variables/app.go +++ b/test/go/post_variables/app.go @@ -15,5 +15,5 @@ func handler(w http.ResponseWriter, r *http.Request) { func main() { http.HandleFunc("/", handler) - unit.ListenAndServe(":7080", nil) + unit.ListenAndServe(":8080", nil) } diff --git a/test/go/variables/app.go b/test/go/variables/app.go index 4be60cb7..9ef18aae 100644 --- a/test/go/variables/app.go +++ b/test/go/variables/app.go @@ -26,5 +26,5 @@ func handler(w http.ResponseWriter, r *http.Request) { func main() { http.HandleFunc("/", handler) - unit.ListenAndServe(":7080", nil) + unit.ListenAndServe(":8080", nil) } diff --git a/test/node/404/app.js b/test/node/404/app.js index ba15c104..8beec34a 100644 --- a/test/node/404/app.js +++ b/test/node/404/app.js @@ -3,4 +3,4 @@ var fs = require('fs'); require('http').createServer(function (req, res) { res.writeHead(404, {}).end(fs.readFileSync('404.html')); -}).listen(7080); +}).listen(8080); diff --git a/test/node/basic/app.js b/test/node/basic/app.js index 9092022c..2d870003 100644 --- a/test/node/basic/app.js +++ b/test/node/basic/app.js @@ -2,4 +2,4 @@ require('http').createServer(function (req, res) { res.writeHead(200, {'Content-Length': 12, 'Content-Type': 'text/plain'}) .end('Hello World\n'); -}).listen(7080); +}).listen(8080); diff --git a/test/node/double_end/app.js b/test/node/double_end/app.js index 653e33b1..e721db77 100644 --- a/test/node/double_end/app.js +++ b/test/node/double_end/app.js @@ -1,4 +1,4 @@ require('http').createServer(function (req, res) { res.end().end(); -}).listen(7080); +}).listen(8080); diff --git a/test/node/flush_headers/app.js b/test/node/flush_headers/app.js new file mode 100644 index 00000000..4c0e93bc --- /dev/null +++ b/test/node/flush_headers/app.js @@ -0,0 +1,7 @@ + +require('http').createServer(function (req, res) { + res.setHeader('X-Header', 'blah'); + res.flushHeaders(); + res.flushHeaders(); // Should be idempotent. + res.end(); +}).listen(8080); diff --git a/test/node/get_header_names/app.js b/test/node/get_header_names/app.js index a938b762..77aa0327 100644 --- a/test/node/get_header_names/app.js +++ b/test/node/get_header_names/app.js @@ -4,4 +4,4 @@ require('http').createServer(function (req, res) { res.setHeader('X-Header', 'blah'); res.setHeader('X-Names', res.getHeaderNames()); res.end(); -}).listen(7080); +}).listen(8080); diff --git a/test/node/get_header_type/app.js b/test/node/get_header_type/app.js index 6e45b71f..d5a591e8 100644 --- a/test/node/get_header_type/app.js +++ b/test/node/get_header_type/app.js @@ -3,4 +3,4 @@ require('http').createServer(function (req, res) { res.setHeader('X-Number', 100); res.setHeader('X-Type', typeof(res.getHeader('X-Number'))); res.end(); -}).listen(7080); +}).listen(8080); diff --git a/test/node/get_variables/app.js b/test/node/get_variables/app.js index cded43d2..8f317cb4 100644 --- a/test/node/get_variables/app.js +++ b/test/node/get_variables/app.js @@ -5,4 +5,4 @@ require('http').createServer(function (req, res) { res.setHeader('X-Var-2', query.var2); res.setHeader('X-Var-3', query.var3); res.end(); -}).listen(7080); +}).listen(8080); diff --git a/test/node/has_header/app.js b/test/node/has_header/app.js index 04b13916..30e75e4d 100644 --- a/test/node/has_header/app.js +++ b/test/node/has_header/app.js @@ -2,4 +2,4 @@ require('http').createServer(function (req, res) { res.setHeader('X-Has-Header', res.hasHeader(req.headers['x-header']) + ''); res.end(); -}).listen(7080); +}).listen(8080); diff --git a/test/node/header_name_case/app.js b/test/node/header_name_case/app.js index af157547..45acd507 100644 --- a/test/node/header_name_case/app.js +++ b/test/node/header_name_case/app.js @@ -4,4 +4,4 @@ require('http').createServer(function (req, res) { res.setHeader('X-header', '2'); res.setHeader('X-HEADER', '3'); res.end(); -}).listen(7080); +}).listen(8080); diff --git a/test/node/header_name_valid/app.js b/test/node/header_name_valid/app.js index c0c36098..9ac0c3a2 100644 --- a/test/node/header_name_valid/app.js +++ b/test/node/header_name_valid/app.js @@ -3,4 +3,4 @@ require('http').createServer(function (req, res) { res.writeHead(200, {}); res.setHeader('@$', 'test'); res.end(); -}).listen(7080); +}).listen(8080); diff --git a/test/node/header_value_object/app.js b/test/node/header_value_object/app.js index bacdc7d5..6f3d74bc 100644 --- a/test/node/header_value_object/app.js +++ b/test/node/header_value_object/app.js @@ -2,4 +2,4 @@ require('http').createServer(function (req, res) { res.setHeader('X-Header', {}); res.end(); -}).listen(7080); +}).listen(8080); diff --git a/test/node/loader/es_modules_http/app.mjs b/test/node/loader/es_modules_http/app.mjs index c7bcfe49..28ff08d8 100644 --- a/test/node/loader/es_modules_http/app.mjs +++ b/test/node/loader/es_modules_http/app.mjs @@ -3,4 +3,4 @@ import http from "http" http.createServer(function (req, res) { res.writeHead(200, {'Content-Length': 12, 'Content-Type': 'text/plain'}) .end('Hello World\n'); -}).listen(7080); +}).listen(8080); diff --git a/test/node/loader/es_modules_http_indirect/module.mjs b/test/node/loader/es_modules_http_indirect/module.mjs index c7bcfe49..28ff08d8 100644 --- a/test/node/loader/es_modules_http_indirect/module.mjs +++ b/test/node/loader/es_modules_http_indirect/module.mjs @@ -3,4 +3,4 @@ import http from "http" http.createServer(function (req, res) { res.writeHead(200, {'Content-Length': 12, 'Content-Type': 'text/plain'}) .end('Hello World\n'); -}).listen(7080); +}).listen(8080); diff --git a/test/node/loader/es_modules_websocket/app.mjs b/test/node/loader/es_modules_websocket/app.mjs index a71ffa9d..361d855b 100644 --- a/test/node/loader/es_modules_websocket/app.mjs +++ b/test/node/loader/es_modules_websocket/app.mjs @@ -4,7 +4,7 @@ import websocket from "websocket" let server = http.createServer(function() {}); let webSocketServer = websocket.server; -server.listen(7080, function() {}); +server.listen(8080, function() {}); var wsServer = new webSocketServer({ maxReceivedMessageSize: 0x1000000000, diff --git a/test/node/loader/es_modules_websocket_indirect/module.mjs b/test/node/loader/es_modules_websocket_indirect/module.mjs index a71ffa9d..361d855b 100644 --- a/test/node/loader/es_modules_websocket_indirect/module.mjs +++ b/test/node/loader/es_modules_websocket_indirect/module.mjs @@ -4,7 +4,7 @@ import websocket from "websocket" let server = http.createServer(function() {}); let webSocketServer = websocket.server; -server.listen(7080, function() {}); +server.listen(8080, function() {}); var wsServer = new webSocketServer({ maxReceivedMessageSize: 0x1000000000, diff --git a/test/node/loader/transitive_dependency/transitive_http.js b/test/node/loader/transitive_dependency/transitive_http.js index f1eb98e5..171758ed 100644 --- a/test/node/loader/transitive_dependency/transitive_http.js +++ b/test/node/loader/transitive_dependency/transitive_http.js @@ -3,6 +3,6 @@ const http = require("http"); http.createServer(function (req, res) { res.writeHead(200, {'Content-Length': 12, 'Content-Type': 'text/plain'}) .end('Hello World\n'); -}).listen(7080); +}).listen(8080); module.exports = http; diff --git a/test/node/loader/unit_http/app.js b/test/node/loader/unit_http/app.js index 9172e44f..0e0c2b24 100644 --- a/test/node/loader/unit_http/app.js +++ b/test/node/loader/unit_http/app.js @@ -1,4 +1,4 @@ require("unit-http").createServer(function (req, res) { res.writeHead(200, {'Content-Length': 12, 'Content-Type': 'text/plain'}) .end('Hello World\n'); -}).listen(7080); +}).listen(8080); diff --git a/test/node/mirror/app.js b/test/node/mirror/app.js index bdefe1cd..bdb89489 100644 --- a/test/node/mirror/app.js +++ b/test/node/mirror/app.js @@ -8,4 +8,4 @@ require('http').createServer(function (req, res) { res.writeHead(200, {'Content-Length': Buffer.byteLength(body)}) .end(body); }); -}).listen(7080); +}).listen(8080); diff --git a/test/node/options/app.js b/test/node/options/app.js new file mode 100644 index 00000000..bc538080 --- /dev/null +++ b/test/node/options/app.js @@ -0,0 +1,4 @@ +require('http').createServer({}, function (req, res) { + res.writeHead(200, {'Content-Length': 12, 'Content-Type': 'text/plain'}) + .end('Hello World\n'); +}).listen(8080); diff --git a/test/node/post_variables/app.js b/test/node/post_variables/app.js index 12b867cb..4d88434d 100644 --- a/test/node/post_variables/app.js +++ b/test/node/post_variables/app.js @@ -11,4 +11,4 @@ require('http').createServer(function (req, res) { res.setHeader('X-Var-3', query.var3); res.end(); }); -}).listen(7080); +}).listen(8080); diff --git a/test/node/promise_end/app.js b/test/node/promise_end/app.js index 373c3bc6..75c2ef7d 100644 --- a/test/node/promise_end/app.js +++ b/test/node/promise_end/app.js @@ -12,4 +12,4 @@ require('http').createServer(function (req, res) { fs.appendFile('callback', '', function() {}); }); -}).listen(7080); +}).listen(8080); diff --git a/test/node/promise_handler/app.js b/test/node/promise_handler/app.js index 32d7d7b9..7bcb1ae9 100644 --- a/test/node/promise_handler/app.js +++ b/test/node/promise_handler/app.js @@ -13,4 +13,4 @@ require('http').createServer(function (req, res) { fs.appendFile(data.toString(), '', function() {}); }); }); -}).listen(7080); +}).listen(8080); diff --git a/test/node/remove_header/app.js b/test/node/remove_header/app.js index 2a591235..877a7351 100644 --- a/test/node/remove_header/app.js +++ b/test/node/remove_header/app.js @@ -7,4 +7,4 @@ require('http').createServer(function (req, res) { res.setHeader('Has-Header', res.hasHeader('X-Header').toString()); res.end(); -}).listen(7080); +}).listen(8080); diff --git a/test/node/set_header_array/app.js b/test/node/set_header_array/app.js index 965330e2..0dee477e 100644 --- a/test/node/set_header_array/app.js +++ b/test/node/set_header_array/app.js @@ -2,4 +2,4 @@ require('http').createServer(function (req, res) { res.setHeader('Set-Cookie', ['tc=one,two,three', 'tc=four,five,six']); res.end(); -}).listen(7080); +}).listen(8080); diff --git a/test/node/status_message/app.js b/test/node/status_message/app.js index ba51d35b..53eea5c7 100644 --- a/test/node/status_message/app.js +++ b/test/node/status_message/app.js @@ -1,4 +1,4 @@ require('http').createServer(function (req, res) { res.writeHead(200, 'blah', {'Content-Type': 'text/plain'}).end(); -}).listen(7080); +}).listen(8080); diff --git a/test/node/update_header/app.js b/test/node/update_header/app.js index 905ac294..dc3415e6 100644 --- a/test/node/update_header/app.js +++ b/test/node/update_header/app.js @@ -3,4 +3,4 @@ require('http').createServer(function (req, res) { res.setHeader('X-Header', 'test'); res.setHeader('X-Header', 'new'); res.end(); -}).listen(7080); +}).listen(8080); diff --git a/test/node/variables/app.js b/test/node/variables/app.js index a569dddd..b6f36f37 100644 --- a/test/node/variables/app.js +++ b/test/node/variables/app.js @@ -15,4 +15,4 @@ require('http').createServer(function (req, res) { res.setHeader('Http-Host', req.headers['host']); res.writeHead(200, {}).end(body); }); -}).listen(7080); +}).listen(8080); diff --git a/test/node/websockets/mirror/app.js b/test/node/websockets/mirror/app.js index 0443adb2..ee2bdd18 100644 --- a/test/node/websockets/mirror/app.js +++ b/test/node/websockets/mirror/app.js @@ -2,7 +2,7 @@ server = require('http').createServer(function() {}); webSocketServer = require('websocket').server; -server.listen(7080, function() {}); +server.listen(8080, function() {}); var wsServer = new webSocketServer({ maxReceivedMessageSize: 0x1000000000, diff --git a/test/node/websockets/mirror_fragmentation/app.js b/test/node/websockets/mirror_fragmentation/app.js index ea580ac2..ca539ad6 100644 --- a/test/node/websockets/mirror_fragmentation/app.js +++ b/test/node/websockets/mirror_fragmentation/app.js @@ -2,7 +2,7 @@ server = require('http').createServer(function() {}); webSocketServer = require('websocket').server; -server.listen(7080, function() {}); +server.listen(8080, function() {}); var wsServer = new webSocketServer({ httpServer: server diff --git a/test/node/write_array/app.js b/test/node/write_array/app.js index b7abb3fc..761e8537 100644 --- a/test/node/write_array/app.js +++ b/test/node/write_array/app.js @@ -1,4 +1,4 @@ require('http').createServer(function (req, res) { res.writeHead(200, {'Content-Length': 5, 'Content-Type': 'text/plain'}) .end(new Uint8Array(Buffer.from('array', 'utf8'))); -}).listen(7080); +}).listen(8080); diff --git a/test/node/write_before_write_head/app.js b/test/node/write_before_write_head/app.js index 2293111a..10c8b9d6 100644 --- a/test/node/write_before_write_head/app.js +++ b/test/node/write_before_write_head/app.js @@ -2,4 +2,4 @@ require('http').createServer(function (req, res) { res.write('blah'); res.writeHead(200, {'Content-Type': 'text/plain'}).end(); -}).listen(7080); +}).listen(8080); diff --git a/test/node/write_buffer/app.js b/test/node/write_buffer/app.js index 72e9c600..24585ef2 100644 --- a/test/node/write_buffer/app.js +++ b/test/node/write_buffer/app.js @@ -2,4 +2,4 @@ require('http').createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}) .end(Buffer.from('buffer', 'utf8')); -}).listen(7080); +}).listen(8080); diff --git a/test/node/write_callback/app.js b/test/node/write_callback/app.js index 71eb4116..f03cb213 100644 --- a/test/node/write_callback/app.js +++ b/test/node/write_callback/app.js @@ -9,4 +9,4 @@ require('http').createServer(function (req, res) { fs.appendFile('callback', '', function() {}); }); res.end(a); -}).listen(7080); +}).listen(8080); diff --git a/test/node/write_multiple/app.js b/test/node/write_multiple/app.js index e9c51ae0..b1e485e7 100644 --- a/test/node/write_multiple/app.js +++ b/test/node/write_multiple/app.js @@ -4,4 +4,4 @@ require('http').createServer(function (req, res) { res.write('write'); res.write('write2'); res.end('end'); -}).listen(7080); +}).listen(8080); diff --git a/test/node/write_return/app.js b/test/node/write_return/app.js index 345b6c4b..d0b3d850 100644 --- a/test/node/write_return/app.js +++ b/test/node/write_return/app.js @@ -2,4 +2,4 @@ require('http').createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}) .end(res.write('body').toString()); -}).listen(7080); +}).listen(8080); diff --git a/test/python/body_bytearray/asgi.py b/test/python/body_bytearray/asgi.py new file mode 100644 index 00000000..6d2f402f --- /dev/null +++ b/test/python/body_bytearray/asgi.py @@ -0,0 +1,20 @@ +async def application(scope, receive, send): + assert scope['type'] == 'http' + + body = b'' + while True: + m = await receive() + body += m.get('body', b'') + if not m.get('more_body', False): + body = bytearray(body) + break + + await send( + { + 'type': 'http.response.start', + 'status': 200, + 'headers': [(b'content-length', str(len(body)).encode())], + } + ) + + await send({'type': 'http.response.body', 'body': body}) diff --git a/test/python/body_generate/wsgi.py b/test/python/body_generate/wsgi.py index 73462be6..3d12ac60 100644 --- a/test/python/body_generate/wsgi.py +++ b/test/python/body_generate/wsgi.py @@ -1,6 +1,6 @@ def application(env, start_response): length = env.get('HTTP_X_LENGTH', '10') - bytes = b'X' * int(length) + body = b'X' * int(length) start_response('200', [('Content-Length', length)]) - return [bytes] + return [body] diff --git a/test/python/delayed/asgi.py b/test/python/delayed/asgi.py index 1cb15a92..3c25e49a 100644 --- a/test/python/delayed/asgi.py +++ b/test/python/delayed/asgi.py @@ -33,7 +33,9 @@ async def application(scope, receive, send): { 'type': 'http.response.start', 'status': 200, - 'headers': [(b'content-length', str(len(body)).encode()),], + 'headers': [ + (b'content-length', str(len(body)).encode()), + ], } ) diff --git a/test/python/environment/wsgi.py b/test/python/environment/wsgi.py index 91e0ba49..622b8bc0 100644 --- a/test/python/environment/wsgi.py +++ b/test/python/environment/wsgi.py @@ -2,9 +2,11 @@ import os def application(env, start_response): - vars = env.get('HTTP_X_VARIABLES').split(',') + variables = env.get('HTTP_X_VARIABLES').split(',') - body = ','.join([str(os.environ[var]) for var in vars if var in os.environ]) + body = ','.join( + [str(os.environ[var]) for var in variables if var in os.environ] + ) body = body.encode() start_response('200', [('Content-Length', str(len(body)))]) diff --git a/test/python/iter_exception/wsgi.py b/test/python/iter_exception/wsgi.py index 2779a845..66a09af7 100644 --- a/test/python/iter_exception/wsgi.py +++ b/test/python/iter_exception/wsgi.py @@ -8,9 +8,7 @@ class application: def __iter__(self): self.__i = 0 self._skip_level = int(self.environ.get('HTTP_X_SKIP', 0)) - self._not_skip_close = int( - self.environ.get('HTTP_X_NOT_SKIP_CLOSE', 0) - ) + self._not_skip_close = int(self.environ.get('HTTP_X_NOT_SKIP_CLOSE', 0)) self._is_chunked = self.environ.get('HTTP_X_CHUNKED') headers = [(('Content-Length', '10'))] diff --git a/test/python/legacy/asgi.py b/test/python/legacy/asgi.py index 1d45cc4f..8be8932e 100644 --- a/test/python/legacy/asgi.py +++ b/test/python/legacy/asgi.py @@ -9,6 +9,8 @@ async def app_http(receive, send): { 'type': 'http.response.start', 'status': 200, - 'headers': [(b'content-length', b'0'),], + 'headers': [ + (b'content-length', b'0'), + ], } ) diff --git a/test/python/legacy_force/asgi.py b/test/python/legacy_force/asgi.py index ad2785f2..56c7061d 100644 --- a/test/python/legacy_force/asgi.py +++ b/test/python/legacy_force/asgi.py @@ -1,11 +1,10 @@ def application(scope, receive=None, send=None): assert scope['type'] == 'http' - if receive == None and send == None: + if receive is None and send is None: return app_http - else: - return app_http(receive, send) + return app_http(receive, send) async def app_http(receive, send): @@ -13,6 +12,8 @@ async def app_http(receive, send): { 'type': 'http.response.start', 'status': 200, - 'headers': [(b'content-length', b'0'),], + 'headers': [ + (b'content-length', b'0'), + ], } ) diff --git a/test/python/lifespan/empty/asgi.py b/test/python/lifespan/empty/asgi.py index 27395a28..2071e6e0 100644 --- a/test/python/lifespan/empty/asgi.py +++ b/test/python/lifespan/empty/asgi.py @@ -3,7 +3,7 @@ import os async def handler(prefix, scope, receive, send): if scope['type'] == 'lifespan': - with open(f'{prefix}version', 'w+') as f: + with open(f'{prefix}version', 'w+', encoding='utf-8') as f: f.write( f"{scope['asgi']['version']} {scope['asgi']['spec_version']}" ) diff --git a/test/python/lifespan/failed/asgi.py b/test/python/lifespan/failed/asgi.py index 8f315f70..fbd006f9 100644 --- a/test/python/lifespan/failed/asgi.py +++ b/test/python/lifespan/failed/asgi.py @@ -6,6 +6,6 @@ async def application(scope, receive, send): await send({"type": "lifespan.startup.failed"}) raise Exception('Exception blah') - elif message['type'] == 'lifespan.shutdown': + if message['type'] == 'lifespan.shutdown': await send({'type': 'lifespan.shutdown.complete'}) return diff --git a/test/python/restart/longstart.py b/test/python/restart/longstart.py index 777398ac..3bc383b7 100644 --- a/test/python/restart/longstart.py +++ b/test/python/restart/longstart.py @@ -3,6 +3,7 @@ import time time.sleep(2) + def application(environ, start_response): body = str(os.getpid()).encode() diff --git a/test/python/unicode/wsgi.py b/test/python/unicode/wsgi.py index f2f85f5d..8c9a59dd 100644 --- a/test/python/unicode/wsgi.py +++ b/test/python/unicode/wsgi.py @@ -1,7 +1,7 @@ def application(environ, start_response): temp_dir = environ.get('HTTP_TEMP_DIR') - with open(f'{temp_dir}/tempfile', 'w') as f: + with open(f'{temp_dir}/tempfile', 'w', encoding='utf-8') as f: f.write('\u26a0\ufe0f') start_response('200', [('Content-Length', '0')]) diff --git a/test/python/user_group/wsgi.py b/test/python/user_group/wsgi.py index 4003c064..8f3ba50d 100644 --- a/test/python/user_group/wsgi.py +++ b/test/python/user_group/wsgi.py @@ -6,7 +6,12 @@ def application(environ, start_response): uid = os.geteuid() gid = os.getegid() - out = json.dumps({'UID': uid, 'GID': gid,}).encode('utf-8') + out = json.dumps( + { + 'UID': uid, + 'GID': gid, + } + ).encode('utf-8') start_response( '200 OK', diff --git a/test/ruby/header_array/config.ru b/test/ruby/header_array/config.ru new file mode 100644 index 00000000..6401ab4b --- /dev/null +++ b/test/ruby/header_array/config.ru @@ -0,0 +1,7 @@ +app = Proc.new do |env| + ['200', { + 'x-array' => ['name=value', '', 'value', 'av'], + }, []] +end + +run app diff --git a/test/ruby/header_array_empty/config.ru b/test/ruby/header_array_empty/config.ru new file mode 100644 index 00000000..df40ffdd --- /dev/null +++ b/test/ruby/header_array_empty/config.ru @@ -0,0 +1,7 @@ +app = Proc.new do |env| + ['200', { + 'x-array' => [], + }, []] +end + +run app diff --git a/test/ruby/header_array_nil/config.ru b/test/ruby/header_array_nil/config.ru new file mode 100644 index 00000000..04550c8d --- /dev/null +++ b/test/ruby/header_array_nil/config.ru @@ -0,0 +1,7 @@ +app = Proc.new do |env| + ['200', { + 'x-array' => [nil], + }, []] +end + +run app diff --git a/test/ruby/input_rewind/config.ru b/test/ruby/input_rewind/config.ru deleted file mode 100644 index fc0d6535..00000000 --- a/test/ruby/input_rewind/config.ru +++ /dev/null @@ -1,8 +0,0 @@ -app = Proc.new do |env| - env['rack.input'].read - env['rack.input'].rewind - body = env['rack.input'].read - ['200', {'Content-Length' => body.length.to_s}, [body]] -end - -run app diff --git a/test/ruby/multipart/config.ru b/test/ruby/multipart/config.ru new file mode 100644 index 00000000..9187997c --- /dev/null +++ b/test/ruby/multipart/config.ru @@ -0,0 +1,7 @@ +app = Proc.new do |env| + [200, { + 'x-multipart-buffer' => env['rack.multipart.buffer_size'].to_s + }, []] +end + +run app diff --git a/test/ruby/session/config.ru b/test/ruby/session/config.ru new file mode 100644 index 00000000..8cea0588 --- /dev/null +++ b/test/ruby/session/config.ru @@ -0,0 +1,6 @@ +app = Proc.new do |env| + env['rack.session'].clear + [200, {}, []] +end + +run app diff --git a/test/test_access_log.py b/test/test_access_log.py index bccea56f..1b0ec8ad 100644 --- a/test/test_access_log.py +++ b/test/test_access_log.py @@ -1,6 +1,7 @@ import time import pytest + from unit.applications.lang.python import ApplicationPython from unit.option import option @@ -17,16 +18,20 @@ def load(script): ), 'access_log configure' -def set_format(format): +def set_format(log_format): assert 'success' in client.conf( { 'path': f'{option.temp_dir}/access.log', - 'format': format, + 'format': log_format, }, 'access_log', ), 'access_log format' +def set_if(condition): + assert 'success' in client.conf(f'"{condition}"', 'access_log/if') + + def test_access_log_keepalive(wait_for_record): load('mirror') @@ -93,7 +98,7 @@ def test_access_log_ipv6(wait_for_record): load('empty') assert 'success' in client.conf( - {"[::1]:7080": {"pass": "applications/empty"}}, 'listeners' + {"[::1]:8080": {"pass": "applications/empty"}}, 'listeners' ) client.get(sock_type='ipv6') @@ -283,14 +288,14 @@ def test_access_log_change(temp_dir, wait_for_record): def test_access_log_format(wait_for_record): load('empty') - def check_format(format, expect, url='/'): - set_format(format) + def check_format(log_format, expect, url='/'): + set_format(log_format) assert client.get(url=url)['status'] == 200 assert wait_for_record(expect, 'access.log') is not None, 'found' - format = 'BLAH\t0123456789' - check_format(format, format) + log_format = 'BLAH\t0123456789' + check_format(log_format, log_format) check_format('$uri $status $uri $status', '/ 200 / 200') @@ -307,6 +312,62 @@ def test_access_log_variables(wait_for_record): ), '$body_bytes_sent' +def test_access_log_if(search_in_file, wait_for_record): + load('empty') + set_format('$uri') + + def try_if(condition): + set_if(condition) + assert client.get(url=f'/{condition}')['status'] == 200 + + # const + + try_if('') + try_if('0') + try_if('false') + try_if('undefined') + try_if('!') + try_if('!null') + try_if('1') + + # variable + + set_if('$arg_foo') + assert client.get(url='/bar?bar')['status'] == 200 + assert client.get(url='/foo_empty?foo')['status'] == 200 + assert client.get(url='/foo?foo=1')['status'] == 200 + + # check results + + assert wait_for_record(r'^/foo$', 'access.log') is not None + + assert search_in_file(r'^/$', 'access.log') is None + assert search_in_file(r'^/0$', 'access.log') is None + assert search_in_file(r'^/false$', 'access.log') is None + assert search_in_file(r'^/undefined$', 'access.log') is None + assert search_in_file(r'^/!$', 'access.log') is not None + assert search_in_file(r'^/!null$', 'access.log') is not None + assert search_in_file(r'^/1$', 'access.log') is not None + + assert search_in_file(r'^/bar$', 'access.log') is None + assert search_in_file(r'^/foo_empty$', 'access.log') is None + + +def test_access_log_if_njs(require, search_in_file, wait_for_record): + require({'modules': {'njs': 'any'}}) + + load('empty') + set_format('$uri') + + set_if('`${args.foo == \'1\'}`') + + assert client.get(url='/foo_2?foo=2')['status'] == 200 + assert client.get(url='/foo_1?foo=1')['status'] == 200 + + assert wait_for_record(r'^/foo_1$', 'access.log') is not None + assert search_in_file(r'^/foo_2$', 'access.log') is None + + def test_access_log_incorrect(temp_dir, skip_alert): skip_alert(r'failed to apply new conf') @@ -322,3 +383,5 @@ def test_access_log_incorrect(temp_dir, skip_alert): }, 'access_log', ), 'access_log format incorrect' + + assert 'error' in client.conf('$arg_', 'access_log/if') diff --git a/test/test_asgi_application.py b/test/test_asgi_application.py index 98d4bcd5..226a1ed7 100644 --- a/test/test_asgi_application.py +++ b/test/test_asgi_application.py @@ -3,6 +3,7 @@ import time import pytest from packaging import version + from unit.applications.lang.python import ApplicationPython prerequisites = { @@ -60,7 +61,7 @@ def test_asgi_application_ipv6(): client.load('empty') assert 'success' in client.conf( - {"[::1]:7080": {"pass": "applications/empty"}}, 'listeners' + {"[::1]:8080": {"pass": "applications/empty"}}, 'listeners' ) assert client.get(sock_type='ipv6')['status'] == 200 @@ -172,7 +173,7 @@ def test_asgi_application_server_port(): client.load('server_port') assert ( - client.get()['headers']['Server-Port'] == '7080' + client.get()['headers']['Server-Port'] == '8080' ), 'Server-Port header' @@ -217,6 +218,14 @@ def test_asgi_application_shm_ack_handle(): assert resp['body'] == body, 'keep-alive 1' +def test_asgi_application_body_bytearray(): + client.load('body_bytearray') + + body = '0123456789' + + assert client.post(body=body)['body'] == body + + def test_asgi_keepalive_body(): client.load('mirror') diff --git a/test/test_asgi_application_unix_abstract.py b/test/test_asgi_application_unix_abstract.py index 980a98a9..f35ce0d7 100644 --- a/test/test_asgi_application_unix_abstract.py +++ b/test/test_asgi_application_unix_abstract.py @@ -1,4 +1,5 @@ from packaging import version + from unit.applications.lang.python import ApplicationPython prerequisites = { diff --git a/test/test_asgi_lifespan.py b/test/test_asgi_lifespan.py index 499f523d..e09ea1cc 100644 --- a/test/test_asgi_lifespan.py +++ b/test/test_asgi_lifespan.py @@ -1,7 +1,8 @@ -import os +from pathlib import Path -from conftest import unit_stop from packaging import version + +from conftest import unit_stop from unit.applications.lang.python import ApplicationPython from unit.option import option @@ -14,32 +15,26 @@ client = ApplicationPython(load_module='asgi') def assert_cookies(prefix): for name in ['startup', 'shutdown']: - path = f'{option.test_dir}/python/lifespan/empty/{prefix}{name}' - exists = os.path.isfile(path) - if exists: - os.remove(path) + path = Path(f'{option.test_dir}/python/lifespan/empty/{prefix}{name}') + exists = path.is_file() + path.unlink(missing_ok=True) assert not exists, name - path = f'{option.test_dir}/python/lifespan/empty/{prefix}version' + path = Path(f'{option.test_dir}/python/lifespan/empty/{prefix}version') + versions = path.read_text(encoding='utf-8') + path.unlink() - with open(path, 'r') as f: - version = f.read() - - os.remove(path) - - assert version == '3.0 2.0', 'version' + assert versions == '3.0 2.0', 'versions' def setup_cookies(prefix): - base_dir = f'{option.test_dir}/python/lifespan/empty' - - os.chmod(base_dir, 0o777) + base_dir = Path(f'{option.test_dir}/python/lifespan/empty') + base_dir.chmod(0o777) for name in ['startup', 'shutdown', 'version']: - path = f'{option.test_dir}/python/lifespan/empty/{prefix}{name}' - open(path, 'a').close() - os.chmod(path, 0o777) + path = Path(f'{option.test_dir}/python/lifespan/empty/{prefix}{name}') + path.touch(0o777) def test_asgi_lifespan(): @@ -59,7 +54,7 @@ def test_asgi_lifespan_targets(): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [ { "match": {"uri": "/1"}, diff --git a/test/test_asgi_targets.py b/test/test_asgi_targets.py index c3ec22f0..3d4e2e24 100644 --- a/test/test_asgi_targets.py +++ b/test/test_asgi_targets.py @@ -1,5 +1,6 @@ import pytest from packaging import version + from unit.applications.lang.python import ApplicationPython from unit.option import option @@ -16,7 +17,7 @@ def setup_method_fixture(): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [ { "match": {"uri": "/1"}, diff --git a/test/test_asgi_websockets.py b/test/test_asgi_websockets.py index eb7a20e7..f93c97ab 100644 --- a/test/test_asgi_websockets.py +++ b/test/test_asgi_websockets.py @@ -3,6 +3,7 @@ import time import pytest from packaging import version + from unit.applications.lang.python import ApplicationPython from unit.applications.websockets import ApplicationWebsocket diff --git a/test/test_client_ip.py b/test/test_client_ip.py index 82c76718..538db18b 100644 --- a/test/test_client_ip.py +++ b/test/test_client_ip.py @@ -1,4 +1,5 @@ import pytest + from unit.applications.lang.python import ApplicationPython from unit.option import option @@ -15,11 +16,11 @@ def setup_method_fixture(): def client_ip(options): assert 'success' in client.conf( { - "127.0.0.1:7081": { + "127.0.0.1:8081": { "client_ip": options, "pass": "applications/client_ip", }, - "[::1]:7082": { + "[::1]:8082": { "client_ip": options, "pass": "applications/client_ip", }, @@ -34,8 +35,8 @@ def client_ip(options): def get_xff(xff, sock_type='ipv4'): address = { - 'ipv4': ('127.0.0.1', 7081), - 'ipv6': ('::1', 7082), + 'ipv4': ('127.0.0.1', 8081), + 'ipv6': ('::1', 8082), 'unix': (f'{option.temp_dir}/sock', None), } (addr, port) = address[sock_type] @@ -51,9 +52,9 @@ def get_xff(xff, sock_type='ipv4'): def test_client_ip_single_ip(): client_ip({'header': 'X-Forwarded-For', 'source': '123.123.123.123'}) - assert client.get(port=7081)['body'] == '127.0.0.1', 'ipv4 default' + assert client.get(port=8081)['body'] == '127.0.0.1', 'ipv4 default' assert ( - client.get(sock_type='ipv6', port=7082)['body'] == '::1' + client.get(sock_type='ipv6', port=8082)['body'] == '::1' ), 'ipv6 default' assert get_xff('1.1.1.1') == '127.0.0.1', 'bad source' assert get_xff('blah') == '127.0.0.1', 'bad header' @@ -61,9 +62,9 @@ def test_client_ip_single_ip(): client_ip({'header': 'X-Forwarded-For', 'source': '127.0.0.1'}) - assert client.get(port=7081)['body'] == '127.0.0.1', 'ipv4 default 2' + assert client.get(port=8081)['body'] == '127.0.0.1', 'ipv4 default 2' assert ( - client.get(sock_type='ipv6', port=7082)['body'] == '::1' + client.get(sock_type='ipv6', port=8082)['body'] == '::1' ), 'ipv6 default 2' assert get_xff('1.1.1.1') == '1.1.1.1', 'replace' assert get_xff('blah') == '127.0.0.1', 'bad header 2' @@ -159,7 +160,7 @@ def test_client_ip_empty_source(): def test_client_ip_invalid(): assert 'error' in client.conf( { - "127.0.0.1:7081": { + "127.0.0.1:8081": { "client_ip": {"source": '127.0.0.1'}, "pass": "applications/client_ip", } @@ -170,7 +171,7 @@ def test_client_ip_invalid(): def check_invalid_source(source): assert 'error' in client.conf( { - "127.0.0.1:7081": { + "127.0.0.1:8081": { "client_ip": { "header": "X-Forwarded-For", "source": source, diff --git a/test/test_configuration.py b/test/test_configuration.py index 19a2a1a5..a7d519e9 100644 --- a/test/test_configuration.py +++ b/test/test_configuration.py @@ -1,6 +1,7 @@ import socket import pytest + from unit.control import Control prerequisites = {'modules': {'python': 'any'}} @@ -234,12 +235,12 @@ def test_applications_relative_path(): @pytest.mark.skip('not yet, unsafe') def test_listeners_empty(): - assert 'error' in client.conf({"*:7080": {}}, 'listeners'), 'listener empty' + assert 'error' in client.conf({"*:8080": {}}, 'listeners'), 'listener empty' def test_listeners_no_app(): assert 'error' in client.conf( - {"*:7080": {"pass": "applications/app"}}, 'listeners' + {"*:8080": {"pass": "applications/app"}}, 'listeners' ), 'listeners no app' @@ -254,9 +255,9 @@ def test_listeners_unix_abstract(system): def test_listeners_addr(): - assert 'success' in try_addr("*:7080"), 'wildcard' - assert 'success' in try_addr("127.0.0.1:7081"), 'explicit' - assert 'success' in try_addr("[::1]:7082"), 'explicit ipv6' + assert 'success' in try_addr("*:8080"), 'wildcard' + assert 'success' in try_addr("127.0.0.1:8081"), 'explicit' + assert 'success' in try_addr("[::1]:8082"), 'explicit ipv6' def test_listeners_addr_error(): @@ -266,7 +267,7 @@ def test_listeners_addr_error(): def test_listeners_addr_error_2(skip_alert): skip_alert(r'bind.*failed', r'failed to apply new conf') - assert 'error' in try_addr("[f607:7403:1e4b:6c66:33b2:843f:2517:da27]:7080") + assert 'error' in try_addr("[f607:7403:1e4b:6c66:33b2:843f:2517:da27]:8080") def test_listeners_port_release(): @@ -277,7 +278,7 @@ def test_listeners_port_release(): client.conf( { - "listeners": {"127.0.0.1:7080": {"pass": "routes"}}, + "listeners": {"127.0.0.1:8080": {"pass": "routes"}}, "routes": [], } ) @@ -285,7 +286,7 @@ def test_listeners_port_release(): resp = client.conf({"listeners": {}, "applications": {}}) try: - s.bind(('127.0.0.1', 7080)) + s.bind(('127.0.0.1', 8080)) s.listen() except OSError: @@ -302,7 +303,7 @@ def test_json_application_name_large(): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": f"applications/{name}"}}, + "listeners": {"*:8080": {"pass": f"applications/{name}"}}, "applications": { name: { "type": "python", @@ -349,7 +350,7 @@ def test_json_application_python_prefix(): "prefix": "/app", } }, - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [ { "match": {"uri": "/app/*"}, @@ -378,7 +379,7 @@ def test_json_application_prefix_target(): }, } }, - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [ { "match": {"uri": "/app/*"}, @@ -405,7 +406,7 @@ def test_json_application_invalid_python_prefix(): "prefix": "app", } }, - "listeners": {"*:7080": {"pass": "applications/sub-app"}}, + "listeners": {"*:8080": {"pass": "applications/sub-app"}}, } assert 'error' in client.conf(conf) @@ -422,7 +423,7 @@ def test_json_application_empty_python_prefix(): "prefix": "", } }, - "listeners": {"*:7080": {"pass": "applications/sub-app"}}, + "listeners": {"*:8080": {"pass": "applications/sub-app"}}, } assert 'error' in client.conf(conf) @@ -441,7 +442,7 @@ def test_json_application_many2(): # open files limit due to the lack of file descriptors. for a in range(100) }, - "listeners": {"*:7080": {"pass": "applications/app-1"}}, + "listeners": {"*:8080": {"pass": "applications/app-1"}}, } assert 'success' in client.conf(conf) diff --git a/test/test_forwarded_header.py b/test/test_forwarded_header.py index c3f4a4c6..4b2f9424 100644 --- a/test/test_forwarded_header.py +++ b/test/test_forwarded_header.py @@ -1,4 +1,5 @@ import pytest + from unit.applications.lang.python import ApplicationPython prerequisites = {'modules': {'python': 'any'}} @@ -14,11 +15,11 @@ def setup_method_fixture(): def forwarded_header(forwarded): assert 'success' in client.conf( { - "127.0.0.1:7081": { + "127.0.0.1:8081": { "forwarded": forwarded, "pass": "applications/forwarded_header", }, - "[::1]:7082": { + "[::1]:8082": { "forwarded": forwarded, "pass": "applications/forwarded_header", }, @@ -28,7 +29,7 @@ def forwarded_header(forwarded): def get_fwd(sock_type='ipv4', xff=None, xfp=None): - port = 7081 if sock_type == 'ipv4' else 7082 + port = 8081 if sock_type == 'ipv4' else 8082 headers = {'Connection': 'close'} @@ -243,7 +244,7 @@ def test_forwarded_header_source_range(): def test_forwarded_header_invalid(): assert 'error' in client.conf( { - "127.0.0.1:7081": { + "127.0.0.1:8081": { "forwarded": {"source": '127.0.0.1'}, "pass": "applications/forwarded_header", } @@ -254,7 +255,7 @@ def test_forwarded_header_invalid(): def check_invalid_source(source): assert 'error' in client.conf( { - "127.0.0.1:7081": { + "127.0.0.1:8081": { "forwarded": { "client_ip": "X-Forwarded-For", "source": source, diff --git a/test/test_go_application.py b/test/test_go_application.py index 8f406744..469d4346 100644 --- a/test/test_go_application.py +++ b/test/test_go_application.py @@ -123,7 +123,7 @@ def test_go_application_command_line_arguments_type(): client.load('command_line_arguments') assert 'error' in client.conf( - '' "a b c", 'applications/command_line_arguments/arguments' + "a b c", 'applications/command_line_arguments/arguments' ), 'arguments type' diff --git a/test/test_go_isolation.py b/test/test_go_isolation.py index ba3390ea..a864e9f6 100644 --- a/test/test_go_isolation.py +++ b/test/test_go_isolation.py @@ -3,6 +3,7 @@ import os import pwd import pytest + from unit.applications.lang.go import ApplicationGo from unit.option import option from unit.utils import getns @@ -319,7 +320,7 @@ def test_go_isolation_rootfs_container_priv(require, temp_dir): def test_go_isolation_rootfs_automount_tmpfs(is_su, require, temp_dir): try: - open("/proc/self/mountinfo") + open("/proc/self/mountinfo", encoding='utf-8') except: pytest.skip('The system lacks /proc/self/mountinfo file') diff --git a/test/test_http_header.py b/test/test_http_header.py index af836e6f..f6579df5 100644 --- a/test/test_http_header.py +++ b/test/test_http_header.py @@ -1,4 +1,5 @@ import pytest + from unit.applications.lang.python import ApplicationPython prerequisites = {'modules': {'python': 'any'}} @@ -333,7 +334,7 @@ def test_http_header_host_port(): client.load('host') resp = client.get( - headers={'Host': 'exmaple.com:7080', 'Connection': 'close'} + headers={'Host': 'exmaple.com:8080', 'Connection': 'close'} ) assert resp['status'] == 200, 'Host port status' @@ -341,7 +342,7 @@ def test_http_header_host_port(): resp['headers']['X-Server-Name'] == 'exmaple.com' ), 'Host port SERVER_NAME' assert ( - resp['headers']['X-Http-Host'] == 'exmaple.com:7080' + resp['headers']['X-Http-Host'] == 'exmaple.com:8080' ), 'Host port HTTP_HOST' @@ -373,14 +374,14 @@ def test_http_header_host_literal(): def test_http_header_host_literal_ipv6(): client.load('host') - resp = client.get(headers={'Host': '[::1]:7080', 'Connection': 'close'}) + resp = client.get(headers={'Host': '[::1]:8080', 'Connection': 'close'}) assert resp['status'] == 200, 'Host literal ipv6 status' assert ( resp['headers']['X-Server-Name'] == '[::1]' ), 'Host literal ipv6 SERVER_NAME' assert ( - resp['headers']['X-Http-Host'] == '[::1]:7080' + resp['headers']['X-Http-Host'] == '[::1]:8080' ), 'Host literal ipv6 HTTP_HOST' diff --git a/test/test_java_application.py b/test/test_java_application.py index eefc5c79..33151182 100644 --- a/test/test_java_application.py +++ b/test/test_java_application.py @@ -1,7 +1,7 @@ import io -import os import re import time +from pathlib import Path from unit.applications.lang.java import ApplicationJava from unit.option import option @@ -20,7 +20,7 @@ def test_java_conf_error(temp_dir, skip_alert): ) assert 'error' in client.conf( { - "listeners": {"*:7080": {"pass": "applications/app"}}, + "listeners": {"*:8080": {"pass": "applications/app"}}, "applications": { "app": { "type": client.get_application_type(), @@ -875,6 +875,7 @@ def test_java_application_get_headers(): assert headers['X-Reply-0'] == 'blah', 'get headers' assert headers['X-Reply-1'] == 'blah', 'get headers 2' + def test_java_application_many_headers(): client.load('get_headers') @@ -956,7 +957,7 @@ def test_java_application_multipart(search_in_file, temp_dir): reldst = '/uploads' fulldst = f'{temp_dir}{reldst}' - os.mkdir(fulldst) + Path(fulldst).mkdir(parents=True) public_dir(fulldst) fields = { diff --git a/test/test_java_isolation_rootfs.py b/test/test_java_isolation_rootfs.py index 66b2a81e..0ed66133 100644 --- a/test/test_java_isolation_rootfs.py +++ b/test/test_java_isolation_rootfs.py @@ -2,6 +2,7 @@ import os import subprocess import pytest + from unit.applications.lang.java import ApplicationJava from unit.option import option @@ -25,6 +26,7 @@ def setup_method_fixture(temp_dir): f'{temp_dir}/jars', ], stderr=subprocess.STDOUT, + check=True, ) except KeyboardInterrupt: @@ -39,6 +41,7 @@ def setup_method_fixture(temp_dir): subprocess.run( ["umount", "--lazy", f"{option.temp_dir}/jars"], stderr=subprocess.STDOUT, + check=True, ) except KeyboardInterrupt: diff --git a/test/test_java_websockets.py b/test/test_java_websockets.py index c323830b..94ac6f86 100644 --- a/test/test_java_websockets.py +++ b/test/test_java_websockets.py @@ -2,6 +2,7 @@ import struct import time import pytest + from unit.applications.lang.java import ApplicationJava from unit.applications.websockets import ApplicationWebsocket diff --git a/test/test_njs.py b/test/test_njs.py index 162cc0bd..8ef815fd 100644 --- a/test/test_njs.py +++ b/test/test_njs.py @@ -1,6 +1,7 @@ -import os +from pathlib import Path import pytest + from unit.applications.proto import ApplicationProto from unit.option import option from unit.utils import waitforfiles @@ -14,7 +15,7 @@ client = ApplicationProto() def setup_method_fixture(temp_dir): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [{"action": {"share": f"{temp_dir}/assets$uri"}}], } ) @@ -22,9 +23,9 @@ def setup_method_fixture(temp_dir): def create_files(*files): assets_dir = f'{option.temp_dir}/assets/' - os.makedirs(assets_dir) + Path(assets_dir).mkdir(exist_ok=True) - [open(assets_dir + f, 'a') for f in files] + _ = [Path(assets_dir + f).touch() for f in files] waitforfiles(*[assets_dir + f for f in files]) @@ -82,6 +83,38 @@ def test_njs_variables(temp_dir): set_share(f'"`{temp_dir}/assets/${{args.foo}}`"') assert client.get(url='/?foo=str')['status'] == 200, 'args' + check_expression('/${vars.header_host}') + + set_share(f'"`{temp_dir}/assets/${{vars[\\"arg_foo\\"]}}`"') + assert client.get(url='/?foo=str')['status'] == 200, 'vars' + + set_share(f'"`{temp_dir}/assets/${{vars.non_exist}}`"') + assert client.get()['status'] == 404, 'undefined' + + create_files('undefined') + assert client.get()['status'] == 200, 'undefined 2' + + +def test_njs_variables_cacheable(temp_dir): + create_files('str') + + def check_rewrite(rewrite, uri): + assert 'success' in client.conf( + [ + { + "action": { + "rewrite": rewrite, + "share": f"`{temp_dir}/assets{uri}`", + }, + }, + ], + 'routes', + ) + assert client.get()['status'] == 200 + + check_rewrite('/str', '${uri}') + check_rewrite('/str', '${vars.uri}') + def test_njs_invalid(skip_alert): skip_alert(r'js exception:') @@ -92,6 +125,7 @@ def test_njs_invalid(skip_alert): check_invalid('"`a"') check_invalid('"`a``"') check_invalid('"`a`/"') + check_invalid('"`${vars.}`"') def check_invalid_resolve(template): assert 'success' in client.conf(template, 'routes/0/action/share') @@ -99,3 +133,4 @@ def test_njs_invalid(skip_alert): check_invalid_resolve('"`${a}`"') check_invalid_resolve('"`${uri.a.a}`"') + check_invalid_resolve('"`${vars.a.a}`"') diff --git a/test/test_njs_modules.py b/test/test_njs_modules.py index d821d455..a639fabd 100644 --- a/test/test_njs_modules.py +++ b/test/test_njs_modules.py @@ -23,7 +23,7 @@ def test_njs_modules(): assert 'success' in client.conf( { "settings": {"js_module": "next"}, - "listeners": {"*:7080": {"pass": "routes/first"}}, + "listeners": {"*:8080": {"pass": "routes/first"}}, "routes": { "first": [{"action": {"pass": "`routes/${next.route()}`"}}], "next": [{"action": {"return": 200}}], @@ -68,7 +68,7 @@ def test_njs_modules_import(): assert 'success' in client.conf( { "settings": {"js_module": "import_from"}, - "listeners": {"*:7080": {"pass": "routes/first"}}, + "listeners": {"*:8080": {"pass": "routes/first"}}, "routes": { "first": [ {"action": {"pass": "`routes/${import_from.num()}`"}} @@ -86,7 +86,7 @@ def test_njs_modules_this(): assert 'success' in client.conf( { "settings": {"js_module": "global_this"}, - "listeners": {"*:7080": {"pass": "routes/first"}}, + "listeners": {"*:8080": {"pass": "routes/first"}}, "routes": { "first": [ {"action": {"pass": "`routes/${global_this.str()}`"}} diff --git a/test/test_node_application.py b/test/test_node_application.py index ab8aa8f8..88ae3136 100644 --- a/test/test_node_application.py +++ b/test/test_node_application.py @@ -1,6 +1,7 @@ import re import pytest + from unit.applications.lang.node import ApplicationNode from unit.utils import waitforfiles @@ -20,6 +21,12 @@ def test_node_application_basic(): assert_basic_application() +def test_node_application_options(wait_for_record): + client.load('options') + + assert_basic_application() + assert wait_for_record(r'constructor was called with unsupported') is not None + def test_node_application_loader_unit_http(): client.load('loader/unit_http') @@ -79,7 +86,7 @@ def test_node_application_variables(date_to_sec_epoch, sec_epoch): 'Request-Method': 'POST', 'Request-Uri': '/', 'Http-Host': 'localhost', - 'Server-Protocol': 'HTTP/1.1', + 'Server-Protocol': '1.1', 'Custom-Header': 'blah', }, 'headers' assert resp['body'] == body, 'body' @@ -149,11 +156,13 @@ def test_node_application_write_buffer(): assert client.get()['body'] == 'buffer', 'write buffer' + def test_node_application_write_array(): client.load('write_array') assert client.get()['body'] == 'array', 'write array' + def test_node_application_write_callback(temp_dir): client.load('write_callback') @@ -304,6 +313,12 @@ def test_node_application_get_header_names(): ], 'get header names' +def test_node_application_flush_headers(): + client.load('flush_headers') + + assert client.get()['headers']['X-Header'] == 'blah' + + def test_node_application_has_header(): client.load('has_header') diff --git a/test/test_node_es_modules.py b/test/test_node_es_modules.py index ac2c545f..4effafea 100644 --- a/test/test_node_es_modules.py +++ b/test/test_node_es_modules.py @@ -1,4 +1,5 @@ from packaging import version + from unit.applications.lang.node import ApplicationNode from unit.applications.websockets import ApplicationWebsocket diff --git a/test/test_node_websockets.py b/test/test_node_websockets.py index d26452aa..68bdd578 100644 --- a/test/test_node_websockets.py +++ b/test/test_node_websockets.py @@ -2,6 +2,7 @@ import struct import time import pytest + from unit.applications.lang.node import ApplicationNode from unit.applications.websockets import ApplicationWebsocket diff --git a/test/test_perl_application.py b/test/test_perl_application.py index 7e6571fb..ad355117 100644 --- a/test/test_perl_application.py +++ b/test/test_perl_application.py @@ -1,6 +1,7 @@ import re import pytest + from unit.applications.lang.perl import ApplicationPerl prerequisites = {'modules': {'perl': 'all'}} @@ -88,7 +89,7 @@ def test_perl_application_server_port(): client.load('server_port') assert ( - client.get()['headers']['Server-Port'] == '7080' + client.get()['headers']['Server-Port'] == '8080' ), 'Server-Port header' diff --git a/test/test_php_application.py b/test/test_php_application.py index 17440909..90db38fa 100644 --- a/test/test_php_application.py +++ b/test/test_php_application.py @@ -7,6 +7,7 @@ import time from pathlib import Path import pytest + from unit.applications.lang.php import ApplicationPHP from unit.option import option @@ -93,13 +94,13 @@ def set_opcache(app, val): def set_preload(preload): - with open(f'{option.temp_dir}/php.ini', 'w') as ini: - ini.write( - f"""opcache.preload = {option.test_dir}/php/opcache/preload\ + Path(f'{option.temp_dir}/php.ini').write_text( + f"""opcache.preload = {option.test_dir}/php/opcache/preload\ /{preload} opcache.preload_user = {option.user or getpass.getuser()} -""" - ) +""", + encoding='utf-8', + ) assert 'success' in client.conf( {"file": f"{option.temp_dir}/php.ini"}, @@ -174,7 +175,7 @@ def test_php_application_query_string_empty(): def test_php_application_query_string_rewrite(): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [ { "action": { @@ -678,7 +679,7 @@ def test_php_application_error_log(findall, wait_for_record): def test_php_application_script(): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "applications/script"}}, + "listeners": {"*:8080": {"pass": "applications/script"}}, "applications": { "script": { "type": client.get_application_type(), @@ -699,7 +700,7 @@ def test_php_application_script(): def test_php_application_index_default(): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "applications/phpinfo"}}, + "listeners": {"*:8080": {"pass": "applications/phpinfo"}}, "applications": { "phpinfo": { "type": client.get_application_type(), @@ -718,16 +719,18 @@ def test_php_application_index_default(): def test_php_application_trailing_slash(temp_dir): new_root = f'{temp_dir}/php-root' - os.makedirs(f'{new_root}/path') - Path(f'{new_root}/path/index.php').write_text('<?php echo "OK\n"; ?>') + Path(f'{new_root}/path').mkdir(parents=True) + Path(f'{new_root}/path/index.php').write_text( + '<?php echo "OK\n"; ?>', encoding='utf-8' + ) addr = f'{temp_dir}/sock' assert 'success' in client.conf( { "listeners": { - "*:7080": {"pass": "applications/php-path"}, + "*:8080": {"pass": "applications/php-path"}, f'unix:{addr}': {"pass": "applications/php-path"}, }, "applications": { @@ -745,7 +748,7 @@ def test_php_application_trailing_slash(temp_dir): resp = client.get(url='/path?q=a') assert resp['status'] == 301, 'uri without trailing /' assert ( - resp['headers']['Location'] == 'http://localhost:7080/path/?q=a' + resp['headers']['Location'] == 'http://localhost:8080/path/?q=a' ), 'Location with query string' resp = client.get( @@ -761,13 +764,11 @@ def test_php_application_trailing_slash(temp_dir): def test_php_application_forbidden(temp_dir): - new_root = f'{temp_dir}/php-root/path' - os.makedirs(new_root) - os.chmod(new_root, 0o000) + Path(f'{temp_dir}/php-root/path').mkdir(mode=0o000, parents=True) assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "applications/php-path"}}, + "listeners": {"*:8080": {"pass": "applications/php-path"}}, "applications": { "php-path": { "type": client.get_application_type(), @@ -787,12 +788,12 @@ def test_php_application_extension_check(temp_dir): assert client.get(url='/index.wrong')['status'] != 200, 'status' new_root = f'{temp_dir}/php' - os.mkdir(new_root) + Path(new_root).mkdir(parents=True) shutil.copy(f'{option.test_dir}/php/phpinfo/index.wrong', new_root) assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "applications/phpinfo"}}, + "listeners": {"*:8080": {"pass": "applications/phpinfo"}}, "applications": { "phpinfo": { "type": client.get_application_type(), diff --git a/test/test_php_basic.py b/test/test_php_basic.py index 64754961..076b7498 100644 --- a/test/test_php_basic.py +++ b/test/test_php_basic.py @@ -14,7 +14,7 @@ conf_app = { } conf_basic = { - "listeners": {"*:7080": {"pass": "applications/app"}}, + "listeners": {"*:8080": {"pass": "applications/app"}}, "applications": conf_app, } @@ -60,14 +60,14 @@ def test_php_get_listeners(): assert 'success' in client.conf(conf_basic) assert client.conf_get()['listeners'] == { - "*:7080": {"pass": "applications/app"} + "*:8080": {"pass": "applications/app"} }, 'listeners' assert client.conf_get('listeners') == { - "*:7080": {"pass": "applications/app"} + "*:8080": {"pass": "applications/app"} }, 'listeners prefix' - assert client.conf_get('listeners/*:7080') == { + assert client.conf_get('listeners/*:8080') == { "pass": "applications/app" }, 'listeners prefix 2' @@ -75,23 +75,23 @@ def test_php_get_listeners(): def test_php_change_listener(): assert 'success' in client.conf(conf_basic) assert 'success' in client.conf( - {"*:7081": {"pass": "applications/app"}}, 'listeners' + {"*:8081": {"pass": "applications/app"}}, 'listeners' ) assert client.conf_get('listeners') == { - "*:7081": {"pass": "applications/app"} + "*:8081": {"pass": "applications/app"} }, 'change listener' def test_php_add_listener(): assert 'success' in client.conf(conf_basic) assert 'success' in client.conf( - {"pass": "applications/app"}, 'listeners/*:7082' + {"pass": "applications/app"}, 'listeners/*:8082' ) assert client.conf_get('listeners') == { - "*:7080": {"pass": "applications/app"}, - "*:7082": {"pass": "applications/app"}, + "*:8080": {"pass": "applications/app"}, + "*:8082": {"pass": "applications/app"}, }, 'add listener' @@ -113,7 +113,7 @@ def test_php_delete(): assert 'success' in client.conf(conf_basic) assert 'error' in client.conf_delete('applications/app') - assert 'success' in client.conf_delete('listeners/*:7080') + assert 'success' in client.conf_delete('listeners/*:8080') assert 'success' in client.conf_delete('applications/app') assert 'error' in client.conf_delete('applications/app') @@ -126,5 +126,5 @@ def test_php_delete_blocks(): assert 'success' in client.conf(conf_app, 'applications') assert 'success' in client.conf( - {"*:7081": {"pass": "applications/app"}}, 'listeners' + {"*:8081": {"pass": "applications/app"}}, 'listeners' ), 'applications restore' diff --git a/test/test_php_targets.py b/test/test_php_targets.py index 857a2dc8..6085db40 100644 --- a/test/test_php_targets.py +++ b/test/test_php_targets.py @@ -10,7 +10,7 @@ def test_php_application_targets(): targets_dir = f"{option.test_dir}/php/targets" assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [ { "match": {"uri": "/1"}, @@ -65,7 +65,7 @@ def test_php_application_targets(): def test_php_application_targets_error(): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "applications/targets/default"}}, + "listeners": {"*:8080": {"pass": "applications/targets/default"}}, "applications": { "targets": { "type": client.get_application_type(), @@ -83,7 +83,7 @@ def test_php_application_targets_error(): assert client.get()['status'] == 200 assert 'error' in client.conf( - {"pass": "applications/targets/blah"}, 'listeners/*:7080' + {"pass": "applications/targets/blah"}, 'listeners/*:8080' ), 'invalid targets pass' assert 'error' in client.conf( f'"{option.test_dir}/php/targets"', diff --git a/test/test_python_procman.py b/test/test_procman.py index 4643a9b8..b4378c4f 100644 --- a/test/test_python_procman.py +++ b/test/test_procman.py @@ -4,6 +4,7 @@ import subprocess import time import pytest + from unit.applications.lang.python import ApplicationPython from unit.option import option diff --git a/test/test_proxy.py b/test/test_proxy.py index 207e90e7..cd16fe5e 100644 --- a/test/test_proxy.py +++ b/test/test_proxy.py @@ -3,6 +3,7 @@ import socket import time import pytest + from conftest import run_process from unit.applications.lang.python import ApplicationPython from unit.option import option @@ -23,10 +24,10 @@ def setup_method_fixture(): assert 'success' in client.conf( { "listeners": { - "*:7080": {"pass": "routes"}, - "*:7081": {"pass": "applications/mirror"}, + "*:8080": {"pass": "routes"}, + "*:8081": {"pass": "applications/mirror"}, }, - "routes": [{"action": {"proxy": "http://127.0.0.1:7081"}}], + "routes": [{"action": {"proxy": "http://127.0.0.1:8081"}}], "applications": { "mirror": { "type": client.get_application_type(), @@ -110,19 +111,19 @@ def test_proxy_chain(): assert 'success' in client.conf( { "listeners": { - "*:7080": {"pass": "routes/first"}, - "*:7081": {"pass": "routes/second"}, - "*:7082": {"pass": "routes/third"}, - "*:7083": {"pass": "routes/fourth"}, - "*:7084": {"pass": "routes/fifth"}, - "*:7085": {"pass": "applications/mirror"}, + "*:8080": {"pass": "routes/first"}, + "*:8081": {"pass": "routes/second"}, + "*:8082": {"pass": "routes/third"}, + "*:8083": {"pass": "routes/fourth"}, + "*:8084": {"pass": "routes/fifth"}, + "*:8085": {"pass": "applications/mirror"}, }, "routes": { - "first": [{"action": {"proxy": "http://127.0.0.1:7081"}}], - "second": [{"action": {"proxy": "http://127.0.0.1:7082"}}], - "third": [{"action": {"proxy": "http://127.0.0.1:7083"}}], - "fourth": [{"action": {"proxy": "http://127.0.0.1:7084"}}], - "fifth": [{"action": {"proxy": "http://127.0.0.1:7085"}}], + "first": [{"action": {"proxy": "http://127.0.0.1:8081"}}], + "second": [{"action": {"proxy": "http://127.0.0.1:8082"}}], + "third": [{"action": {"proxy": "http://127.0.0.1:8083"}}], + "fourth": [{"action": {"proxy": "http://127.0.0.1:8084"}}], + "fifth": [{"action": {"proxy": "http://127.0.0.1:8085"}}], }, "applications": { "mirror": { @@ -210,7 +211,7 @@ def test_proxy_parallel(): def test_proxy_header(): assert 'success' in client.conf( - {"pass": "applications/custom_header"}, 'listeners/*:7081' + {"pass": "applications/custom_header"}, 'listeners/*:8081' ), 'custom_header configure' header_value = 'blah' @@ -325,7 +326,7 @@ def test_proxy_fragmented_body_close(): def test_proxy_nowhere(): assert 'success' in client.conf( - [{"action": {"proxy": "http://127.0.0.1:7082"}}], 'routes' + [{"action": {"proxy": "http://127.0.0.1:8082"}}], 'routes' ), 'proxy path changed' assert get_http10()['status'] == 502, 'status' @@ -334,14 +335,14 @@ def test_proxy_nowhere(): def test_proxy_ipv6(): assert 'success' in client.conf( { - "*:7080": {"pass": "routes"}, - "[::1]:7081": {'application': 'mirror'}, + "*:8080": {"pass": "routes"}, + "[::1]:8081": {'application': 'mirror'}, }, 'listeners', ), 'add ipv6 listener configure' assert 'success' in client.conf( - [{"action": {"proxy": "http://[::1]:7081"}}], 'routes' + [{"action": {"proxy": "http://[::1]:8081"}}], 'routes' ), 'proxy ipv6 configure' assert get_http10()['status'] == 200, 'status' @@ -352,7 +353,7 @@ def test_proxy_unix(temp_dir): assert 'success' in client.conf( { - "*:7080": {"pass": "routes"}, + "*:8080": {"pass": "routes"}, f'unix:{addr}': {'application': 'mirror'}, }, 'listeners', @@ -367,7 +368,7 @@ def test_proxy_unix(temp_dir): def test_proxy_delayed(): assert 'success' in client.conf( - {"pass": "applications/delayed"}, 'listeners/*:7081' + {"pass": "applications/delayed"}, 'listeners/*:8081' ), 'delayed configure' body = '0123456789' * 1000 @@ -400,7 +401,7 @@ def test_proxy_delayed(): def test_proxy_delayed_close(): assert 'success' in client.conf( - {"pass": "applications/delayed"}, 'listeners/*:7081' + {"pass": "applications/delayed"}, 'listeners/*:8081' ), 'delayed configure' sock = post_http10( @@ -469,11 +470,11 @@ def test_proxy_invalid(): check_proxy('http://127.0.0.1:') check_proxy('http://127.0.0.1:blah') check_proxy('http://127.0.0.1:-1') - check_proxy('http://127.0.0.1:7080b') + check_proxy('http://127.0.0.1:8080b') check_proxy('http://[]') - check_proxy('http://[]:7080') - check_proxy('http://[:]:7080') - check_proxy('http://[::7080') + check_proxy('http://[]:8080') + check_proxy('http://[:]:8080') + check_proxy('http://[::8080') @pytest.mark.skip('not yet') @@ -486,11 +487,11 @@ def test_proxy_loop(skip_alert): assert 'success' in client.conf( { "listeners": { - "*:7080": {"pass": "routes"}, - "*:7081": {"pass": "applications/mirror"}, - "*:7082": {"pass": "routes"}, + "*:8080": {"pass": "routes"}, + "*:8081": {"pass": "applications/mirror"}, + "*:8082": {"pass": "routes"}, }, - "routes": [{"action": {"proxy": "http://127.0.0.1:7082"}}], + "routes": [{"action": {"proxy": "http://127.0.0.1:8082"}}], "applications": { "mirror": { "type": client.get_application_type(), diff --git a/test/test_proxy_chunked.py b/test/test_proxy_chunked.py index a066e1e8..23476cd9 100644 --- a/test/test_proxy_chunked.py +++ b/test/test_proxy_chunked.py @@ -4,6 +4,7 @@ import socket import time import pytest + from conftest import run_process from unit.applications.lang.python import ApplicationPython from unit.utils import waitforsocket @@ -22,7 +23,7 @@ def setup_method_fixture(): assert 'success' in client.conf( { "listeners": { - "*:7080": {"pass": "routes"}, + "*:8080": {"pass": "routes"}, }, "routes": [ {"action": {"proxy": f'http://127.0.0.1:{SERVER_PORT}'}} @@ -52,7 +53,7 @@ def run_server(server_port): part = sock.recv(buff_size) data += part - if not len(part): + if not part: break return data @@ -80,7 +81,7 @@ def run_server(server_port): req = f'{req}{add}\r\n' for chunk in re.split(r'([@#])', req): - if chunk == '@' or chunk == '#': + if chunk in ('@', '#'): if chunk == '#': time.sleep(0.1) continue @@ -90,10 +91,10 @@ def run_server(server_port): connection.close() -def chunks(chunks): +def chunks(chunks_lst): body = '\r\n\r\n' - for l, c in chunks: + for l, c in chunks_lst: body = f'{body}{l}\r\n{c}\r\n' return f'{body}0\r\n\r\n' diff --git a/test/test_python_application.py b/test/test_python_application.py index 18473d59..466a59a2 100644 --- a/test/test_python_application.py +++ b/test/test_python_application.py @@ -8,6 +8,7 @@ import venv import pytest from packaging import version + from unit.applications.lang.python import ApplicationPython prerequisites = {'modules': {'python': 'all'}} @@ -160,7 +161,7 @@ def test_python_application_server_port(): client.load('server_port') assert ( - client.get()['headers']['Server-Port'] == '7080' + client.get()['headers']['Server-Port'] == '8080' ), 'Server-Port header' @@ -586,8 +587,8 @@ def test_python_application_encoding(): if not matches: pytest.skip('no available locales') - def unify(str): - str.upper().replace('-', '').replace('_', '') + def unify(enc): + enc.upper().replace('-', '').replace('_', '') for loc in matches: assert 'success' in client.conf( diff --git a/test/test_python_basic.py b/test/test_python_basic.py index 37859c8c..81c768aa 100644 --- a/test/test_python_basic.py +++ b/test/test_python_basic.py @@ -14,7 +14,7 @@ conf_app = { } conf_basic = { - "listeners": {"*:7080": {"pass": "applications/app"}}, + "listeners": {"*:8080": {"pass": "applications/app"}}, "applications": conf_app, } @@ -64,14 +64,14 @@ def test_python_get_listeners(): assert 'success' in client.conf(conf_basic) assert client.conf_get()['listeners'] == { - "*:7080": {"pass": "applications/app"} + "*:8080": {"pass": "applications/app"} }, 'listeners' assert client.conf_get('listeners') == { - "*:7080": {"pass": "applications/app"} + "*:8080": {"pass": "applications/app"} }, 'listeners prefix' - assert client.conf_get('listeners/*:7080') == { + assert client.conf_get('listeners/*:8080') == { "pass": "applications/app" }, 'listeners prefix 2' @@ -79,23 +79,23 @@ def test_python_get_listeners(): def test_python_change_listener(): assert 'success' in client.conf(conf_basic) assert 'success' in client.conf( - {"*:7081": {"pass": "applications/app"}}, 'listeners' + {"*:8081": {"pass": "applications/app"}}, 'listeners' ) assert client.conf_get('listeners') == { - "*:7081": {"pass": "applications/app"} + "*:8081": {"pass": "applications/app"} }, 'change listener' def test_python_add_listener(): assert 'success' in client.conf(conf_basic) assert 'success' in client.conf( - {"pass": "applications/app"}, 'listeners/*:7082' + {"pass": "applications/app"}, 'listeners/*:8082' ) assert client.conf_get('listeners') == { - "*:7080": {"pass": "applications/app"}, - "*:7082": {"pass": "applications/app"}, + "*:8080": {"pass": "applications/app"}, + "*:8082": {"pass": "applications/app"}, }, 'add listener' @@ -117,7 +117,7 @@ def test_python_delete(): assert 'success' in client.conf(conf_basic) assert 'error' in client.conf_delete('applications/app') - assert 'success' in client.conf_delete('listeners/*:7080') + assert 'success' in client.conf_delete('listeners/*:8080') assert 'success' in client.conf_delete('applications/app') assert 'error' in client.conf_delete('applications/app') @@ -130,5 +130,5 @@ def test_python_delete_blocks(): assert 'success' in client.conf(conf_app, 'applications') assert 'success' in client.conf( - {"*:7081": {"pass": "applications/app"}}, 'listeners' + {"*:8081": {"pass": "applications/app"}}, 'listeners' ), 'applications restore' diff --git a/test/test_python_isolation.py b/test/test_python_isolation.py index 260a87a2..fd692cb6 100644 --- a/test/test_python_isolation.py +++ b/test/test_python_isolation.py @@ -1,9 +1,9 @@ -import os import re import subprocess from pathlib import Path import pytest + from unit.applications.lang.python import ApplicationPython from unit.option import option from unit.utils import findmnt @@ -24,10 +24,10 @@ def get_cgroup(app_name): cgroup = f'/proc/{pid}/cgroup' - if not os.path.isfile(cgroup): + if not Path(cgroup).is_file(): pytest.skip(f'no cgroup at {cgroup}') - with open(cgroup, 'r') as f: + with open(cgroup, 'r', encoding='utf-8') as f: return f.read().rstrip() @@ -151,8 +151,8 @@ def test_python_isolation_cgroup_two(require): assert 'success' in client.conf( { "listeners": { - "*:7080": {"pass": "applications/one"}, - "*:7081": {"pass": "applications/two"}, + "*:8080": {"pass": "applications/one"}, + "*:8081": {"pass": "applications/two"}, }, "applications": { "one": { @@ -193,7 +193,7 @@ def test_python_isolation_cgroup_invalid(require): script_path = f'{option.test_dir}/python/empty' assert 'error' in client.conf( { - "listeners": {"*:7080": {"pass": "applications/empty"}}, + "listeners": {"*:8080": {"pass": "applications/empty"}}, "applications": { "empty": { "type": "python", diff --git a/test/test_python_targets.py b/test/test_python_targets.py index 46e77c19..10f10b70 100644 --- a/test/test_python_targets.py +++ b/test/test_python_targets.py @@ -11,7 +11,7 @@ def test_python_targets(): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [ { "match": {"uri": "/1"}, @@ -56,7 +56,7 @@ def test_python_targets_prefix(): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [ { "match": {"uri": ["/app*"]}, diff --git a/test/test_reconfigure.py b/test/test_reconfigure.py index 53258b41..28d1b4c9 100644 --- a/test/test_reconfigure.py +++ b/test/test_reconfigure.py @@ -1,6 +1,7 @@ import time import pytest + from unit.applications.proto import ApplicationProto client = ApplicationProto() @@ -10,7 +11,7 @@ client = ApplicationProto() def setup_method_fixture(): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [{"action": {"return": 200}}], "applications": {}, } diff --git a/test/test_reconfigure_tls.py b/test/test_reconfigure_tls.py index b473b147..4f7d344a 100644 --- a/test/test_reconfigure_tls.py +++ b/test/test_reconfigure_tls.py @@ -3,7 +3,9 @@ import ssl import time import pytest + from unit.applications.tls import ApplicationTLS +from unit.option import option prerequisites = {'modules': {'openssl': 'any'}} @@ -20,7 +22,7 @@ def setup_method_fixture(): assert 'success' in client.conf( { "listeners": { - "*:7080": { + "*:8080": { "pass": "routes", "tls": {"certificate": "default"}, } @@ -40,7 +42,7 @@ def create_socket(): ssl_sock = ctx.wrap_socket( s, server_hostname='localhost', do_handshake_on_connect=False ) - ssl_sock.connect(('127.0.0.1', 7080)) + ssl_sock.connect(('127.0.0.1', 8080)) return ssl_sock @@ -51,7 +53,7 @@ def clear_conf(): @pytest.mark.skip('not yet') def test_reconfigure_tls_switch(): - assert 'success' in client.conf_delete('listeners/*:7080/tls') + assert 'success' in client.conf_delete('listeners/*:8080/tls') (_, sock) = client.get( headers={'Host': 'localhost', 'Connection': 'keep-alive'}, @@ -61,7 +63,7 @@ def test_reconfigure_tls_switch(): assert 'success' in client.conf( {"pass": "routes", "tls": {"certificate": "default"}}, - 'listeners/*:7080', + 'listeners/*:8080', ) assert client.get(sock=sock)['status'] == 200, 'reconfigure' @@ -69,6 +71,9 @@ def test_reconfigure_tls_switch(): def test_reconfigure_tls(): + if option.configure_flag['asan']: + pytest.skip('not yet, router crash') + ssl_sock = create_socket() ssl_sock.sendall("""GET / HTTP/1.1\r\n""".encode()) @@ -93,6 +98,8 @@ def test_reconfigure_tls_2(): clear_conf() + success = False + try: ssl_sock.do_handshake() except ssl.SSLError: @@ -104,6 +111,9 @@ def test_reconfigure_tls_2(): def test_reconfigure_tls_3(): + if option.configure_flag['asan']: + pytest.skip('not yet, router crash') + ssl_sock = create_socket() ssl_sock.do_handshake() diff --git a/test/test_respawn.py b/test/test_respawn.py index dc465cda..03254037 100644 --- a/test/test_respawn.py +++ b/test/test_respawn.py @@ -3,6 +3,7 @@ import subprocess import time import pytest + from unit.applications.lang.python import ApplicationPython prerequisites = {'modules': {'python': 'any'}} diff --git a/test/test_response_headers.py b/test/test_response_headers.py index 50f47d9a..e62c1293 100644 --- a/test/test_response_headers.py +++ b/test/test_response_headers.py @@ -1,8 +1,9 @@ from pathlib import Path import pytest -from unit.applications.proto import ApplicationProto + from unit.applications.lang.python import ApplicationPython +from unit.applications.proto import ApplicationProto from unit.option import option client = ApplicationProto() @@ -12,12 +13,12 @@ client_python = ApplicationPython() @pytest.fixture(autouse=True) def setup_method_fixture(temp_dir): path = Path(f'{temp_dir}/index.html') - path.write_text('0123456789') + path.write_text('0123456789', encoding='utf-8') assert 'success' in client.conf( { "listeners": { - "*:7080": {"pass": "routes"}, + "*:8080": {"pass": "routes"}, }, "routes": [ { @@ -59,7 +60,7 @@ def test_response_last_action(): assert 'success' in client.conf( { "listeners": { - "*:7080": {"pass": "routes/first"}, + "*:8080": {"pass": "routes/first"}, }, "routes": { "first": [ @@ -91,7 +92,7 @@ def test_response_pass(require): assert 'success' in client_python.conf( { "listeners": { - "*:7080": {"pass": "routes"}, + "*:8080": {"pass": "routes"}, }, "routes": [ { @@ -121,7 +122,7 @@ def test_response_pass(require): def test_response_fallback(): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [ { "action": { diff --git a/test/test_return.py b/test/test_return.py index 35525ed5..af15b886 100644 --- a/test/test_return.py +++ b/test/test_return.py @@ -1,6 +1,7 @@ import re import pytest + from unit.applications.proto import ApplicationProto client = ApplicationProto() @@ -10,7 +11,7 @@ client = ApplicationProto() def setup_method_fixture(): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [{"action": {"return": 200}}], "applications": {}, } @@ -90,7 +91,7 @@ def test_return_update(): def test_return_location(): reserved = ":/?#[]@!&'()*+,;=" unreserved = ( - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" "0123456789-._~" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~" ) unsafe = " \"%<>\\^`{|}" unsafe_enc = "%20%22%25%3C%3E%5C%5E%60%7B%7C%7D" diff --git a/test/test_rewrite.py b/test/test_rewrite.py index 8d81ec49..f94fb528 100644 --- a/test/test_rewrite.py +++ b/test/test_rewrite.py @@ -1,6 +1,7 @@ -import os +from pathlib import Path import pytest + from unit.applications.proto import ApplicationProto client = ApplicationProto() @@ -10,7 +11,7 @@ client = ApplicationProto() def setup_method_fixture(): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [ { "match": {"uri": "/"}, @@ -39,9 +40,9 @@ def set_rewrite(rewrite, uri): def test_rewrite(findall, wait_for_record): assert client.get()['status'] == 200 - assert wait_for_record(rf'\[notice\].*"routes/1" selected') is not None - assert len(findall(rf'\[notice\].*URI rewritten to "/new"')) == 1 - assert len(findall(rf'\[notice\].*URI rewritten')) == 1 + assert wait_for_record(r'\[notice\].*"routes/1" selected') is not None + assert len(findall(r'\[notice\].*URI rewritten to "/new"')) == 1 + assert len(findall(r'\[notice\].*URI rewritten')) == 1 set_rewrite("", "") assert client.get()['status'] == 200 @@ -112,7 +113,7 @@ def test_rewrite_location(): def check_location(rewrite, expect): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [ { "action": { @@ -131,17 +132,15 @@ def test_rewrite_location(): def test_rewrite_share(temp_dir): - os.makedirs(f'{temp_dir}/dir') - os.makedirs(f'{temp_dir}/foo') - - with open(f'{temp_dir}/foo/index.html', 'w') as fooindex: - fooindex.write('fooindex') + Path(f'{temp_dir}/dir').mkdir() + Path(f'{temp_dir}/foo/').mkdir() + Path(f'{temp_dir}/foo/index.html').write_text('fooindex', encoding='utf-8') # same action block assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [ { "action": { @@ -162,7 +161,7 @@ def test_rewrite_share(temp_dir): index_path = f'{temp_dir}${{request_uri}}/index.html' assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [ { "match": {"uri": "/foo"}, @@ -182,7 +181,7 @@ def test_rewrite_share(temp_dir): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [ { "match": {"uri": "/foo"}, diff --git a/test/test_routing.py b/test/test_routing.py index a18edb04..0b6eced2 100644 --- a/test/test_routing.py +++ b/test/test_routing.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import pytest + from unit.applications.lang.python import ApplicationPython from unit.option import option @@ -12,7 +13,7 @@ client = ApplicationPython() def setup_method_fixture(): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [ { "match": {"method": "GET"}, @@ -24,8 +25,8 @@ def setup_method_fixture(): ), 'routing configure' -def route(route): - return client.conf([route], 'routes') +def route(conf_route): + return client.conf([conf_route], 'routes') def route_match(match): @@ -40,19 +41,21 @@ def route_match_invalid(match): ), 'route match configure invalid' -def host(host, status): +def host(host_header, status): assert ( - client.get(headers={'Host': host, 'Connection': 'close'})['status'] + client.get(headers={'Host': host_header, 'Connection': 'close'})[ + 'status' + ] == status ), 'match host' -def cookie(cookie, status): +def cookie(cookie_header, status): assert ( client.get( headers={ 'Host': 'localhost', - 'Cookie': cookie, + 'Cookie': cookie_header, 'Connection': 'close', }, )['status'] @@ -322,7 +325,7 @@ def test_routes_pass_encode(): def check_pass(path, name): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": f'applications/{path}'}}, + "listeners": {"*:8080": {"pass": f'applications/{path}'}}, "applications": { name: { "type": client.get_application_type(), @@ -345,7 +348,7 @@ def test_routes_pass_encode(): def check_pass_error(path, name): assert 'error' in client.conf( { - "listeners": {"*:7080": {"pass": f'applications/{path}'}}, + "listeners": {"*:8080": {"pass": f'applications/{path}'}}, "applications": { name: { "type": client.get_application_type(), @@ -365,7 +368,7 @@ def test_routes_pass_encode(): def test_routes_absent(): assert 'success' in client.conf( { - "listeners": {"*:7081": {"pass": "applications/empty"}}, + "listeners": {"*:8081": {"pass": "applications/empty"}}, "applications": { "empty": { "type": client.get_application_type(), @@ -378,19 +381,19 @@ def test_routes_absent(): } ) - assert client.get(port=7081)['status'] == 200, 'routes absent' + assert client.get(port=8081)['status'] == 200, 'routes absent' def test_routes_pass_invalid(): assert 'error' in client.conf( - {"pass": "routes/blah"}, 'listeners/*:7080' + {"pass": "routes/blah"}, 'listeners/*:8080' ), 'routes invalid' def test_route_empty(): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes/main"}}, + "listeners": {"*:8080": {"pass": "routes/main"}}, "routes": {"main": []}, "applications": {}, } @@ -437,14 +440,14 @@ def test_routes_route_pass(): "upstreams": { "one": { "servers": { - "127.0.0.1:7081": {}, - "127.0.0.1:7082": {}, + "127.0.0.1:8081": {}, + "127.0.0.1:8082": {}, }, }, "two": { "servers": { - "127.0.0.1:7081": {}, - "127.0.0.1:7082": {}, + "127.0.0.1:8081": {}, + "127.0.0.1:8082": {}, }, }, }, @@ -480,14 +483,14 @@ def test_routes_route_pass_invalid(): "upstreams": { "one": { "servers": { - "127.0.0.1:7081": {}, - "127.0.0.1:7082": {}, + "127.0.0.1:8081": {}, + "127.0.0.1:8082": {}, }, }, "two": { "servers": { - "127.0.0.1:7081": {}, - "127.0.0.1:7082": {}, + "127.0.0.1:8081": {}, + "127.0.0.1:8082": {}, }, }, }, @@ -512,10 +515,10 @@ def test_routes_action_unique(temp_dir): assert 'success' in client.conf( { "listeners": { - "*:7080": {"pass": "routes"}, - "*:7081": {"pass": "applications/app"}, + "*:8080": {"pass": "routes"}, + "*:8081": {"pass": "applications/app"}, }, - "routes": [{"action": {"proxy": "http://127.0.0.1:7081"}}], + "routes": [{"action": {"proxy": "http://127.0.0.1:8081"}}], "applications": { "app": { "type": client.get_application_type(), @@ -528,12 +531,12 @@ def test_routes_action_unique(temp_dir): ) assert 'error' in client.conf( - {"proxy": "http://127.0.0.1:7081", "share": temp_dir}, + {"proxy": "http://127.0.0.1:8081", "share": temp_dir}, 'routes/0/action', ), 'proxy share' assert 'error' in client.conf( { - "proxy": "http://127.0.0.1:7081", + "proxy": "http://127.0.0.1:8081", "pass": "applications/app", }, 'routes/0/action', @@ -560,7 +563,7 @@ def test_routes_rules_two(): def test_routes_two(): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes/first"}}, + "listeners": {"*:8080": {"pass": "routes/first"}}, "routes": { "first": [ { @@ -606,14 +609,14 @@ def test_routes_match_host_ipv4(): route_match({"host": "127.0.0.1"}) host('127.0.0.1', 200) - host('127.0.0.1:7080', 200) + host('127.0.0.1:8080', 200) def test_routes_match_host_ipv6(): route_match({"host": "[::1]"}) host('[::1]', 200) - host('[::1]:7080', 200) + host('[::1]:8080', 200) def test_routes_match_host_positive_many(): @@ -649,7 +652,7 @@ def test_routes_match_host_case_insensitive(): def test_routes_match_host_port(): route_match({"host": "example.com"}) - host('example.com:7080', 200) + host('example.com:8080', 200) def test_routes_match_host_empty(): @@ -718,7 +721,7 @@ def test_routes_reconfigure(): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes/main"}}, + "listeners": {"*:8080": {"pass": "routes/main"}}, "routes": {"main": [{"action": {"return": 200}}]}, "applications": {}, } @@ -801,7 +804,7 @@ def test_routes_edit(): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes/main"}}, + "listeners": {"*:8080": {"pass": "routes/main"}}, "routes": {"main": [{"action": {"return": 200}}]}, "applications": {}, } @@ -817,7 +820,7 @@ def test_routes_edit(): assert client.get()['status'] == 200, 'routes edit GET 7' assert 'success' in client.conf_delete( - 'listeners/*:7080' + 'listeners/*:8080' ), 'route edit configure 8' assert 'success' in client.conf_delete( 'routes/main' @@ -1646,14 +1649,14 @@ def test_routes_source_port(): def test_routes_source_addr(): assert 'success' in client.conf( { - "*:7080": {"pass": "routes"}, - "[::1]:7081": {"pass": "routes"}, + "*:8080": {"pass": "routes"}, + "[::1]:8081": {"pass": "routes"}, }, 'listeners', ), 'source listeners configure' def get_ipv6(): - return client.get(sock_type='ipv6', port=7081) + return client.get(sock_type='ipv6', port=8081) route_match({"source": "127.0.0.1"}) assert client.get()['status'] == 200, 'exact' @@ -1707,64 +1710,64 @@ def test_routes_source_addr(): def test_routes_source_ipv6(): assert 'success' in client.conf( { - "[::1]:7080": {"pass": "routes"}, - "127.0.0.1:7081": {"pass": "routes"}, + "[::1]:8080": {"pass": "routes"}, + "127.0.0.1:8081": {"pass": "routes"}, }, 'listeners', ), 'source listeners configure' route_match({"source": "::1"}) assert client.get(sock_type='ipv6')['status'] == 200, 'exact' - assert client.get(port=7081)['status'] == 404, 'exact ipv4' + assert client.get(port=8081)['status'] == 404, 'exact ipv4' route_match({"source": ["::1"]}) assert client.get(sock_type='ipv6')['status'] == 200, 'exact 2' - assert client.get(port=7081)['status'] == 404, 'exact 2 ipv4' + assert client.get(port=8081)['status'] == 404, 'exact 2 ipv4' route_match({"source": "!::1"}) assert client.get(sock_type='ipv6')['status'] == 404, 'exact neg' - assert client.get(port=7081)['status'] == 200, 'exact neg ipv4' + assert client.get(port=8081)['status'] == 200, 'exact neg ipv4' route_match({"source": "::2"}) assert client.get(sock_type='ipv6')['status'] == 404, 'exact 3' - assert client.get(port=7081)['status'] == 404, 'exact 3 ipv4' + assert client.get(port=8081)['status'] == 404, 'exact 3 ipv4' route_match({"source": "::1-::1"}) assert client.get(sock_type='ipv6')['status'] == 200, 'range' - assert client.get(port=7081)['status'] == 404, 'range ipv4' + assert client.get(port=8081)['status'] == 404, 'range ipv4' route_match({"source": "::2-::2"}) assert client.get(sock_type='ipv6')['status'] == 404, 'range 2' - assert client.get(port=7081)['status'] == 404, 'range 2 ipv4' + assert client.get(port=8081)['status'] == 404, 'range 2 ipv4' route_match({"source": "::2-::3"}) assert client.get(sock_type='ipv6')['status'] == 404, 'range 3' - assert client.get(port=7081)['status'] == 404, 'range 3 ipv4' + assert client.get(port=8081)['status'] == 404, 'range 3 ipv4' route_match({"source": "::1-::2"}) assert client.get(sock_type='ipv6')['status'] == 200, 'range 4' - assert client.get(port=7081)['status'] == 404, 'range 4 ipv4' + assert client.get(port=8081)['status'] == 404, 'range 4 ipv4' route_match({"source": "::0-::2"}) assert client.get(sock_type='ipv6')['status'] == 200, 'range 5' - assert client.get(port=7081)['status'] == 404, 'range 5 ipv4' + assert client.get(port=8081)['status'] == 404, 'range 5 ipv4' route_match({"source": "::0-::1"}) assert client.get(sock_type='ipv6')['status'] == 200, 'range 6' - assert client.get(port=7081)['status'] == 404, 'range 6 ipv4' + assert client.get(port=8081)['status'] == 404, 'range 6 ipv4' def test_routes_source_cidr(): assert 'success' in client.conf( { - "*:7080": {"pass": "routes"}, - "[::1]:7081": {"pass": "routes"}, + "*:8080": {"pass": "routes"}, + "[::1]:8081": {"pass": "routes"}, }, 'listeners', ), 'source listeners configure' def get_ipv6(): - return client.get(sock_type='ipv6', port=7081) + return client.get(sock_type='ipv6', port=8081) route_match({"source": "127.0.0.1/32"}) assert client.get()['status'] == 200, '32' @@ -1790,35 +1793,35 @@ def test_routes_source_cidr(): def test_routes_source_cidr_ipv6(): assert 'success' in client.conf( { - "[::1]:7080": {"pass": "routes"}, - "127.0.0.1:7081": {"pass": "routes"}, + "[::1]:8080": {"pass": "routes"}, + "127.0.0.1:8081": {"pass": "routes"}, }, 'listeners', ), 'source listeners configure' route_match({"source": "::1/128"}) assert client.get(sock_type='ipv6')['status'] == 200, '128' - assert client.get(port=7081)['status'] == 404, '128 ipv4' + assert client.get(port=8081)['status'] == 404, '128 ipv4' route_match({"source": "::0/128"}) assert client.get(sock_type='ipv6')['status'] == 404, '128 2' - assert client.get(port=7081)['status'] == 404, '128 ipv4' + assert client.get(port=8081)['status'] == 404, '128 ipv4' route_match({"source": "::0/127"}) assert client.get(sock_type='ipv6')['status'] == 200, '127' - assert client.get(port=7081)['status'] == 404, '127 ipv4' + assert client.get(port=8081)['status'] == 404, '127 ipv4' route_match({"source": "::0/32"}) assert client.get(sock_type='ipv6')['status'] == 200, '32' - assert client.get(port=7081)['status'] == 404, '32 ipv4' + assert client.get(port=8081)['status'] == 404, '32 ipv4' route_match({"source": "::0/1"}) assert client.get(sock_type='ipv6')['status'] == 200, '1' - assert client.get(port=7081)['status'] == 404, '1 ipv4' + assert client.get(port=8081)['status'] == 404, '1 ipv4' route_match({"source": "::/0"}) assert client.get(sock_type='ipv6')['status'] == 200, '0' - assert client.get(port=7081)['status'] == 404, '0 ipv4' + assert client.get(port=8081)['status'] == 404, '0 ipv4' def test_routes_source_unix(temp_dir): @@ -1826,7 +1829,7 @@ def test_routes_source_unix(temp_dir): assert 'success' in client.conf( { - "127.0.0.1:7081": {"pass": "routes"}, + "127.0.0.1:8081": {"pass": "routes"}, f'unix:{addr}': {"pass": "routes"}, }, 'listeners', @@ -1843,7 +1846,7 @@ def test_routes_source_unix(temp_dir): ), 'unix ipv6 neg' route_match({"source": "unix"}) - assert client.get(port=7081)['status'] == 404, 'unix ipv4' + assert client.get(port=8081)['status'] == 404, 'unix ipv4' assert client.get(sock_type='unix', addr=addr)['status'] == 200, 'unix' @@ -1916,7 +1919,7 @@ def test_routes_match_source_invalid(): route_match_invalid({"source": "2001::/129"}) route_match_invalid({"source": "::FFFFF"}) route_match_invalid({"source": "[::1]:"}) - route_match_invalid({"source": "[:::]:7080"}) + route_match_invalid({"source": "[:::]:8080"}) route_match_invalid({"source": "*:"}) route_match_invalid({"source": "*:1-a"}) route_match_invalid({"source": "*:65536"}) @@ -1929,74 +1932,74 @@ def test_routes_match_source_none(): def test_routes_match_destination(): assert 'success' in client.conf( - {"*:7080": {"pass": "routes"}, "*:7081": {"pass": "routes"}}, + {"*:8080": {"pass": "routes"}, "*:8081": {"pass": "routes"}}, 'listeners', ), 'listeners configure' - route_match({"destination": "*:7080"}) + route_match({"destination": "*:8080"}) assert client.get()['status'] == 200, 'dest' - assert client.get(port=7081)['status'] == 404, 'dest 2' + assert client.get(port=8081)['status'] == 404, 'dest 2' - route_match({"destination": ["127.0.0.1:7080"]}) + route_match({"destination": ["127.0.0.1:8080"]}) assert client.get()['status'] == 200, 'dest 3' - assert client.get(port=7081)['status'] == 404, 'dest 4' + assert client.get(port=8081)['status'] == 404, 'dest 4' - route_match({"destination": "!*:7080"}) + route_match({"destination": "!*:8080"}) assert client.get()['status'] == 404, 'dest neg' - assert client.get(port=7081)['status'] == 200, 'dest neg 2' + assert client.get(port=8081)['status'] == 200, 'dest neg 2' - route_match({"destination": ['!*:7080', '!*:7081']}) + route_match({"destination": ['!*:8080', '!*:8081']}) assert client.get()['status'] == 404, 'dest neg 3' - assert client.get(port=7081)['status'] == 404, 'dest neg 4' + assert client.get(port=8081)['status'] == 404, 'dest neg 4' - route_match({"destination": ['!*:7081', '!*:7082']}) + route_match({"destination": ['!*:8081', '!*:8082']}) assert client.get()['status'] == 200, 'dest neg 5' - route_match({"destination": ['*:7080', '!*:7080']}) + route_match({"destination": ['*:8080', '!*:8080']}) assert client.get()['status'] == 404, 'dest neg 6' - route_match({"destination": ['127.0.0.1:7080', '*:7081', '!*:7080']}) + route_match({"destination": ['127.0.0.1:8080', '*:8081', '!*:8080']}) assert client.get()['status'] == 404, 'dest neg 7' - assert client.get(port=7081)['status'] == 200, 'dest neg 8' + assert client.get(port=8081)['status'] == 200, 'dest neg 8' - route_match({"destination": ['!*:7081', '!*:7082', '*:7083']}) + route_match({"destination": ['!*:8081', '!*:8082', '*:8083']}) assert client.get()['status'] == 404, 'dest neg 9' - route_match({"destination": ['*:7081', '!127.0.0.1:7080', '*:7080']}) + route_match({"destination": ['*:8081', '!127.0.0.1:8080', '*:8080']}) assert client.get()['status'] == 404, 'dest neg 10' - assert client.get(port=7081)['status'] == 200, 'dest neg 11' + assert client.get(port=8081)['status'] == 200, 'dest neg 11' assert 'success' in client.conf_delete( 'routes/0/match/destination/0' ), 'remove destination rule' assert client.get()['status'] == 404, 'dest neg 12' - assert client.get(port=7081)['status'] == 404, 'dest neg 13' + assert client.get(port=8081)['status'] == 404, 'dest neg 13' assert 'success' in client.conf_delete( 'routes/0/match/destination/0' ), 'remove destination rule 2' assert client.get()['status'] == 200, 'dest neg 14' - assert client.get(port=7081)['status'] == 404, 'dest neg 15' + assert client.get(port=8081)['status'] == 404, 'dest neg 15' assert 'success' in client.conf_post( "\"!127.0.0.1\"", 'routes/0/match/destination' ), 'add destination rule' assert client.get()['status'] == 404, 'dest neg 16' - assert client.get(port=7081)['status'] == 404, 'dest neg 17' + assert client.get(port=8081)['status'] == 404, 'dest neg 17' def test_routes_match_destination_proxy(): assert 'success' in client.conf( { "listeners": { - "*:7080": {"pass": "routes/first"}, - "*:7081": {"pass": "routes/second"}, + "*:8080": {"pass": "routes/first"}, + "*:8081": {"pass": "routes/second"}, }, "routes": { - "first": [{"action": {"proxy": "http://127.0.0.1:7081"}}], + "first": [{"action": {"proxy": "http://127.0.0.1:8081"}}], "second": [ { - "match": {"destination": ["127.0.0.1:7081"]}, + "match": {"destination": ["127.0.0.1:8081"]}, "action": {"return": 200}, } ], diff --git a/test/test_routing_tls.py b/test/test_routing_tls.py index 4a97c8e4..f8cef546 100644 --- a/test/test_routing_tls.py +++ b/test/test_routing_tls.py @@ -11,8 +11,8 @@ def test_routes_match_scheme_tls(): assert 'success' in client.conf( { "listeners": { - "*:7080": {"pass": "routes"}, - "*:7081": { + "*:8080": {"pass": "routes"}, + "*:8081": { "pass": "routes", "tls": {"certificate": 'default'}, }, @@ -26,4 +26,4 @@ def test_routes_match_scheme_tls(): ), 'scheme configure' assert client.get()['status'] == 200, 'http' - assert client.get_ssl(port=7081)['status'] == 201, 'https' + assert client.get_ssl(port=8081)['status'] == 201, 'https' diff --git a/test/test_ruby_application.py b/test/test_ruby_application.py index 6f533b70..127b75b7 100644 --- a/test/test_ruby_application.py +++ b/test/test_ruby_application.py @@ -2,6 +2,7 @@ import re import subprocess import pytest + from unit.applications.lang.ruby import ApplicationRuby prerequisites = {'modules': {'ruby': 'all'}} @@ -91,7 +92,7 @@ def test_ruby_application_server_port(): client.load('server_port') assert ( - client.get()['headers']['Server-Port'] == '7080' + client.get()['headers']['Server-Port'] == '8080' ), 'Server-Port header' @@ -164,15 +165,6 @@ def test_ruby_application_input_each(): @pytest.mark.skip('not yet') -def test_ruby_application_input_rewind(): - client.load('input_rewind') - - body = '0123456789' - - assert client.post(body=body)['body'] == body, 'input rewind' - - -@pytest.mark.skip('not yet') def test_ruby_application_syntax_error(skip_alert): skip_alert( r'Failed to parse rack script', @@ -317,6 +309,26 @@ def test_ruby_application_header_status(): assert client.get()['status'] == 200, 'header status' +def test_ruby_application_header_array(): + client.load('header_array') + + assert client.get()['headers']['x-array'] == 'name=value; ; value; av' + + +def test_ruby_application_header_array_nil(): + client.load('header_array_nil') + + assert client.get()['status'] == 503 + + +def test_ruby_application_header_array_empty(): + client.load('header_array_empty') + + headers = client.get()['headers'] + assert 'x-array' in headers + assert headers['x-array'] == '' + + @pytest.mark.skip('not yet') def test_ruby_application_header_rack(): client.load('header_rack') @@ -324,6 +336,20 @@ def test_ruby_application_header_rack(): assert client.get()['status'] == 500, 'header rack' +@pytest.mark.skip('not yet') +def test_ruby_application_session(): + client.load('session') + + assert client.get()['status'] == 200 + + +@pytest.mark.skip('not yet') +def test_ruby_application_multipart(): + client.load('multipart') + + assert client.get()['status'] == 200 + + def test_ruby_application_body_empty(): client.load('body_empty') diff --git a/test/test_ruby_hooks.py b/test/test_ruby_hooks.py index 38893e47..dd9e0fca 100644 --- a/test/test_ruby_hooks.py +++ b/test/test_ruby_hooks.py @@ -1,8 +1,11 @@ from unit.applications.lang.ruby import ApplicationRuby from unit.option import option from unit.utils import waitforglob +from packaging import version -prerequisites = {'modules': {'ruby': 'all'}} +prerequisites = { + 'modules': {'ruby': lambda v: version.parse(v) >= version.parse('3.0')} +} client = ApplicationRuby() diff --git a/test/test_settings.py b/test/test_settings.py index 33180046..9d37d6ca 100644 --- a/test/test_settings.py +++ b/test/test_settings.py @@ -4,6 +4,7 @@ import subprocess import time import pytest + from unit.applications.lang.python import ApplicationPython prerequisites = {'modules': {'python': 'any'}} @@ -460,7 +461,7 @@ def test_settings_log_route(findall, search_in_file, wait_for_record): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [ { "match": { @@ -487,7 +488,7 @@ def test_settings_log_route(findall, search_in_file, wait_for_record): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes/main"}}, + "listeners": {"*:8080": {"pass": "routes/main"}}, "routes": { "main": [ { @@ -516,7 +517,7 @@ def test_settings_log_route(findall, search_in_file, wait_for_record): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes/first"}}, + "listeners": {"*:8080": {"pass": "routes/first"}}, "routes": { "first": [ { @@ -541,7 +542,7 @@ def test_settings_log_route(findall, search_in_file, wait_for_record): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes/fall"}}, + "listeners": {"*:8080": {"pass": "routes/fall"}}, "routes": { "fall": [ { diff --git a/test/test_static.py b/test/test_static.py index d46247d9..e2fc2283 100644 --- a/test/test_static.py +++ b/test/test_static.py @@ -1,7 +1,9 @@ import os import socket +from pathlib import Path import pytest + from unit.applications.proto import ApplicationProto from unit.utils import waitforfiles @@ -11,22 +13,17 @@ client = ApplicationProto() @pytest.fixture(autouse=True) def setup_method_fixture(temp_dir): - os.makedirs(f'{temp_dir}/assets/dir') assets_dir = f'{temp_dir}/assets' - with open(f'{assets_dir}/index.html', 'w') as index, open( - f'{assets_dir}/README', 'w' - ) as readme, open(f'{assets_dir}/log.log', 'w') as log, open( - f'{assets_dir}/dir/file', 'w' - ) as file: - index.write('0123456789') - readme.write('readme') - log.write('[debug]') - file.write('blah') + Path(f'{assets_dir}/dir').mkdir(parents=True) + Path(f'{assets_dir}/index.html').write_text('0123456789', encoding='utf-8') + Path(f'{assets_dir}/README').write_text('readme', encoding='utf-8') + Path(f'{assets_dir}/log.log').write_text('[debug]', encoding='utf-8') + Path(f'{assets_dir}/dir/file').write_text('blah', encoding='utf-8') assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [{"action": {"share": f'{assets_dir}$uri'}}], "settings": { "http": { @@ -103,7 +100,7 @@ def test_static_etag(temp_dir): assert etag != etag_2, 'different ETag' assert etag == client.get(url='/')['headers']['ETag'], 'same ETag' - with open(f'{temp_dir}/assets/index.html', 'w') as f: + with open(f'{temp_dir}/assets/index.html', 'w', encoding='utf-8') as f: f.write('blah') assert etag != client.get(url='/')['headers']['ETag'], 'new ETag' @@ -119,18 +116,16 @@ def test_static_redirect(): def test_static_space_in_name(temp_dir): assets_dir = f'{temp_dir}/assets' - os.rename( - f'{assets_dir}/dir/file', - f'{assets_dir}/dir/fi le', - ) + Path(f'{assets_dir}/dir/file').rename(f'{assets_dir}/dir/fi le') + assert waitforfiles(f'{assets_dir}/dir/fi le') assert client.get(url='/dir/fi le')['body'] == 'blah', 'file name' - os.rename(f'{assets_dir}/dir', f'{assets_dir}/di r') + Path(f'{assets_dir}/dir').rename(f'{assets_dir}/di r') assert waitforfiles(f'{assets_dir}/di r/fi le') assert client.get(url='/di r/fi le')['body'] == 'blah', 'dir name' - os.rename(f'{assets_dir}/di r', f'{assets_dir}/ di r ') + Path(f'{assets_dir}/di r').rename(f'{assets_dir}/ di r ') assert waitforfiles(f'{assets_dir}/ di r /fi le') assert ( client.get(url='/ di r /fi le')['body'] == 'blah' @@ -149,17 +144,14 @@ def test_static_space_in_name(temp_dir): == 'blah' ), 'encoded 2' - os.rename( - f'{assets_dir}/ di r /fi le', - f'{assets_dir}/ di r / fi le ', - ) + Path(f'{assets_dir}/ di r /fi le').rename(f'{assets_dir}/ di r / fi le ') assert waitforfiles(f'{assets_dir}/ di r / fi le ') assert ( client.get(url='/%20di%20r%20/%20fi%20le%20')['body'] == 'blah' ), 'file name enclosing' try: - open(f'{temp_dir}/ф а', 'a').close() + Path(f'{temp_dir}/ф а').touch() utf8 = True except KeyboardInterrupt: @@ -169,17 +161,13 @@ def test_static_space_in_name(temp_dir): utf8 = False if utf8: - os.rename( - f'{assets_dir}/ di r / fi le ', - f'{assets_dir}/ di r /фа йл', + Path(f'{assets_dir}/ di r / fi le ').rename( + f'{assets_dir}/ di r /фа йл' ) assert waitforfiles(f'{assets_dir}/ di r /фа йл') assert client.get(url='/ di r /фа йл')['body'] == 'blah' - os.rename( - f'{assets_dir}/ di r ', - f'{assets_dir}/ди ректория', - ) + Path(f'{assets_dir}/ di r ').rename(f'{assets_dir}/ди ректория') assert waitforfiles(f'{assets_dir}/ди ректория/фа йл') assert ( client.get(url='/ди ректория/фа йл')['body'] == 'blah' diff --git a/test/test_static_chroot.py b/test/test_static_chroot.py index 6b4dd89a..31e10b4e 100644 --- a/test/test_static_chroot.py +++ b/test/test_static_chroot.py @@ -2,25 +2,27 @@ import os from pathlib import Path import pytest + from unit.applications.proto import ApplicationProto from unit.option import option prerequisites = {'features': {'chroot': True}} client = ApplicationProto() +test_path = f'/{os.path.relpath(Path(__file__))}' @pytest.fixture(autouse=True) def setup_method_fixture(temp_dir): - os.makedirs(f'{temp_dir}/assets/dir') - Path(f'{temp_dir}/assets/index.html').write_text('0123456789') - Path(f'{temp_dir}/assets/dir/file').write_text('blah') - - client.test_path = f'/{os.path.relpath(Path(__file__))}' + Path(f'{temp_dir}/assets/dir').mkdir(parents=True) + Path(f'{temp_dir}/assets/index.html').write_text( + '0123456789', encoding='utf-8' + ) + Path(f'{temp_dir}/assets/dir/file').write_text('blah', encoding='utf-8') assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [{"action": {"share": f'{temp_dir}/assets$uri'}}], } ) @@ -85,7 +87,7 @@ def test_static_chroot_empty(): assert client.get(url='/dir/file')['status'] == 200, 'empty absolute' assert 'success' in update_action("", ".$uri") - assert client.get(url=client.test_path)['status'] == 200, 'empty relative' + assert client.get(url=test_path)['status'] == 200, 'empty relative' def test_static_chroot_relative(require): @@ -95,10 +97,10 @@ def test_static_chroot_relative(require): assert client.get(url='/dir/file')['status'] == 403, 'relative chroot' assert 'success' in client.conf({"share": ".$uri"}, 'routes/0/action') - assert client.get(url=client.test_path)['status'] == 200, 'relative share' + assert client.get(url=test_path)['status'] == 200, 'relative share' assert 'success' in update_action(".", ".$uri") - assert client.get(url=client.test_path)['status'] == 200, 'relative' + assert client.get(url=test_path)['status'] == 200, 'relative' def test_static_chroot_variables(temp_dir): diff --git a/test/test_static_fallback.py b/test/test_static_fallback.py index ffc888ab..9b5fcb53 100644 --- a/test/test_static_fallback.py +++ b/test/test_static_fallback.py @@ -2,6 +2,7 @@ import os from pathlib import Path import pytest + from unit.applications.proto import ApplicationProto client = ApplicationProto() @@ -11,7 +12,7 @@ client = ApplicationProto() def setup_method_fixture(temp_dir): assets_dir = f'{temp_dir}/assets' os.makedirs(f'{assets_dir}/dir') - Path(f'{assets_dir}/index.html').write_text('0123456789') + Path(f'{assets_dir}/index.html').write_text('0123456789', encoding='utf-8') os.makedirs(f'{assets_dir}/403') os.chmod(f'{assets_dir}/403', 0o000) @@ -19,8 +20,8 @@ def setup_method_fixture(temp_dir): assert 'success' in client.conf( { "listeners": { - "*:7080": {"pass": "routes"}, - "*:7081": {"pass": "routes"}, + "*:8080": {"pass": "routes"}, + "*:8081": {"pass": "routes"}, }, "routes": [{"action": {"share": f'{assets_dir}$uri'}}], "applications": {}, @@ -108,13 +109,13 @@ def test_static_fallback_proxy(): assert 'success' in client.conf( [ { - "match": {"destination": "*:7081"}, + "match": {"destination": "*:8081"}, "action": {"return": 200}, }, { "action": { "share": "/blah", - "fallback": {"proxy": "http://127.0.0.1:7081"}, + "fallback": {"proxy": "http://127.0.0.1:8081"}, } }, ], @@ -136,11 +137,11 @@ def test_static_fallback_proxy_loop(skip_alert): ) action_update( - {"share": "/blah", "fallback": {"proxy": "http://127.0.0.1:7080"}} + {"share": "/blah", "fallback": {"proxy": "http://127.0.0.1:8080"}} ) client.get(no_recv=True) - assert 'success' in client.conf_delete('listeners/*:7081') + assert 'success' in client.conf_delete('listeners/*:8081') client.get(read_timeout=1) @@ -152,6 +153,6 @@ def test_static_fallback_invalid(): check_error({"share": "/blah", "fallback": ""}) check_error({"return": 200, "fallback": {"share": "/blah"}}) check_error( - {"proxy": "http://127.0.0.1:7081", "fallback": {"share": "/blah"}} + {"proxy": "http://127.0.0.1:8081", "fallback": {"share": "/blah"}} ) check_error({"fallback": {"share": "/blah"}}) diff --git a/test/test_static_mount.py b/test/test_static_mount.py index ccd18919..41b436e2 100644 --- a/test/test_static_mount.py +++ b/test/test_static_mount.py @@ -3,6 +3,7 @@ import subprocess from pathlib import Path import pytest + from unit.applications.proto import ApplicationProto prerequisites = {'features': {'chroot': True}, 'privileged_user': True} @@ -15,9 +16,11 @@ def setup_method_fixture(temp_dir): os.makedirs(f'{temp_dir}/assets/dir/mount') os.makedirs(f'{temp_dir}/assets/dir/dir') os.makedirs(f'{temp_dir}/assets/mount') - Path(f'{temp_dir}/assets/index.html').write_text('index') - Path(f'{temp_dir}/assets/dir/dir/file').write_text('file') - Path(f'{temp_dir}/assets/mount/index.html').write_text('mount') + Path(f'{temp_dir}/assets/index.html').write_text('index', encoding='utf-8') + Path(f'{temp_dir}/assets/dir/dir/file').write_text('file', encoding='utf-8') + Path(f'{temp_dir}/assets/mount/index.html').write_text( + 'mount', encoding='utf-8' + ) try: subprocess.check_output( @@ -38,7 +41,7 @@ def setup_method_fixture(temp_dir): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [{"action": {"share": f'{temp_dir}/assets/dir$uri'}}], } ) diff --git a/test/test_static_share.py b/test/test_static_share.py index ac5afb50..ee53fe9b 100644 --- a/test/test_static_share.py +++ b/test/test_static_share.py @@ -2,6 +2,7 @@ import os from pathlib import Path import pytest + from unit.applications.proto import ApplicationProto client = ApplicationProto() @@ -12,12 +13,12 @@ def setup_method_fixture(temp_dir): os.makedirs(f'{temp_dir}/assets/dir') os.makedirs(f'{temp_dir}/assets/dir2') - Path(f'{temp_dir}/assets/dir/file').write_text('1') - Path(f'{temp_dir}/assets/dir2/file2').write_text('2') + Path(f'{temp_dir}/assets/dir/file').write_text('1', encoding='utf-8') + Path(f'{temp_dir}/assets/dir2/file2').write_text('2', encoding='utf-8') assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [{"action": {"share": f'{temp_dir}/assets$uri'}}], "applications": {}, } diff --git a/test/test_static_symlink.py b/test/test_static_symlink.py index 1f7d7907..2d402d48 100644 --- a/test/test_static_symlink.py +++ b/test/test_static_symlink.py @@ -2,6 +2,7 @@ import os from pathlib import Path import pytest + from unit.applications.proto import ApplicationProto prerequisites = {'features': {'chroot': True}} @@ -12,12 +13,14 @@ client = ApplicationProto() @pytest.fixture(autouse=True) def setup_method_fixture(temp_dir): os.makedirs(f'{temp_dir}/assets/dir/dir') - Path(f'{temp_dir}/assets/index.html').write_text('0123456789') - Path(f'{temp_dir}/assets/dir/file').write_text('blah') + Path(f'{temp_dir}/assets/index.html').write_text( + '0123456789', encoding='utf-8' + ) + Path(f'{temp_dir}/assets/dir/file').write_text('blah', encoding='utf-8') assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [{"action": {"share": f'{temp_dir}/assets$uri'}}], } ) diff --git a/test/test_static_types.py b/test/test_static_types.py index 8cd28ca4..e931d949 100644 --- a/test/test_static_types.py +++ b/test/test_static_types.py @@ -1,6 +1,7 @@ from pathlib import Path import pytest + from unit.applications.proto import ApplicationProto client = ApplicationProto() @@ -10,15 +11,15 @@ client = ApplicationProto() def setup_method_fixture(temp_dir): Path(f'{temp_dir}/assets').mkdir() for ext in ['.xml', '.mp4', '.php', '', '.txt', '.html', '.png']: - Path(f'{temp_dir}/assets/file{ext}').write_text(ext) + Path(f'{temp_dir}/assets/file{ext}').write_text(ext, encoding='utf-8') - Path(f'{temp_dir}/assets/index.html').write_text('index') + Path(f'{temp_dir}/assets/index.html').write_text('index', encoding='utf-8') assert 'success' in client.conf( { "listeners": { - "*:7080": {"pass": "routes"}, - "*:7081": {"pass": "routes"}, + "*:8080": {"pass": "routes"}, + "*:8081": {"pass": "routes"}, }, "routes": [{"action": {"share": f'{temp_dir}/assets$uri'}}], "applications": {}, @@ -124,14 +125,14 @@ def test_static_types_fallback(temp_dir): assert 'success' in client.conf( [ { - "match": {"destination": "*:7081"}, + "match": {"destination": "*:8081"}, "action": {"return": 200}, }, { "action": { "share": f'{temp_dir}/assets$uri', "types": ["!application/x-httpd-php"], - "fallback": {"proxy": "http://127.0.0.1:7081"}, + "fallback": {"proxy": "http://127.0.0.1:8081"}, } }, ], @@ -155,7 +156,7 @@ def test_static_types_index(temp_dir): def test_static_types_custom_mime(temp_dir): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [{"action": {"share": f'{temp_dir}/assets$uri'}}], "applications": {}, "settings": { diff --git a/test/test_static_variables.py b/test/test_static_variables.py index bc39e90e..62753750 100644 --- a/test/test_static_variables.py +++ b/test/test_static_variables.py @@ -2,6 +2,7 @@ import os from pathlib import Path import pytest + from unit.applications.proto import ApplicationProto client = ApplicationProto() @@ -11,13 +12,15 @@ client = ApplicationProto() def setup_method_fixture(temp_dir): os.makedirs(f'{temp_dir}/assets/dir') os.makedirs(f'{temp_dir}/assets/d$r') - Path(f'{temp_dir}/assets/index.html').write_text('0123456789') - Path(f'{temp_dir}/assets/dir/file').write_text('file') - Path(f'{temp_dir}/assets/d$r/file').write_text('d$r') + Path(f'{temp_dir}/assets/index.html').write_text( + '0123456789', encoding='utf-8' + ) + Path(f'{temp_dir}/assets/dir/file').write_text('file', encoding='utf-8') + Path(f'{temp_dir}/assets/d$r/file').write_text('d$r', encoding='utf-8') assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [{"action": {"share": f'{temp_dir}/assets$uri'}}], } ) diff --git a/test/test_status.py b/test/test_status.py index 11b140cf..a52f7486 100644 --- a/test/test_status.py +++ b/test/test_status.py @@ -39,9 +39,9 @@ def test_status_requests(skip_alert): assert 'success' in client.conf( { "listeners": { - "*:7080": {"pass": "routes"}, - "*:7081": {"pass": "applications/empty"}, - "*:7082": {"pass": "applications/blah"}, + "*:8080": {"pass": "routes"}, + "*:8081": {"pass": "applications/empty"}, + "*:8082": {"pass": "applications/blah"}, }, "routes": [{"action": {"return": 200}}], "applications": { @@ -60,7 +60,7 @@ def test_status_requests(skip_alert): assert client.get()['status'] == 200 assert Status.get('/requests/total') == 1, '2xx' - assert client.get(port=7081)['status'] == 200 + assert client.get(port=8081)['status'] == 200 assert Status.get('/requests/total') == 2, '2xx app' assert ( @@ -69,7 +69,7 @@ def test_status_requests(skip_alert): ) assert Status.get('/requests/total') == 3, '4xx' - assert client.get(port=7082)['status'] == 503 + assert client.get(port=8082)['status'] == 503 assert Status.get('/requests/total') == 4, '5xx' client.http( @@ -85,7 +85,7 @@ Connection: close ) assert Status.get('/requests/total') == 6, 'pipeline' - sock = client.get(port=7081, no_recv=True) + sock = client.get(port=8081, no_recv=True) time.sleep(1) @@ -98,8 +98,8 @@ def test_status_connections(): assert 'success' in client.conf( { "listeners": { - "*:7080": {"pass": "routes"}, - "*:7081": {"pass": "applications/delayed"}, + "*:8080": {"pass": "routes"}, + "*:8081": {"pass": "applications/delayed"}, }, "routes": [{"action": {"return": 200}}], "applications": { @@ -136,7 +136,7 @@ def test_status_connections(): 'X-Delay': '2', 'Connection': 'close', }, - port=7081, + port=8081, start=True, read_timeout=1, ) @@ -194,8 +194,8 @@ def test_status_applications(): assert 'success' in client.conf( { "listeners": { - "*:7080": {"pass": "applications/restart"}, - "*:7081": {"pass": "applications/delayed"}, + "*:8080": {"pass": "applications/restart"}, + "*:8081": {"pass": "applications/delayed"}, }, "routes": [], "applications": { @@ -220,13 +220,13 @@ def test_status_proxy(): assert 'success' in client.conf( { "listeners": { - "*:7080": {"pass": "routes"}, - "*:7081": {"pass": "applications/empty"}, + "*:8080": {"pass": "routes"}, + "*:8081": {"pass": "applications/empty"}, }, "routes": [ { "match": {"uri": "/"}, - "action": {"proxy": "http://127.0.0.1:7081"}, + "action": {"proxy": "http://127.0.0.1:8081"}, } ], "applications": { diff --git a/test/test_status_tls.py b/test/test_status_tls.py index 784b4960..f69f021e 100644 --- a/test/test_status_tls.py +++ b/test/test_status_tls.py @@ -12,8 +12,8 @@ def test_status_tls_requests(): assert 'success' in client.conf( { "listeners": { - "*:7080": {"pass": "routes"}, - "*:7081": { + "*:8080": {"pass": "routes"}, + "*:8081": { "pass": "routes", "tls": {"certificate": "default"}, }, @@ -26,6 +26,6 @@ def test_status_tls_requests(): Status.init() assert client.get()['status'] == 200 - assert client.get_ssl(port=7081)['status'] == 200 + assert client.get_ssl(port=8081)['status'] == 200 assert Status.get('/requests/total') == 2 diff --git a/test/test_tls.py b/test/test_tls.py index 54fdb665..09921773 100644 --- a/test/test_tls.py +++ b/test/test_tls.py @@ -2,8 +2,10 @@ import io import ssl import subprocess import time +from pathlib import Path import pytest + from unit.applications.tls import ApplicationTLS from unit.option import option @@ -12,7 +14,7 @@ prerequisites = {'modules': {'python': 'any', 'openssl': 'any'}} client = ApplicationTLS() -def add_tls(application='empty', cert='default', port=7080): +def add_tls(application='empty', cert='default', port=8080): assert 'success' in client.conf( { "pass": f"applications/{application}", @@ -53,9 +55,8 @@ def context_cert_req(cert='root'): def generate_ca_conf(): - with open(f'{option.temp_dir}/ca.conf', 'w') as f: - f.write( - f"""[ ca ] + Path(f'{option.temp_dir}/ca.conf').write_text( + f"""[ ca ] default_ca = myca [ myca ] @@ -72,20 +73,16 @@ copy_extensions = copy commonName = optional [ myca_extensions ] -basicConstraints = critical,CA:TRUE""" - ) - - with open(f'{option.temp_dir}/certserial', 'w') as f: - f.write('1000') - - with open(f'{option.temp_dir}/certindex', 'w') as f: - f.write('') +basicConstraints = critical,CA:TRUE""", + encoding='utf-8', + ) - with open(f'{option.temp_dir}/certindex.attr', 'w') as f: - f.write('') + Path(f'{option.temp_dir}/certserial').write_text('1000', encoding='utf-8') + Path(f'{option.temp_dir}/certindex').touch() + Path(f'{option.temp_dir}/certindex.attr').touch() -def remove_tls(application='empty', port=7080): +def remove_tls(application='empty', port=8080): assert 'success' in client.conf( {"pass": f"applications/{application}"}, f'listeners/*:{port}' ) @@ -178,12 +175,12 @@ def test_tls_certificate_update(): add_tls() - cert_old = ssl.get_server_certificate(('127.0.0.1', 7080)) + cert_old = ssl.get_server_certificate(('127.0.0.1', 8080)) client.certificate() assert cert_old != ssl.get_server_certificate( - ('127.0.0.1', 7080) + ('127.0.0.1', 8080) ), 'update certificate' @@ -207,12 +204,12 @@ def test_tls_certificate_change(): add_tls() - cert_old = ssl.get_server_certificate(('127.0.0.1', 7080)) + cert_old = ssl.get_server_certificate(('127.0.0.1', 8080)) add_tls(cert='new') assert cert_old != ssl.get_server_certificate( - ('127.0.0.1', 7080) + ('127.0.0.1', 8080) ), 'change certificate' @@ -322,8 +319,8 @@ def test_tls_certificate_chain(temp_dir): with open(crt_path, 'wb') as crt, open(end_path, 'rb') as end, open( int_path, 'rb' - ) as int: - crt.write(end.read() + int.read()) + ) as inter: + crt.write(end.read() + inter.read()) # incomplete chain @@ -428,7 +425,9 @@ def test_tls_certificate_chain_long(temp_dir): else f'{temp_dir}/int{i}.crt' ) - with open(f'{temp_dir}/all.crt', 'a') as chain, open(path) as cert: + with open(f'{temp_dir}/all.crt', 'a', encoding='utf-8') as chain, open( + path, encoding='utf-8' + ) as cert: chain.write(cert.read()) assert 'success' in client.certificate_load( @@ -542,7 +541,7 @@ def test_tls_no_close_notify(): assert 'success' in client.conf( { "listeners": { - "*:7080": { + "*:8080": { "pass": "routes", "tls": {"certificate": "default"}, } @@ -576,7 +575,7 @@ def test_tls_keepalive_certificate_remove(): ) assert 'success' in client.conf( - {"pass": "applications/empty"}, 'listeners/*:7080' + {"pass": "applications/empty"}, 'listeners/*:8080' ) assert 'success' in client.conf_delete('/certificates/default') @@ -697,8 +696,8 @@ def test_tls_multi_listener(): client.certificate() add_tls() - add_tls(port=7081) + add_tls(port=8081) assert client.get_ssl()['status'] == 200, 'listener #1' - assert client.get_ssl(port=7081)['status'] == 200, 'listener #2' + assert client.get_ssl(port=8081)['status'] == 200, 'listener #2' diff --git a/test/test_tls_conf_command.py b/test/test_tls_conf_command.py index 49df7bf3..5a9a3f32 100644 --- a/test/test_tls_conf_command.py +++ b/test/test_tls_conf_command.py @@ -1,6 +1,7 @@ import ssl import pytest + from unit.applications.tls import ApplicationTLS prerequisites = {'modules': {'openssl': 'any'}} @@ -15,7 +16,7 @@ def setup_method_fixture(): assert 'success' in client.conf( { "listeners": { - "*:7080": { + "*:8080": { "pass": "routes", "tls": {"certificate": "default"}, } @@ -55,7 +56,7 @@ def test_tls_conf_command(): "certificate": "default", "conf_commands": {"protocol": f'-{protocol}'}, }, - 'listeners/*:7080/tls', + 'listeners/*:8080/tls', ), 'protocol disabled' sock.close() @@ -84,7 +85,7 @@ def test_tls_conf_command(): "cipherstring": f"{cipher[1]}:!{cipher[0]}", }, }, - 'listeners/*:7080/tls', + 'listeners/*:8080/tls', ), 'cipher disabled' if len(ciphers) > 1: @@ -106,7 +107,7 @@ def test_tls_conf_command_invalid(skip_alert): def check_conf_commands(conf_commands): assert 'error' in client.conf( {"certificate": "default", "conf_commands": conf_commands}, - 'listeners/*:7080/tls', + 'listeners/*:8080/tls', ), 'ivalid conf_commands' check_conf_commands([]) diff --git a/test/test_tls_session.py b/test/test_tls_session.py index 8b2b04fd..8da0306a 100644 --- a/test/test_tls_session.py +++ b/test/test_tls_session.py @@ -26,7 +26,7 @@ def setup_method_fixture(): assert 'success' in client.conf( { "listeners": { - "*:7080": { + "*:8080": { "pass": "routes", "tls": {"certificate": "default", "session": {}}, } @@ -45,11 +45,11 @@ def add_session(cache_size=None, timeout=None): if timeout is not None: session['timeout'] = timeout - return client.conf(session, 'listeners/*:7080/tls/session') + return client.conf(session, 'listeners/*:8080/tls/session') def connect(ctx=None, session=None): - sock = socket.create_connection(('127.0.0.1', 7080)) + sock = socket.create_connection(('127.0.0.1', 8080)) if ctx is None: ctx = Context(TLSv1_2_METHOD) diff --git a/test/test_tls_sni.py b/test/test_tls_sni.py index 253d9813..61d72125 100644 --- a/test/test_tls_sni.py +++ b/test/test_tls_sni.py @@ -2,6 +2,7 @@ import ssl import subprocess import pytest + from unit.applications.tls import ApplicationTLS from unit.option import option @@ -14,7 +15,7 @@ client = ApplicationTLS() def setup_method_fixture(): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [{"action": {"return": 200}}], "applications": {}, } @@ -24,7 +25,7 @@ def setup_method_fixture(): def add_tls(cert='default'): assert 'success' in client.conf( {"pass": "routes", "tls": {"certificate": cert}}, - 'listeners/*:7080', + 'listeners/*:8080', ) @@ -104,7 +105,7 @@ def config_bundles(bundles): def generate_ca_conf(): - with open(f'{option.temp_dir}/ca.conf', 'w') as f: + with open(f'{option.temp_dir}/ca.conf', 'w', encoding='utf-8') as f: f.write( f"""[ ca ] default_ca = myca @@ -126,10 +127,10 @@ commonName = optional basicConstraints = critical,CA:TRUE""" ) - with open(f'{option.temp_dir}/certserial', 'w') as f: + with open(f'{option.temp_dir}/certserial', 'w', encoding='utf-8') as f: f.write('1000') - with open(f'{option.temp_dir}/certindex', 'w') as f: + with open(f'{option.temp_dir}/certindex', 'w', encoding='utf-8') as f: f.write('') @@ -141,7 +142,7 @@ def load_certs(bundles): def remove_tls(): - assert 'success' in client.conf({"pass": "routes"}, 'listeners/*:7080') + assert 'success' in client.conf({"pass": "routes"}, 'listeners/*:8080') def test_tls_sni(): @@ -289,7 +290,7 @@ def test_tls_sni_invalid(): def check_certificate(cert): assert 'error' in client.conf( {"pass": "routes", "tls": {"certificate": cert}}, - 'listeners/*:7080', + 'listeners/*:8080', ) check_certificate('') diff --git a/test/test_tls_tickets.py b/test/test_tls_tickets.py index 0d8e4f36..5e899a9b 100644 --- a/test/test_tls_tickets.py +++ b/test/test_tls_tickets.py @@ -36,9 +36,9 @@ def setup_method_fixture(): assert 'success' in client.conf( { "listeners": { - "*:7080": listener_conf, - "*:7081": listener_conf, - "*:7082": listener_conf, + "*:8080": listener_conf, + "*:8081": listener_conf, + "*:8082": listener_conf, }, "routes": [{"action": {"return": 200}}], "applications": {}, @@ -46,7 +46,7 @@ def setup_method_fixture(): ), 'load application configuration' -def connect(ctx=None, session=None, port=7080): +def connect(ctx=None, session=None, port=8080): sock = socket.create_connection(('127.0.0.1', port)) if ctx is None: @@ -72,7 +72,7 @@ def has_ticket(sess): return _lib.SSL_SESSION_has_ticket(sess._session) -def set_tickets(tickets=True, port=7080): +def set_tickets(tickets=True, port=8080): assert 'success' in client.conf( {"cache_size": 0, "tickets": tickets}, f'listeners/*:{port}/tls/session', @@ -98,7 +98,7 @@ def test_tls_ticket(): assert not has_ticket(sess), 'tickets False' assert 'success' in client.conf_delete( - 'listeners/*:7080/tls/session/tickets' + 'listeners/*:8080/tls/session/tickets' ), 'tickets default configure' sess, _, _ = connect() @@ -118,13 +118,13 @@ def test_tls_ticket_string(): assert has_ticket(sess2), 'tickets string reconnect' assert reused, 'tickets string reused' - sess2, _, reused = connect(ctx, sess, port=7081) + sess2, _, reused = connect(ctx, sess, port=8081) assert has_ticket(sess2), 'connect True' assert not reused, 'connect True not reused' - set_tickets(TICKET2, port=7081) + set_tickets(TICKET2, port=8081) - sess2, _, reused = connect(ctx, sess, port=7081) + sess2, _, reused = connect(ctx, sess, port=8081) assert has_ticket(sess2), 'wrong ticket' assert not reused, 'wrong ticket not reused' @@ -137,7 +137,7 @@ def test_tls_ticket_string(): assert has_ticket(sess2), 'tickets string 80 reconnect' assert reused, 'tickets string 80 reused' - sess2, _, reused = connect(ctx, sess, port=7081) + sess2, _, reused = connect(ctx, sess, port=8081) assert has_ticket(sess2), 'wrong ticket 80' assert not reused, 'wrong ticket 80 not reused' @@ -153,34 +153,34 @@ def test_tls_ticket_array(): assert not has_ticket(sess), 'tickets array empty' set_tickets([TICKET, TICKET2]) - set_tickets(TICKET, port=7081) - set_tickets(TICKET2, port=7082) + set_tickets(TICKET, port=8081) + set_tickets(TICKET2, port=8082) sess, ctx, _ = connect() - _, _, reused = connect(ctx, sess, port=7081) + _, _, reused = connect(ctx, sess, port=8081) assert not reused, 'not last ticket' - _, _, reused = connect(ctx, sess, port=7082) + _, _, reused = connect(ctx, sess, port=8082) assert reused, 'last ticket' - sess, ctx, _ = connect(port=7081) + sess, ctx, _ = connect(port=8081) _, _, reused = connect(ctx, sess) assert reused, 'first ticket' - sess, ctx, _ = connect(port=7082) + sess, ctx, _ = connect(port=8082) _, _, reused = connect(ctx, sess) assert reused, 'second ticket' assert 'success' in client.conf_delete( - 'listeners/*:7080/tls/session/tickets/0' + 'listeners/*:8080/tls/session/tickets/0' ), 'removed first ticket' assert 'success' in client.conf_post( - f'"{TICKET}"', 'listeners/*:7080/tls/session/tickets' + f'"{TICKET}"', 'listeners/*:8080/tls/session/tickets' ), 'add new ticket to the end of array' sess, ctx, _ = connect() - _, _, reused = connect(ctx, sess, port=7082) + _, _, reused = connect(ctx, sess, port=8082) assert not reused, 'not last ticket 2' - _, _, reused = connect(ctx, sess, port=7081) + _, _, reused = connect(ctx, sess, port=8081) assert reused, 'last ticket 2' @@ -188,7 +188,7 @@ def test_tls_ticket_invalid(): def check_tickets(tickets): assert 'error' in client.conf( {"tickets": tickets}, - 'listeners/*:7080/tls/session', + 'listeners/*:8080/tls/session', ) check_tickets({}) diff --git a/test/test_unix_abstract.py b/test/test_unix_abstract.py index 7ed2389c..6e304293 100644 --- a/test/test_unix_abstract.py +++ b/test/test_unix_abstract.py @@ -18,7 +18,7 @@ def test_unix_abstract_source(): assert 'success' in client.conf( { "listeners": { - "127.0.0.1:7080": {"pass": "routes"}, + "127.0.0.1:8080": {"pass": "routes"}, f"unix:@{addr[1:]}": {"pass": "routes"}, }, "routes": [ @@ -44,8 +44,8 @@ def test_unix_abstract_source(): def test_unix_abstract_client_ip(): def get_xff(xff, sock_type='ipv4'): address = { - 'ipv4': ('127.0.0.1', 7080), - 'ipv6': ('::1', 7081), + 'ipv4': ('127.0.0.1', 8080), + 'ipv6': ('::1', 8081), 'unix': ('\0sock', None), } (addr, port) = address[sock_type] @@ -61,14 +61,14 @@ def test_unix_abstract_client_ip(): assert 'success' in client.conf( { "listeners": { - "127.0.0.1:7080": { + "127.0.0.1:8080": { "client_ip": { "header": "X-Forwarded-For", "source": "unix", }, "pass": "applications/client_ip", }, - "[::1]:7081": { + "[::1]:8081": { "client_ip": { "header": "X-Forwarded-For", "source": "unix", diff --git a/test/test_upstreams_rr.py b/test/test_upstreams_rr.py index 046b5614..a2dc5c68 100644 --- a/test/test_upstreams_rr.py +++ b/test/test_upstreams_rr.py @@ -2,6 +2,7 @@ import os import re import pytest + from unit.applications.lang.python import ApplicationPython from unit.option import option @@ -15,23 +16,23 @@ def setup_method_fixture(): assert 'success' in client.conf( { "listeners": { - "*:7080": {"pass": "upstreams/one"}, - "*:7090": {"pass": "upstreams/two"}, - "*:7081": {"pass": "routes/one"}, - "*:7082": {"pass": "routes/two"}, - "*:7083": {"pass": "routes/three"}, + "*:8080": {"pass": "upstreams/one"}, + "*:8090": {"pass": "upstreams/two"}, + "*:8081": {"pass": "routes/one"}, + "*:8082": {"pass": "routes/two"}, + "*:8083": {"pass": "routes/three"}, }, "upstreams": { "one": { "servers": { - "127.0.0.1:7081": {}, - "127.0.0.1:7082": {}, + "127.0.0.1:8081": {}, + "127.0.0.1:8082": {}, }, }, "two": { "servers": { - "127.0.0.1:7081": {}, - "127.0.0.1:7082": {}, + "127.0.0.1:8081": {}, + "127.0.0.1:8082": {}, }, }, }, @@ -47,7 +48,7 @@ def setup_method_fixture(): client.cpu_count = os.cpu_count() -def get_resps(req=100, port=7080): +def get_resps(req=100, port=8080): resps = [0] for _ in range(req): @@ -64,7 +65,7 @@ def get_resps(req=100, port=7080): return resps -def get_resps_sc(req=100, port=7080): +def get_resps_sc(req=100, port=8080): to_send = b"""GET / HTTP/1.1 Host: localhost @@ -96,14 +97,14 @@ def test_upstreams_rr_no_weight(): assert abs(resps[0] - resps[1]) <= client.cpu_count, 'no weight' assert 'success' in client.conf_delete( - 'upstreams/one/servers/127.0.0.1:7081' + 'upstreams/one/servers/127.0.0.1:8081' ), 'no weight server remove' resps = get_resps(req=50) assert resps[1] == 50, 'no weight 2' assert 'success' in client.conf( - {}, 'upstreams/one/servers/127.0.0.1:7081' + {}, 'upstreams/one/servers/127.0.0.1:8081' ), 'no weight server revert' resps = get_resps() @@ -111,7 +112,7 @@ def test_upstreams_rr_no_weight(): assert abs(resps[0] - resps[1]) <= client.cpu_count, 'no weight 3' assert 'success' in client.conf( - {}, 'upstreams/one/servers/127.0.0.1:7083' + {}, 'upstreams/one/servers/127.0.0.1:8083' ), 'no weight server new' resps = get_resps() @@ -126,7 +127,7 @@ def test_upstreams_rr_no_weight(): def test_upstreams_rr_weight(): assert 'success' in client.conf( - {"weight": 3}, 'upstreams/one/servers/127.0.0.1:7081' + {"weight": 3}, 'upstreams/one/servers/127.0.0.1:8081' ), 'configure weight' resps = get_resps_sc() @@ -134,14 +135,14 @@ def test_upstreams_rr_weight(): assert resps[1] == 25, 'weight 3 1' assert 'success' in client.conf_delete( - 'upstreams/one/servers/127.0.0.1:7081/weight' + 'upstreams/one/servers/127.0.0.1:8081/weight' ), 'configure weight remove' resps = get_resps_sc(req=10) assert resps[0] == 5, 'weight 0 0' assert resps[1] == 5, 'weight 0 1' assert 'success' in client.conf( - '1', 'upstreams/one/servers/127.0.0.1:7081/weight' + '1', 'upstreams/one/servers/127.0.0.1:8081/weight' ), 'configure weight 1' resps = get_resps_sc() @@ -150,8 +151,8 @@ def test_upstreams_rr_weight(): assert 'success' in client.conf( { - "127.0.0.1:7081": {"weight": 3}, - "127.0.0.1:7083": {"weight": 2}, + "127.0.0.1:8081": {"weight": 3}, + "127.0.0.1:8083": {"weight": 2}, }, 'upstreams/one/servers', ), 'configure weight 2' @@ -165,8 +166,8 @@ def test_upstreams_rr_weight_rational(): def set_weights(w1, w2): assert 'success' in client.conf( { - "127.0.0.1:7081": {"weight": w1}, - "127.0.0.1:7082": {"weight": w2}, + "127.0.0.1:8081": {"weight": w1}, + "127.0.0.1:8082": {"weight": w2}, }, 'upstreams/one/servers', ), 'configure weights' @@ -195,15 +196,15 @@ def test_upstreams_rr_weight_rational(): set_weights(0.25, 0.25) assert 'success' in client.conf_delete( - 'upstreams/one/servers/127.0.0.1:7081/weight' + 'upstreams/one/servers/127.0.0.1:8081/weight' ), 'delete weight' check_reqs(1, 0.25) assert 'success' in client.conf( { - "127.0.0.1:7081": {"weight": 0.1}, - "127.0.0.1:7082": {"weight": 1}, - "127.0.0.1:7083": {"weight": 0.9}, + "127.0.0.1:8081": {"weight": 0.1}, + "127.0.0.1:8082": {"weight": 1}, + "127.0.0.1:8083": {"weight": 0.9}, }, 'upstreams/one/servers', ), 'configure weights' @@ -221,7 +222,7 @@ def test_upstreams_rr_independent(): return sum_r - resps = get_resps_sc(req=30, port=7090) + resps = get_resps_sc(req=30, port=8090) assert resps[0] == 15, 'dep two before 0' assert resps[1] == 15, 'dep two before 1' @@ -230,10 +231,10 @@ def test_upstreams_rr_independent(): assert resps[1] == 15, 'dep one before 1' assert 'success' in client.conf( - '2', 'upstreams/two/servers/127.0.0.1:7081/weight' + '2', 'upstreams/two/servers/127.0.0.1:8081/weight' ), 'configure dep weight' - resps = get_resps_sc(req=30, port=7090) + resps = get_resps_sc(req=30, port=8090) assert resps[0] == 20, 'dep two 0' assert resps[1] == 10, 'dep two 1' @@ -242,13 +243,13 @@ def test_upstreams_rr_independent(): assert resps[1] == 15, 'dep one 1' assert 'success' in client.conf( - '1', 'upstreams/two/servers/127.0.0.1:7081/weight' + '1', 'upstreams/two/servers/127.0.0.1:8081/weight' ), 'configure dep weight 1' r_one, r_two = [0, 0], [0, 0] for _ in range(10): r_one = sum_resps(r_one, get_resps(req=10)) - r_two = sum_resps(r_two, get_resps(req=10, port=7090)) + r_two = sum_resps(r_two, get_resps(req=10, port=8090)) assert sum(r_one) == 100, 'dep one mix sum' assert abs(r_one[0] - r_one[1]) <= client.cpu_count, 'dep one mix' @@ -261,25 +262,25 @@ def test_upstreams_rr_delay(): assert 'success' in client.conf( { "listeners": { - "*:7080": {"pass": "upstreams/one"}, - "*:7081": {"pass": "routes"}, - "*:7082": {"pass": "routes"}, + "*:8080": {"pass": "upstreams/one"}, + "*:8081": {"pass": "routes"}, + "*:8082": {"pass": "routes"}, }, "upstreams": { "one": { "servers": { - "127.0.0.1:7081": {}, - "127.0.0.1:7082": {}, + "127.0.0.1:8081": {}, + "127.0.0.1:8082": {}, }, }, }, "routes": [ { - "match": {"destination": "*:7081"}, + "match": {"destination": "*:8081"}, "action": {"pass": "applications/delayed"}, }, { - "match": {"destination": "*:7082"}, + "match": {"destination": "*:8082"}, "action": {"return": 201}, }, ], @@ -351,14 +352,14 @@ Connection: close assert client.get()['body'] == '' assert 'success' in client.conf( - {"127.0.0.1:7083": {"weight": 2}}, + {"127.0.0.1:8083": {"weight": 2}}, 'upstreams/one/servers', ), 'active req new server' assert 'success' in client.conf_delete( - 'upstreams/one/servers/127.0.0.1:7083' + 'upstreams/one/servers/127.0.0.1:8083' ), 'active req server remove' assert 'success' in client.conf_delete( - 'listeners/*:7080' + 'listeners/*:8080' ), 'delete listener' assert 'success' in client.conf_delete( 'upstreams/one' @@ -377,7 +378,7 @@ Connection: close def test_upstreams_rr_bad_server(): assert 'success' in client.conf( - {"weight": 1}, 'upstreams/one/servers/127.0.0.1:7084' + {"weight": 1}, 'upstreams/one/servers/127.0.0.1:8084' ), 'configure bad server' resps = get_resps_sc(req=30) @@ -409,7 +410,7 @@ def test_upstreams_rr_unix(temp_dir): assert 'success' in client.conf( { - "*:7080": {"pass": "upstreams/one"}, + "*:8080": {"pass": "upstreams/one"}, f"unix:{addr_0}": {"pass": "routes/one"}, f"unix:{addr_1}": {"pass": "routes/two"}, }, @@ -430,15 +431,15 @@ def test_upstreams_rr_unix(temp_dir): def test_upstreams_rr_ipv6(): assert 'success' in client.conf( { - "*:7080": {"pass": "upstreams/one"}, - "[::1]:7081": {"pass": "routes/one"}, - "[::1]:7082": {"pass": "routes/two"}, + "*:8080": {"pass": "upstreams/one"}, + "[::1]:8081": {"pass": "routes/one"}, + "[::1]:8082": {"pass": "routes/two"}, }, 'listeners', ), 'configure listeners ipv6' assert 'success' in client.conf( - {"[::1]:7081": {}, "[::1]:7082": {}}, 'upstreams/one/servers' + {"[::1]:8081": {}, "[::1]:8082": {}}, 'upstreams/one/servers' ), 'configure servers ipv6' resps = get_resps_sc() @@ -454,13 +455,13 @@ def test_upstreams_rr_servers_empty(): assert client.get()['status'] == 502, 'servers empty' assert 'success' in client.conf( - {"127.0.0.1:7081": {"weight": 0}}, 'upstreams/one/servers' + {"127.0.0.1:8081": {"weight": 0}}, 'upstreams/one/servers' ), 'configure servers empty one' assert client.get()['status'] == 502, 'servers empty one' assert 'success' in client.conf( { - "127.0.0.1:7081": {"weight": 0}, - "127.0.0.1:7082": {"weight": 0}, + "127.0.0.1:8081": {"weight": 0}, + "127.0.0.1:8082": {"weight": 0}, }, 'upstreams/one/servers', ), 'configure servers empty two' @@ -474,12 +475,12 @@ def test_upstreams_rr_invalid(): {}, 'upstreams/one/servers/127.0.0.1' ), 'invalid address' assert 'error' in client.conf( - {}, 'upstreams/one/servers/127.0.0.1:7081/blah' + {}, 'upstreams/one/servers/127.0.0.1:8081/blah' ), 'invalid server option' def check_weight(w): assert 'error' in client.conf( - w, 'upstreams/one/servers/127.0.0.1:7081/weight' + w, 'upstreams/one/servers/127.0.0.1:8081/weight' ), 'invalid weight option' check_weight({}) diff --git a/test/test_usr1.py b/test/test_usr1.py index ce756fc0..ecb4d8fd 100644 --- a/test/test_usr1.py +++ b/test/test_usr1.py @@ -1,5 +1,6 @@ import os import signal +from pathlib import Path from unit.applications.lang.python import ApplicationPython from unit.log import Log @@ -23,14 +24,14 @@ def test_usr1_access_log(search_in_file, temp_dir, unit_pid, wait_for_record): assert waitforfiles(log_path), 'open' - os.rename(log_path, f'{temp_dir}/{log_new}') + Path(log_path).rename(f'{temp_dir}/{log_new}') assert client.get()['status'] == 200 assert ( wait_for_record(r'"GET / HTTP/1.1" 200 0 "-" "-"', log_new) is not None ), 'rename new' - assert not os.path.isfile(log_path), 'rename old' + assert not Path(log_path).is_file(), 'rename old' os.kill(unit_pid, signal.SIGUSR1) @@ -51,7 +52,7 @@ def test_usr1_unit_log(search_in_file, temp_dir, unit_pid, wait_for_record): log_path = f'{temp_dir}/unit.log' log_path_new = f'{temp_dir}/{log_new}' - os.rename(log_path, log_path_new) + Path(log_path).rename(log_path_new) Log.swap(log_new) @@ -60,7 +61,7 @@ def test_usr1_unit_log(search_in_file, temp_dir, unit_pid, wait_for_record): assert client.post(body=body)['status'] == 200 assert wait_for_record(body, log_new) is not None, 'rename new' - assert not os.path.isfile(log_path), 'rename old' + assert not Path(log_path).is_file(), 'rename old' os.kill(unit_pid, signal.SIGUSR1) @@ -75,13 +76,10 @@ def test_usr1_unit_log(search_in_file, temp_dir, unit_pid, wait_for_record): finally: # merge two log files into unit.log to check alerts - with open(log_path, 'r', errors='ignore') as unit_log: - log = unit_log.read() - - with open(log_path, 'w') as unit_log, open( - log_path_new, 'r', errors='ignore' - ) as unit_log_new: - unit_log.write(unit_log_new.read()) - unit_log.write(log) + path_log = Path(log_path) + log = path_log.read_text(encoding='utf-8', errors='ignore') + Path( + log_path_new + ).read_text(encoding='utf-8', errors='ignore') + path_log.write_text(log, encoding='utf-8', errors='ignore') Log.swap(log_new) diff --git a/test/test_variables.py b/test/test_variables.py index c9b173fa..9aab8a62 100644 --- a/test/test_variables.py +++ b/test/test_variables.py @@ -1,11 +1,11 @@ -import os -from pathlib import Path import re import time +from pathlib import Path import pytest -from unit.applications.proto import ApplicationProto + from unit.applications.lang.python import ApplicationPython +from unit.applications.proto import ApplicationProto from unit.option import option client = ApplicationProto() @@ -16,17 +16,17 @@ client_python = ApplicationPython() def setup_method_fixture(): assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [{"action": {"return": 200}}], }, ), 'configure routes' -def set_format(format): +def set_format(log_format): assert 'success' in client.conf( { 'path': f'{option.temp_dir}/access.log', - 'format': format, + 'format': log_format, }, 'access_log', ), 'access_log format' @@ -127,12 +127,12 @@ def test_variables_uri(search_in_file, wait_for_record): def test_variables_uri_no_cache(temp_dir): - os.makedirs(f'{temp_dir}/foo/bar') - Path(f'{temp_dir}/foo/bar/index.html').write_text('index') + Path(f'{temp_dir}/foo/bar').mkdir(parents=True) + Path(f'{temp_dir}/foo/bar/index.html').write_text('index', encoding='utf-8') assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [ { "action": { @@ -163,7 +163,7 @@ def test_variables_host(search_in_file, wait_for_record): check_host('localhost') check_host('localhost1.', 'localhost1') - check_host('localhost2:7080', 'localhost2') + check_host('localhost2:8080', 'localhost2') check_host('.localhost') check_host('www.localhost') @@ -175,7 +175,7 @@ def test_variables_remote_addr(search_in_file, wait_for_record): assert wait_for_record(r'^127\.0\.0\.1$', 'access.log') is not None assert 'success' in client.conf( - {"[::1]:7080": {"pass": "routes"}}, 'listeners' + {"[::1]:8080": {"pass": "routes"}}, 'listeners' ) reg = r'^::1$' @@ -211,6 +211,26 @@ def test_variables_request_line(search_in_file, wait_for_record): assert wait_for_record(reg, 'access.log') is not None +def test_variables_request_id(search_in_file, wait_for_record, findall): + set_format('$uri $request_id $request_id') + + assert search_in_file(r'/request_id', 'access.log') is None + assert client.get(url='/request_id_1')['status'] == 200 + assert client.get(url='/request_id_2')['status'] == 200 + assert wait_for_record(r'/request_id_2', 'access.log') is not None + + id1 = findall( + r'^\/request_id_1 ([0-9a-f]{32}) ([0-9a-f]{32})$', 'access.log' + )[0] + id2 = findall( + r'^\/request_id_2 ([0-9a-f]{32}) ([0-9a-f]{32})$', 'access.log' + )[0] + + assert id1[0] == id1[1], 'same ids first' + assert id2[0] == id2[1], 'same ids second' + assert id1[0] != id2[0], 'first id != second id' + + def test_variables_status(search_in_file, wait_for_record): set_format('$status') @@ -423,11 +443,11 @@ def test_variables_response_header(temp_dir, wait_for_record): # share Path(f'{temp_dir}/foo').mkdir() - Path(f'{temp_dir}/foo/index.html').write_text('index') + Path(f'{temp_dir}/foo/index.html').write_text('index', encoding='utf-8') assert 'success' in client.conf( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [ { "action": { @@ -494,11 +514,11 @@ def test_variables_response_header_application(require, wait_for_record): def test_variables_invalid(temp_dir): - def check_variables(format): + def check_variables(log_format): assert 'error' in client.conf( { 'path': f'{temp_dir}/access.log', - 'format': format, + 'format': log_format, }, 'access_log', ), 'access_log format' diff --git a/test/unit/applications/lang/go.py b/test/unit/applications/lang/go.py index 93e0738b..2479d4f6 100644 --- a/test/unit/applications/lang/go.py +++ b/test/unit/applications/lang/go.py @@ -53,7 +53,7 @@ class ApplicationGo(ApplicationProto): replace_path = f'{option.current_dir}/build/go/src/unit.nginx.org/go' - with open(f'{temp_dir}go.mod', 'w') as f: + with open(f'{temp_dir}go.mod', 'w', encoding='utf-8') as f: f.write( f"""module test/app require unit.nginx.org/go v0.0.0 @@ -91,7 +91,7 @@ replace unit.nginx.org/go => {replace_path} ApplicationGo.prepare_env(script, name, static=static_build) conf = { - "listeners": {"*:7080": {"pass": f"applications/{script}"}}, + "listeners": {"*:8080": {"pass": f"applications/{script}"}}, "applications": { script: { "type": "external", diff --git a/test/unit/applications/lang/java.py b/test/unit/applications/lang/java.py index dc6d2bfc..351d04ce 100644 --- a/test/unit/applications/lang/java.py +++ b/test/unit/applications/lang/java.py @@ -53,7 +53,7 @@ class ApplicationJava(ApplicationProto): os.makedirs(classes_path) classpath = ( - f'{option.current_dir}/build/tomcat-servlet-api-9.0.82.jar' + f'{option.current_dir}/build/tomcat-servlet-api-9.0.86.jar' ) ws_jars = glob.glob( @@ -97,7 +97,7 @@ class ApplicationJava(ApplicationProto): script_path = f'{option.test_dir}/java/{script}/' self._load_conf( { - "listeners": {"*:7080": {"pass": f"applications/{script}"}}, + "listeners": {"*:8080": {"pass": f"applications/{script}"}}, "applications": { script: { "unit_jars": f'{option.current_dir}/build', diff --git a/test/unit/applications/lang/node.py b/test/unit/applications/lang/node.py index 4f18c780..ff95fbd5 100644 --- a/test/unit/applications/lang/node.py +++ b/test/unit/applications/lang/node.py @@ -44,7 +44,7 @@ class ApplicationNode(ApplicationProto): self._load_conf( { "listeners": { - "*:7080": {"pass": f"applications/{quote(script, '')}"} + "*:8080": {"pass": f"applications/{quote(script, '')}"} }, "applications": { script: { diff --git a/test/unit/applications/lang/perl.py b/test/unit/applications/lang/perl.py index 037e98e8..e99c2aca 100644 --- a/test/unit/applications/lang/perl.py +++ b/test/unit/applications/lang/perl.py @@ -11,7 +11,7 @@ class ApplicationPerl(ApplicationProto): self._load_conf( { - "listeners": {"*:7080": {"pass": f"applications/{script}"}}, + "listeners": {"*:8080": {"pass": f"applications/{script}"}}, "applications": { script: { "type": self.get_application_type(), diff --git a/test/unit/applications/lang/php.py b/test/unit/applications/lang/php.py index b9b6dbf1..ac59ec1b 100644 --- a/test/unit/applications/lang/php.py +++ b/test/unit/applications/lang/php.py @@ -1,4 +1,4 @@ -import os +from pathlib import Path import shutil from unit.applications.proto import ApplicationProto @@ -15,10 +15,9 @@ class ApplicationPHP(ApplicationProto): if kwargs.get('isolation') and kwargs['isolation'].get('rootfs'): rootfs = kwargs['isolation']['rootfs'] - if not os.path.exists(f'{rootfs}/app/php/'): - os.makedirs(f'{rootfs}/app/php/') + Path(f'{rootfs}/app/php/').mkdir(parents=True, exist_ok=True) - if not os.path.exists(f'{rootfs}/app/php/{script}'): + if not Path(f'{rootfs}/app/php/{script}').exists(): shutil.copytree(script_path, f'{rootfs}/app/php/{script}') script_path = f'/app/php/{script}' @@ -42,7 +41,7 @@ class ApplicationPHP(ApplicationProto): self._load_conf( { - "listeners": {"*:7080": {"pass": f"applications/{script}"}}, + "listeners": {"*:8080": {"pass": f"applications/{script}"}}, "applications": {script: app}, }, **kwargs, diff --git a/test/unit/applications/lang/python.py b/test/unit/applications/lang/python.py index 4e1fd897..67684b04 100644 --- a/test/unit/applications/lang/python.py +++ b/test/unit/applications/lang/python.py @@ -1,4 +1,4 @@ -import os +from pathlib import Path import shutil from urllib.parse import quote @@ -26,10 +26,9 @@ class ApplicationPython(ApplicationProto): if kwargs.get('isolation') and kwargs['isolation'].get('rootfs'): rootfs = kwargs['isolation']['rootfs'] - if not os.path.exists(f'{rootfs}/app/python/'): - os.makedirs(f'{rootfs}/app/python/') + Path(f'{rootfs}/app/python/').mkdir(parents=True, exist_ok=True) - if not os.path.exists(f'{rootfs}/app/python/{name}'): + if not Path(f'{rootfs}/app/python/{name}').exists(): shutil.copytree(script_path, f'{rootfs}/app/python/{name}') script_path = f'/app/python/{name}' @@ -59,7 +58,7 @@ class ApplicationPython(ApplicationProto): self._load_conf( { "listeners": { - "*:7080": {"pass": f"applications/{quote(name, '')}"} + "*:8080": {"pass": f"applications/{quote(name, '')}"} }, "applications": {name: app}, }, diff --git a/test/unit/applications/lang/ruby.py b/test/unit/applications/lang/ruby.py index f6c4f6c3..1268f8c7 100644 --- a/test/unit/applications/lang/ruby.py +++ b/test/unit/applications/lang/ruby.py @@ -37,7 +37,7 @@ class ApplicationRuby(ApplicationProto): self._load_conf( { - "listeners": {"*:7080": {"pass": f"applications/{script}"}}, + "listeners": {"*:8080": {"pass": f"applications/{script}"}}, "applications": {script: app}, }, **kwargs, diff --git a/test/unit/applications/tls.py b/test/unit/applications/tls.py index e9bcc514..75354dd9 100644 --- a/test/unit/applications/tls.py +++ b/test/unit/applications/tls.py @@ -79,7 +79,7 @@ subjectAltName = @alt_names {a_names}''' - with open(conf_path, 'w') as f: + with open(conf_path, 'w', encoding='utf-8') as f: f.write( f'''[ req ] default_bits = 2048 @@ -97,7 +97,7 @@ distinguished_name = req_distinguished_name script_path = f'{option.test_dir}/python/{script}' self._load_conf( { - "listeners": {"*:7080": {"pass": f"applications/{name}"}}, + "listeners": {"*:8080": {"pass": f"applications/{name}"}}, "applications": { name: { "type": "python", diff --git a/test/unit/applications/websockets.py b/test/unit/applications/websockets.py index 29725943..a8e563d0 100644 --- a/test/unit/applications/websockets.py +++ b/test/unit/applications/websockets.py @@ -6,6 +6,7 @@ import select import struct import pytest + from unit.applications.proto import ApplicationProto GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" @@ -69,7 +70,7 @@ class ApplicationWebsocket(ApplicationProto): return struct.pack('!H', code) + reason.encode('utf-8') def frame_read(self, sock, read_timeout=60): - def recv_bytes(sock, bytes): + def recv_bytes(sock, bytes_len): data = b'' while True: rlist = select.select([sock], [], [], read_timeout)[0] @@ -80,9 +81,9 @@ class ApplicationWebsocket(ApplicationProto): pytest.fail("Can't read response from server.") break - data += sock.recv(bytes - len(data)) + data += sock.recv(bytes_len - len(data)) - if len(data) == bytes: + if len(data) == bytes_len: break return data @@ -206,18 +207,18 @@ class ApplicationWebsocket(ApplicationProto): end = frame_len pos = end - def message(self, sock, type, message, fragmention_size=None, **kwargs): + def message(self, sock, mes_type, message, fragmention_size=None, **kwargs): message_len = len(message) if fragmention_size is None: fragmention_size = message_len if message_len <= fragmention_size: - self.frame_write(sock, type, message, **kwargs) + self.frame_write(sock, mes_type, message, **kwargs) return pos = 0 - op_code = type + op_code = mes_type while pos < message_len: end = min(pos + fragmention_size, message_len) fin = end == message_len diff --git a/test/unit/check/check_prerequisites.py b/test/unit/check/check_prerequisites.py index 44c3f10f..ea319346 100644 --- a/test/unit/check/check_prerequisites.py +++ b/test/unit/check/check_prerequisites.py @@ -1,4 +1,5 @@ import pytest + from unit.option import option diff --git a/test/unit/check/chroot.py b/test/unit/check/chroot.py index b749fab6..466b6ba4 100644 --- a/test/unit/check/chroot.py +++ b/test/unit/check/chroot.py @@ -15,7 +15,7 @@ def check_chroot(): addr=f'{option.temp_dir}/control.unit.sock', body=json.dumps( { - "listeners": {"*:7080": {"pass": "routes"}}, + "listeners": {"*:8080": {"pass": "routes"}}, "routes": [ { "action": { diff --git a/test/unit/check/discover_available.py b/test/unit/check/discover_available.py index 0942581b..1383a0c3 100644 --- a/test/unit/check/discover_available.py +++ b/test/unit/check/discover_available.py @@ -18,6 +18,8 @@ def discover_available(unit): [unit['unitd'], '--version'], stderr=subprocess.STDOUT ).decode() + option.configure_flag['asan'] = '-fsanitize=address' in output_version + # wait for controller start if Log.wait_for_record(r'controller started') is None: diff --git a/test/unit/check/isolation.py b/test/unit/check/isolation.py index e4674f4d..861c0818 100644 --- a/test/unit/check/isolation.py +++ b/test/unit/check/isolation.py @@ -1,5 +1,5 @@ import json -import os +from pathlib import Path from unit.applications.lang.go import ApplicationGo from unit.applications.lang.java import ApplicationJava @@ -21,7 +21,7 @@ def check_isolation(): ApplicationGo().prepare_env('empty', 'app') conf = { - "listeners": {"*:7080": {"pass": "applications/empty"}}, + "listeners": {"*:8080": {"pass": "applications/empty"}}, "applications": { "empty": { "type": "external", @@ -35,7 +35,7 @@ def check_isolation(): elif 'python' in available['modules']: conf = { - "listeners": {"*:7080": {"pass": "applications/empty"}}, + "listeners": {"*:8080": {"pass": "applications/empty"}}, "applications": { "empty": { "type": "python", @@ -50,7 +50,7 @@ def check_isolation(): elif 'php' in available['modules']: conf = { - "listeners": {"*:7080": {"pass": "applications/phpinfo"}}, + "listeners": {"*:8080": {"pass": "applications/phpinfo"}}, "applications": { "phpinfo": { "type": "php", @@ -67,7 +67,7 @@ def check_isolation(): ApplicationRuby().prepare_env('empty') conf = { - "listeners": {"*:7080": {"pass": "applications/empty"}}, + "listeners": {"*:8080": {"pass": "applications/empty"}}, "applications": { "empty": { "type": "ruby", @@ -83,7 +83,7 @@ def check_isolation(): ApplicationJava().prepare_env('empty') conf = { - "listeners": {"*:7080": {"pass": "applications/empty"}}, + "listeners": {"*:8080": {"pass": "applications/empty"}}, "applications": { "empty": { "unit_jars": f"{option.current_dir}/build", @@ -100,7 +100,7 @@ def check_isolation(): ApplicationNode().prepare_env('basic') conf = { - "listeners": {"*:7080": {"pass": "applications/basic"}}, + "listeners": {"*:8080": {"pass": "applications/basic"}}, "applications": { "basic": { "type": "external", @@ -114,7 +114,7 @@ def check_isolation(): elif 'perl' in available['modules']: conf = { - "listeners": {"*:7080": {"pass": "applications/body_empty"}}, + "listeners": {"*:8080": {"pass": "applications/body_empty"}}, "applications": { "body_empty": { "type": "perl", @@ -145,11 +145,12 @@ def check_isolation(): isolation = {'user': userns} - unp_clone_path = '/proc/sys/kernel/unprivileged_userns_clone' - if os.path.exists(unp_clone_path): - with open(unp_clone_path, 'r') as f: - if str(f.read()).rstrip() == '1': - isolation['unprivileged_userns_clone'] = True + path_clone = Path('/proc/sys/kernel/unprivileged_userns_clone') + if ( + path_clone.exists() + and path_clone.read_text(encoding='utf-8').rstrip() == '1' + ): + isolation['unprivileged_userns_clone'] = True for ns in allns: ns_value = getns(ns) diff --git a/test/unit/check/node.py b/test/unit/check/node.py index 6a3d581f..b206e914 100644 --- a/test/unit/check/node.py +++ b/test/unit/check/node.py @@ -1,11 +1,11 @@ -import os import subprocess +from pathlib import Path from unit.option import option def check_node(): - if not os.path.exists(f'{option.current_dir}/node/node_modules'): + if not Path(f'{option.current_dir}/node/node_modules').exists(): return False try: diff --git a/test/unit/control.py b/test/unit/control.py index 164d0e60..8cdf1887 100644 --- a/test/unit/control.py +++ b/test/unit/control.py @@ -16,7 +16,7 @@ def args_handler(conf_func): elif argcount == 3: conf = args[0] - if isinstance(conf, dict) or isinstance(conf, list): + if isinstance(conf, (dict, list)): conf = json.dumps(conf) url = args[1] if len(args) == 2 else url_default diff --git a/test/unit/http.py b/test/unit/http.py index 347382f5..9401501b 100644 --- a/test/unit/http.py +++ b/test/unit/http.py @@ -7,13 +7,14 @@ import select import socket import pytest + from unit.option import option class HTTP1: def http(self, start_str, **kwargs): sock_type = kwargs.get('sock_type', 'ipv4') - port = kwargs.get('port', 7080) + port = kwargs.get('port', 8080) url = kwargs.get('url', '/') http = 'HTTP/1.0' if 'http_10' in kwargs else 'HTTP/1.1' @@ -38,10 +39,7 @@ class HTTP1: if 'sock' not in kwargs: sock = socket.socket(sock_types[sock_type], socket.SOCK_STREAM) - if ( - sock_type == sock_types['ipv4'] - or sock_type == sock_types['ipv6'] - ): + if sock_type in (sock_types['ipv4'], sock_types['ipv6']): sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) if 'wrapper' in kwargs: @@ -202,7 +200,7 @@ class HTTP1: data += part - if not len(part): + if not part: break return data @@ -263,7 +261,7 @@ class HTTP1: size = int(chunks.pop(0), 16) except ValueError: - pytest.fail(f'Invalid chunk size {size}') + pytest.fail('Invalid chunk size') if size == 0: assert len(chunks) == 1, 'last zero size' diff --git a/test/unit/option.py b/test/unit/option.py index ee1f46dd..7c66c619 100644 --- a/test/unit/option.py +++ b/test/unit/option.py @@ -6,6 +6,7 @@ class Options: _options = { 'architecture': platform.architecture()[0], 'available': {'modules': {}, 'features': {}}, + 'configure_flag': {}, 'is_privileged': os.geteuid() == 0, 'skip_alerts': [], 'skip_sanitizer': False, diff --git a/test/unit/status.py b/test/unit/status.py index 84c958a3..95096a96 100644 --- a/test/unit/status.py +++ b/test/unit/status.py @@ -30,16 +30,16 @@ class Status: for k in d1 if k in d2 } - else: - return d1 - d2 + + return d1 - d2 return find_diffs(Status.control.conf_get('/status'), Status._status) def get(path='/'): - path = path.split('/')[1:] + path_lst = path.split('/')[1:] diff = Status.diff() - for p in path: - diff = diff[p] + for part in path_lst: + diff = diff[part] return diff diff --git a/tools/README.md b/tools/README.md index 883bc107..e7caae34 100644 --- a/tools/README.md +++ b/tools/README.md @@ -33,7 +33,7 @@ web page with NGINX Unit. | Options | | |---------|-| -| filename … | Read configuration data consequently from the specified files instead of stdin. +| filename … | Read configuration data consecutively from the specified files instead of stdin. | _HTTP method_ | It is usually not required to specify a HTTP method. `GET` is used to read the configuration. `PUT` is used when making configuration changes unless a specific method is provided. | `edit` | Opens **URI** in the default editor for interactive configuration. The [jq](https://stedolan.github.io/jq/) tool is required for this option. | `INSERT` | A _virtual_ HTTP method that prepends data when the URI specifies an existing array. The [jq](https://stedolan.github.io/jq/) tool is required for this option. diff --git a/tools/setup-unit b/tools/setup-unit index 38592fe3..688e87a1 100755 --- a/tools/setup-unit +++ b/tools/setup-unit @@ -3,7 +3,8 @@ ##################################################################### # # Copyright (C) NGINX, Inc. -# Author: NGINX Unit Team, F5 Inc. +# Author: NGINX Unit Team, F5 Inc. +# Copyright 2024, Alejandro Colomar <alx@kernel.org> # ##################################################################### @@ -28,6 +29,7 @@ test -v BASH_VERSION \ test -v ZSH_VERSION \ && setopt sh_word_split; + export LC_ALL=C dry_run='no'; @@ -36,7 +38,7 @@ help_unit() { cat <<__EOF__ ; SYNOPSIS - $0 [-h] COMMAND [ARGS] + $0 [-h[h]] COMMAND [ARGS] Subcommands ├── repo-config [-hn] [PKG-MANAGER OS-NAME OS-VERSION] @@ -61,9 +63,8 @@ OPTIONS -h, --help Print this help. - --help-more - Print help for more commands. They are experimental. Using - these isn't recommended, unless you know what you're doing. + -hh, --help-more + Print help for more (advanced) commands. __EOF__ } @@ -72,14 +73,14 @@ help_more_unit() { cat <<__EOF__ ; SYNOPSIS - $0 [-h] COMMAND [ARGS] + $0 [-h[h]] COMMAND [ARGS] Subcommands ├── cmd [-h] ├── ctl [-h] [-s SOCK] SUBCOMMAND [ARGS] - │ ├── edit [-h] PATH - │ ├── http [-h] [-c CURLOPT] METHOD PATH - │ └── insert [-h] PATH INDEX + │ ├── edit [-h] PATH + │ ├── http [-h] [-c CURLOPT] METHOD PATH + │ └── insert [-h] PATH INDEX ├── freeport [-h] ├── json-ins [-hn] JSON INDEX ├── os-probe [-h] @@ -87,8 +88,8 @@ SYNOPSIS ├── repo-config [-hn] [PKG-MANAGER OS-NAME OS-VERSION] ├── restart [-hls] ├── sock [-h] SUBCOMMAND [ARGS] - │ ├── filter [-chs] - │ └── find [-h] + │ ├── filter [-chs] + │ └── find [-h] └── welcome [-hn] DESCRIPTION @@ -118,6 +119,9 @@ COMMANDS Configure your package manager with the NGINX Unit repository for later installation. + restart + Restart all running unitd(8) instances. + sock Print the control API socket address. welcome @@ -128,12 +132,17 @@ OPTIONS -h, --help Print basic help (some commands are hidden). - --help-more + -hh, --help-more Print the hidden help with more commands. __EOF__ } +info() +{ + >&2 echo "$(basename "$0"): info: $*"; +} + warn() { >&2 echo "$(basename "$0"): error: $*"; @@ -161,6 +170,18 @@ dry_run_eval() fi; } +run_trap() +{ + trap -p "$1" \ + | tr -d '\n' \ + | sed "s/[^']*'\(.*\)'[^']*/\1/" \ + | sed "s/'\\\\''/'/g" \ + | read -r trap_cmd; + + eval $trap_cmd; + trap - "$1"; +} + help_unit_cmd() { @@ -299,25 +320,44 @@ unit_ctl() if echo $sock | grep '^ssh://' >/dev/null; then local remote="$(echo $sock | sed 's,\(ssh://[^/]*\).*,\1,')"; local sock="$(echo $sock | sed 's,ssh://[^/]*\(.*\),unix:\1,')"; + + local remote_sock="$(echo "$sock" | unit_sock_filter -s)"; + local local_sock="$(mktemp -u -p /var/run/unit/)"; + local ssh_ctrl="$(mktemp -u -p /var/run/unit/)"; + + mkdir -p /var/run/unit/; + + ssh -fMNnT -S "$ssh_ctrl" \ + -o 'ExitOnForwardFailure yes' \ + -L "$local_sock:$remote_sock" "$remote"; + + trap "ssh -S '$ssh_ctrl' -O exit '$remote' 2>/dev/null; + unlink '$local_sock';" EXIT; + + sock="unix:$local_sock"; fi; case $1 in edit) shift; - unit_ctl_edit ${remote:+ ---r $remote} ---s "$sock" $@; + unit_ctl_edit ---s "$sock" $@; ;; http) shift; - unit_ctl_http ${remote:+ ---r $remote} ---s "$sock" $@; + unit_ctl_http ---s "$sock" $@; ;; insert) shift; - unit_ctl_insert ${remote:+ ---r $remote} ---s "$sock" $@; + unit_ctl_insert ---s "$sock" $@; ;; *) err "ctl: $1: Unknown argument."; ;; esac; + + if test -v remote; then + run_trap EXIT; + fi; } @@ -362,10 +402,6 @@ unit_ctl_edit() help_unit_ctl_edit; exit 0; ;; - ---r | ----remote) - local remote="$2"; - shift; - ;; ---s | ----sock) local sock="$2"; shift; @@ -381,29 +417,19 @@ unit_ctl_edit() done; if ! test $# -ge 1; then - err 'ctl: insert: PATH: Missing argument.'; + err 'ctl: edit: PATH: Missing argument.'; fi; local req_path="$1"; - if test -v remote; then - local remote_sock="$(echo "$sock" | unit_sock_filter -s)"; - local local_sock="$(mktemp -u -p /var/run/unit/)"; - local ssh_ctrl="$(mktemp -u -p /var/run/unit/)"; - - mkdir -p /var/run/unit/; - - ssh -fMNnT -S "$ssh_ctrl" \ - -o 'ExitOnForwardFailure yes' \ - -L "$local_sock:$remote_sock" "$remote"; - - sock="unix:$local_sock"; - fi; - - local tmp="$(mktemp ||:)"; + echo "$req_path" \ + | sed 's%^/js_modules/.*%.js%' \ + | sed 's%^/config\>.*%.json%' \ + | sed 's%^/.*%.txt%' \ + | xargs mktemp --suffix \ + | read -r tmp; unit_ctl_http ---s "$sock" -c --no-progress-meter GET "$req_path" \ - </dev/null >"$tmp" \ - ||:; + </dev/null >"$tmp"; $( ((test -v VISUAL && test -n "$VISUAL") && printf '%s\n' "$VISUAL") \ @@ -412,16 +438,13 @@ unit_ctl_edit() || command -v vi \ || command -v vim \ || echo ed; - ) "$tmp" \ - ||:; + ) "$tmp"; - unit_ctl_http ---s "$sock" PUT "$req_path" <"$tmp" \ - ||:; + trap "info 'ctl: edit: Invalid configuration saved in <$tmp>.'" ERR - if test -v remote; then - ssh -S "$ssh_ctrl" -O exit "$remote" 2>/dev/null; - unlink "$local_sock"; - fi; + unit_ctl_http ---s "$sock" PUT "$req_path" <"$tmp"; + + trap - ERR; } @@ -477,10 +500,6 @@ unit_ctl_http() help_unit_ctl_http; exit 0; ;; - ---r | ----remote) - local remote="$2"; - shift; - ;; ---s | ----sock) local sock="$2"; shift; @@ -505,28 +524,8 @@ unit_ctl_http() fi; local req_path="$2"; - if test -v remote; then - local remote_sock="$(echo "$sock" | unit_sock_filter -s)"; - local local_sock="$(mktemp -u -p /var/run/unit/)"; - local ssh_ctrl="$(mktemp -u -p /var/run/unit/)"; - - mkdir -p /var/run/unit/; - - ssh -fMNnT -S "$ssh_ctrl" \ - -o 'ExitOnForwardFailure yes' \ - -L "$local_sock:$remote_sock" "$remote"; - - sock="unix:$local_sock"; - fi; - - curl $curl_options -X $method -d@- \ - $(echo "$sock" | unit_sock_filter -c)${req_path} \ - ||:; - - if test -v remote; then - ssh -S "$ssh_ctrl" -O exit "$remote" 2>/dev/null; - unlink "$local_sock"; - fi; + curl --fail-with-body $curl_options -X $method -d@- \ + $(echo "$sock" | unit_sock_filter -c)${req_path}; } @@ -561,10 +560,6 @@ unit_ctl_insert() help_unit_ctl_insert; exit 0; ;; - ---r | ----remote) - local remote="$2"; - shift; - ;; ---s | ----sock) local sock="$2"; shift; @@ -589,34 +584,13 @@ unit_ctl_insert() fi; local idx="$2"; - if test -v remote; then - local remote_sock="$(echo "$sock" | unit_sock_filter -s)"; - local local_sock="$(mktemp -u -p /var/run/unit/)"; - local ssh_ctrl="$(mktemp -u -p /var/run/unit/)"; - - mkdir -p /var/run/unit/; - - ssh -fMNnT -S "$ssh_ctrl" \ - -o 'ExitOnForwardFailure yes' \ - -L "$local_sock:$remote_sock" "$remote"; - - sock="unix:$local_sock"; - fi; - - local old="$(mktemp ||:)"; + local old="$(mktemp)"; unit_ctl_http ---s "$sock" -c --no-progress-meter GET "$req_path" \ - </dev/null >"$old" \ - ||:; + </dev/null >"$old"; unit_json_ins "$old" "$idx" \ - | unit_ctl_http ---s "$sock" PUT "$req_path" \ - ||:; - - if test -v remote; then - ssh -S "$ssh_ctrl" -O exit "$remote" 2>/dev/null; - unlink "$local_sock"; - fi; + | unit_ctl_http ---s "$sock" PUT "$req_path"; } @@ -786,7 +760,7 @@ unit_ctl_welcome() <hr> <p><a href="https://unit.nginx.org/?referer=welcome">NGINX Unit — the universal web app server</a><br> - NGINX, Inc. © 2023</p> + NGINX, Inc. © 2024</p> </body> </html> __EOF__'; @@ -1634,7 +1608,7 @@ while test $# -ge 1; do help_unit; exit 0; ;; - --help-more) + -hh | --help-more) help_more_unit; exit 0; ;; diff --git a/tools/unitc b/tools/unitc index 4ab5f663..9fba4c6d 100755 --- a/tools/unitc +++ b/tools/unitc @@ -1,7 +1,7 @@ #!/bin/bash # unitc - a curl wrapper for configuring NGINX Unit -# https://github.com/nginx/unit/tree/master/tools -# NGINX, Inc. (c) 2023 +# https://github.com/nginx/unit/tree/master/tools +# NGINX, Inc. (c) 2024 # Defaults # @@ -186,9 +186,9 @@ if [ $REMOTE -eq 0 ]; then echo "${0##*/}: WARNING: unable to identify unitd command line parameters for PID $PID, assuming unitd defaults from \$PATH" PARAMS=unitd fi - CTRL_ADDR=$(echo "$PARAMS" | grep '\--control' | cut -f2 -d' ') + CTRL_ADDR=$(echo "$PARAMS" | grep '\--control ' | cut -f2 -d' ') if [ "$CTRL_ADDR" = "" ]; then - CTRL_ADDR=$($(echo "$PARAMS") --help | grep -A1 '\--control' | tail -1 | cut -f2 -d\") + CTRL_ADDR=$($(echo "$PARAMS") --help | grep -A1 '\--control ADDRESS' | tail -1 | cut -f2 -d\") fi if [ "$CTRL_ADDR" = "" ]; then echo "${0##*/}: ERROR: cannot detect control socket. Did you start unitd with a relative path? Try starting unitd with --control option." @@ -292,7 +292,7 @@ else exit 1 fi NEW_ELEMENT=$(cat ${CONF_FILES[@]}) - echo $NEW_ELEMENT | jq > /dev/null || exit $? # Test the input is valid JSON before proceeding + echo $NEW_ELEMENT | jq > /dev/null || exit $? # Test the input is valid JSON before proceeding OLD_ARRAY=$($RPC_CMD curl -s $UNIT_CTRL$URI) if [ "$(echo $OLD_ARRAY | jq -r type)" = "array" ]; then echo $OLD_ARRAY | jq ". |= [$NEW_ELEMENT] + ." | $RPC_CMD curl -X PUT --data-binary @- $UNIT_CTRL$URI 2> /tmp/${0##*/}.$$ | $OUTPUT @@ -1,5 +1,5 @@ # Copyright (C) NGINX, Inc. -NXT_VERSION=1.31.1 -NXT_VERNUM=13101 +NXT_VERSION=1.32.0 +NXT_VERNUM=13200 |