summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.gitattributes5
-rw-r--r--.github/workflows/ci.yml339
-rw-r--r--.mailmap20
-rw-r--r--.rustfmt.toml1
-rw-r--r--CHANGES53
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--NOTICE18
-rw-r--r--README.md6
-rw-r--r--SECURITY.txt24
-rw-r--r--auto/cc/deps4
-rw-r--r--auto/endian2
-rw-r--r--auto/help9
-rw-r--r--auto/modules/conf4
-rw-r--r--auto/modules/java6
-rw-r--r--auto/modules/java_jar.sha51226
-rw-r--r--auto/modules/nodejs4
-rw-r--r--auto/modules/wasm-wasi-component120
-rw-r--r--docs/changes.xml176
-rw-r--r--docs/man/man8/unitd.8.in45
-rw-r--r--docs/unit-openapi.yaml2
-rw-r--r--go/ldflags-darwin.go15
-rw-r--r--go/ldflags-lrt.go1
-rw-r--r--pkg/Makefile9
-rw-r--r--pkg/contrib/src/libunit-wasm/version4
-rw-r--r--pkg/contrib/src/njs/SHA512SUMS2
-rw-r--r--pkg/contrib/src/njs/version2
-rw-r--r--pkg/deb/Makefile17
-rw-r--r--pkg/deb/Makefile.jsc2171
-rw-r--r--pkg/deb/Makefile.python31246
-rw-r--r--pkg/deb/Makefile.wasm6
-rw-r--r--pkg/deb/debian.module/copyright.unit-jsc1118
-rw-r--r--pkg/deb/debian.module/copyright.unit-jsc818
-rw-r--r--pkg/deb/debian.module/unit.example-jsc21-config15
-rw-r--r--pkg/deb/debian.module/unit.example-python3.12-config16
-rw-r--r--pkg/deb/debian/copyright18
-rw-r--r--pkg/docker/Dockerfile.go1.218
-rw-r--r--pkg/docker/Dockerfile.go1.22 (renamed from pkg/docker/Dockerfile.go1.20)12
-rw-r--r--pkg/docker/Dockerfile.jsc118
-rw-r--r--pkg/docker/Dockerfile.minimal8
-rw-r--r--pkg/docker/Dockerfile.node208
-rw-r--r--pkg/docker/Dockerfile.node21 (renamed from pkg/docker/Dockerfile.node18)12
-rw-r--r--pkg/docker/Dockerfile.perl5.368
-rw-r--r--pkg/docker/Dockerfile.perl5.388
-rw-r--r--pkg/docker/Dockerfile.php8.28
-rw-r--r--pkg/docker/Dockerfile.php8.389
-rw-r--r--pkg/docker/Dockerfile.python3.118
-rw-r--r--pkg/docker/Dockerfile.python3.1289
-rw-r--r--pkg/docker/Dockerfile.ruby3.28
-rw-r--r--pkg/docker/Dockerfile.ruby3.389
-rw-r--r--pkg/docker/Dockerfile.wasm19
-rw-r--r--pkg/docker/Makefile20
-rw-r--r--pkg/docker/template.Dockerfile6
-rw-r--r--pkg/docker/welcome.html2
-rw-r--r--pkg/rpm/Makefile18
-rw-r--r--pkg/rpm/Makefile.jsc-common5
-rw-r--r--pkg/rpm/Makefile.jsc1713
-rw-r--r--pkg/rpm/Makefile.python31253
-rw-r--r--pkg/rpm/Makefile.wasm6
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/COPYRIGHT.unit-jsc1118
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/COPYRIGHT.unit-jsc818
-rw-r--r--pkg/rpm/rpmbuild/SOURCES/unit.example-python312-config16
-rw-r--r--src/java/nginx/unit/websocket/DigestAuthenticator.java4
-rw-r--r--src/java/nginx/unit/websocket/Util.java2
-rw-r--r--src/java/nginx/unit/websocket/server/WsSessionListener.java2
-rw-r--r--src/nodejs/unit-http/http.js4
-rw-r--r--src/nodejs/unit-http/http_server.js13
-rw-r--r--src/nodejs/unit-http/loader.js2
-rw-r--r--src/nodejs/unit-http/loader.mjs2
-rw-r--r--src/nodejs/unit-http/unit.cpp8
-rw-r--r--src/nodejs/unit-http/websocket_connection.js8
-rw-r--r--src/nodejs/unit-http/websocket_request.js8
-rw-r--r--src/nxt_application.c3
-rw-r--r--src/nxt_application.h9
-rw-r--r--src/nxt_atomic.h4
-rw-r--r--src/nxt_clone.c14
-rw-r--r--src/nxt_clone.h8
-rw-r--r--src/nxt_conf.c11
-rw-r--r--src/nxt_conf.h3
-rw-r--r--src/nxt_conf_validation.c150
-rw-r--r--src/nxt_conn_write.c7
-rw-r--r--src/nxt_credential.c7
-rw-r--r--src/nxt_file.c82
-rw-r--r--src/nxt_file.h2
-rw-r--r--src/nxt_http_js.c49
-rw-r--r--src/nxt_http_request.c69
-rw-r--r--src/nxt_http_static.c11
-rw-r--r--src/nxt_http_variables.c50
-rw-r--r--src/nxt_isolation.c6
-rw-r--r--src/nxt_js.c64
-rw-r--r--src/nxt_listen_socket.c14
-rw-r--r--src/nxt_main_process.c15
-rw-r--r--src/nxt_openssl.c2
-rw-r--r--src/nxt_php_sapi.c2
-rw-r--r--src/nxt_router.c26
-rw-r--r--src/nxt_router.h2
-rw-r--r--src/nxt_router_access_log.c25
-rw-r--r--src/nxt_runtime.c55
-rw-r--r--src/nxt_runtime.h6
-rw-r--r--src/nxt_socket.c8
-rw-r--r--src/nxt_unit.h38
-rw-r--r--src/nxt_var.c74
-rw-r--r--src/nxt_var.h6
-rw-r--r--src/python/nxt_python_asgi_http.c31
-rw-r--r--src/python/nxt_python_wsgi.c33
-rw-r--r--src/ruby/nxt_ruby.c77
-rw-r--r--src/test/nxt_rbtree1.c2
-rw-r--r--src/test/nxt_rbtree1.h2
-rw-r--r--src/wasm-wasi-component/.gitignore1
-rw-r--r--src/wasm-wasi-component/Cargo.lock2293
-rw-r--r--src/wasm-wasi-component/Cargo.toml30
-rw-r--r--src/wasm-wasi-component/build.rs33
-rw-r--r--src/wasm-wasi-component/src/lib.rs611
-rw-r--r--src/wasm-wasi-component/wrapper.h5
-rw-r--r--test/conftest.py87
-rw-r--r--test/go/404/app.go2
-rw-r--r--test/go/command_line_arguments/app.go2
-rw-r--r--test/go/cookies/app.go2
-rw-r--r--test/go/empty/app.go2
-rw-r--r--test/go/get_variables/app.go2
-rw-r--r--test/go/mirror/app.go2
-rw-r--r--test/go/ns_inspect/app.go2
-rw-r--r--test/go/post_variables/app.go2
-rw-r--r--test/go/variables/app.go2
-rw-r--r--test/node/404/app.js2
-rw-r--r--test/node/basic/app.js2
-rw-r--r--test/node/double_end/app.js2
-rw-r--r--test/node/flush_headers/app.js7
-rw-r--r--test/node/get_header_names/app.js2
-rw-r--r--test/node/get_header_type/app.js2
-rw-r--r--test/node/get_variables/app.js2
-rw-r--r--test/node/has_header/app.js2
-rw-r--r--test/node/header_name_case/app.js2
-rw-r--r--test/node/header_name_valid/app.js2
-rw-r--r--test/node/header_value_object/app.js2
-rw-r--r--test/node/loader/es_modules_http/app.mjs2
-rw-r--r--test/node/loader/es_modules_http_indirect/module.mjs2
-rw-r--r--test/node/loader/es_modules_websocket/app.mjs2
-rw-r--r--test/node/loader/es_modules_websocket_indirect/module.mjs2
-rw-r--r--test/node/loader/transitive_dependency/transitive_http.js2
-rw-r--r--test/node/loader/unit_http/app.js2
-rw-r--r--test/node/mirror/app.js2
-rw-r--r--test/node/options/app.js4
-rw-r--r--test/node/post_variables/app.js2
-rw-r--r--test/node/promise_end/app.js2
-rw-r--r--test/node/promise_handler/app.js2
-rw-r--r--test/node/remove_header/app.js2
-rw-r--r--test/node/set_header_array/app.js2
-rw-r--r--test/node/status_message/app.js2
-rw-r--r--test/node/update_header/app.js2
-rw-r--r--test/node/variables/app.js2
-rw-r--r--test/node/websockets/mirror/app.js2
-rw-r--r--test/node/websockets/mirror_fragmentation/app.js2
-rw-r--r--test/node/write_array/app.js2
-rw-r--r--test/node/write_before_write_head/app.js2
-rw-r--r--test/node/write_buffer/app.js2
-rw-r--r--test/node/write_callback/app.js2
-rw-r--r--test/node/write_multiple/app.js2
-rw-r--r--test/node/write_return/app.js2
-rw-r--r--test/python/body_bytearray/asgi.py20
-rw-r--r--test/python/body_generate/wsgi.py4
-rw-r--r--test/python/delayed/asgi.py4
-rw-r--r--test/python/environment/wsgi.py6
-rw-r--r--test/python/iter_exception/wsgi.py4
-rw-r--r--test/python/legacy/asgi.py4
-rw-r--r--test/python/legacy_force/asgi.py9
-rw-r--r--test/python/lifespan/empty/asgi.py2
-rw-r--r--test/python/lifespan/failed/asgi.py2
-rw-r--r--test/python/restart/longstart.py1
-rw-r--r--test/python/unicode/wsgi.py2
-rw-r--r--test/python/user_group/wsgi.py7
-rw-r--r--test/ruby/header_array/config.ru7
-rw-r--r--test/ruby/header_array_empty/config.ru7
-rw-r--r--test/ruby/header_array_nil/config.ru7
-rw-r--r--test/ruby/input_rewind/config.ru8
-rw-r--r--test/ruby/multipart/config.ru7
-rw-r--r--test/ruby/session/config.ru6
-rw-r--r--test/test_access_log.py77
-rw-r--r--test/test_asgi_application.py13
-rw-r--r--test/test_asgi_application_unix_abstract.py1
-rw-r--r--test/test_asgi_lifespan.py35
-rw-r--r--test/test_asgi_targets.py3
-rw-r--r--test/test_asgi_websockets.py1
-rw-r--r--test/test_client_ip.py21
-rw-r--r--test/test_configuration.py29
-rw-r--r--test/test_forwarded_header.py11
-rw-r--r--test/test_go_application.py2
-rw-r--r--test/test_go_isolation.py3
-rw-r--r--test/test_http_header.py9
-rw-r--r--test/test_java_application.py7
-rw-r--r--test/test_java_isolation_rootfs.py3
-rw-r--r--test/test_java_websockets.py1
-rw-r--r--test/test_njs.py43
-rw-r--r--test/test_njs_modules.py6
-rw-r--r--test/test_node_application.py17
-rw-r--r--test/test_node_es_modules.py1
-rw-r--r--test/test_node_websockets.py1
-rw-r--r--test/test_perl_application.py3
-rw-r--r--test/test_php_application.py37
-rw-r--r--test/test_php_basic.py22
-rw-r--r--test/test_php_targets.py6
-rw-r--r--test/test_procman.py (renamed from test/test_python_procman.py)1
-rw-r--r--test/test_proxy.py61
-rw-r--r--test/test_proxy_chunked.py11
-rw-r--r--test/test_python_application.py7
-rw-r--r--test/test_python_basic.py22
-rw-r--r--test/test_python_isolation.py12
-rw-r--r--test/test_python_targets.py4
-rw-r--r--test/test_reconfigure.py3
-rw-r--r--test/test_reconfigure_tls.py18
-rw-r--r--test/test_respawn.py1
-rw-r--r--test/test_response_headers.py13
-rw-r--r--test/test_return.py5
-rw-r--r--test/test_rewrite.py27
-rw-r--r--test/test_routing.py173
-rw-r--r--test/test_routing_tls.py6
-rw-r--r--test/test_ruby_application.py46
-rw-r--r--test/test_ruby_hooks.py5
-rw-r--r--test/test_settings.py9
-rw-r--r--test/test_static.py48
-rw-r--r--test/test_static_chroot.py20
-rw-r--r--test/test_static_fallback.py17
-rw-r--r--test/test_static_mount.py11
-rw-r--r--test/test_static_share.py7
-rw-r--r--test/test_static_symlink.py9
-rw-r--r--test/test_static_types.py15
-rw-r--r--test/test_static_variables.py11
-rw-r--r--test/test_status.py28
-rw-r--r--test/test_status_tls.py6
-rw-r--r--test/test_tls.py51
-rw-r--r--test/test_tls_conf_command.py9
-rw-r--r--test/test_tls_session.py6
-rw-r--r--test/test_tls_sni.py15
-rw-r--r--test/test_tls_tickets.py42
-rw-r--r--test/test_unix_abstract.py10
-rw-r--r--test/test_upstreams_rr.py103
-rw-r--r--test/test_usr1.py22
-rw-r--r--test/test_variables.py50
-rw-r--r--test/unit/applications/lang/go.py4
-rw-r--r--test/unit/applications/lang/java.py4
-rw-r--r--test/unit/applications/lang/node.py2
-rw-r--r--test/unit/applications/lang/perl.py2
-rw-r--r--test/unit/applications/lang/php.py9
-rw-r--r--test/unit/applications/lang/python.py9
-rw-r--r--test/unit/applications/lang/ruby.py2
-rw-r--r--test/unit/applications/tls.py4
-rw-r--r--test/unit/applications/websockets.py13
-rw-r--r--test/unit/check/check_prerequisites.py1
-rw-r--r--test/unit/check/chroot.py2
-rw-r--r--test/unit/check/discover_available.py2
-rw-r--r--test/unit/check/isolation.py27
-rw-r--r--test/unit/check/node.py4
-rw-r--r--test/unit/control.py2
-rw-r--r--test/unit/http.py12
-rw-r--r--test/unit/option.py1
-rw-r--r--test/unit/status.py10
-rw-r--r--tools/README.md2
-rwxr-xr-xtools/setup-unit176
-rwxr-xr-xtools/unitc10
-rw-r--r--version4
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'
diff --git a/.mailmap b/.mailmap
index d6405683..23a492fa 100644
--- a/.mailmap
+++ b/.mailmap
@@ -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
diff --git a/CHANGES b/CHANGES
index b16751ae..5204a2a1 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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/)).
diff --git a/NOTICE b/NOTICE
index 42d51c92..257f478c 100644
--- a/NOTICE
+++ b/NOTICE
@@ -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/README.md b/README.md
index badd2391..4e230767 100644
--- a/README.md
+++ b/README.md
@@ -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"
diff --git a/auto/help b/auto/help
index b6d9919f..d23c67ed 100644
--- a/auto/help
+++ b/auto/help
@@ -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 &lt;nginx-packaging@f5.com&gt;">
+
+<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 &lt;nginx-packaging@f5.com&gt;">
+
+<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 &lt;nginx-packaging@f5.com&gt;">
+
+<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 &lt;nginx-packaging@f5.com&gt;">
+
+<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 &mdash; the universal web app server</a><br>
- NGINX, Inc. &copy; 2023</p>
+ NGINX, Inc. &copy; 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 &mdash; the universal web app server</a><br>
- NGINX, Inc. &copy; 2023</p>
+ NGINX, Inc. &copy; 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
diff --git a/version b/version
index 3de0f8d3..fa657bae 100644
--- a/version
+++ b/version
@@ -1,5 +1,5 @@
# Copyright (C) NGINX, Inc.
-NXT_VERSION=1.31.1
-NXT_VERNUM=13101
+NXT_VERSION=1.32.0
+NXT_VERNUM=13200