diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 43350e8d4..32a32d207 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -51,7 +51,7 @@ body: label: BunkerWeb version description: What version of BunkerWeb are you running? placeholder: Version - value: 1.6.10~rc7 + value: 1.6.11~rc1 validations: required: true - type: dropdown diff --git a/.github/workflows/beta.yml b/.github/workflows/beta.yml index 1b8825388..6c2bddec4 100644 --- a/.github/workflows/beta.yml +++ b/.github/workflows/beta.yml @@ -110,7 +110,6 @@ jobs: ubuntu, debian-bookworm, debian-trixie, - fedora-42, fedora-43, fedora-44, rhel-8, @@ -127,8 +126,6 @@ jobs: package: deb - linux: debian-trixie package: deb - - linux: fedora-42 - package: rpm - linux: fedora-43 package: rpm - linux: fedora-44 @@ -232,7 +229,6 @@ jobs: ubuntu, debian-bookworm, debian-trixie, - fedora-42, fedora-43, fedora-44, el-8, @@ -259,11 +255,6 @@ jobs: suffix: "" version: trixie package: deb - - linux: fedora-42 - separator: "-" - suffix: "1." - version: 42 - package: rpm - linux: fedora-43 separator: "-" suffix: "1." @@ -303,9 +294,6 @@ jobs: - linux: debian-trixie arch: amd64 package_arch: amd64 - - linux: fedora-42 - arch: amd64 - package_arch: x86_64 - linux: fedora-43 arch: amd64 package_arch: x86_64 @@ -333,9 +321,6 @@ jobs: - linux: debian-trixie arch: arm64 package_arch: arm64 - - linux: fedora-42 - arch: arm64 - package_arch: aarch64 - linux: fedora-43 arch: arm64 package_arch: aarch64 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c7410ff6e..6d26193ed 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -36,12 +36,12 @@ jobs: python -m pip install --no-cache-dir --require-hashes -r src/common/db/requirements.txt echo "CODEQL_PYTHON=$(which python)" >> $GITHUB_ENV - name: Initialize CodeQL - uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 + uses: github/codeql-action/init@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5 with: languages: ${{ matrix.language }} config-file: ./.github/codeql.yml setup-python-dependencies: false - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 + uses: github/codeql-action/analyze@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/container-build.yml b/.github/workflows/container-build.yml index 42e01192a..2af05ad19 100644 --- a/.github/workflows/container-build.yml +++ b/.github/workflows/container-build.yml @@ -105,7 +105,7 @@ jobs: # Build cached image - name: Build image if: inputs.CACHE == true - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 + uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 with: context: . file: ${{ inputs.DOCKERFILE }} @@ -118,7 +118,7 @@ jobs: # Build non-cached image - name: Build image if: inputs.CACHE != true - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 + uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 with: context: . file: ${{ inputs.DOCKERFILE }} @@ -130,7 +130,7 @@ jobs: # Check vulnerabilities with Docker Scout - name: Docker Scout CVE Analysis if: ${{ startsWith(inputs.CACHE_SUFFIX, 'arm') == false }} - uses: docker/scout-action@bacf462e8d090c09660de30a6ccc718035f961e3 # v1.20.4 + uses: docker/scout-action@cd72f264beff1cd72735de31148b9d3244a0234a # v1.21.0 with: command: cves,recommendations image: local/${{ inputs.IMAGE }} diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index f1316c99a..e3d6f2eda 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -53,7 +53,6 @@ jobs: ubuntu, debian-bookworm, debian-trixie, - fedora-42, fedora-43, fedora-44, rhel-8, @@ -70,8 +69,6 @@ jobs: package: deb - linux: debian-trixie package: deb - - linux: fedora-42 - package: rpm - linux: fedora-43 package: rpm - linux: fedora-44 @@ -216,7 +213,6 @@ jobs: ubuntu, debian-bookworm, debian-trixie, - fedora-42, fedora-43, fedora-44, el-8, @@ -246,12 +242,6 @@ jobs: suffix: "" version: trixie package: deb - - linux: fedora-42 - package_arch: x86_64 - separator: "-" - suffix: "1." - version: 42 - package: rpm - linux: fedora-43 package_arch: x86_64 separator: "-" diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml index 8e2b4234a..04fe3626b 100644 --- a/.github/workflows/linux-build.yml +++ b/.github/workflows/linux-build.yml @@ -107,7 +107,7 @@ jobs: # Build testing package image - name: Build package image if: inputs.RELEASE == 'testing' || inputs.RELEASE == 'dev' || inputs.RELEASE == 'ui' || inputs.RELEASE == '1.5' - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 + uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 with: context: . load: true @@ -119,7 +119,7 @@ jobs: # Build non-testing package image - name: Build package image if: inputs.RELEASE != 'testing' && inputs.RELEASE != 'dev' && inputs.RELEASE != 'ui' && inputs.RELEASE != '1.5' - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 + uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 with: context: . load: true @@ -157,7 +157,7 @@ jobs: images: ghcr.io/bunkerity/${{ inputs.LINUX }}-tests:${{ inputs.RELEASE }} - name: Build test image if: inputs.TEST == true - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 + uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 with: context: . file: tests/linux/Dockerfile-${{ inputs.LINUX }} diff --git a/.github/workflows/push-docker.yml b/.github/workflows/push-docker.yml index ce57a125f..cf562c519 100644 --- a/.github/workflows/push-docker.yml +++ b/.github/workflows/push-docker.yml @@ -87,7 +87,7 @@ jobs: images: bunkerity/${{ inputs.IMAGE }} # Build and push - name: Build and push - uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 + uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 with: context: . file: ${{ inputs.DOCKERFILE }} diff --git a/.github/workflows/push-packagecloud.yml b/.github/workflows/push-packagecloud.yml index 91ce9fa38..7166a00de 100644 --- a/.github/workflows/push-packagecloud.yml +++ b/.github/workflows/push-packagecloud.yml @@ -42,7 +42,7 @@ jobs: - name: Check out repository code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install ruby - uses: ruby/setup-ruby@c4e5b1316158f92e3d49443a9d58b31d25ac0f8f # v1.306.0 + uses: ruby/setup-ruby@afeafc3d1ab54a631816aba4c914a0081c12ff2f # v1.310.0 with: ruby-version: "3.0" - name: Install packagecloud diff --git a/.github/workflows/rc.yml b/.github/workflows/rc.yml index 26bfe6985..1c6eb32a3 100644 --- a/.github/workflows/rc.yml +++ b/.github/workflows/rc.yml @@ -110,7 +110,6 @@ jobs: ubuntu, debian-bookworm, debian-trixie, - fedora-42, fedora-43, fedora-44, rhel-8, @@ -127,8 +126,6 @@ jobs: package: deb - linux: debian-trixie package: deb - - linux: fedora-42 - package: rpm - linux: fedora-43 package: rpm - linux: fedora-44 @@ -236,7 +233,6 @@ jobs: ubuntu, debian-bookworm, debian-trixie, - fedora-42, fedora-43, fedora-44, el-8, @@ -263,11 +259,6 @@ jobs: suffix: "" version: trixie package: deb - - linux: fedora-42 - separator: "-" - suffix: "1." - version: 42 - package: rpm - linux: fedora-43 separator: "-" suffix: "1." @@ -307,9 +298,6 @@ jobs: - linux: debian-trixie arch: amd64 package_arch: amd64 - - linux: fedora-42 - arch: amd64 - package_arch: x86_64 - linux: fedora-43 arch: amd64 package_arch: x86_64 @@ -337,9 +325,6 @@ jobs: - linux: debian-trixie arch: arm64 package_arch: arm64 - - linux: fedora-42 - arch: arm64 - package_arch: aarch64 - linux: fedora-43 arch: arm64 package_arch: aarch64 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 67d1f7b5b..e7778250e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -120,7 +120,6 @@ jobs: ubuntu, debian-bookworm, debian-trixie, - fedora-42, fedora-43, fedora-44, rhel-8, @@ -137,8 +136,6 @@ jobs: package: deb - linux: debian-trixie package: deb - - linux: fedora-42 - package: rpm - linux: fedora-43 package: rpm - linux: fedora-44 @@ -242,7 +239,6 @@ jobs: ubuntu, debian-bookworm, debian-trixie, - fedora-42, fedora-43, fedora-44, el-8, @@ -269,11 +265,6 @@ jobs: suffix: "" version: trixie package: deb - - linux: fedora-42 - separator: "-" - suffix: "1." - version: 42 - package: rpm - linux: fedora-43 separator: "-" suffix: "1." @@ -313,9 +304,6 @@ jobs: - linux: debian-trixie arch: amd64 package_arch: amd64 - - linux: fedora-42 - arch: amd64 - package_arch: x86_64 - linux: fedora-43 arch: amd64 package_arch: x86_64 @@ -343,9 +331,6 @@ jobs: - linux: debian-trixie arch: arm64 package_arch: arm64 - - linux: fedora-42 - arch: arm64 - package_arch: aarch64 - linux: fedora-43 arch: arm64 package_arch: aarch64 diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 59ccfe842..099709686 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -25,6 +25,6 @@ jobs: results_format: sarif publish_results: true - name: "Upload SARIF results to code scanning" - uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 + uses: github/codeql-action/upload-sarif@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5 with: sarif_file: results.sarif diff --git a/.github/workflows/staging-create-infra.yml b/.github/workflows/staging-create-infra.yml index b4743570c..f4ebedaef 100644 --- a/.github/workflows/staging-create-infra.yml +++ b/.github/workflows/staging-create-infra.yml @@ -23,7 +23,7 @@ jobs: - name: Checkout source code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install terraform - uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # v4.0.0 + uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1 - name: Install kubectl uses: azure/setup-kubectl@829323503d1be3d00ca8346e5391ca0b07a9ab0d # v5.1.0 if: inputs.TYPE == 'k8s' diff --git a/.github/workflows/staging-delete-infra.yml b/.github/workflows/staging-delete-infra.yml index 6338894f7..b1daa9e98 100644 --- a/.github/workflows/staging-delete-infra.yml +++ b/.github/workflows/staging-delete-infra.yml @@ -22,7 +22,7 @@ jobs: - name: Checkout source code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install terraform - uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # v4.0.0 + uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1 - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: tf-${{ inputs.TYPE }} diff --git a/.github/workflows/staging-tests.yml b/.github/workflows/staging-tests.yml index 957ddf7b1..e835c61e0 100644 --- a/.github/workflows/staging-tests.yml +++ b/.github/workflows/staging-tests.yml @@ -41,7 +41,7 @@ jobs: - name: Install test dependencies run: PIP_BREAK_SYSTEM_PACKAGES=1 pip3 install --no-cache-dir --require-hashes --no-deps -r tests/requirements.txt - name: Install Terraform - uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # v4.0.0 + uses: hashicorp/setup-terraform@dfe3c3f87815947d99a8997f908cb6525fc44e9e # v4.0.1 if: inputs.TYPE == 'k8s' - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: @@ -81,9 +81,6 @@ jobs: - name: Pull BW linux debian Trixie test image if: inputs.TYPE == 'linux' run: docker pull ghcr.io/bunkerity/debian-trixie-tests:testing && docker tag ghcr.io/bunkerity/debian-trixie-tests:testing local/debian-trixie:latest - - name: Pull BW linux fedora 42 test image - if: inputs.TYPE == 'linux' - run: docker pull ghcr.io/bunkerity/fedora-42-tests:testing && docker tag ghcr.io/bunkerity/fedora-42-tests:testing local/fedora-42:latest - name: Pull BW linux fedora 43 test image if: inputs.TYPE == 'linux' run: docker pull ghcr.io/bunkerity/fedora-43-tests:testing && docker tag ghcr.io/bunkerity/fedora-43-tests:testing local/fedora-43:latest @@ -142,12 +139,6 @@ jobs: env: TEST_DOMAINS: ${{ secrets.TEST_DOMAINS_LINUX }} ROOT_DOMAIN: ${{ secrets.ROOT_DOMAIN }} - - name: Run Linux fedora 42 tests - if: inputs.TYPE == 'linux' - run: export $(echo "$TEST_DOMAINS" | xargs) && chmod +x ./tests/main.py && ./tests/main.py "linux" "fedora-42" - env: - TEST_DOMAINS: ${{ secrets.TEST_DOMAINS_LINUX }} - ROOT_DOMAIN: ${{ secrets.ROOT_DOMAIN }} - name: Run Linux fedora 43 tests if: inputs.TYPE == 'linux' run: export $(echo "$TEST_DOMAINS" | xargs) && chmod +x ./tests/main.py && ./tests/main.py "linux" "fedora-43" diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index 9141920e2..4e2a0d91e 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -53,7 +53,6 @@ jobs: ubuntu, debian-bookworm, debian-trixie, - fedora-42, fedora-43, fedora-44, rhel-8, @@ -68,8 +67,6 @@ jobs: package: deb - linux: debian-trixie package: deb - - linux: fedora-42 - package: rpm - linux: fedora-43 package: rpm - linux: fedora-44 @@ -189,7 +186,6 @@ jobs: ubuntu, debian-bookworm, debian-trixie, - fedora-42, fedora-43, fedora-44, el-8, @@ -219,12 +215,6 @@ jobs: suffix: "" version: trixie package: deb - - linux: fedora-42 - package_arch: x86_64 - separator: "-" - suffix: "1." - version: 42 - package: rpm - linux: fedora-43 package_arch: x86_64 separator: "-" diff --git a/BUILD.md b/BUILD.md index 00373c387..2f6fb677e 100644 --- a/BUILD.md +++ b/BUILD.md @@ -93,7 +93,6 @@ Linux package generation can be done directly with Docker in 2 steps: - `ubuntu-jammy` - `debian-bookworm` - `debian-trixie` -- `fedora-42` - `fedora-43` - `fedora-44` - `rhel-8` diff --git a/CHANGELOG.md b/CHANGELOG.md index bac11cc86..50f5cfff2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,30 @@ # Changelog -## v1.6.10~rc7 - 2026/??/?? +## v1.6.11~rc1 - ????/??/?? + +- [SECURITY] `nginx`: update nginx to 1.30.2 (except for Fedora as it is not yet available) to fix CVE-2026-9256 — a heap buffer overflow in `ngx_http_rewrite_module` with overlapping captures that could lead to worker-process arbitrary code execution. +- [SECURITY] `antibot`: Cap.js `script-src` now uses a strict per-request nonce (no more `'unsafe-inline'`); every challenge response also sends `Cache-Control: no-store`. Requires Cap.js widget `0.1.48`+. +- [SECURITY] `letsencrypt` (UI): harden delete + new heal flow — per-request scratch dir, `fcntl.flock`, `.`/`..` rejected in `cert_name`, DOMPurify + `markupsafe.escape` at every HTML sink, 500 on persistence failure; new `/letsencrypt/{orphans,accounts,cache-status,heal}` endpoints, per-row Heal button, sidebar orphan toast. +- [SECURITY] `linux`: `after-remove` hooks now preserve `/var/log/bunkerweb`, `/etc/bunkerweb`, `/var/lib/bunkerweb` and `/var/tmp` upgrade backups on plain uninstall (only purge wipes configs + DB; logs and backups always kept, disposal commands printed); upgrade backups are written via `install -m 0600 -o root -g root` (atomic) and any pre-existing world-readable backups are retro-tightened, closing a local-read window on admin credentials and the SQLite DB. +- [BUGFIX] `letsencrypt` (core): fix self-propagating cache poisoning that caused fleet-wide `certbot AccountNotFound`; add CA-agnostic consistency gate (LE + ZeroSSL paths), server-scoped `select_account_id`, auto-purge + re-register when the ACME server reports a pinned `--account` as deleted (stale-account JWS recovery), redacted-value `Configurator` WARN logs. +- [FEATURE] `scheduler`: new `SCHEDULER_MAX_WORKERS` env var caps the job-executor thread pool to bound DB-pool pressure on shared MariaDB/MySQL/PostgreSQL; auto default tightened from `min(8, cpu*4)` to `min(8, max(2, cpu*2))` and a warning is emitted when the resolved value exceeds `DATABASE_POOL_SIZE` + `DATABASE_POOL_MAX_OVERFLOW`. +- [FEATURE] `ui`: `ADMIN_PASSWORD` now also accepts a pre-hashed bcrypt value (`$2a$`/`$2b$`/`$2y$`), stored as-is so the plaintext never lands in env files or secrets (env create + `OVERRIDE_ADMIN_CREDS` paths only; wizard and profile still take plaintext). The strength policy is skipped for a hash, a cost factor below 12 logs a warning. + +## v1.6.10 - 2026/05/19 + +- [SECURITY] `nginx` : update nginx to 1.30.1 to fix various CVEs +- [BUGFIX] `reverseproxy`: pin a `USE_UI=yes` service upstream to HTTP/1.1 so a global `REVERSE_PROXY_HTTP_VERSION=2` no longer locks out the web UI. (Fixes #3550) +- [BUGFIX] `autoconf`: fix Docker/Podman instance discovery looping on `No instance found`. Container conversion no longer assumes the inspect payload exposes `State.Health` (Podman/no-`HEALTHCHECK` may omit it): health falls back to run-state, env parsing is hardened, and the wait loop logs the exception instead of swallowing it. +- [ALL-IN-ONE] Update CrowdSec version to 1.7.8 + +## v1.6.10~rc7 - 2026/05/15 - [FEATURE] `installer`: `misc/install-bunkerweb.sh` interactive prompts now use a modern inline TUI via [gum](https://github.com/charmbracelet/gum) (`--tui` / `--no-tui` / `BW_INSTALL_TUI`). Three-tier dispatch — gum → whiptail (only if pre-installed) → plain `read` — keeps every host usable. - [SECURITY] `ui`: neutralize CSV/XLSX formula injection (CWE-1236) in bans and reports exports. Server-side CSV now goes through `defusedcsv` (new pinned dep) and a shared `csv_safe()` helper escapes openpyxl XLSX cells; client-side DataTables `csv`/`excel`/`copy` buttons inherit the same rule via a global `bwCsvSafe` hook in `dataTableInit.js`. Cells whose first character is `= + - @ | %` are prefixed with `'`, and embedded `|` is backslash-escaped. - [BUGFIX] `metrics`: bound per-worker LRU and per-key event-history arrays via new `MAX_LRU_HISTORY` setting (default `1k`) to close OSS RAM leak under high-cardinality block traffic. - [BUGFIX] `metrics`: lower `METRICS_MAX_BLOCKED_REQUESTS_REDIS` default `100000` → `10k`. - [BUGFIX] `datastore`: lower shared worker-LRU default `100000` → `1k`, configurable via new `DATASTORE_LRU_SIZE` global setting. +- [BUGFIX] `modsec` : fix memory leak in variables retrieval from modsecurity to lua - [FEATURE] `metrics`/`misc`: `METRICS_MAX_BLOCKED_REQUESTS`, `METRICS_MAX_BLOCKED_REQUESTS_REDIS`, `MAX_LRU_HISTORY`, and `DATASTORE_LRU_SIZE` accept `k`/`m` shorthand. - [UI] List pages: unrestricted `10/25/50/100` page-size dropdown, header checkbox selects current page only, with opt-in "Select all N matching" banner so bulk actions cover every page. (Fixes #3513) - [FEATURE] `all-in-one`: embedded Redis now boots from a generated `/var/lib/bunkerweb/redis-runtime.conf` (copy of `/etc/redis.conf` + env-driven defaults for directives the conf is silent about). `.conf` always prevails; env vars `REDIS_MAXMEMORY`, `REDIS_MAXMEMORY_POLICY`, `REDIS_APPENDONLY`, `REDIS_SAVE`/`REDIS_SAVE_` (BunkerWeb multi-value pattern; empty disables RDB) and `REDIS_PASSWORD` (wired to `requirepass`) only fill the gaps. Defaults follow the documented Redis Best Practices. diff --git a/README.md b/README.md index be8198810..389ecaabb 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- BunkerWeb logo + BunkerWeb logo

@@ -40,7 +40,7 @@ | 🧩 Templates | - 🛡️ Examples + 🛡️ Examples
💬 Chat | @@ -60,14 +60,14 @@ # BunkerWeb

- Overview banner + Overview banner

BunkerWeb is a next-generation, open-source Web Application Firewall (WAF). -Being a full-featured web server (based on [NGINX](https://nginx.org/) under the hood), it will protect your web services to make them "secure by default." BunkerWeb integrates seamlessly into your existing environments ([Linux](https://docs.bunkerweb.io/1.6.10~rc7/integrations/?utm_campaign=self&utm_source=github#linux), [Docker](https://docs.bunkerweb.io/1.6.10~rc7/integrations/?utm_campaign=self&utm_source=github#docker), [Swarm](https://docs.bunkerweb.io/1.6.10~rc7/integrations/?utm_campaign=self&utm_source=github#swarm), [Kubernetes](https://docs.bunkerweb.io/1.6.10~rc7/integrations/?utm_campaign=self&utm_source=github#kubernetes), …) as a reverse proxy and is fully configurable (don't panic, there is an [awesome web UI](https://docs.bunkerweb.io/1.6.10~rc7/web-ui/?utm_campaign=self&utm_source=github) if you don't like the CLI) to meet your own use cases. In other words, cybersecurity is no longer a hassle. +Being a full-featured web server (based on [NGINX](https://nginx.org/) under the hood), it will protect your web services to make them "secure by default." BunkerWeb integrates seamlessly into your existing environments ([Linux](https://docs.bunkerweb.io/1.6.11~rc1/integrations/?utm_campaign=self&utm_source=github#linux), [Docker](https://docs.bunkerweb.io/1.6.11~rc1/integrations/?utm_campaign=self&utm_source=github#docker), [Swarm](https://docs.bunkerweb.io/1.6.11~rc1/integrations/?utm_campaign=self&utm_source=github#swarm), [Kubernetes](https://docs.bunkerweb.io/1.6.11~rc1/integrations/?utm_campaign=self&utm_source=github#kubernetes), …) as a reverse proxy and is fully configurable (don't panic, there is an [awesome web UI](https://docs.bunkerweb.io/1.6.11~rc1/web-ui/?utm_campaign=self&utm_source=github) if you don't like the CLI) to meet your own use cases. In other words, cybersecurity is no longer a hassle. -BunkerWeb contains primary [security features](https://docs.bunkerweb.io/1.6.10~rc7/advanced/?utm_campaign=self&utm_source=github#security-tuning) as part of the core but can be easily extended with additional ones thanks to a [plugin system](https://docs.bunkerweb.io/1.6.10~rc7/plugins/?utm_campaign=self&utm_source=github). +BunkerWeb contains primary [security features](https://docs.bunkerweb.io/1.6.11~rc1/advanced/?utm_campaign=self&utm_source=github#security-tuning) as part of the core but can be easily extended with additional ones thanks to a [plugin system](https://docs.bunkerweb.io/1.6.11~rc1/plugins/?utm_campaign=self&utm_source=github). ## Why BunkerWeb? @@ -94,7 +94,7 @@ A non-exhaustive list of security features: - **Block known bad IPs** with external blacklists and DNSBL - And much more... -Learn more about the core security features in the [security tuning](https://docs.bunkerweb.io/1.6.10~rc7/advanced/?utm_campaign=self&utm_source=github#security-tuning) section of the documentation. +Learn more about the core security features in the [security tuning](https://docs.bunkerweb.io/1.6.11~rc1/advanced/?utm_campaign=self&utm_source=github#security-tuning) section of the documentation. ## Demo @@ -129,13 +129,13 @@ When using BunkerWeb, you have the choice of the version you want to use: open-s Whether it's enhanced security, an enriched user experience, or technical monitoring, the BunkerWeb PRO version allows you to fully benefit from BunkerWeb and meet your professional needs. -In the documentation or the user interface, PRO features are annotated with a crown crown pro icon to distinguish them from those integrated into the open-source version. +In the documentation or the user interface, PRO features are annotated with a crown crown pro icon to distinguish them from those integrated into the open-source version. You can upgrade from the open-source version to the PRO one easily and at any time. The process is straightforward: - Claim your [free trial on the BunkerWeb panel](https://panel.bunkerweb.io/store/bunkerweb-pro?utm_campaign=self&utm_source=doc) by using the `freetrial` promo code at checkout - Once connected to the client area, copy your PRO license key -- Paste your license key into BunkerWeb using the [web UI](https://docs.bunkerweb.io/1.6.10~rc7/web-ui/#upgrade-to-pro) or a [specific setting](https://docs.bunkerweb.io/1.6.10~rc7/features/#pro) +- Paste your license key into BunkerWeb using the [web UI](https://docs.bunkerweb.io/1.6.11~rc1/web-ui/#upgrade-to-pro) or a [specific setting](https://docs.bunkerweb.io/1.6.11~rc1/features/#pro) Do not hesitate to visit the [BunkerWeb panel](https://panel.bunkerweb.io/knowledgebase?utm_campaign=self&utm_source=doc) or [contact us](https://panel.bunkerweb.io/contact.php?utm_campaign=self&utm_source=doc) if you have any questions regarding the PRO version. @@ -168,10 +168,10 @@ Community and social networks: # Concepts

- Concepts banner + Concepts banner

-You will find more information about the key concepts of BunkerWeb in the [documentation](https://docs.bunkerweb.io/1.6.10~rc7/concepts/?utm_campaign=self&utm_source=github). +You will find more information about the key concepts of BunkerWeb in the [documentation](https://docs.bunkerweb.io/1.6.11~rc1/concepts/?utm_campaign=self&utm_source=github). ## Integrations @@ -179,12 +179,12 @@ The first concept is the integration of BunkerWeb into the target environment. W The following integrations are officially supported: -- [Docker](https://docs.bunkerweb.io/1.6.10~rc7/integrations/?utm_campaign=self&utm_source=github#docker) -- [Linux](https://docs.bunkerweb.io/1.6.10~rc7/integrations/?utm_campaign=self&utm_source=github#linux) -- [Docker autoconf](https://docs.bunkerweb.io/1.6.10~rc7/integrations/?utm_campaign=self&utm_source=github#docker-autoconf) -- [Kubernetes](https://docs.bunkerweb.io/1.6.10~rc7/integrations/?utm_campaign=self&utm_source=github#kubernetes) -- [Swarm](https://docs.bunkerweb.io/1.6.10~rc7/integrations/?utm_campaign=self&utm_source=github#swarm) -- [Microsoft Azure](https://docs.bunkerweb.io/1.6.10~rc7/integrations/?utm_campaign=self&utm_source=github#microsoft-azure) +- [Docker](https://docs.bunkerweb.io/1.6.11~rc1/integrations/?utm_campaign=self&utm_source=github#docker) +- [Linux](https://docs.bunkerweb.io/1.6.11~rc1/integrations/?utm_campaign=self&utm_source=github#linux) +- [Docker autoconf](https://docs.bunkerweb.io/1.6.11~rc1/integrations/?utm_campaign=self&utm_source=github#docker-autoconf) +- [Kubernetes](https://docs.bunkerweb.io/1.6.11~rc1/integrations/?utm_campaign=self&utm_source=github#kubernetes) +- [Swarm](https://docs.bunkerweb.io/1.6.11~rc1/integrations/?utm_campaign=self&utm_source=github#swarm) +- [Microsoft Azure](https://docs.bunkerweb.io/1.6.11~rc1/integrations/?utm_campaign=self&utm_source=github#microsoft-azure) ## Settings @@ -214,7 +214,7 @@ When multisite mode is enabled, BunkerWeb will serve and protect multiple web ap ## Custom configurations -Because meeting all the use cases only using the settings is not an option (even with [external plugins](https://docs.bunkerweb.io/1.6.10~rc7/plugins/?utm_campaign=self&utm_source=github)), you can use custom configurations to solve your specific challenges. +Because meeting all the use cases only using the settings is not an option (even with [external plugins](https://docs.bunkerweb.io/1.6.11~rc1/plugins/?utm_campaign=self&utm_source=github)), you can use custom configurations to solve your specific challenges. Under the hood, BunkerWeb uses the notorious NGINX web server, that's why you can leverage its configuration system for your specific needs. Custom NGINX configurations can be included in different [contexts](https://docs.nginx.com/nginx/admin-guide/basic-functionality/managing-configuration-files/#contexts) like HTTP or server (all servers and/or specific server block). @@ -223,7 +223,7 @@ Another core component of BunkerWeb is the ModSecurity Web Application Firewall: ## Database

- Database model + Database model

The state of the current configuration of BunkerWeb is stored in a backend database which contains the following data: @@ -252,7 +252,7 @@ In other words, the scheduler is the brain of BunkerWeb. " CRLF +"" CRLF +"" CRLF +"" CRLF +"" CRLF +"" CRLF +; + + +static u_char ngx_http_msie_refresh_head[] = +"" CRLF; + + +static char ngx_http_error_301_page[] = +"" CRLF +"301 Moved Permanently" CRLF +"" CRLF +"

301 Moved Permanently

" CRLF +; + + +static char ngx_http_error_302_page[] = +"" CRLF +"302 Found" CRLF +"" CRLF +"

302 Found

" CRLF +; + + +static char ngx_http_error_303_page[] = +"" CRLF +"303 See Other" CRLF +"" CRLF +"

303 See Other

" CRLF +; + + +static char ngx_http_error_307_page[] = +"" CRLF +"307 Temporary Redirect" CRLF +"" CRLF +"

307 Temporary Redirect

" CRLF +; + + +static char ngx_http_error_308_page[] = +"" CRLF +"308 Permanent Redirect" CRLF +"" CRLF +"

308 Permanent Redirect

" CRLF +; + + +static char ngx_http_error_400_page[] = +"" CRLF +"400 Bad Request" CRLF +"" CRLF +"

400 Bad Request

" CRLF +; + + +static char ngx_http_error_401_page[] = +"" CRLF +"401 Authorization Required" CRLF +"" CRLF +"

401 Authorization Required

" CRLF +; + + +static char ngx_http_error_402_page[] = +"" CRLF +"402 Payment Required" CRLF +"" CRLF +"

402 Payment Required

" CRLF +; + + +static char ngx_http_error_403_page[] = +"" CRLF +"403 Forbidden" CRLF +"" CRLF +"

403 Forbidden

" CRLF +; + + +static char ngx_http_error_404_page[] = +"" CRLF +"404 Not Found" CRLF +"" CRLF +"

404 Not Found

" CRLF +; + + +static char ngx_http_error_405_page[] = +"" CRLF +"405 Not Allowed" CRLF +"" CRLF +"

405 Not Allowed

" CRLF +; + + +static char ngx_http_error_406_page[] = +"" CRLF +"406 Not Acceptable" CRLF +"" CRLF +"

406 Not Acceptable

" CRLF +; + + +static char ngx_http_error_407_page[] = +"" CRLF +"407 Proxy Authentication Required" CRLF +"" CRLF +"

407 Proxy Authentication Required

" CRLF +; + + +static char ngx_http_error_408_page[] = +"" CRLF +"408 Request Time-out" CRLF +"" CRLF +"

408 Request Time-out

" CRLF +; + + +static char ngx_http_error_409_page[] = +"" CRLF +"409 Conflict" CRLF +"" CRLF +"

409 Conflict

" CRLF +; + + +static char ngx_http_error_410_page[] = +"" CRLF +"410 Gone" CRLF +"" CRLF +"

410 Gone

" CRLF +; + + +static char ngx_http_error_411_page[] = +"" CRLF +"411 Length Required" CRLF +"" CRLF +"

411 Length Required

" CRLF +; + + +static char ngx_http_error_412_page[] = +"" CRLF +"412 Precondition Failed" CRLF +"" CRLF +"

412 Precondition Failed

" CRLF +; + + +static char ngx_http_error_413_page[] = +"" CRLF +"413 Request Entity Too Large" CRLF +"" CRLF +"

413 Request Entity Too Large

" CRLF +; + + +static char ngx_http_error_414_page[] = +"" CRLF +"414 Request-URI Too Large" CRLF +"" CRLF +"

414 Request-URI Too Large

" CRLF +; + + +static char ngx_http_error_415_page[] = +"" CRLF +"415 Unsupported Media Type" CRLF +"" CRLF +"

415 Unsupported Media Type

" CRLF +; + + +static char ngx_http_error_416_page[] = +"" CRLF +"416 Requested Range Not Satisfiable" CRLF +"" CRLF +"

416 Requested Range Not Satisfiable

" CRLF +; + + +static char ngx_http_error_421_page[] = +"" CRLF +"421 Misdirected Request" CRLF +"" CRLF +"

421 Misdirected Request

" CRLF +; + + +static char ngx_http_error_429_page[] = +"" CRLF +"429 Too Many Requests" CRLF +"" CRLF +"

429 Too Many Requests

" CRLF +; + + +static char ngx_http_error_494_page[] = +"" CRLF +"400 Request Header Or Cookie Too Large" +CRLF +"" CRLF +"

400 Bad Request

" CRLF +"
Request Header Or Cookie Too Large
" CRLF +; + + +static char ngx_http_error_495_page[] = +"" CRLF +"400 The SSL certificate error" +CRLF +"" CRLF +"

400 Bad Request

" CRLF +"
The SSL certificate error
" CRLF +; + + +static char ngx_http_error_496_page[] = +"" CRLF +"400 No required SSL certificate was sent" +CRLF +"" CRLF +"

400 Bad Request

" CRLF +"
No required SSL certificate was sent
" CRLF +; + + +static char ngx_http_error_497_page[] = +"" CRLF +"400 The plain HTTP request was sent to HTTPS port" +CRLF +"" CRLF +"

400 Bad Request

" CRLF +"
The plain HTTP request was sent to HTTPS port
" CRLF +; + + +static char ngx_http_error_500_page[] = +"" CRLF +"500 Internal Server Error" CRLF +"" CRLF +"

500 Internal Server Error

" CRLF +; + + +static char ngx_http_error_501_page[] = +"" CRLF +"501 Not Implemented" CRLF +"" CRLF +"

501 Not Implemented

" CRLF +; + + +static char ngx_http_error_502_page[] = +"" CRLF +"502 Bad Gateway" CRLF +"" CRLF +"

502 Bad Gateway

" CRLF +; + + +static char ngx_http_error_503_page[] = +"" CRLF +"503 Service Temporarily Unavailable" CRLF +"" CRLF +"

503 Service Temporarily Unavailable

" CRLF +; + + +static char ngx_http_error_504_page[] = +"" CRLF +"504 Gateway Time-out" CRLF +"" CRLF +"

504 Gateway Time-out

" CRLF +; + + +static char ngx_http_error_505_page[] = +"" CRLF +"505 HTTP Version Not Supported" CRLF +"" CRLF +"

505 HTTP Version Not Supported

" CRLF +; + + +static char ngx_http_error_507_page[] = +"" CRLF +"507 Insufficient Storage" CRLF +"" CRLF +"

507 Insufficient Storage

" CRLF +; + + +static ngx_str_t ngx_http_error_pages[] = { + + ngx_null_string, /* 201, 204 */ + +#define NGX_HTTP_LAST_2XX 202 +#define NGX_HTTP_OFF_3XX (NGX_HTTP_LAST_2XX - 201) + + /* ngx_null_string, */ /* 300 */ + ngx_string(ngx_http_error_301_page), + ngx_string(ngx_http_error_302_page), + ngx_string(ngx_http_error_303_page), + ngx_null_string, /* 304 */ + ngx_null_string, /* 305 */ + ngx_null_string, /* 306 */ + ngx_string(ngx_http_error_307_page), + ngx_string(ngx_http_error_308_page), + +#define NGX_HTTP_LAST_3XX 309 +#define NGX_HTTP_OFF_4XX (NGX_HTTP_LAST_3XX - 301 + NGX_HTTP_OFF_3XX) + + ngx_string(ngx_http_error_400_page), + ngx_string(ngx_http_error_401_page), + ngx_string(ngx_http_error_402_page), + ngx_string(ngx_http_error_403_page), + ngx_string(ngx_http_error_404_page), + ngx_string(ngx_http_error_405_page), + ngx_string(ngx_http_error_406_page), + ngx_string(ngx_http_error_407_page), + ngx_string(ngx_http_error_408_page), + ngx_string(ngx_http_error_409_page), + ngx_string(ngx_http_error_410_page), + ngx_string(ngx_http_error_411_page), + ngx_string(ngx_http_error_412_page), + ngx_string(ngx_http_error_413_page), + ngx_string(ngx_http_error_414_page), + ngx_string(ngx_http_error_415_page), + ngx_string(ngx_http_error_416_page), + ngx_null_string, /* 417 */ + ngx_null_string, /* 418 */ + ngx_null_string, /* 419 */ + ngx_null_string, /* 420 */ + ngx_string(ngx_http_error_421_page), + ngx_null_string, /* 422 */ + ngx_null_string, /* 423 */ + ngx_null_string, /* 424 */ + ngx_null_string, /* 425 */ + ngx_null_string, /* 426 */ + ngx_null_string, /* 427 */ + ngx_null_string, /* 428 */ + ngx_string(ngx_http_error_429_page), + +#define NGX_HTTP_LAST_4XX 430 +#define NGX_HTTP_OFF_5XX (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX) + + ngx_string(ngx_http_error_494_page), /* 494, request header too large */ + ngx_string(ngx_http_error_495_page), /* 495, https certificate error */ + ngx_string(ngx_http_error_496_page), /* 496, https no certificate */ + ngx_string(ngx_http_error_497_page), /* 497, http to https */ + ngx_string(ngx_http_error_404_page), /* 498, canceled */ + ngx_null_string, /* 499, client has closed connection */ + + ngx_string(ngx_http_error_500_page), + ngx_string(ngx_http_error_501_page), + ngx_string(ngx_http_error_502_page), + ngx_string(ngx_http_error_503_page), + ngx_string(ngx_http_error_504_page), + ngx_string(ngx_http_error_505_page), + ngx_null_string, /* 506 */ + ngx_string(ngx_http_error_507_page) + +#define NGX_HTTP_LAST_5XX 508 + +}; + + +ngx_int_t +ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error) +{ + ngx_uint_t i, err; + ngx_http_err_page_t *err_page; + ngx_http_core_loc_conf_t *clcf; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http special response: %i, \"%V?%V\"", + error, &r->uri, &r->args); + + r->err_status = error; + + if (r->keepalive) { + switch (error) { + case NGX_HTTP_BAD_REQUEST: + case NGX_HTTP_REQUEST_ENTITY_TOO_LARGE: + case NGX_HTTP_REQUEST_URI_TOO_LARGE: + case NGX_HTTP_TO_HTTPS: + case NGX_HTTPS_CERT_ERROR: + case NGX_HTTPS_NO_CERT: + case NGX_HTTP_INTERNAL_SERVER_ERROR: + case NGX_HTTP_NOT_IMPLEMENTED: + r->keepalive = 0; + } + } + + if (r->lingering_close) { + switch (error) { + case NGX_HTTP_BAD_REQUEST: + case NGX_HTTP_TO_HTTPS: + case NGX_HTTPS_CERT_ERROR: + case NGX_HTTPS_NO_CERT: + r->lingering_close = 0; + } + } + + r->headers_out.content_type.len = 0; + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (!r->error_page && clcf->error_pages && r->uri_changes != 0) { + + if (clcf->recursive_error_pages == 0) { + r->error_page = 1; + } + + err_page = clcf->error_pages->elts; + + for (i = 0; i < clcf->error_pages->nelts; i++) { + if (err_page[i].status == error) { + return ngx_http_send_error_page(r, &err_page[i]); + } + } + } + + r->expect_tested = 1; + + if (ngx_http_discard_request_body(r) != NGX_OK) { + r->keepalive = 0; + } + + if (clcf->msie_refresh + && r->headers_in.msie + && (error == NGX_HTTP_MOVED_PERMANENTLY + || error == NGX_HTTP_MOVED_TEMPORARILY)) + { + return ngx_http_send_refresh(r); + } + + if (error == NGX_HTTP_CREATED) { + /* 201 */ + err = 0; + + } else if (error == NGX_HTTP_NO_CONTENT) { + /* 204 */ + err = 0; + + } else if (error >= NGX_HTTP_MOVED_PERMANENTLY + && error < NGX_HTTP_LAST_3XX) + { + /* 3XX */ + err = error - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX; + + } else if (error >= NGX_HTTP_BAD_REQUEST + && error < NGX_HTTP_LAST_4XX) + { + /* 4XX */ + err = error - NGX_HTTP_BAD_REQUEST + NGX_HTTP_OFF_4XX; + + } else if (error >= NGX_HTTP_NGINX_CODES + && error < NGX_HTTP_LAST_5XX) + { + /* 49X, 5XX */ + err = error - NGX_HTTP_NGINX_CODES + NGX_HTTP_OFF_5XX; + switch (error) { + case NGX_HTTP_TO_HTTPS: + case NGX_HTTPS_CERT_ERROR: + case NGX_HTTPS_NO_CERT: + case NGX_HTTP_REQUEST_HEADER_TOO_LARGE: + r->err_status = NGX_HTTP_BAD_REQUEST; + } + + } else { + /* unknown code, zero body */ + err = 0; + } + + return ngx_http_send_special_response(r, clcf, err); +} + + +ngx_int_t +ngx_http_filter_finalize_request(ngx_http_request_t *r, ngx_module_t *m, + ngx_int_t error) +{ + void *ctx; + ngx_int_t rc; + + ngx_http_clean_header(r); + + ctx = NULL; + + if (m) { + ctx = r->ctx[m->ctx_index]; + } + + /* clear the modules contexts */ + ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module); + + if (m) { + r->ctx[m->ctx_index] = ctx; + } + + r->filter_finalize = 1; + + rc = ngx_http_special_response_handler(r, error); + + /* NGX_ERROR resets any pending data */ + + switch (rc) { + + case NGX_OK: + case NGX_DONE: + return NGX_ERROR; + + default: + return rc; + } +} + + +void +ngx_http_clean_header(ngx_http_request_t *r) +{ + ngx_memzero(&r->headers_out.status, + sizeof(ngx_http_headers_out_t) + - offsetof(ngx_http_headers_out_t, status)); + + r->headers_out.headers.part.nelts = 0; + r->headers_out.headers.part.next = NULL; + r->headers_out.headers.last = &r->headers_out.headers.part; + + r->headers_out.trailers.part.nelts = 0; + r->headers_out.trailers.part.next = NULL; + r->headers_out.trailers.last = &r->headers_out.trailers.part; + + r->headers_out.content_length_n = -1; + r->headers_out.last_modified_time = -1; +} + + +static ngx_int_t +ngx_http_send_error_page(ngx_http_request_t *r, ngx_http_err_page_t *err_page) +{ + ngx_int_t overwrite; + ngx_str_t uri, args; + ngx_table_elt_t *location; + ngx_http_core_loc_conf_t *clcf; + + overwrite = err_page->overwrite; + + if (overwrite && overwrite != NGX_HTTP_OK) { + r->expect_tested = 1; + } + + if (overwrite >= 0) { + r->err_status = overwrite; + } + + if (ngx_http_complex_value(r, &err_page->value, &uri) != NGX_OK) { + return NGX_ERROR; + } + + if (uri.len && uri.data[0] == '/') { + + if (err_page->value.lengths) { + ngx_http_split_args(r, &uri, &args); + + } else { + args = err_page->args; + } + + if (r->method != NGX_HTTP_HEAD) { + r->method = NGX_HTTP_GET; + r->method_name = ngx_http_core_get_method; + } + + return ngx_http_internal_redirect(r, &uri, &args); + } + + if (uri.len && uri.data[0] == '@') { + return ngx_http_named_location(r, &uri); + } + + r->expect_tested = 1; + + if (ngx_http_discard_request_body(r) != NGX_OK) { + r->keepalive = 0; + } + + location = ngx_list_push(&r->headers_out.headers); + + if (location == NULL) { + return NGX_ERROR; + } + + if (overwrite != NGX_HTTP_MOVED_PERMANENTLY + && overwrite != NGX_HTTP_MOVED_TEMPORARILY + && overwrite != NGX_HTTP_SEE_OTHER + && overwrite != NGX_HTTP_TEMPORARY_REDIRECT + && overwrite != NGX_HTTP_PERMANENT_REDIRECT) + { + r->err_status = NGX_HTTP_MOVED_TEMPORARILY; + } + + location->hash = 1; + location->next = NULL; + ngx_str_set(&location->key, "Location"); + location->value = uri; + + ngx_http_clear_location(r); + + r->headers_out.location = location; + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->msie_refresh && r->headers_in.msie) { + return ngx_http_send_refresh(r); + } + + return ngx_http_send_special_response(r, clcf, r->err_status + - NGX_HTTP_MOVED_PERMANENTLY + + NGX_HTTP_OFF_3XX); +} + + +static ngx_int_t +ngx_http_send_special_response(ngx_http_request_t *r, + ngx_http_core_loc_conf_t *clcf, ngx_uint_t err) +{ + u_char *tail; + size_t len; + ngx_int_t rc; + ngx_buf_t *b; + ngx_uint_t msie_padding; + ngx_chain_t out[3]; + + if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { + len = sizeof(ngx_http_error_full_tail) - 1; + tail = ngx_http_error_full_tail; + + } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { + len = sizeof(ngx_http_error_build_tail) - 1; + tail = ngx_http_error_build_tail; + + } else { + len = sizeof(ngx_http_error_tail) - 1; + tail = ngx_http_error_tail; + } + + msie_padding = 0; + + if (ngx_http_error_pages[err].len) { + r->headers_out.content_length_n = ngx_http_error_pages[err].len + len; + if (clcf->msie_padding + && (r->headers_in.msie || r->headers_in.chrome) + && r->http_version >= NGX_HTTP_VERSION_10 + && err >= NGX_HTTP_OFF_4XX) + { + r->headers_out.content_length_n += + sizeof(ngx_http_msie_padding) - 1; + msie_padding = 1; + } + + r->headers_out.content_type_len = sizeof("text/html") - 1; + ngx_str_set(&r->headers_out.content_type, "text/html"); + r->headers_out.content_type_lowcase = NULL; + + } else { + r->headers_out.content_length_n = 0; + } + + if (r->headers_out.content_length) { + r->headers_out.content_length->hash = 0; + r->headers_out.content_length = NULL; + } + + ngx_http_clear_accept_ranges(r); + ngx_http_clear_last_modified(r); + ngx_http_clear_etag(r); + + rc = ngx_http_send_header(r); + + if (rc == NGX_ERROR || r->header_only) { + return rc; + } + + if (ngx_http_error_pages[err].len == 0) { + return ngx_http_send_special(r, NGX_HTTP_LAST); + } + + b = ngx_calloc_buf(r->pool); + if (b == NULL) { + return NGX_ERROR; + } + + b->memory = 1; + b->pos = ngx_http_error_pages[err].data; + b->last = ngx_http_error_pages[err].data + ngx_http_error_pages[err].len; + + out[0].buf = b; + out[0].next = &out[1]; + + b = ngx_calloc_buf(r->pool); + if (b == NULL) { + return NGX_ERROR; + } + + b->memory = 1; + + b->pos = tail; + b->last = tail + len; + + out[1].buf = b; + out[1].next = NULL; + + if (msie_padding) { + b = ngx_calloc_buf(r->pool); + if (b == NULL) { + return NGX_ERROR; + } + + b->memory = 1; + b->pos = ngx_http_msie_padding; + b->last = ngx_http_msie_padding + sizeof(ngx_http_msie_padding) - 1; + + out[1].next = &out[2]; + out[2].buf = b; + out[2].next = NULL; + } + + if (r == r->main) { + b->last_buf = 1; + } + + b->last_in_chain = 1; + + return ngx_http_output_filter(r, &out[0]); +} + + +static ngx_int_t +ngx_http_send_refresh(ngx_http_request_t *r) +{ + u_char *p, *location; + size_t len, size; + uintptr_t escape; + ngx_int_t rc; + ngx_buf_t *b; + ngx_chain_t out; + + len = r->headers_out.location->value.len; + location = r->headers_out.location->value.data; + + escape = 2 * ngx_escape_uri(NULL, location, len, NGX_ESCAPE_REFRESH); + + size = sizeof(ngx_http_msie_refresh_head) - 1 + + escape + len + + sizeof(ngx_http_msie_refresh_tail) - 1; + + r->err_status = NGX_HTTP_OK; + + r->headers_out.content_type_len = sizeof("text/html") - 1; + ngx_str_set(&r->headers_out.content_type, "text/html"); + r->headers_out.content_type_lowcase = NULL; + + r->headers_out.location->hash = 0; + r->headers_out.location = NULL; + + r->headers_out.content_length_n = size; + + if (r->headers_out.content_length) { + r->headers_out.content_length->hash = 0; + r->headers_out.content_length = NULL; + } + + ngx_http_clear_accept_ranges(r); + ngx_http_clear_last_modified(r); + ngx_http_clear_etag(r); + + rc = ngx_http_send_header(r); + + if (rc == NGX_ERROR || r->header_only) { + return rc; + } + + b = ngx_create_temp_buf(r->pool, size); + if (b == NULL) { + return NGX_ERROR; + } + + p = ngx_cpymem(b->pos, ngx_http_msie_refresh_head, + sizeof(ngx_http_msie_refresh_head) - 1); + + if (escape == 0) { + p = ngx_cpymem(p, location, len); + + } else { + p = (u_char *) ngx_escape_uri(p, location, len, NGX_ESCAPE_REFRESH); + } + + b->last = ngx_cpymem(p, ngx_http_msie_refresh_tail, + sizeof(ngx_http_msie_refresh_tail) - 1); + + b->last_buf = (r == r->main) ? 1 : 0; + b->last_in_chain = 1; + + out.buf = b; + out.next = NULL; + + return ngx_http_output_filter(r, &out); +} diff --git a/src/deps/src/nginx-1.30.1/src/http/ngx_http_upstream.c b/src/deps/src/nginx-1.30.1/src/http/ngx_http_upstream.c new file mode 100644 index 000000000..b64561369 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/ngx_http_upstream.c @@ -0,0 +1,7305 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +#if (NGX_HTTP_CACHE) +static ngx_int_t ngx_http_upstream_cache(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static ngx_int_t ngx_http_upstream_cache_get(ngx_http_request_t *r, + ngx_http_upstream_t *u, ngx_http_file_cache_t **cache); +static ngx_int_t ngx_http_upstream_cache_send(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static ngx_int_t ngx_http_upstream_cache_background_update( + ngx_http_request_t *r, ngx_http_upstream_t *u); +static ngx_int_t ngx_http_upstream_cache_check_range(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static ngx_int_t ngx_http_upstream_cache_status(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_upstream_cache_last_modified(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_upstream_cache_etag(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +#endif + +static void ngx_http_upstream_init_request(ngx_http_request_t *r); +static void ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx); +static void ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r); +static void ngx_http_upstream_wr_check_broken_connection(ngx_http_request_t *r); +static void ngx_http_upstream_check_broken_connection(ngx_http_request_t *r, + ngx_event_t *ev); +static void ngx_http_upstream_connect(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static ngx_int_t ngx_http_upstream_reinit(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static void ngx_http_upstream_send_request(ngx_http_request_t *r, + ngx_http_upstream_t *u, ngx_uint_t do_write); +static ngx_int_t ngx_http_upstream_send_request_body(ngx_http_request_t *r, + ngx_http_upstream_t *u, ngx_uint_t do_write); +static void ngx_http_upstream_send_request_handler(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static void ngx_http_upstream_read_request_handler(ngx_http_request_t *r); +static void ngx_http_upstream_process_header(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static ngx_int_t ngx_http_upstream_process_early_hints(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static void ngx_http_upstream_early_hints_writer(ngx_http_request_t *r); +static ngx_int_t ngx_http_upstream_test_next(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static ngx_int_t ngx_http_upstream_intercept_errors(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static ngx_int_t ngx_http_upstream_test_connect(ngx_connection_t *c); +static ngx_int_t ngx_http_upstream_process_headers(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static ngx_int_t ngx_http_upstream_process_trailers(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static void ngx_http_upstream_send_response(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static void ngx_http_upstream_upgrade(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static void ngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r); +static void ngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r); +static void ngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static void ngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static void ngx_http_upstream_process_upgraded(ngx_http_request_t *r, + ngx_uint_t from_upstream, ngx_uint_t do_write); +static void + ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r); +static void + ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static void + ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r, + ngx_uint_t do_write); +#if (NGX_THREADS) +static ngx_int_t ngx_http_upstream_thread_handler(ngx_thread_task_t *task, + ngx_file_t *file); +static void ngx_http_upstream_thread_event_handler(ngx_event_t *ev); +#endif +static ngx_int_t ngx_http_upstream_output_filter(void *data, + ngx_chain_t *chain); +static void ngx_http_upstream_process_downstream(ngx_http_request_t *r); +static void ngx_http_upstream_process_upstream(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static void ngx_http_upstream_process_request(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static void ngx_http_upstream_store(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static void ngx_http_upstream_dummy_handler(ngx_http_request_t *r, + ngx_http_upstream_t *u); +static void ngx_http_upstream_next(ngx_http_request_t *r, + ngx_http_upstream_t *u, ngx_uint_t ft_type); +static void ngx_http_upstream_cleanup(void *data); +static void ngx_http_upstream_finalize_request(ngx_http_request_t *r, + ngx_http_upstream_t *u, ngx_int_t rc); + +static ngx_int_t ngx_http_upstream_process_header_line(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t + ngx_http_upstream_process_multi_header_lines(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_process_content_length(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_process_last_modified(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_process_set_cookie(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t + ngx_http_upstream_process_cache_control(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +#if (NGX_HTTP_CACHE) +static ngx_int_t ngx_http_upstream_process_delta_seconds(u_char *p, + u_char *last); +#endif +static ngx_int_t ngx_http_upstream_ignore_header_line(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_process_expires(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_process_accel_expires(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_process_limit_rate(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_process_buffering(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_process_charset(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_process_connection(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t + ngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_process_vary(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_copy_header_line(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t + ngx_http_upstream_copy_multi_header_lines(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_copy_content_type(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_copy_last_modified(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_rewrite_location(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_rewrite_refresh(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_rewrite_set_cookie(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); +static ngx_int_t ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset); + +static ngx_int_t ngx_http_upstream_add_variables(ngx_conf_t *cf); +static ngx_int_t ngx_http_upstream_addr_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_upstream_status_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_upstream_response_time_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_upstream_response_length_variable( + ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_upstream_header_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_upstream_trailer_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_upstream_cookie_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + +static char *ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy); +static char *ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +#if (NGX_HTTP_UPSTREAM_ZONE) +static char *ngx_http_upstream_resolver(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +#endif + +static ngx_int_t ngx_http_upstream_set_local(ngx_http_request_t *r, + ngx_http_upstream_t *u, ngx_http_upstream_local_t *local); + +static void *ngx_http_upstream_create_main_conf(ngx_conf_t *cf); +static char *ngx_http_upstream_init_main_conf(ngx_conf_t *cf, void *conf); + +#if (NGX_HTTP_SSL) +static void ngx_http_upstream_ssl_init_connection(ngx_http_request_t *, + ngx_http_upstream_t *u, ngx_connection_t *c); +static void ngx_http_upstream_ssl_handshake_handler(ngx_connection_t *c); +static void ngx_http_upstream_ssl_handshake(ngx_http_request_t *, + ngx_http_upstream_t *u, ngx_connection_t *c); +static void ngx_http_upstream_ssl_save_session(ngx_connection_t *c); +static ngx_int_t ngx_http_upstream_ssl_name(ngx_http_request_t *r, + ngx_http_upstream_t *u, ngx_connection_t *c); +static ngx_int_t ngx_http_upstream_ssl_certificate(ngx_http_request_t *r, + ngx_http_upstream_t *u, ngx_connection_t *c); +#endif + + +static ngx_http_upstream_header_t ngx_http_upstream_headers_in[] = { + + { ngx_string("Status"), + ngx_http_upstream_process_header_line, + offsetof(ngx_http_upstream_headers_in_t, status), + ngx_http_upstream_copy_header_line, 0, 0 }, + + { ngx_string("Content-Type"), + ngx_http_upstream_process_header_line, + offsetof(ngx_http_upstream_headers_in_t, content_type), + ngx_http_upstream_copy_content_type, 0, 1 }, + + { ngx_string("Content-Length"), + ngx_http_upstream_process_content_length, 0, + ngx_http_upstream_ignore_header_line, 0, 0 }, + + { ngx_string("Date"), + ngx_http_upstream_process_header_line, + offsetof(ngx_http_upstream_headers_in_t, date), + ngx_http_upstream_copy_header_line, + offsetof(ngx_http_headers_out_t, date), 0 }, + + { ngx_string("Last-Modified"), + ngx_http_upstream_process_last_modified, 0, + ngx_http_upstream_copy_last_modified, 0, 0 }, + + { ngx_string("ETag"), + ngx_http_upstream_process_header_line, + offsetof(ngx_http_upstream_headers_in_t, etag), + ngx_http_upstream_copy_header_line, + offsetof(ngx_http_headers_out_t, etag), 0 }, + + { ngx_string("Server"), + ngx_http_upstream_process_header_line, + offsetof(ngx_http_upstream_headers_in_t, server), + ngx_http_upstream_copy_header_line, + offsetof(ngx_http_headers_out_t, server), 0 }, + + { ngx_string("WWW-Authenticate"), + ngx_http_upstream_process_multi_header_lines, + offsetof(ngx_http_upstream_headers_in_t, www_authenticate), + ngx_http_upstream_copy_header_line, 0, 0 }, + + { ngx_string("Location"), + ngx_http_upstream_process_header_line, + offsetof(ngx_http_upstream_headers_in_t, location), + ngx_http_upstream_rewrite_location, 0, 0 }, + + { ngx_string("Refresh"), + ngx_http_upstream_process_header_line, + offsetof(ngx_http_upstream_headers_in_t, refresh), + ngx_http_upstream_rewrite_refresh, 0, 0 }, + + { ngx_string("Set-Cookie"), + ngx_http_upstream_process_set_cookie, + offsetof(ngx_http_upstream_headers_in_t, set_cookie), + ngx_http_upstream_rewrite_set_cookie, 0, 1 }, + + { ngx_string("Content-Disposition"), + ngx_http_upstream_ignore_header_line, 0, + ngx_http_upstream_copy_header_line, 0, 1 }, + + { ngx_string("Cache-Control"), + ngx_http_upstream_process_cache_control, 0, + ngx_http_upstream_copy_multi_header_lines, + offsetof(ngx_http_headers_out_t, cache_control), 1 }, + + { ngx_string("Expires"), + ngx_http_upstream_process_expires, 0, + ngx_http_upstream_copy_header_line, + offsetof(ngx_http_headers_out_t, expires), 1 }, + + { ngx_string("Accept-Ranges"), + ngx_http_upstream_ignore_header_line, 0, + ngx_http_upstream_copy_allow_ranges, + offsetof(ngx_http_headers_out_t, accept_ranges), 1 }, + + { ngx_string("Content-Range"), + ngx_http_upstream_ignore_header_line, 0, + ngx_http_upstream_copy_header_line, + offsetof(ngx_http_headers_out_t, content_range), 0 }, + + { ngx_string("Connection"), + ngx_http_upstream_process_connection, 0, + ngx_http_upstream_ignore_header_line, 0, 0 }, + + { ngx_string("Keep-Alive"), + ngx_http_upstream_ignore_header_line, 0, + ngx_http_upstream_ignore_header_line, 0, 0 }, + + { ngx_string("Vary"), + ngx_http_upstream_process_vary, 0, + ngx_http_upstream_copy_header_line, 0, 0 }, + + { ngx_string("Link"), + ngx_http_upstream_ignore_header_line, 0, + ngx_http_upstream_copy_multi_header_lines, + offsetof(ngx_http_headers_out_t, link), 0 }, + + { ngx_string("X-Accel-Expires"), + ngx_http_upstream_process_accel_expires, 0, + ngx_http_upstream_copy_header_line, 0, 0 }, + + { ngx_string("X-Accel-Redirect"), + ngx_http_upstream_process_header_line, + offsetof(ngx_http_upstream_headers_in_t, x_accel_redirect), + ngx_http_upstream_copy_header_line, 0, 0 }, + + { ngx_string("X-Accel-Limit-Rate"), + ngx_http_upstream_process_limit_rate, 0, + ngx_http_upstream_copy_header_line, 0, 0 }, + + { ngx_string("X-Accel-Buffering"), + ngx_http_upstream_process_buffering, 0, + ngx_http_upstream_copy_header_line, 0, 0 }, + + { ngx_string("X-Accel-Charset"), + ngx_http_upstream_process_charset, 0, + ngx_http_upstream_copy_header_line, 0, 0 }, + + { ngx_string("Transfer-Encoding"), + ngx_http_upstream_process_transfer_encoding, 0, + ngx_http_upstream_ignore_header_line, 0, 0 }, + + { ngx_string("Content-Encoding"), + ngx_http_upstream_ignore_header_line, 0, + ngx_http_upstream_copy_header_line, + offsetof(ngx_http_headers_out_t, content_encoding), 0 }, + + { ngx_null_string, NULL, 0, NULL, 0, 0 } +}; + + +static ngx_command_t ngx_http_upstream_commands[] = { + + { ngx_string("upstream"), + NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1, + ngx_http_upstream, + 0, + 0, + NULL }, + + { ngx_string("server"), + NGX_HTTP_UPS_CONF|NGX_CONF_1MORE, + ngx_http_upstream_server, + NGX_HTTP_SRV_CONF_OFFSET, + 0, + NULL }, + +#if (NGX_HTTP_UPSTREAM_ZONE) + + { ngx_string("resolver"), + NGX_HTTP_UPS_CONF|NGX_CONF_1MORE, + ngx_http_upstream_resolver, + NGX_HTTP_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("resolver_timeout"), + NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_upstream_srv_conf_t, resolver_timeout), + NULL }, + +#endif + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_upstream_module_ctx = { + ngx_http_upstream_add_variables, /* preconfiguration */ + NULL, /* postconfiguration */ + + ngx_http_upstream_create_main_conf, /* create main configuration */ + ngx_http_upstream_init_main_conf, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + NULL, /* create location configuration */ + NULL /* merge location configuration */ +}; + + +ngx_module_t ngx_http_upstream_module = { + NGX_MODULE_V1, + &ngx_http_upstream_module_ctx, /* module context */ + ngx_http_upstream_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_http_variable_t ngx_http_upstream_vars[] = { + + { ngx_string("upstream_addr"), NULL, + ngx_http_upstream_addr_variable, 0, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("upstream_status"), NULL, + ngx_http_upstream_status_variable, 0, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("upstream_connect_time"), NULL, + ngx_http_upstream_response_time_variable, 2, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("upstream_header_time"), NULL, + ngx_http_upstream_response_time_variable, 1, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("upstream_response_time"), NULL, + ngx_http_upstream_response_time_variable, 0, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("upstream_response_length"), NULL, + ngx_http_upstream_response_length_variable, 0, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("upstream_bytes_received"), NULL, + ngx_http_upstream_response_length_variable, 1, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("upstream_bytes_sent"), NULL, + ngx_http_upstream_response_length_variable, 2, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + +#if (NGX_HTTP_CACHE) + + { ngx_string("upstream_cache_status"), NULL, + ngx_http_upstream_cache_status, 0, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("upstream_cache_last_modified"), NULL, + ngx_http_upstream_cache_last_modified, 0, + NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, + + { ngx_string("upstream_cache_etag"), NULL, + ngx_http_upstream_cache_etag, 0, + NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, + +#endif + + { ngx_string("upstream_http_"), NULL, ngx_http_upstream_header_variable, + 0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 }, + + { ngx_string("upstream_trailer_"), NULL, ngx_http_upstream_trailer_variable, + 0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 }, + + { ngx_string("upstream_cookie_"), NULL, ngx_http_upstream_cookie_variable, + 0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 }, + + ngx_http_null_variable +}; + + +static ngx_http_upstream_next_t ngx_http_upstream_next_errors[] = { + { 500, NGX_HTTP_UPSTREAM_FT_HTTP_500 }, + { 502, NGX_HTTP_UPSTREAM_FT_HTTP_502 }, + { 503, NGX_HTTP_UPSTREAM_FT_HTTP_503 }, + { 504, NGX_HTTP_UPSTREAM_FT_HTTP_504 }, + { 403, NGX_HTTP_UPSTREAM_FT_HTTP_403 }, + { 404, NGX_HTTP_UPSTREAM_FT_HTTP_404 }, + { 429, NGX_HTTP_UPSTREAM_FT_HTTP_429 }, + { 0, 0 } +}; + + +ngx_conf_bitmask_t ngx_http_upstream_cache_method_mask[] = { + { ngx_string("GET"), NGX_HTTP_GET }, + { ngx_string("HEAD"), NGX_HTTP_HEAD }, + { ngx_string("POST"), NGX_HTTP_POST }, + { ngx_null_string, 0 } +}; + + +ngx_conf_bitmask_t ngx_http_upstream_ignore_headers_masks[] = { + { ngx_string("X-Accel-Redirect"), NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT }, + { ngx_string("X-Accel-Expires"), NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES }, + { ngx_string("X-Accel-Limit-Rate"), NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE }, + { ngx_string("X-Accel-Buffering"), NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING }, + { ngx_string("X-Accel-Charset"), NGX_HTTP_UPSTREAM_IGN_XA_CHARSET }, + { ngx_string("Expires"), NGX_HTTP_UPSTREAM_IGN_EXPIRES }, + { ngx_string("Cache-Control"), NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL }, + { ngx_string("Set-Cookie"), NGX_HTTP_UPSTREAM_IGN_SET_COOKIE }, + { ngx_string("Vary"), NGX_HTTP_UPSTREAM_IGN_VARY }, + { ngx_null_string, 0 } +}; + + +ngx_int_t +ngx_http_upstream_create(ngx_http_request_t *r) +{ + ngx_http_upstream_t *u; + + u = r->upstream; + + if (u && u->cleanup) { + r->main->count++; + ngx_http_upstream_cleanup(r); + } + + u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t)); + if (u == NULL) { + return NGX_ERROR; + } + + r->upstream = u; + + u->peer.log = r->connection->log; + u->peer.log_error = NGX_ERROR_ERR; + +#if (NGX_HTTP_CACHE) + r->cache = NULL; +#endif + + u->headers_in.content_length_n = -1; + u->headers_in.last_modified_time = -1; + + return NGX_OK; +} + + +void +ngx_http_upstream_init(ngx_http_request_t *r) +{ + ngx_connection_t *c; + + c = r->connection; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http init upstream, client timer: %d", c->read->timer_set); + +#if (NGX_HTTP_V2) + if (r->stream) { + ngx_http_upstream_init_request(r); + return; + } +#endif + +#if (NGX_HTTP_V3) + if (c->quic) { + ngx_http_upstream_init_request(r); + return; + } +#endif + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (ngx_event_flags & NGX_USE_CLEAR_EVENT) { + + if (!c->write->active) { + if (ngx_add_event(c->write, NGX_WRITE_EVENT, NGX_CLEAR_EVENT) + == NGX_ERROR) + { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + } + } + + ngx_http_upstream_init_request(r); +} + + +static void +ngx_http_upstream_init_request(ngx_http_request_t *r) +{ + ngx_str_t *host; + ngx_uint_t i; + ngx_resolver_ctx_t *ctx, temp; + ngx_http_cleanup_t *cln; + ngx_http_upstream_t *u; + ngx_http_core_loc_conf_t *clcf; + ngx_http_upstream_srv_conf_t *uscf, **uscfp; + ngx_http_upstream_main_conf_t *umcf; + + if (r->aio) { + return; + } + + u = r->upstream; + +#if (NGX_HTTP_CACHE) + + if (u->conf->cache) { + ngx_int_t rc; + + rc = ngx_http_upstream_cache(r, u); + + if (rc == NGX_BUSY) { + r->write_event_handler = ngx_http_upstream_init_request; + return; + } + + r->write_event_handler = ngx_http_request_empty_handler; + + if (rc == NGX_ERROR) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + if (rc == NGX_OK) { + rc = ngx_http_upstream_cache_send(r, u); + + if (rc == NGX_DONE) { + return; + } + + if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) { + rc = NGX_DECLINED; + r->cached = 0; + u->buffer.start = NULL; + u->cache_status = NGX_HTTP_CACHE_MISS; + u->request_sent = 1; + } + } + + if (rc != NGX_DECLINED) { + ngx_http_finalize_request(r, rc); + return; + } + } + +#endif + + u->store = u->conf->store; + + if (!u->store && !r->post_action && !u->conf->ignore_client_abort) { + + if (r->connection->read->ready) { + ngx_post_event(r->connection->read, &ngx_posted_events); + + } else { + if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + } + + r->read_event_handler = ngx_http_upstream_rd_check_broken_connection; + r->write_event_handler = ngx_http_upstream_wr_check_broken_connection; + } + + if (r->request_body) { + u->request_bufs = r->request_body->bufs; + } + + if (u->create_request(r) != NGX_OK) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + if (ngx_http_upstream_set_local(r, u, u->conf->local) != NGX_OK) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + if (u->conf->socket_keepalive) { + u->peer.so_keepalive = 1; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + u->output.alignment = clcf->directio_alignment; + u->output.pool = r->pool; + u->output.bufs.num = 1; + u->output.bufs.size = clcf->client_body_buffer_size; + + if (u->output.output_filter == NULL) { + u->output.output_filter = ngx_chain_writer; + u->output.filter_ctx = &u->writer; + } + + u->writer.pool = r->pool; + + if (r->upstream_states == NULL) { + + r->upstream_states = ngx_array_create(r->pool, 1, + sizeof(ngx_http_upstream_state_t)); + if (r->upstream_states == NULL) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + } else { + + u->state = ngx_array_push(r->upstream_states); + if (u->state == NULL) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t)); + } + + cln = ngx_http_cleanup_add(r, 0); + if (cln == NULL) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + cln->handler = ngx_http_upstream_cleanup; + cln->data = r; + u->cleanup = &cln->handler; + + if (u->resolved == NULL) { + + uscf = u->conf->upstream; + + } else { + +#if (NGX_HTTP_SSL) + u->ssl_name = u->resolved->host; +#endif + + host = &u->resolved->host; + + umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); + + uscfp = umcf->upstreams.elts; + + for (i = 0; i < umcf->upstreams.nelts; i++) { + + uscf = uscfp[i]; + + if (uscf->host.len == host->len + && ((uscf->port == 0 && u->resolved->no_port) + || uscf->port == u->resolved->port) + && ngx_strncasecmp(uscf->host.data, host->data, host->len) == 0) + { + goto found; + } + } + + if (u->resolved->sockaddr) { + + if (u->resolved->port == 0 + && u->resolved->sockaddr->sa_family != AF_UNIX) + { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "no port in upstream \"%V\"", host); + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + if (ngx_http_upstream_create_round_robin_peer(r, u->resolved) + != NGX_OK) + { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + ngx_http_upstream_connect(r, u); + + return; + } + + if (u->resolved->port == 0) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "no port in upstream \"%V\"", host); + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + temp.name = *host; + + ctx = ngx_resolve_start(clcf->resolver, &temp); + if (ctx == NULL) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + if (ctx == NGX_NO_RESOLVER) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "no resolver defined to resolve %V", host); + + ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY); + return; + } + + ctx->name = *host; + ctx->handler = ngx_http_upstream_resolve_handler; + ctx->data = r; + ctx->timeout = clcf->resolver_timeout; + + u->resolved->ctx = ctx; + + if (ngx_resolve_name(ctx) != NGX_OK) { + u->resolved->ctx = NULL; + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + return; + } + +found: + + if (uscf == NULL) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "no upstream configuration"); + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + u->upstream = uscf; + +#if (NGX_HTTP_SSL) + u->ssl_name = uscf->host; +#endif + + if (uscf->peer.init(r, uscf) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + u->peer.start_time = ngx_current_msec; + + if (u->conf->next_upstream_tries + && u->peer.tries > u->conf->next_upstream_tries) + { + u->peer.tries = u->conf->next_upstream_tries; + } + + ngx_http_upstream_connect(r, u); +} + + +#if (NGX_HTTP_CACHE) + +static ngx_int_t +ngx_http_upstream_cache(ngx_http_request_t *r, ngx_http_upstream_t *u) +{ + ngx_int_t rc; + ngx_http_cache_t *c; + ngx_http_file_cache_t *cache; + + c = r->cache; + + if (c == NULL) { + + if (!(r->method & u->conf->cache_methods)) { + return NGX_DECLINED; + } + + rc = ngx_http_upstream_cache_get(r, u, &cache); + + if (rc != NGX_OK) { + return rc; + } + + if (r->method == NGX_HTTP_HEAD && u->conf->cache_convert_head) { + u->method = ngx_http_core_get_method; + } + + if (ngx_http_file_cache_new(r) != NGX_OK) { + return NGX_ERROR; + } + + if (u->create_key(r) != NGX_OK) { + return NGX_ERROR; + } + + /* TODO: add keys */ + + ngx_http_file_cache_create_key(r); + + if (r->cache->header_start + 256 > u->conf->buffer_size) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "%V_buffer_size %uz is not enough for cache key, " + "it should be increased to at least %uz", + &u->conf->module, u->conf->buffer_size, + ngx_align(r->cache->header_start + 256, 1024)); + + r->cache = NULL; + return NGX_DECLINED; + } + + u->cacheable = 1; + + c = r->cache; + + c->body_start = u->conf->buffer_size; + c->min_uses = u->conf->cache_min_uses; + c->file_cache = cache; + + switch (ngx_http_test_predicates(r, u->conf->cache_bypass)) { + + case NGX_ERROR: + return NGX_ERROR; + + case NGX_DECLINED: + u->cache_status = NGX_HTTP_CACHE_BYPASS; + return NGX_DECLINED; + + default: /* NGX_OK */ + break; + } + + c->lock = u->conf->cache_lock; + c->lock_timeout = u->conf->cache_lock_timeout; + c->lock_age = u->conf->cache_lock_age; + + u->cache_status = NGX_HTTP_CACHE_MISS; + } + + rc = ngx_http_file_cache_open(r); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http upstream cache: %i", rc); + + switch (rc) { + + case NGX_HTTP_CACHE_STALE: + + if (((u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING) + || c->stale_updating) && !r->background + && u->conf->cache_background_update) + { + if (ngx_http_upstream_cache_background_update(r, u) == NGX_OK) { + r->cache->background = 1; + u->cache_status = rc; + rc = NGX_OK; + + } else { + rc = NGX_ERROR; + } + } + + break; + + case NGX_HTTP_CACHE_UPDATING: + + if (((u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING) + || c->stale_updating) && !r->background) + { + u->cache_status = rc; + rc = NGX_OK; + + } else { + rc = NGX_HTTP_CACHE_STALE; + } + + break; + + case NGX_OK: + u->cache_status = NGX_HTTP_CACHE_HIT; + } + + switch (rc) { + + case NGX_OK: + + return NGX_OK; + + case NGX_HTTP_CACHE_STALE: + + c->valid_sec = 0; + c->updating_sec = 0; + c->error_sec = 0; + + u->buffer.start = NULL; + u->cache_status = NGX_HTTP_CACHE_EXPIRED; + + break; + + case NGX_DECLINED: + + if ((size_t) (u->buffer.end - u->buffer.start) < u->conf->buffer_size) { + u->buffer.start = NULL; + + } else { + u->buffer.pos = u->buffer.start + c->header_start; + u->buffer.last = u->buffer.pos; + } + + break; + + case NGX_HTTP_CACHE_SCARCE: + + u->cacheable = 0; + + break; + + case NGX_AGAIN: + + return NGX_BUSY; + + case NGX_ERROR: + + return NGX_ERROR; + + default: + + /* cached NGX_HTTP_BAD_GATEWAY, NGX_HTTP_GATEWAY_TIME_OUT, etc. */ + + u->cache_status = NGX_HTTP_CACHE_HIT; + + return rc; + } + + if (ngx_http_upstream_cache_check_range(r, u) == NGX_DECLINED) { + u->cacheable = 0; + } + + r->cached = 0; + + return NGX_DECLINED; +} + + +static ngx_int_t +ngx_http_upstream_cache_get(ngx_http_request_t *r, ngx_http_upstream_t *u, + ngx_http_file_cache_t **cache) +{ + ngx_str_t *name, val; + ngx_uint_t i; + ngx_http_file_cache_t **caches; + + if (u->conf->cache_zone) { + *cache = u->conf->cache_zone->data; + return NGX_OK; + } + + if (ngx_http_complex_value(r, u->conf->cache_value, &val) != NGX_OK) { + return NGX_ERROR; + } + + if (val.len == 0 + || (val.len == 3 && ngx_strncmp(val.data, "off", 3) == 0)) + { + return NGX_DECLINED; + } + + caches = u->caches->elts; + + for (i = 0; i < u->caches->nelts; i++) { + name = &caches[i]->shm_zone->shm.name; + + if (name->len == val.len + && ngx_strncmp(name->data, val.data, val.len) == 0) + { + *cache = caches[i]; + return NGX_OK; + } + } + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "cache \"%V\" not found", &val); + + return NGX_ERROR; +} + + +static ngx_int_t +ngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u) +{ + ngx_int_t rc; + ngx_http_cache_t *c; + + r->cached = 1; + c = r->cache; + + if (c->header_start == c->body_start) { + r->http_version = NGX_HTTP_VERSION_9; + return ngx_http_cache_send(r); + } + + /* TODO: cache stack */ + + u->buffer = *c->buf; + u->buffer.pos += c->header_start; + + ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t)); + u->headers_in.content_length_n = -1; + u->headers_in.last_modified_time = -1; + + if (ngx_list_init(&u->headers_in.headers, r->pool, 8, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_list_init(&u->headers_in.trailers, r->pool, 2, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + rc = u->process_header(r); + + if (rc == NGX_OK) { + + if (ngx_http_upstream_process_headers(r, u) != NGX_OK) { + return NGX_DONE; + } + + return ngx_http_cache_send(r); + } + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_AGAIN || rc == NGX_HTTP_UPSTREAM_EARLY_HINTS) { + rc = NGX_HTTP_UPSTREAM_INVALID_HEADER; + } + + /* rc == NGX_HTTP_UPSTREAM_INVALID_HEADER */ + + ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0, + "cache file \"%s\" contains invalid header", + c->file.name.data); + + /* TODO: delete file */ + + return rc; +} + + +static ngx_int_t +ngx_http_upstream_cache_background_update(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + ngx_http_request_t *sr; + + if (r == r->main) { + r->preserve_body = 1; + } + + if (ngx_http_subrequest(r, &r->uri, &r->args, &sr, NULL, + NGX_HTTP_SUBREQUEST_CLONE + |NGX_HTTP_SUBREQUEST_BACKGROUND) + != NGX_OK) + { + return NGX_ERROR; + } + + sr->header_only = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_cache_check_range(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + off_t offset; + u_char *p, *start; + ngx_table_elt_t *h; + + h = r->headers_in.range; + + if (h == NULL + || !u->cacheable + || u->conf->cache_max_range_offset == NGX_MAX_OFF_T_VALUE) + { + return NGX_OK; + } + + if (u->conf->cache_max_range_offset == 0) { + return NGX_DECLINED; + } + + if (h->value.len < 7 + || ngx_strncasecmp(h->value.data, (u_char *) "bytes=", 6) != 0) + { + return NGX_OK; + } + + p = h->value.data + 6; + + while (*p == ' ') { p++; } + + if (*p == '-') { + return NGX_DECLINED; + } + + start = p; + + while (*p >= '0' && *p <= '9') { p++; } + + offset = ngx_atoof(start, p - start); + + if (offset >= u->conf->cache_max_range_offset) { + return NGX_DECLINED; + } + + return NGX_OK; +} + +#endif + + +static void +ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx) +{ + ngx_uint_t run_posted; + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_upstream_t *u; + ngx_http_upstream_resolved_t *ur; + + run_posted = ctx->async; + + r = ctx->data; + c = r->connection; + + u = r->upstream; + ur = u->resolved; + + ngx_http_set_log_request(c->log, r); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream resolve: \"%V?%V\"", &r->uri, &r->args); + + if (ctx->state) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "%V could not be resolved (%i: %s)", + &ctx->name, ctx->state, + ngx_resolver_strerror(ctx->state)); + + ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY); + goto failed; + } + + ur->naddrs = ctx->naddrs; + ur->addrs = ctx->addrs; + +#if (NGX_DEBUG) + { + u_char text[NGX_SOCKADDR_STRLEN]; + ngx_str_t addr; + ngx_uint_t i; + + addr.data = text; + + for (i = 0; i < ctx->naddrs; i++) { + addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen, + text, NGX_SOCKADDR_STRLEN, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "name was resolved to %V", &addr); + } + } +#endif + + if (ngx_http_upstream_create_round_robin_peer(r, ur) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + goto failed; + } + + ngx_resolve_name_done(ctx); + ur->ctx = NULL; + + u->peer.start_time = ngx_current_msec; + + if (u->conf->next_upstream_tries + && u->peer.tries > u->conf->next_upstream_tries) + { + u->peer.tries = u->conf->next_upstream_tries; + } + + ngx_http_upstream_connect(r, u); + +failed: + + if (run_posted) { + ngx_http_run_posted_requests(c); + } +} + + +static void +ngx_http_upstream_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_upstream_t *u; + + c = ev->data; + r = c->data; + + u = r->upstream; + c = r->connection; + + ngx_http_set_log_request(c->log, r); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream request: \"%V?%V\"", &r->uri, &r->args); + + if (ev->delayed && ev->timedout) { + ev->delayed = 0; + ev->timedout = 0; + } + + if (ev->write) { + u->write_event_handler(r, u); + + } else { + u->read_event_handler(r, u); + } + + ngx_http_run_posted_requests(c); +} + + +static void +ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r) +{ + ngx_http_upstream_check_broken_connection(r, r->connection->read); +} + + +static void +ngx_http_upstream_wr_check_broken_connection(ngx_http_request_t *r) +{ + ngx_http_upstream_check_broken_connection(r, r->connection->write); +} + + +static void +ngx_http_upstream_check_broken_connection(ngx_http_request_t *r, + ngx_event_t *ev) +{ + int n; + char buf[1]; + ngx_err_t err; + ngx_int_t event; + ngx_connection_t *c; + ngx_http_upstream_t *u; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0, + "http upstream check client, write event:%d, \"%V\"", + ev->write, &r->uri); + + c = r->connection; + u = r->upstream; + + if (c->error) { + if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) { + + event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT; + + if (ngx_del_event(ev, event, 0) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + } + + if (!u->cacheable) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_CLIENT_CLOSED_REQUEST); + } + + return; + } + +#if (NGX_HTTP_V2) + if (r->stream) { + return; + } +#endif + +#if (NGX_HTTP_V3) + + if (c->quic) { + if (c->write->error) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_CLIENT_CLOSED_REQUEST); + } + + return; + } + +#endif + +#if (NGX_HAVE_KQUEUE) + + if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { + + if (!ev->pending_eof) { + return; + } + + ev->eof = 1; + c->error = 1; + + if (ev->kq_errno) { + ev->error = 1; + } + + if (!u->cacheable && u->peer.connection) { + ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno, + "kevent() reported that client prematurely closed " + "connection, so upstream connection is closed too"); + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_CLIENT_CLOSED_REQUEST); + return; + } + + ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno, + "kevent() reported that client prematurely closed " + "connection"); + + if (u->peer.connection == NULL) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_CLIENT_CLOSED_REQUEST); + } + + return; + } + +#endif + +#if (NGX_HAVE_EPOLLRDHUP) + + if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) && ngx_use_epoll_rdhup) { + socklen_t len; + + if (!ev->pending_eof) { + return; + } + + ev->eof = 1; + c->error = 1; + + err = 0; + len = sizeof(ngx_err_t); + + /* + * BSDs and Linux return 0 and set a pending error in err + * Solaris returns -1 and sets errno + */ + + if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len) + == -1) + { + err = ngx_socket_errno; + } + + if (err) { + ev->error = 1; + } + + if (!u->cacheable && u->peer.connection) { + ngx_log_error(NGX_LOG_INFO, ev->log, err, + "epoll_wait() reported that client prematurely closed " + "connection, so upstream connection is closed too"); + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_CLIENT_CLOSED_REQUEST); + return; + } + + ngx_log_error(NGX_LOG_INFO, ev->log, err, + "epoll_wait() reported that client prematurely closed " + "connection"); + + if (u->peer.connection == NULL) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_CLIENT_CLOSED_REQUEST); + } + + return; + } + +#endif + + n = recv(c->fd, buf, 1, MSG_PEEK); + + err = ngx_socket_errno; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, err, + "http upstream recv(): %d", n); + + if (ev->write && (n >= 0 || err == NGX_EAGAIN)) { + return; + } + + if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) { + + event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT; + + if (ngx_del_event(ev, event, 0) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + } + + if (n > 0) { + return; + } + + if (n == -1) { + if (err == NGX_EAGAIN) { + return; + } + + ev->error = 1; + + } else { /* n == 0 */ + err = 0; + } + + ev->eof = 1; + c->error = 1; + + if (!u->cacheable && u->peer.connection) { + ngx_log_error(NGX_LOG_INFO, ev->log, err, + "client prematurely closed connection, " + "so upstream connection is closed too"); + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_CLIENT_CLOSED_REQUEST); + return; + } + + ngx_log_error(NGX_LOG_INFO, ev->log, err, + "client prematurely closed connection"); + + if (u->peer.connection == NULL) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_CLIENT_CLOSED_REQUEST); + } +} + + +static void +ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u) +{ + ngx_int_t rc; + ngx_connection_t *c; + ngx_http_core_loc_conf_t *clcf; + + r->connection->log->action = "connecting to upstream"; + + if (u->state && u->state->response_time == (ngx_msec_t) -1) { + u->state->response_time = ngx_current_msec - u->start_time; + } + + u->state = ngx_array_push(r->upstream_states); + if (u->state == NULL) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t)); + + u->start_time = ngx_current_msec; + + u->state->response_time = (ngx_msec_t) -1; + u->state->connect_time = (ngx_msec_t) -1; + u->state->header_time = (ngx_msec_t) -1; + + rc = ngx_event_connect_peer(&u->peer); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http upstream connect: %i", rc); + + if (rc == NGX_ERROR) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + u->state->peer = u->peer.name; + +#if (NGX_HTTP_UPSTREAM_ZONE) + if (u->upstream && u->upstream->shm_zone + && (u->upstream->flags & NGX_HTTP_UPSTREAM_MODIFY)) + { + u->state->peer = ngx_palloc(r->pool, + sizeof(ngx_str_t) + u->peer.name->len); + if (u->state->peer == NULL) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + u->state->peer->len = u->peer.name->len; + u->state->peer->data = (u_char *) (u->state->peer + 1); + ngx_memcpy(u->state->peer->data, u->peer.name->data, u->peer.name->len); + + u->peer.name = u->state->peer; + } +#endif + + if (rc == NGX_BUSY) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no live upstreams"); + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_NOLIVE); + return; + } + + if (rc == NGX_DECLINED) { + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); + return; + } + + /* rc == NGX_OK || rc == NGX_AGAIN || rc == NGX_DONE */ + + c = u->peer.connection; + + c->requests++; + + c->data = r; + + c->write->handler = ngx_http_upstream_handler; + c->read->handler = ngx_http_upstream_handler; + + u->write_event_handler = ngx_http_upstream_send_request_handler; + u->read_event_handler = ngx_http_upstream_process_header; + + c->sendfile &= r->connection->sendfile; + u->output.sendfile = c->sendfile; + + if (r->connection->tcp_nopush == NGX_TCP_NOPUSH_DISABLED) { + c->tcp_nopush = NGX_TCP_NOPUSH_DISABLED; + } + + if (c->pool == NULL) { + + /* we need separate pool here to be able to cache SSL connections */ + + c->pool = ngx_create_pool(128, r->connection->log); + if (c->pool == NULL) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + } + + c->log = r->connection->log; + c->pool->log = c->log; + c->read->log = c->log; + c->write->log = c->log; + + /* init or reinit the ngx_output_chain() and ngx_chain_writer() contexts */ + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + u->writer.out = NULL; + u->writer.last = &u->writer.out; + u->writer.connection = c; + u->writer.limit = clcf->sendfile_max_chunk; + + if (u->request_sent || u->response_received) { + if (ngx_http_upstream_reinit(r, u) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + } + + if (r->request_body + && r->request_body->buf + && r->request_body->temp_file + && r == r->main) + { + /* + * the r->request_body->buf can be reused for one request only, + * the subrequests should allocate their own temporary bufs + */ + + u->output.free = ngx_alloc_chain_link(r->pool); + if (u->output.free == NULL) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + u->output.free->buf = r->request_body->buf; + u->output.free->next = NULL; + u->output.allocated = 1; + + r->request_body->buf->pos = r->request_body->buf->start; + r->request_body->buf->last = r->request_body->buf->start; + r->request_body->buf->tag = u->output.tag; + } + + u->request_sent = 0; + u->request_body_sent = 0; + u->request_body_blocked = 0; + u->response_received = 0; + + if (rc == NGX_AGAIN) { + ngx_add_timer(c->write, u->conf->connect_timeout); + return; + } + +#if (NGX_HTTP_SSL) + + if (u->ssl && c->ssl == NULL) { + ngx_http_upstream_ssl_init_connection(r, u, c); + return; + } + +#endif + + ngx_http_upstream_send_request(r, u, 1); +} + + +#if (NGX_HTTP_SSL) + +static void +ngx_http_upstream_ssl_init_connection(ngx_http_request_t *r, + ngx_http_upstream_t *u, ngx_connection_t *c) +{ + ngx_int_t rc; + ngx_http_core_loc_conf_t *clcf; + + if (ngx_http_upstream_test_connect(c) != NGX_OK) { + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); + return; + } + + if (ngx_ssl_create_connection(u->conf->ssl, c, + NGX_SSL_BUFFER|NGX_SSL_CLIENT) + != NGX_OK) + { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + if (u->conf->ssl_server_name || u->conf->ssl_verify) { + if (ngx_http_upstream_ssl_name(r, u, c) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + } + + if (u->conf->ssl_certificate + && u->conf->ssl_certificate->value.len + && (u->conf->ssl_certificate->lengths + || u->conf->ssl_certificate_key->lengths)) + { + if (ngx_http_upstream_ssl_certificate(r, u, c) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + } + +#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation + + if (u->ssl_alpn_protocol.len) { + if (SSL_set_alpn_protos(c->ssl->connection, u->ssl_alpn_protocol.data, + u->ssl_alpn_protocol.len) + != 0) + { + ngx_ssl_error(NGX_LOG_ERR, c->log, 0, + "SSL_set_alpn_protos() failed"); + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + } + +#endif + + if (u->conf->ssl_session_reuse) { + c->ssl->save_session = ngx_http_upstream_ssl_save_session; + + if (u->peer.set_session(&u->peer, u->peer.data) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + /* abbreviated SSL handshake may interact badly with Nagle */ + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + } + + r->connection->log->action = "SSL handshaking to upstream"; + + rc = ngx_ssl_handshake(c); + + if (rc == NGX_AGAIN) { + + if (!c->write->timer_set) { + ngx_add_timer(c->write, u->conf->connect_timeout); + } + + c->ssl->handler = ngx_http_upstream_ssl_handshake_handler; + return; + } + + ngx_http_upstream_ssl_handshake(r, u, c); +} + + +static void +ngx_http_upstream_ssl_handshake_handler(ngx_connection_t *c) +{ + ngx_http_request_t *r; + ngx_http_upstream_t *u; + + r = c->data; + + u = r->upstream; + c = r->connection; + + ngx_http_set_log_request(c->log, r); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream ssl handshake: \"%V?%V\"", + &r->uri, &r->args); + + ngx_http_upstream_ssl_handshake(r, u, u->peer.connection); + + ngx_http_run_posted_requests(c); +} + + +static void +ngx_http_upstream_ssl_handshake(ngx_http_request_t *r, ngx_http_upstream_t *u, + ngx_connection_t *c) +{ + long rc; + + if (c->ssl->handshaked) { + + if (u->conf->ssl_verify) { + rc = SSL_get_verify_result(c->ssl->connection); + + if (rc != X509_V_OK) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "upstream SSL certificate verify error: (%l:%s)", + rc, X509_verify_cert_error_string(rc)); + goto failed; + } + + if (ngx_ssl_check_host(c, &u->ssl_name) != NGX_OK) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "upstream SSL certificate does not match \"%V\"", + &u->ssl_name); + goto failed; + } + } + + if (!c->ssl->sendfile) { + c->sendfile = 0; + u->output.sendfile = 0; + } + + c->write->handler = ngx_http_upstream_handler; + c->read->handler = ngx_http_upstream_handler; + + ngx_http_upstream_send_request(r, u, 1); + + return; + } + + if (c->write->timedout) { + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT); + return; + } + +failed: + + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); +} + + +static void +ngx_http_upstream_ssl_save_session(ngx_connection_t *c) +{ + ngx_http_request_t *r; + ngx_http_upstream_t *u; + + if (c->idle) { + return; + } + + r = c->data; + + u = r->upstream; + c = r->connection; + + ngx_http_set_log_request(c->log, r); + + u->peer.save_session(&u->peer, u->peer.data); +} + + +static ngx_int_t +ngx_http_upstream_ssl_name(ngx_http_request_t *r, ngx_http_upstream_t *u, + ngx_connection_t *c) +{ + u_char *p, *last; + ngx_str_t name; + + if (u->conf->ssl_name) { + if (ngx_http_complex_value(r, u->conf->ssl_name, &name) != NGX_OK) { + return NGX_ERROR; + } + + } else { + name = u->ssl_name; + } + + if (name.len == 0) { + goto done; + } + + /* + * ssl name here may contain port, notably if derived from $proxy_host + * or $http_host; we have to strip it + */ + + p = name.data; + last = name.data + name.len; + + if (*p == '[') { + p = ngx_strlchr(p, last, ']'); + + if (p == NULL) { + p = name.data; + } + } + + p = ngx_strlchr(p, last, ':'); + + if (p != NULL) { + name.len = p - name.data; + } + + if (!u->conf->ssl_server_name) { + goto done; + } + +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + + /* as per RFC 6066, literal IPv4 and IPv6 addresses are not permitted */ + + if (name.len == 0 || *name.data == '[') { + goto done; + } + + if (ngx_inet_addr(name.data, name.len) != INADDR_NONE) { + goto done; + } + + /* + * SSL_set_tlsext_host_name() needs a null-terminated string, + * hence we explicitly null-terminate name here + */ + + p = ngx_pnalloc(r->pool, name.len + 1); + if (p == NULL) { + return NGX_ERROR; + } + + (void) ngx_cpystrn(p, name.data, name.len + 1); + + name.data = p; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "upstream SSL server name: \"%s\"", name.data); + + if (SSL_set_tlsext_host_name(c->ssl->connection, + (char *) name.data) + == 0) + { + ngx_ssl_error(NGX_LOG_ERR, r->connection->log, 0, + "SSL_set_tlsext_host_name(\"%s\") failed", name.data); + return NGX_ERROR; + } + +#endif + +done: + + u->ssl_name = name; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_ssl_certificate(ngx_http_request_t *r, + ngx_http_upstream_t *u, ngx_connection_t *c) +{ + ngx_str_t cert, key; + + if (ngx_http_complex_value(r, u->conf->ssl_certificate, &cert) + != NGX_OK) + { + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream ssl cert: \"%s\"", cert.data); + + if (*cert.data == '\0') { + return NGX_OK; + } + + if (ngx_http_complex_value(r, u->conf->ssl_certificate_key, &key) + != NGX_OK) + { + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream ssl key: \"%s\"", key.data); + + if (ngx_ssl_connection_certificate(c, r->pool, &cert, &key, + u->conf->ssl_certificate_cache, + u->conf->ssl_passwords) + != NGX_OK) + { + return NGX_ERROR; + } + + return NGX_OK; +} + +#endif + + +static ngx_int_t +ngx_http_upstream_reinit(ngx_http_request_t *r, ngx_http_upstream_t *u) +{ + off_t file_pos; + ngx_chain_t *cl; + + if (u->reinit_request(r) != NGX_OK) { + return NGX_ERROR; + } + + u->early_hints_length = 0; + u->keepalive = 0; + u->upgrade = 0; + u->error = 0; + + ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t)); + u->headers_in.content_length_n = -1; + u->headers_in.last_modified_time = -1; + + if (ngx_list_init(&u->headers_in.headers, r->pool, 8, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_list_init(&u->headers_in.trailers, r->pool, 2, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + /* reinit the request chain */ + + file_pos = 0; + + for (cl = u->request_bufs; cl; cl = cl->next) { + cl->buf->pos = cl->buf->start; + + /* there is at most one file */ + + if (cl->buf->in_file) { + cl->buf->file_pos = file_pos; + file_pos = cl->buf->file_last; + } + } + + /* reinit the subrequest's ngx_output_chain() context */ + + if (r->request_body && r->request_body->temp_file + && r != r->main && u->output.buf) + { + u->output.free = ngx_alloc_chain_link(r->pool); + if (u->output.free == NULL) { + return NGX_ERROR; + } + + u->output.free->buf = u->output.buf; + u->output.free->next = NULL; + + u->output.buf->pos = u->output.buf->start; + u->output.buf->last = u->output.buf->start; + } + + u->output.buf = NULL; + u->output.in = NULL; + u->output.busy = NULL; + + /* reinit u->buffer */ + + u->buffer.pos = u->buffer.start; + +#if (NGX_HTTP_CACHE) + + if (r->cache) { + u->buffer.pos += r->cache->header_start; + } + +#endif + + u->buffer.last = u->buffer.pos; + + return NGX_OK; +} + + +static void +ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u, + ngx_uint_t do_write) +{ + ngx_int_t rc; + ngx_connection_t *c; + + c = u->peer.connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream send request"); + + if (u->state->connect_time == (ngx_msec_t) -1) { + u->state->connect_time = ngx_current_msec - u->start_time; + } + + if (!u->request_sent && ngx_http_upstream_test_connect(c) != NGX_OK) { + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); + return; + } + + c->log->action = "sending request to upstream"; + + rc = ngx_http_upstream_send_request_body(r, u, do_write); + + if (rc == NGX_ERROR) { + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); + return; + } + + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + ngx_http_upstream_finalize_request(r, u, rc); + return; + } + + if (rc == NGX_AGAIN) { + if (!c->write->ready || u->request_body_blocked) { + ngx_add_timer(c->write, u->conf->send_timeout); + + } else if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + if (ngx_handle_write_event(c->write, u->conf->send_lowat) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + if (c->write->ready && c->tcp_nopush == NGX_TCP_NOPUSH_SET) { + if (ngx_tcp_push(c->fd) == -1) { + ngx_log_error(NGX_LOG_CRIT, c->log, ngx_socket_errno, + ngx_tcp_push_n " failed"); + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + c->tcp_nopush = NGX_TCP_NOPUSH_UNSET; + } + + if (c->read->ready) { + ngx_post_event(c->read, &ngx_posted_events); + } + + return; + } + + /* rc == NGX_OK */ + + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) { + if (ngx_tcp_push(c->fd) == -1) { + ngx_log_error(NGX_LOG_CRIT, c->log, ngx_socket_errno, + ngx_tcp_push_n " failed"); + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + c->tcp_nopush = NGX_TCP_NOPUSH_UNSET; + } + + if (!u->conf->preserve_output) { + u->write_event_handler = ngx_http_upstream_dummy_handler; + } + + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + if (!u->request_body_sent) { + u->request_body_sent = 1; + + if (u->header_sent) { + return; + } + + ngx_add_timer(c->read, u->conf->read_timeout); + + if (c->read->ready) { + ngx_http_upstream_process_header(r, u); + return; + } + } +} + + +static ngx_int_t +ngx_http_upstream_send_request_body(ngx_http_request_t *r, + ngx_http_upstream_t *u, ngx_uint_t do_write) +{ + ngx_int_t rc; + ngx_chain_t *out, *cl, *ln; + ngx_connection_t *c; + ngx_http_core_loc_conf_t *clcf; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http upstream send request body"); + + if (!r->request_body_no_buffering) { + + /* buffered request body */ + + if (!u->request_sent) { + u->request_sent = 1; + out = u->request_bufs; + + } else { + out = NULL; + } + + rc = ngx_output_chain(&u->output, out); + + if (rc == NGX_AGAIN) { + u->request_body_blocked = 1; + + } else { + u->request_body_blocked = 0; + } + + return rc; + } + + if (!u->request_sent) { + u->request_sent = 1; + out = u->request_bufs; + + if (r->request_body->bufs) { + for (cl = out; cl->next; cl = cl->next) { /* void */ } + cl->next = r->request_body->bufs; + r->request_body->bufs = NULL; + } + + c = u->peer.connection; + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) { + return NGX_ERROR; + } + + r->read_event_handler = ngx_http_upstream_read_request_handler; + + } else { + out = NULL; + } + + for ( ;; ) { + + if (do_write) { + rc = ngx_output_chain(&u->output, out); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + while (out) { + ln = out; + out = out->next; + ngx_free_chain(r->pool, ln); + } + + if (rc == NGX_AGAIN) { + u->request_body_blocked = 1; + + } else { + u->request_body_blocked = 0; + } + + if (rc == NGX_OK && !r->reading_body) { + break; + } + } + + if (r->reading_body) { + /* read client request body */ + + rc = ngx_http_read_unbuffered_request_body(r); + + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + out = r->request_body->bufs; + r->request_body->bufs = NULL; + } + + /* stop if there is nothing to send */ + + if (out == NULL) { + rc = NGX_AGAIN; + break; + } + + do_write = 1; + } + + if (!r->reading_body) { + if (!u->store && !r->post_action && !u->conf->ignore_client_abort) { + r->read_event_handler = + ngx_http_upstream_rd_check_broken_connection; + } + } + + return rc; +} + + +static void +ngx_http_upstream_send_request_handler(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + ngx_connection_t *c; + + c = u->peer.connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http upstream send request handler"); + + if (c->write->timedout) { + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT); + return; + } + +#if (NGX_HTTP_SSL) + + if (u->ssl && c->ssl == NULL) { + ngx_http_upstream_ssl_init_connection(r, u, c); + return; + } + +#endif + + if (u->header_sent && !u->conf->preserve_output) { + u->write_event_handler = ngx_http_upstream_dummy_handler; + + (void) ngx_handle_write_event(c->write, 0); + + return; + } + + ngx_http_upstream_send_request(r, u, 1); +} + + +static void +ngx_http_upstream_read_request_handler(ngx_http_request_t *r) +{ + ngx_connection_t *c; + ngx_http_upstream_t *u; + + c = r->connection; + u = r->upstream; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http upstream read request handler"); + + if (c->read->timedout) { + c->timedout = 1; + ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT); + return; + } + + ngx_http_upstream_send_request(r, u, 0); +} + + +static void +ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u) +{ + ssize_t n; + ngx_int_t rc; + ngx_connection_t *c; + + c = u->peer.connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream process header"); + + c->log->action = "reading response header from upstream"; + + if (c->read->timedout) { + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT); + return; + } + + if (!u->request_sent && ngx_http_upstream_test_connect(c) != NGX_OK) { + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); + return; + } + + if (u->buffer.start == NULL) { + u->buffer.start = ngx_palloc(r->pool, u->conf->buffer_size); + if (u->buffer.start == NULL) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + u->buffer.pos = u->buffer.start; + u->buffer.last = u->buffer.start; + u->buffer.end = u->buffer.start + u->conf->buffer_size; + u->buffer.temporary = 1; + + u->buffer.tag = u->output.tag; + + if (ngx_list_init(&u->headers_in.headers, r->pool, 8, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + if (ngx_list_init(&u->headers_in.trailers, r->pool, 2, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + +#if (NGX_HTTP_CACHE) + + if (r->cache) { + u->buffer.pos += r->cache->header_start; + u->buffer.last = u->buffer.pos; + } +#endif + } + + for ( ;; ) { + + n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last); + + if (n == NGX_AGAIN) { +#if 0 + ngx_add_timer(rev, u->read_timeout); +#endif + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + return; + } + + if (n == 0) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "upstream prematurely closed connection"); + } + + if (n == NGX_ERROR || n == 0) { + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); + return; + } + +#if (NGX_HTTP_SSL) + if (u->ssl && c->ssl == NULL) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "upstream prematurely sent response"); + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); + return; + } +#endif + + u->state->bytes_received += n; + + u->buffer.last += n; + +#if 0 + u->valid_header_in = 0; + + u->peer.cached = 0; +#endif + + u->response_received = 1; + +again: + + rc = u->process_header(r); + + if (rc == NGX_AGAIN) { + + if (u->buffer.last == u->buffer.end) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "upstream sent too big header"); + + ngx_http_upstream_next(r, u, + NGX_HTTP_UPSTREAM_FT_INVALID_HEADER); + return; + } + + continue; + } + + if (rc == NGX_HTTP_UPSTREAM_EARLY_HINTS) { + rc = ngx_http_upstream_process_early_hints(r, u); + + if (rc == NGX_OK) { + goto again; + } + } + + break; + } + + if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) { + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_INVALID_HEADER); + return; + } + + if (rc == NGX_ERROR) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + /* rc == NGX_OK */ + + u->state->header_time = ngx_current_msec - u->start_time; + + if (u->headers_in.status_n >= NGX_HTTP_SPECIAL_RESPONSE) { + + if (ngx_http_upstream_test_next(r, u) == NGX_OK) { + return; + } + + if (ngx_http_upstream_intercept_errors(r, u) == NGX_OK) { + return; + } + } + + if (u->peer.notify) { + u->peer.notify(&u->peer, u->peer.data, NGX_HTTP_UPSTREAM_NOTIFY_HEADER); + } + + if (ngx_http_upstream_process_headers(r, u) != NGX_OK) { + return; + } + + ngx_http_upstream_send_response(r, u); +} + + +static ngx_int_t +ngx_http_upstream_process_early_hints(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + u_char *p; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_table_elt_t *h, *ho; + ngx_connection_t *c; + + c = r->connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http upstream early hints"); + + if (u->conf->pass_early_hints) { + + u->early_hints_length += u->buffer.pos - u->buffer.start; + + if (u->early_hints_length <= (off_t) u->conf->buffer_size) { + + part = &u->headers_in.headers.part; + h = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + h = part->elts; + i = 0; + } + + if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash, + h[i].lowcase_key, h[i].key.len)) + { + continue; + } + + ho = ngx_list_push(&r->headers_out.headers); + if (ho == NULL) { + return NGX_ERROR; + } + + *ho = h[i]; + } + + if (ngx_http_send_early_hints(r) == NGX_ERROR) { + return NGX_ERROR; + } + + if (c->buffered) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { + return NGX_ERROR; + } + + r->write_event_handler = ngx_http_upstream_early_hints_writer; + } + + } else { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "upstream sent too big early hints"); + } + } + + ngx_http_clean_header(r); + + ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t)); + u->headers_in.content_length_n = -1; + u->headers_in.last_modified_time = -1; + + if (ngx_list_init(&u->headers_in.headers, r->pool, 8, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_list_init(&u->headers_in.trailers, r->pool, 2, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + p = u->buffer.pos; + + u->buffer.pos = u->buffer.start; + +#if (NGX_HTTP_CACHE) + + if (r->cache) { + u->buffer.pos += r->cache->header_start; + } + +#endif + + u->buffer.last = ngx_movemem(u->buffer.pos, p, u->buffer.last - p); + + return NGX_OK; +} + + +static void +ngx_http_upstream_early_hints_writer(ngx_http_request_t *r) +{ + ngx_connection_t *c; + ngx_http_upstream_t *u; + + c = r->connection; + u = r->upstream; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream early hints writer"); + + c->log->action = "sending early hints to client"; + + if (ngx_http_write_filter(r, NULL) == NGX_ERROR) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + if (!c->buffered) { + if (!u->store && !r->post_action && !u->conf->ignore_client_abort) { + r->write_event_handler = + ngx_http_upstream_wr_check_broken_connection; + + } else { + r->write_event_handler = ngx_http_request_empty_handler; + } + } + + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + } +} + + +static ngx_int_t +ngx_http_upstream_test_next(ngx_http_request_t *r, ngx_http_upstream_t *u) +{ + ngx_msec_t timeout; + ngx_uint_t status, mask; + ngx_http_upstream_next_t *un; + + status = u->headers_in.status_n; + + for (un = ngx_http_upstream_next_errors; un->status; un++) { + + if (status != un->status) { + continue; + } + + timeout = u->conf->next_upstream_timeout; + + if (u->request_sent + && (r->method & (NGX_HTTP_POST|NGX_HTTP_LOCK|NGX_HTTP_PATCH))) + { + mask = un->mask | NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT; + + } else { + mask = un->mask; + } + + if (u->peer.tries > 1 + && ((u->conf->next_upstream & mask) == mask) + && !(u->request_sent && r->request_body_no_buffering) + && !(timeout && ngx_current_msec - u->peer.start_time >= timeout)) + { + ngx_http_upstream_next(r, u, un->mask); + return NGX_OK; + } + +#if (NGX_HTTP_CACHE) + + if (u->cache_status == NGX_HTTP_CACHE_EXPIRED + && (u->conf->cache_use_stale & un->mask)) + { + ngx_int_t rc; + + rc = u->reinit_request(r); + + if (rc != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, rc); + return NGX_OK; + } + + u->cache_status = NGX_HTTP_CACHE_STALE; + rc = ngx_http_upstream_cache_send(r, u); + + if (rc == NGX_DONE) { + return NGX_OK; + } + + if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) { + rc = NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_http_upstream_finalize_request(r, u, rc); + return NGX_OK; + } + +#endif + + break; + } + +#if (NGX_HTTP_CACHE) + + if (status == NGX_HTTP_NOT_MODIFIED + && u->cache_status == NGX_HTTP_CACHE_EXPIRED + && u->conf->cache_revalidate) + { + time_t now, valid, updating, error; + ngx_int_t rc; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http upstream not modified"); + + now = ngx_time(); + + valid = r->cache->valid_sec; + updating = r->cache->updating_sec; + error = r->cache->error_sec; + + rc = u->reinit_request(r); + + if (rc != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, rc); + return NGX_OK; + } + + u->cache_status = NGX_HTTP_CACHE_REVALIDATED; + rc = ngx_http_upstream_cache_send(r, u); + + if (rc == NGX_DONE) { + return NGX_OK; + } + + if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) { + rc = NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (valid == 0) { + valid = r->cache->valid_sec; + updating = r->cache->updating_sec; + error = r->cache->error_sec; + } + + if (valid == 0) { + valid = ngx_http_file_cache_valid(u->conf->cache_valid, + u->headers_in.status_n); + if (valid) { + valid = now + valid; + } + } + + if (valid) { + r->cache->valid_sec = valid; + r->cache->updating_sec = updating; + r->cache->error_sec = error; + + r->cache->date = now; + + ngx_http_file_cache_update_header(r); + } + + ngx_http_upstream_finalize_request(r, u, rc); + return NGX_OK; + } + +#endif + + return NGX_DECLINED; +} + + +static ngx_int_t +ngx_http_upstream_intercept_errors(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + ngx_int_t status; + ngx_uint_t i; + ngx_table_elt_t *h, *ho, **ph; + ngx_http_err_page_t *err_page; + ngx_http_core_loc_conf_t *clcf; + + status = u->headers_in.status_n; + + if (status == NGX_HTTP_NOT_FOUND && u->conf->intercept_404) { + ngx_http_upstream_finalize_request(r, u, NGX_HTTP_NOT_FOUND); + return NGX_OK; + } + + if (!u->conf->intercept_errors) { + return NGX_DECLINED; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->error_pages == NULL) { + return NGX_DECLINED; + } + + err_page = clcf->error_pages->elts; + for (i = 0; i < clcf->error_pages->nelts; i++) { + + if (err_page[i].status == status) { + + if (status == NGX_HTTP_UNAUTHORIZED + && u->headers_in.www_authenticate) + { + h = u->headers_in.www_authenticate; + ph = &r->headers_out.www_authenticate; + + while (h) { + ho = ngx_list_push(&r->headers_out.headers); + + if (ho == NULL) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + + *ho = *h; + ho->next = NULL; + + *ph = ho; + ph = &ho->next; + + h = h->next; + } + } + +#if (NGX_HTTP_CACHE) + + if (r->cache) { + + if (u->headers_in.no_cache || u->headers_in.expired) { + u->cacheable = 0; + } + + if (u->cacheable) { + time_t valid; + + valid = r->cache->valid_sec; + + if (valid == 0) { + valid = ngx_http_file_cache_valid(u->conf->cache_valid, + status); + if (valid) { + r->cache->valid_sec = ngx_time() + valid; + } + } + + if (valid) { + r->cache->error = status; + } + } + + ngx_http_file_cache_free(r->cache, u->pipe->temp_file); + } +#endif + ngx_http_upstream_finalize_request(r, u, status); + + return NGX_OK; + } + } + + return NGX_DECLINED; +} + + +static ngx_int_t +ngx_http_upstream_test_connect(ngx_connection_t *c) +{ + int err; + socklen_t len; + +#if (NGX_HAVE_KQUEUE) + + if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { + if (c->write->pending_eof || c->read->pending_eof) { + if (c->write->pending_eof) { + err = c->write->kq_errno; + + } else { + err = c->read->kq_errno; + } + + c->log->action = "connecting to upstream"; + (void) ngx_connection_error(c, err, + "kevent() reported that connect() failed"); + return NGX_ERROR; + } + + } else +#endif + { + err = 0; + len = sizeof(int); + + /* + * BSDs and Linux return 0 and set a pending error in err + * Solaris returns -1 and sets errno + */ + + if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len) + == -1) + { + err = ngx_socket_errno; + } + + if (err) { + c->log->action = "connecting to upstream"; + (void) ngx_connection_error(c, err, "connect() failed"); + return NGX_ERROR; + } + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_process_headers(ngx_http_request_t *r, ngx_http_upstream_t *u) +{ + ngx_str_t uri, args; + ngx_uint_t i, flags; + ngx_list_part_t *part; + ngx_table_elt_t *h; + ngx_http_upstream_header_t *hh; + ngx_http_upstream_main_conf_t *umcf; + + umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); + + if (u->headers_in.no_cache || u->headers_in.expired) { + u->cacheable = 0; + } + + if (u->headers_in.x_accel_redirect + && !(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT)) + { + ngx_http_upstream_finalize_request(r, u, NGX_DECLINED); + + part = &u->headers_in.headers.part; + h = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + h = part->elts; + i = 0; + } + + if (h[i].hash == 0) { + continue; + } + + hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash, + h[i].lowcase_key, h[i].key.len); + + if (hh && hh->redirect) { + if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) { + ngx_http_finalize_request(r, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_DONE; + } + } + } + + uri = u->headers_in.x_accel_redirect->value; + + if (uri.data[0] == '@') { + ngx_http_named_location(r, &uri); + + } else { + ngx_str_null(&args); + flags = NGX_HTTP_LOG_UNSAFE; + + if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) { + ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND); + return NGX_DONE; + } + + if (r->method != NGX_HTTP_HEAD) { + r->method = NGX_HTTP_GET; + r->method_name = ngx_http_core_get_method; + } + + ngx_http_internal_redirect(r, &uri, &args); + } + + ngx_http_finalize_request(r, NGX_DONE); + return NGX_DONE; + } + + part = &u->headers_in.headers.part; + h = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + h = part->elts; + i = 0; + } + + if (h[i].hash == 0) { + continue; + } + + if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash, + h[i].lowcase_key, h[i].key.len)) + { + continue; + } + + hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash, + h[i].lowcase_key, h[i].key.len); + + if (hh) { + if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_DONE; + } + + continue; + } + + if (ngx_http_upstream_copy_header_line(r, &h[i], 0) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_DONE; + } + } + + if (r->headers_out.server && r->headers_out.server->value.data == NULL) { + r->headers_out.server->hash = 0; + } + + if (r->headers_out.date && r->headers_out.date->value.data == NULL) { + r->headers_out.date->hash = 0; + } + + r->headers_out.status = u->headers_in.status_n; + r->headers_out.status_line = u->headers_in.status_line; + + r->headers_out.content_length_n = u->headers_in.content_length_n; + + r->disable_not_modified = !u->cacheable; + + if (u->conf->force_ranges) { + r->allow_ranges = 1; + r->single_range = 1; + +#if (NGX_HTTP_CACHE) + if (r->cached) { + r->single_range = 0; + } +#endif + } + + u->length = -1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_process_trailers(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + ngx_uint_t i; + ngx_list_part_t *part; + ngx_table_elt_t *h, *ho; + + if (!u->conf->pass_trailers) { + return NGX_OK; + } + + part = &u->headers_in.trailers.part; + h = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + h = part->elts; + i = 0; + } + + if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash, + h[i].lowcase_key, h[i].key.len)) + { + continue; + } + + ho = ngx_list_push(&r->headers_out.trailers); + if (ho == NULL) { + return NGX_ERROR; + } + + *ho = h[i]; + } + + return NGX_OK; +} + + +static void +ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u) +{ + ssize_t n; + ngx_int_t rc; + ngx_event_pipe_t *p; + ngx_connection_t *c; + ngx_http_core_loc_conf_t *clcf; + + rc = ngx_http_send_header(r); + + if (rc == NGX_ERROR || rc > NGX_OK || r->post_action) { + ngx_http_upstream_finalize_request(r, u, rc); + return; + } + + u->header_sent = 1; + + if (u->upgrade) { + +#if (NGX_HTTP_CACHE) + + if (r->cache) { + ngx_http_file_cache_free(r->cache, u->pipe->temp_file); + } + +#endif + + ngx_http_upstream_upgrade(r, u); + return; + } + + c = r->connection; + + if (r->header_only) { + + if (!u->buffering) { + ngx_http_upstream_finalize_request(r, u, rc); + return; + } + + if (!u->cacheable && !u->store) { + ngx_http_upstream_finalize_request(r, u, rc); + return; + } + + u->pipe->downstream_error = 1; + } + + if (r->request_body && r->request_body->temp_file + && r == r->main && !r->preserve_body + && !u->conf->preserve_output) + { + ngx_pool_run_cleanup_file(r->pool, r->request_body->temp_file->file.fd); + r->request_body->temp_file->file.fd = NGX_INVALID_FILE; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (!u->buffering) { + +#if (NGX_HTTP_CACHE) + + if (r->cache) { + ngx_http_file_cache_free(r->cache, u->pipe->temp_file); + } + +#endif + + if (u->input_filter == NULL) { + u->input_filter_init = ngx_http_upstream_non_buffered_filter_init; + u->input_filter = ngx_http_upstream_non_buffered_filter; + u->input_filter_ctx = r; + } + + u->read_event_handler = ngx_http_upstream_process_non_buffered_upstream; + r->write_event_handler = + ngx_http_upstream_process_non_buffered_downstream; + + r->limit_rate = 0; + r->limit_rate_set = 1; + + if (u->input_filter_init(u->input_filter_ctx) == NGX_ERROR) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + + if (clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + + n = u->buffer.last - u->buffer.pos; + + if (n) { + u->buffer.last = u->buffer.pos; + + u->state->response_length += n; + + if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + + ngx_http_upstream_process_non_buffered_downstream(r); + + } else { + u->buffer.pos = u->buffer.start; + u->buffer.last = u->buffer.start; + + if (ngx_http_send_special(r, NGX_HTTP_FLUSH) == NGX_ERROR) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + + ngx_http_upstream_process_non_buffered_upstream(r, u); + } + + return; + } + + /* TODO: preallocate event_pipe bufs, look "Content-Length" */ + +#if (NGX_HTTP_CACHE) + + if (r->cache && r->cache->file.fd != NGX_INVALID_FILE) { + ngx_pool_run_cleanup_file(r->pool, r->cache->file.fd); + r->cache->file.fd = NGX_INVALID_FILE; + } + + switch (ngx_http_test_predicates(r, u->conf->no_cache)) { + + case NGX_ERROR: + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + + case NGX_DECLINED: + u->cacheable = 0; + break; + + default: /* NGX_OK */ + + if (u->cache_status == NGX_HTTP_CACHE_BYPASS) { + + /* create cache if previously bypassed */ + + if (ngx_http_file_cache_create(r) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + } + + break; + } + + if (u->cacheable) { + time_t now, valid; + + now = ngx_time(); + + valid = r->cache->valid_sec; + + if (valid == 0) { + valid = ngx_http_file_cache_valid(u->conf->cache_valid, + u->headers_in.status_n); + if (valid) { + r->cache->valid_sec = now + valid; + } + } + + if (valid) { + r->cache->date = now; + r->cache->body_start = (u_short) (u->buffer.pos - u->buffer.start); + + if (u->headers_in.status_n == NGX_HTTP_OK + || u->headers_in.status_n == NGX_HTTP_PARTIAL_CONTENT) + { + r->cache->last_modified = u->headers_in.last_modified_time; + + if (u->headers_in.etag) { + r->cache->etag = u->headers_in.etag->value; + + } else { + ngx_str_null(&r->cache->etag); + } + + } else { + r->cache->last_modified = -1; + ngx_str_null(&r->cache->etag); + } + + if (ngx_http_file_cache_set_header(r, u->buffer.start) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + + } else { + u->cacheable = 0; + } + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http cacheable: %d", u->cacheable); + + if (u->cacheable == 0 && r->cache) { + ngx_http_file_cache_free(r->cache, u->pipe->temp_file); + } + + if (r->header_only && !u->cacheable && !u->store) { + ngx_http_upstream_finalize_request(r, u, 0); + return; + } + +#endif + + p = u->pipe; + + p->output_filter = ngx_http_upstream_output_filter; + p->output_ctx = r; + p->tag = u->output.tag; + p->bufs = u->conf->bufs; + p->busy_size = u->conf->busy_buffers_size; + p->upstream = u->peer.connection; + p->downstream = c; + p->pool = r->pool; + p->log = c->log; + p->limit_rate = ngx_http_complex_value_size(r, u->conf->limit_rate, 0); + p->start_sec = ngx_time(); + + p->cacheable = u->cacheable || u->store; + + p->temp_file = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); + if (p->temp_file == NULL) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + + p->temp_file->file.fd = NGX_INVALID_FILE; + p->temp_file->file.log = c->log; + p->temp_file->path = u->conf->temp_path; + p->temp_file->pool = r->pool; + + if (p->cacheable) { + p->temp_file->persistent = 1; + +#if (NGX_HTTP_CACHE) + if (r->cache && !r->cache->file_cache->use_temp_path) { + p->temp_file->path = r->cache->file_cache->path; + p->temp_file->file.name = r->cache->file.name; + } +#endif + + } else { + p->temp_file->log_level = NGX_LOG_WARN; + p->temp_file->warn = "an upstream response is buffered " + "to a temporary file"; + } + + p->max_temp_file_size = u->conf->max_temp_file_size; + p->temp_file_write_size = u->conf->temp_file_write_size; + +#if (NGX_THREADS) + if (clcf->aio == NGX_HTTP_AIO_THREADS && clcf->aio_write) { + p->thread_handler = ngx_http_upstream_thread_handler; + p->thread_ctx = r; + } +#endif + + p->preread_bufs = ngx_alloc_chain_link(r->pool); + if (p->preread_bufs == NULL) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + + p->preread_bufs->buf = &u->buffer; + p->preread_bufs->next = NULL; + u->buffer.recycled = 1; + + p->preread_size = u->buffer.last - u->buffer.pos; + + if (u->cacheable) { + + p->buf_to_file = ngx_calloc_buf(r->pool); + if (p->buf_to_file == NULL) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + + p->buf_to_file->start = u->buffer.start; + p->buf_to_file->pos = u->buffer.start; + p->buf_to_file->last = u->buffer.pos; + p->buf_to_file->temporary = 1; + } + + if (ngx_event_flags & NGX_USE_IOCP_EVENT) { + /* the posted aio operation may corrupt a shadow buffer */ + p->single_buf = 1; + } + + /* TODO: p->free_bufs = 0 if use ngx_create_chain_of_bufs() */ + p->free_bufs = 1; + + /* + * event_pipe would do u->buffer.last += p->preread_size + * as though these bytes were read + */ + u->buffer.last = u->buffer.pos; + + if (u->conf->cyclic_temp_file) { + + /* + * we need to disable the use of sendfile() if we use cyclic temp file + * because the writing a new data may interfere with sendfile() + * that uses the same kernel file pages (at least on FreeBSD) + */ + + p->cyclic_temp_file = 1; + c->sendfile = 0; + + } else { + p->cyclic_temp_file = 0; + } + + p->read_timeout = u->conf->read_timeout; + p->send_timeout = clcf->send_timeout; + p->send_lowat = clcf->send_lowat; + + p->length = -1; + + if (u->input_filter_init + && u->input_filter_init(p->input_ctx) != NGX_OK) + { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + + u->read_event_handler = ngx_http_upstream_process_upstream; + r->write_event_handler = ngx_http_upstream_process_downstream; + + ngx_http_upstream_process_upstream(r, u); +} + + +static void +ngx_http_upstream_upgrade(ngx_http_request_t *r, ngx_http_upstream_t *u) +{ + ngx_connection_t *c; + ngx_http_core_loc_conf_t *clcf; + + c = r->connection; + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + /* TODO: prevent upgrade if not requested or not possible */ + + if (r != r->main) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "connection upgrade in subrequest"); + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + + r->keepalive = 0; + c->log->action = "proxying upgraded connection"; + + u->read_event_handler = ngx_http_upstream_upgraded_read_upstream; + u->write_event_handler = ngx_http_upstream_upgraded_write_upstream; + r->read_event_handler = ngx_http_upstream_upgraded_read_downstream; + r->write_event_handler = ngx_http_upstream_upgraded_write_downstream; + + if (clcf->tcp_nodelay) { + + if (ngx_tcp_nodelay(c) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + + if (ngx_tcp_nodelay(u->peer.connection) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + } + + if (ngx_http_send_special(r, NGX_HTTP_FLUSH) == NGX_ERROR) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + + if (u->peer.connection->read->ready + || u->buffer.pos != u->buffer.last) + { + ngx_post_event(c->read, &ngx_posted_events); + ngx_http_upstream_process_upgraded(r, 1, 1); + return; + } + + ngx_http_upstream_process_upgraded(r, 0, 1); +} + + +static void +ngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r) +{ + ngx_http_upstream_process_upgraded(r, 0, 0); +} + + +static void +ngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r) +{ + ngx_http_upstream_process_upgraded(r, 1, 1); +} + + +static void +ngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + ngx_http_upstream_process_upgraded(r, 1, 0); +} + + +static void +ngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + ngx_http_upstream_process_upgraded(r, 0, 1); +} + + +static void +ngx_http_upstream_process_upgraded(ngx_http_request_t *r, + ngx_uint_t from_upstream, ngx_uint_t do_write) +{ + size_t size; + ssize_t n; + ngx_buf_t *b; + ngx_uint_t flags; + ngx_connection_t *c, *downstream, *upstream, *dst, *src; + ngx_http_upstream_t *u; + ngx_http_core_loc_conf_t *clcf; + + c = r->connection; + u = r->upstream; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream process upgraded, fu:%ui", from_upstream); + + downstream = c; + upstream = u->peer.connection; + + if (downstream->write->timedout) { + c->timedout = 1; + ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); + ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT); + return; + } + + if (upstream->read->timedout || upstream->write->timedout) { + ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); + ngx_http_upstream_finalize_request(r, u, NGX_HTTP_GATEWAY_TIME_OUT); + return; + } + + if (from_upstream) { + src = upstream; + dst = downstream; + b = &u->buffer; + + } else { + src = downstream; + dst = upstream; + b = &u->from_client; + + if (r->header_in->last > r->header_in->pos) { + b = r->header_in; + b->end = b->last; + do_write = 1; + } + + if (b->start == NULL) { + b->start = ngx_palloc(r->pool, u->conf->buffer_size); + if (b->start == NULL) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + + b->pos = b->start; + b->last = b->start; + b->end = b->start + u->conf->buffer_size; + b->temporary = 1; + b->tag = u->output.tag; + } + } + + for ( ;; ) { + + if (do_write) { + + size = b->last - b->pos; + + if (size && dst->write->ready) { + + n = dst->send(dst, b->pos, size); + + if (n == NGX_ERROR) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + + if (n > 0) { + b->pos += n; + + if (b->pos == b->last) { + b->pos = b->start; + b->last = b->start; + } + } + } + } + + size = b->end - b->last; + + if (size && src->read->ready) { + + n = src->recv(src, b->last, size); + + if (n == NGX_AGAIN || n == 0) { + break; + } + + if (n > 0) { + do_write = 1; + b->last += n; + + if (from_upstream) { + u->state->bytes_received += n; + } + + continue; + } + + if (n == NGX_ERROR) { + src->read->eof = 1; + } + } + + break; + } + + if ((upstream->read->eof && u->buffer.pos == u->buffer.last) + || (downstream->read->eof && u->from_client.pos == u->from_client.last) + || (downstream->read->eof && upstream->read->eof)) + { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream upgraded done"); + ngx_http_upstream_finalize_request(r, u, 0); + return; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (ngx_handle_write_event(upstream->write, u->conf->send_lowat) + != NGX_OK) + { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + + if (upstream->write->active && !upstream->write->ready) { + ngx_add_timer(upstream->write, u->conf->send_timeout); + + } else if (upstream->write->timer_set) { + ngx_del_timer(upstream->write); + } + + if (upstream->read->eof || upstream->read->error) { + flags = NGX_CLOSE_EVENT; + + } else { + flags = 0; + } + + if (ngx_handle_read_event(upstream->read, flags) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + + if (upstream->read->active && !upstream->read->ready) { + ngx_add_timer(upstream->read, u->conf->read_timeout); + + } else if (upstream->read->timer_set) { + ngx_del_timer(upstream->read); + } + + if (ngx_handle_write_event(downstream->write, clcf->send_lowat) + != NGX_OK) + { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + + if (downstream->read->eof || downstream->read->error) { + flags = NGX_CLOSE_EVENT; + + } else { + flags = 0; + } + + if (ngx_handle_read_event(downstream->read, flags) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + + if (downstream->write->active && !downstream->write->ready) { + ngx_add_timer(downstream->write, clcf->send_timeout); + + } else if (downstream->write->timer_set) { + ngx_del_timer(downstream->write); + } +} + + +static void +ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r) +{ + ngx_event_t *wev; + ngx_connection_t *c; + ngx_http_upstream_t *u; + + c = r->connection; + u = r->upstream; + wev = c->write; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream process non buffered downstream"); + + c->log->action = "sending to client"; + + if (wev->timedout) { + c->timedout = 1; + ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); + ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT); + return; + } + + ngx_http_upstream_process_non_buffered_request(r, 1); +} + + +static void +ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + ngx_connection_t *c; + + c = u->peer.connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream process non buffered upstream"); + + c->log->action = "reading upstream"; + + if (c->read->timedout) { + ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); + ngx_http_upstream_finalize_request(r, u, NGX_HTTP_GATEWAY_TIME_OUT); + return; + } + + ngx_http_upstream_process_non_buffered_request(r, 0); +} + + +static void +ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r, + ngx_uint_t do_write) +{ + size_t size; + ssize_t n; + ngx_buf_t *b; + ngx_int_t rc; + ngx_uint_t flags; + ngx_connection_t *downstream, *upstream; + ngx_http_upstream_t *u; + ngx_http_core_loc_conf_t *clcf; + + u = r->upstream; + downstream = r->connection; + upstream = u->peer.connection; + + b = &u->buffer; + + do_write = do_write || u->length == 0; + + for ( ;; ) { + + if (do_write) { + + if (u->out_bufs || u->busy_bufs || downstream->buffered) { + rc = ngx_http_output_filter(r, u->out_bufs); + + if (rc == NGX_ERROR) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + + ngx_chain_update_chains(r->pool, &u->free_bufs, &u->busy_bufs, + &u->out_bufs, u->output.tag); + } + + if (u->busy_bufs == NULL) { + + if (u->length == 0 + || (upstream->read->eof && u->length == -1)) + { + ngx_http_upstream_finalize_request(r, u, 0); + return; + } + + if (upstream->read->eof) { + ngx_log_error(NGX_LOG_ERR, upstream->log, 0, + "upstream prematurely closed connection"); + + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_BAD_GATEWAY); + return; + } + + if (upstream->read->error || u->error) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_BAD_GATEWAY); + return; + } + + b->pos = b->start; + b->last = b->start; + } + } + + size = b->end - b->last; + + if (size && upstream->read->ready) { + + n = upstream->recv(upstream, b->last, size); + + if (n == NGX_AGAIN) { + break; + } + + if (n > 0) { + u->state->bytes_received += n; + u->state->response_length += n; + + if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + } + + do_write = 1; + + continue; + } + + break; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (downstream->data == r) { + if (ngx_handle_write_event(downstream->write, clcf->send_lowat) + != NGX_OK) + { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + } + + if (downstream->write->active && !downstream->write->ready) { + ngx_add_timer(downstream->write, clcf->send_timeout); + + } else if (downstream->write->timer_set) { + ngx_del_timer(downstream->write); + } + + if (upstream->read->eof || upstream->read->error) { + flags = NGX_CLOSE_EVENT; + + } else { + flags = 0; + } + + if (ngx_handle_read_event(upstream->read, flags) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + + if (upstream->read->active && !upstream->read->ready) { + ngx_add_timer(upstream->read, u->conf->read_timeout); + + } else if (upstream->read->timer_set) { + ngx_del_timer(upstream->read); + } +} + + +ngx_int_t +ngx_http_upstream_non_buffered_filter_init(void *data) +{ + return NGX_OK; +} + + +ngx_int_t +ngx_http_upstream_non_buffered_filter(void *data, ssize_t bytes) +{ + ngx_http_request_t *r = data; + + ngx_buf_t *b; + ngx_chain_t *cl, **ll; + ngx_http_upstream_t *u; + + u = r->upstream; + + if (u->length == 0) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "upstream sent more data than specified in " + "\"Content-Length\" header"); + return NGX_OK; + } + + for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) { + ll = &cl->next; + } + + cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs); + if (cl == NULL) { + return NGX_ERROR; + } + + *ll = cl; + + cl->buf->flush = 1; + cl->buf->memory = 1; + + b = &u->buffer; + + cl->buf->pos = b->last; + b->last += bytes; + cl->buf->last = b->last; + cl->buf->tag = u->output.tag; + + if (u->length == -1) { + return NGX_OK; + } + + if (bytes > u->length) { + + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "upstream sent more data than specified in " + "\"Content-Length\" header"); + + cl->buf->last = cl->buf->pos + u->length; + u->length = 0; + + return NGX_OK; + } + + u->length -= bytes; + + return NGX_OK; +} + + +#if (NGX_THREADS) + +static ngx_int_t +ngx_http_upstream_thread_handler(ngx_thread_task_t *task, ngx_file_t *file) +{ + ngx_str_t name; + ngx_event_pipe_t *p; + ngx_connection_t *c; + ngx_thread_pool_t *tp; + ngx_http_request_t *r; + ngx_http_core_loc_conf_t *clcf; + + r = file->thread_ctx; + p = r->upstream->pipe; + + if (r->aio) { + /* + * tolerate sendfile() calls if another operation is already + * running; this can happen due to subrequests, multiple calls + * of the next body filter from a filter, or in HTTP/2 due to + * a write event on the main connection + */ + + c = r->connection; + +#if (NGX_HTTP_V2) + if (r->stream) { + c = r->stream->connection->connection; + } +#endif + + if (task == c->sendfile_task) { + return NGX_OK; + } + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + tp = clcf->thread_pool; + + if (tp == NULL) { + if (ngx_http_complex_value(r, clcf->thread_pool_value, &name) + != NGX_OK) + { + return NGX_ERROR; + } + + tp = ngx_thread_pool_get((ngx_cycle_t *) ngx_cycle, &name); + + if (tp == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "thread pool \"%V\" not found", &name); + return NGX_ERROR; + } + } + + task->event.data = r; + task->event.handler = ngx_http_upstream_thread_event_handler; + + if (ngx_thread_task_post(tp, task) != NGX_OK) { + return NGX_ERROR; + } + + r->main->blocked++; + r->aio = 1; + p->aio = 1; + + ngx_add_timer(&task->event, 60000); + + return NGX_OK; +} + + +static void +ngx_http_upstream_thread_event_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; + ngx_http_request_t *r; + + r = ev->data; + c = r->connection; + + ngx_http_set_log_request(c->log, r); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream thread: \"%V?%V\"", &r->uri, &r->args); + + if (ev->timedout) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "thread operation took too long"); + ev->timedout = 0; + return; + } + + if (ev->timer_set) { + ngx_del_timer(ev); + } + + r->main->blocked--; + r->aio = 0; + +#if (NGX_HTTP_V2) + + if (r->stream) { + /* + * for HTTP/2, update write event to make sure processing will + * reach the main connection to handle sendfile() in threads + */ + + c->write->ready = 1; + c->write->active = 0; + } + +#endif + + if (r->done || r->main->terminated) { + /* + * trigger connection event handler if the subrequest was + * already finalized (this can happen if the handler is used + * for sendfile() in threads), or if the request was terminated + */ + + c->write->handler(c->write); + + } else { + r->write_event_handler(r); + ngx_http_run_posted_requests(c); + } +} + +#endif + + +static ngx_int_t +ngx_http_upstream_output_filter(void *data, ngx_chain_t *chain) +{ + ngx_int_t rc; + ngx_event_pipe_t *p; + ngx_http_request_t *r; + + r = data; + p = r->upstream->pipe; + + rc = ngx_http_output_filter(r, chain); + + p->aio = r->aio; + + return rc; +} + + +static void +ngx_http_upstream_process_downstream(ngx_http_request_t *r) +{ + ngx_event_t *wev; + ngx_connection_t *c; + ngx_event_pipe_t *p; + ngx_http_upstream_t *u; + + c = r->connection; + u = r->upstream; + p = u->pipe; + wev = c->write; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream process downstream"); + + c->log->action = "sending to client"; + +#if (NGX_THREADS) + p->aio = r->aio; +#endif + + if (wev->timedout) { + + p->downstream_error = 1; + c->timedout = 1; + ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); + + } else { + + if (wev->delayed) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http downstream delayed"); + + if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + } + + return; + } + + if (ngx_event_pipe(p, 1) == NGX_ABORT) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + } + + ngx_http_upstream_process_request(r, u); +} + + +static void +ngx_http_upstream_process_upstream(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + ngx_event_t *rev; + ngx_event_pipe_t *p; + ngx_connection_t *c; + + c = u->peer.connection; + p = u->pipe; + rev = c->read; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream process upstream"); + + c->log->action = "reading upstream"; + + if (rev->timedout) { + + p->upstream_error = 1; + ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); + + } else { + + if (rev->delayed) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http upstream delayed"); + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + } + + return; + } + + if (ngx_event_pipe(p, 0) == NGX_ABORT) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + } + + ngx_http_upstream_process_request(r, u); +} + + +static void +ngx_http_upstream_process_request(ngx_http_request_t *r, + ngx_http_upstream_t *u) +{ + ngx_temp_file_t *tf; + ngx_event_pipe_t *p; + + p = u->pipe; + +#if (NGX_THREADS) + + if (p->writing && !p->aio) { + + /* + * make sure to call ngx_event_pipe() + * if there is an incomplete aio write + */ + + if (ngx_event_pipe(p, 1) == NGX_ABORT) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + return; + } + } + + if (p->writing) { + return; + } + +#endif + + if (u->peer.connection) { + + if (u->store) { + + if (p->upstream_eof || p->upstream_done) { + + tf = p->temp_file; + + if (u->headers_in.status_n == NGX_HTTP_OK + && (p->upstream_done || p->length == -1) + && (u->headers_in.content_length_n == -1 + || u->headers_in.content_length_n == tf->offset)) + { + ngx_http_upstream_store(r, u); + } + } + } + +#if (NGX_HTTP_CACHE) + + if (u->cacheable) { + + if (p->upstream_done) { + ngx_http_file_cache_update(r, p->temp_file); + + } else if (p->upstream_eof) { + + tf = p->temp_file; + + if (p->length == -1 + && (u->headers_in.content_length_n == -1 + || u->headers_in.content_length_n + == tf->offset - (off_t) r->cache->body_start)) + { + ngx_http_file_cache_update(r, tf); + + } else { + ngx_http_file_cache_free(r->cache, tf); + } + + } else if (p->upstream_error) { + ngx_http_file_cache_free(r->cache, p->temp_file); + } + } + +#endif + + if (p->upstream_done || p->upstream_eof || p->upstream_error) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http upstream exit: %p", p->out); + + if (p->upstream_done + || (p->upstream_eof && p->length == -1)) + { + ngx_http_upstream_finalize_request(r, u, 0); + return; + } + + if (p->upstream_eof) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream prematurely closed connection"); + } + + ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY); + return; + } + } + + if (p->downstream_error) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http upstream downstream error"); + + if (!u->cacheable && !u->store && u->peer.connection) { + ngx_http_upstream_finalize_request(r, u, NGX_ERROR); + } + } +} + + +static void +ngx_http_upstream_store(ngx_http_request_t *r, ngx_http_upstream_t *u) +{ + size_t root; + time_t lm; + ngx_str_t path; + ngx_temp_file_t *tf; + ngx_ext_rename_file_t ext; + + tf = u->pipe->temp_file; + + if (tf->file.fd == NGX_INVALID_FILE) { + + /* create file for empty 200 response */ + + tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); + if (tf == NULL) { + return; + } + + tf->file.fd = NGX_INVALID_FILE; + tf->file.log = r->connection->log; + tf->path = u->conf->temp_path; + tf->pool = r->pool; + tf->persistent = 1; + + if (ngx_create_temp_file(&tf->file, tf->path, tf->pool, + tf->persistent, tf->clean, tf->access) + != NGX_OK) + { + return; + } + + u->pipe->temp_file = tf; + } + + ext.access = u->conf->store_access; + ext.path_access = u->conf->store_access; + ext.time = -1; + ext.create_path = 1; + ext.delete_file = 1; + ext.log = r->connection->log; + + if (u->headers_in.last_modified) { + + lm = ngx_parse_http_time(u->headers_in.last_modified->value.data, + u->headers_in.last_modified->value.len); + + if (lm != NGX_ERROR) { + ext.time = lm; + ext.fd = tf->file.fd; + } + } + + if (u->conf->store_lengths == NULL) { + + if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) { + return; + } + + } else { + if (ngx_http_script_run(r, &path, u->conf->store_lengths->elts, 0, + u->conf->store_values->elts) + == NULL) + { + return; + } + } + + path.len--; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "upstream stores \"%s\" to \"%s\"", + tf->file.name.data, path.data); + + if (path.len == 0) { + return; + } + + (void) ngx_ext_rename_file(&tf->file.name, &path, &ext); + + u->store = 0; +} + + +static void +ngx_http_upstream_dummy_handler(ngx_http_request_t *r, ngx_http_upstream_t *u) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http upstream dummy handler"); +} + + +static void +ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u, + ngx_uint_t ft_type) +{ + ngx_msec_t timeout; + ngx_uint_t status, state; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http next upstream, %xi", ft_type); + + if (u->peer.sockaddr) { + + if (u->peer.connection) { + u->state->bytes_sent = u->peer.connection->sent; + } + + if (ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_403 + || ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_404) + { + state = NGX_PEER_NEXT; + + } else { + state = NGX_PEER_FAILED; + } + + u->peer.free(&u->peer, u->peer.data, state); + u->peer.sockaddr = NULL; + +#if (NGX_HTTP_UPSTREAM_SID) + u->peer.sid = NULL; +#endif + } + + if (ft_type == NGX_HTTP_UPSTREAM_FT_TIMEOUT) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ETIMEDOUT, + "upstream timed out"); + } + + if (u->peer.cached && ft_type == NGX_HTTP_UPSTREAM_FT_ERROR) { + /* TODO: inform balancer instead */ + u->peer.tries++; + } + + switch (ft_type) { + + case NGX_HTTP_UPSTREAM_FT_TIMEOUT: + case NGX_HTTP_UPSTREAM_FT_HTTP_504: + status = NGX_HTTP_GATEWAY_TIME_OUT; + break; + + case NGX_HTTP_UPSTREAM_FT_HTTP_500: + status = NGX_HTTP_INTERNAL_SERVER_ERROR; + break; + + case NGX_HTTP_UPSTREAM_FT_HTTP_503: + status = NGX_HTTP_SERVICE_UNAVAILABLE; + break; + + case NGX_HTTP_UPSTREAM_FT_HTTP_403: + status = NGX_HTTP_FORBIDDEN; + break; + + case NGX_HTTP_UPSTREAM_FT_HTTP_404: + status = NGX_HTTP_NOT_FOUND; + break; + + case NGX_HTTP_UPSTREAM_FT_HTTP_429: + status = NGX_HTTP_TOO_MANY_REQUESTS; + break; + + /* + * NGX_HTTP_UPSTREAM_FT_BUSY_LOCK and NGX_HTTP_UPSTREAM_FT_MAX_WAITING + * never reach here + */ + + default: + status = NGX_HTTP_BAD_GATEWAY; + } + + if (r->connection->error) { + ngx_http_upstream_finalize_request(r, u, + NGX_HTTP_CLIENT_CLOSED_REQUEST); + return; + } + + u->state->status = status; + + timeout = u->conf->next_upstream_timeout; + + if (u->request_sent + && (r->method & (NGX_HTTP_POST|NGX_HTTP_LOCK|NGX_HTTP_PATCH))) + { + ft_type |= NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT; + } + + if (u->peer.tries == 0 + || ((u->conf->next_upstream & ft_type) != ft_type) + || (u->request_sent && r->request_body_no_buffering) + || (timeout && ngx_current_msec - u->peer.start_time >= timeout)) + { +#if (NGX_HTTP_CACHE) + + if (u->cache_status == NGX_HTTP_CACHE_EXPIRED + && ((u->conf->cache_use_stale & ft_type) || r->cache->stale_error)) + { + ngx_int_t rc; + + rc = u->reinit_request(r); + + if (rc != NGX_OK) { + ngx_http_upstream_finalize_request(r, u, rc); + return; + } + + u->cache_status = NGX_HTTP_CACHE_STALE; + rc = ngx_http_upstream_cache_send(r, u); + + if (rc == NGX_DONE) { + return; + } + + if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) { + rc = NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_http_upstream_finalize_request(r, u, rc); + return; + } +#endif + + ngx_http_upstream_finalize_request(r, u, status); + return; + } + + if (u->peer.connection) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "close http upstream connection: %d", + u->peer.connection->fd); +#if (NGX_HTTP_SSL) + + if (u->peer.connection->ssl) { + u->peer.connection->ssl->no_wait_shutdown = 1; + u->peer.connection->ssl->no_send_shutdown = 1; + + (void) ngx_ssl_shutdown(u->peer.connection); + } +#endif + + if (u->peer.connection->pool) { + ngx_destroy_pool(u->peer.connection->pool); + } + + ngx_close_connection(u->peer.connection); + u->peer.connection = NULL; + } + + ngx_http_upstream_connect(r, u); +} + + +static void +ngx_http_upstream_cleanup(void *data) +{ + ngx_http_request_t *r = data; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "cleanup http upstream request: \"%V\"", &r->uri); + + ngx_http_upstream_finalize_request(r, r->upstream, NGX_DONE); +} + + +static void +ngx_http_upstream_finalize_request(ngx_http_request_t *r, + ngx_http_upstream_t *u, ngx_int_t rc) +{ + ngx_uint_t flush; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "finalize http upstream request: %i", rc); + + if (u->cleanup == NULL) { + /* the request was already finalized */ + ngx_http_finalize_request(r, NGX_DONE); + return; + } + + *u->cleanup = NULL; + u->cleanup = NULL; + + if (u->resolved && u->resolved->ctx) { + ngx_resolve_name_done(u->resolved->ctx); + u->resolved->ctx = NULL; + } + + if (u->state && u->state->response_time == (ngx_msec_t) -1) { + u->state->response_time = ngx_current_msec - u->start_time; + + if (u->pipe && u->pipe->read_length) { + u->state->bytes_received += u->pipe->read_length + - u->pipe->preread_size; + u->state->response_length = u->pipe->read_length; + } + + if (u->peer.connection) { + u->state->bytes_sent = u->peer.connection->sent; + } + } + + u->finalize_request(r, rc); + + if (u->peer.free && u->peer.sockaddr) { + u->peer.free(&u->peer, u->peer.data, 0); + u->peer.sockaddr = NULL; + +#if (NGX_HTTP_UPSTREAM_SID) + u->peer.sid = NULL; +#endif + } + + if (u->peer.connection) { + +#if (NGX_HTTP_SSL) + + /* TODO: do not shutdown persistent connection */ + + if (u->peer.connection->ssl) { + + /* + * We send the "close notify" shutdown alert to the upstream only + * and do not wait its "close notify" shutdown alert. + * It is acceptable according to the TLS standard. + */ + + u->peer.connection->ssl->no_wait_shutdown = 1; + + (void) ngx_ssl_shutdown(u->peer.connection); + } +#endif + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "close http upstream connection: %d", + u->peer.connection->fd); + + if (u->peer.connection->pool) { + ngx_destroy_pool(u->peer.connection->pool); + } + + ngx_close_connection(u->peer.connection); + } + + u->peer.connection = NULL; + + if (u->pipe) { + u->pipe->upstream = NULL; + } + + if (u->pipe && u->pipe->temp_file) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http upstream temp fd: %d", + u->pipe->temp_file->file.fd); + } + + if (u->store && u->pipe && u->pipe->temp_file + && u->pipe->temp_file->file.fd != NGX_INVALID_FILE) + { + if (ngx_delete_file(u->pipe->temp_file->file.name.data) + == NGX_FILE_ERROR) + { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", + u->pipe->temp_file->file.name.data); + } + } + +#if (NGX_HTTP_CACHE) + + if (r->cache) { + + if (u->cacheable) { + + if (rc == NGX_HTTP_BAD_GATEWAY || rc == NGX_HTTP_GATEWAY_TIME_OUT) { + time_t valid; + + valid = ngx_http_file_cache_valid(u->conf->cache_valid, rc); + + if (valid) { + r->cache->valid_sec = ngx_time() + valid; + r->cache->error = rc; + } + } + } + + ngx_http_file_cache_free(r->cache, u->pipe->temp_file); + } + +#endif + + r->read_event_handler = ngx_http_block_reading; + + if (rc == NGX_DECLINED) { + return; + } + + r->connection->log->action = "sending to client"; + + if (!u->header_sent + || rc == NGX_HTTP_REQUEST_TIME_OUT + || rc == NGX_HTTP_CLIENT_CLOSED_REQUEST) + { + ngx_http_finalize_request(r, rc); + return; + } + + flush = 0; + + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + rc = NGX_ERROR; + flush = 1; + } + + if (r->header_only + || (u->pipe && u->pipe->downstream_error)) + { + ngx_http_finalize_request(r, rc); + return; + } + + if (rc == 0) { + + if (ngx_http_upstream_process_trailers(r, u) != NGX_OK) { + ngx_http_finalize_request(r, NGX_ERROR); + return; + } + + rc = ngx_http_send_special(r, NGX_HTTP_LAST); + + } else if (flush) { + r->keepalive = 0; + rc = ngx_http_send_special(r, NGX_HTTP_FLUSH); + } + + ngx_http_finalize_request(r, rc); +} + + +static ngx_int_t +ngx_http_upstream_process_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + ngx_table_elt_t **ph; + + ph = (ngx_table_elt_t **) ((char *) &r->upstream->headers_in + offset); + + if (*ph) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "upstream sent duplicate header line: \"%V: %V\", " + "previous value: \"%V: %V\", ignored", + &h->key, &h->value, + &(*ph)->key, &(*ph)->value); + h->hash = 0; + return NGX_OK; + } + + *ph = h; + h->next = NULL; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_process_multi_header_lines(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset) +{ + ngx_table_elt_t **ph; + + ph = (ngx_table_elt_t **) ((char *) &r->upstream->headers_in + offset); + + while (*ph) { ph = &(*ph)->next; } + + *ph = h; + h->next = NULL; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_ignore_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_process_content_length(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset) +{ + ngx_http_upstream_t *u; + + u = r->upstream; + + if (u->headers_in.content_length) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent duplicate header line: \"%V: %V\", " + "previous value: \"%V: %V\"", + &h->key, &h->value, + &u->headers_in.content_length->key, + &u->headers_in.content_length->value); + return NGX_HTTP_UPSTREAM_INVALID_HEADER; + } + + if (u->headers_in.transfer_encoding) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent \"Content-Length\" and " + "\"Transfer-Encoding\" headers at the same time"); + return NGX_HTTP_UPSTREAM_INVALID_HEADER; + } + + h->next = NULL; + u->headers_in.content_length = h; + u->headers_in.content_length_n = ngx_atoof(h->value.data, h->value.len); + + if (u->headers_in.content_length_n == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent invalid \"Content-Length\" header: " + "\"%V: %V\"", &h->key, &h->value); + return NGX_HTTP_UPSTREAM_INVALID_HEADER; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_process_last_modified(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset) +{ + ngx_http_upstream_t *u; + + u = r->upstream; + + if (u->headers_in.last_modified) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "upstream sent duplicate header line: \"%V: %V\", " + "previous value: \"%V: %V\", ignored", + &h->key, &h->value, + &u->headers_in.last_modified->key, + &u->headers_in.last_modified->value); + h->hash = 0; + return NGX_OK; + } + + h->next = NULL; + u->headers_in.last_modified = h; + u->headers_in.last_modified_time = ngx_parse_http_time(h->value.data, + h->value.len); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_process_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + ngx_table_elt_t **ph; + ngx_http_upstream_t *u; + + u = r->upstream; + ph = &u->headers_in.set_cookie; + + while (*ph) { ph = &(*ph)->next; } + + *ph = h; + h->next = NULL; + +#if (NGX_HTTP_CACHE) + if (!(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_SET_COOKIE)) { + u->cacheable = 0; + } +#endif + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_process_cache_control(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset) +{ + ngx_table_elt_t **ph; + ngx_http_upstream_t *u; + + u = r->upstream; + ph = &u->headers_in.cache_control; + + while (*ph) { ph = &(*ph)->next; } + + *ph = h; + h->next = NULL; + +#if (NGX_HTTP_CACHE) + { + u_char *p, *start, *last; + ngx_int_t n; + + if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL) { + return NGX_OK; + } + + if (r->cache == NULL) { + return NGX_OK; + } + + start = h->value.data; + last = start + h->value.len; + + if (r->cache->valid_sec != 0 && u->headers_in.x_accel_expires != NULL) { + goto extensions; + } + + if (ngx_strlcasestrn(start, last, (u_char *) "no-cache", 8 - 1) != NULL + || ngx_strlcasestrn(start, last, (u_char *) "no-store", 8 - 1) != NULL + || ngx_strlcasestrn(start, last, (u_char *) "private", 7 - 1) != NULL) + { + u->headers_in.no_cache = 1; + return NGX_OK; + } + + p = ngx_strlcasestrn(start, last, (u_char *) "s-maxage=", 9 - 1); + offset = 9; + + if (p == NULL) { + p = ngx_strlcasestrn(start, last, (u_char *) "max-age=", 8 - 1); + offset = 8; + } + + if (p) { + n = ngx_http_upstream_process_delta_seconds(p + offset, last); + + if (n == NGX_ERROR) { + u->cacheable = 0; + return NGX_OK; + } + + if (n == 0) { + u->headers_in.no_cache = 1; + return NGX_OK; + } + + r->cache->valid_sec = ngx_min((ngx_uint_t) ngx_time() + n, + NGX_MAX_INT_T_VALUE); + u->headers_in.expired = 0; + } + +extensions: + + p = ngx_strlcasestrn(start, last, (u_char *) "stale-while-revalidate=", + 23 - 1); + + if (p) { + n = ngx_http_upstream_process_delta_seconds(p + 23, last); + + if (n == NGX_ERROR) { + u->cacheable = 0; + return NGX_OK; + } + + r->cache->updating_sec = n; + r->cache->error_sec = n; + } + + p = ngx_strlcasestrn(start, last, (u_char *) "stale-if-error=", 15 - 1); + + if (p) { + n = ngx_http_upstream_process_delta_seconds(p + 15, last); + + if (n == NGX_ERROR) { + u->cacheable = 0; + return NGX_OK; + } + + r->cache->error_sec = n; + } + } +#endif + + return NGX_OK; +} + + +#if (NGX_HTTP_CACHE) + +static ngx_int_t +ngx_http_upstream_process_delta_seconds(u_char *p, u_char *last) +{ + ngx_int_t n, cutoff, cutlim; + + cutoff = NGX_MAX_INT_T_VALUE / 10; + cutlim = NGX_MAX_INT_T_VALUE % 10; + + n = 0; + + for ( /* void */ ; p < last; p++) { + if (*p == ',' || *p == ';' || *p == ' ') { + break; + } + + if (*p < '0' || *p > '9') { + return NGX_ERROR; + } + + if (n >= cutoff && (n > cutoff || *p - '0' > cutlim)) { + n = NGX_MAX_INT_T_VALUE; + break; + } + + n = n * 10 + (*p - '0'); + } + + return n; +} + +#endif + + +static ngx_int_t +ngx_http_upstream_process_expires(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + ngx_http_upstream_t *u; + + u = r->upstream; + + if (u->headers_in.expires) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "upstream sent duplicate header line: \"%V: %V\", " + "previous value: \"%V: %V\", ignored", + &h->key, &h->value, + &u->headers_in.expires->key, + &u->headers_in.expires->value); + h->hash = 0; + return NGX_OK; + } + + u->headers_in.expires = h; + h->next = NULL; + +#if (NGX_HTTP_CACHE) + { + time_t expires; + + if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_EXPIRES) { + return NGX_OK; + } + + if (r->cache == NULL) { + return NGX_OK; + } + + if (r->cache->valid_sec != 0) { + return NGX_OK; + } + + expires = ngx_parse_http_time(h->value.data, h->value.len); + + if (expires == NGX_ERROR || expires < ngx_time()) { + u->headers_in.expired = 1; + return NGX_OK; + } + + r->cache->valid_sec = expires; + } +#endif + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_process_accel_expires(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset) +{ + ngx_http_upstream_t *u; + + u = r->upstream; + + if (u->headers_in.x_accel_expires) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "upstream sent duplicate header line: \"%V: %V\", " + "previous value: \"%V: %V\", ignored", + &h->key, &h->value, + &u->headers_in.x_accel_expires->key, + &u->headers_in.x_accel_expires->value); + h->hash = 0; + return NGX_OK; + } + + u->headers_in.x_accel_expires = h; + h->next = NULL; + +#if (NGX_HTTP_CACHE) + { + u_char *p; + size_t len; + ngx_int_t n; + + if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES) { + return NGX_OK; + } + + if (r->cache == NULL) { + return NGX_OK; + } + + len = h->value.len; + p = h->value.data; + + if (p[0] != '@') { + n = ngx_atoi(p, len); + + switch (n) { + case 0: + u->cacheable = 0; + /* fall through */ + + case NGX_ERROR: + return NGX_OK; + + default: + r->cache->valid_sec = ngx_time() + n; + u->headers_in.no_cache = 0; + u->headers_in.expired = 0; + return NGX_OK; + } + } + + p++; + len--; + + n = ngx_atoi(p, len); + + if (n != NGX_ERROR) { + r->cache->valid_sec = n; + u->headers_in.no_cache = 0; + u->headers_in.expired = 0; + } + } +#endif + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_process_limit_rate(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + ngx_int_t n; + ngx_http_upstream_t *u; + + u = r->upstream; + + if (u->headers_in.x_accel_limit_rate) { + ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, + "upstream sent duplicate header line: \"%V: %V\", " + "previous value: \"%V: %V\", ignored", + &h->key, &h->value, + &u->headers_in.x_accel_limit_rate->key, + &u->headers_in.x_accel_limit_rate->value); + h->hash = 0; + return NGX_OK; + } + + u->headers_in.x_accel_limit_rate = h; + h->next = NULL; + + if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE) { + return NGX_OK; + } + + n = ngx_atoi(h->value.data, h->value.len); + + if (n != NGX_ERROR) { + r->limit_rate = (size_t) n; + r->limit_rate_set = 1; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_process_buffering(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + u_char c0, c1, c2; + ngx_http_upstream_t *u; + + u = r->upstream; + + if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING) { + return NGX_OK; + } + + if (u->conf->change_buffering) { + + if (h->value.len == 2) { + c0 = ngx_tolower(h->value.data[0]); + c1 = ngx_tolower(h->value.data[1]); + + if (c0 == 'n' && c1 == 'o') { + u->buffering = 0; + } + + } else if (h->value.len == 3) { + c0 = ngx_tolower(h->value.data[0]); + c1 = ngx_tolower(h->value.data[1]); + c2 = ngx_tolower(h->value.data[2]); + + if (c0 == 'y' && c1 == 'e' && c2 == 's') { + u->buffering = 1; + } + } + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_process_charset(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + ngx_http_upstream_t *u; + + u = r->upstream; + + if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_CHARSET) { + return NGX_OK; + } + + r->headers_out.override_charset = &h->value; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + ngx_table_elt_t **ph; + ngx_http_upstream_t *u; + + u = r->upstream; + ph = &u->headers_in.connection; + + while (*ph) { ph = &(*ph)->next; } + + *ph = h; + h->next = NULL; + + if (ngx_strlcasestrn(h->value.data, h->value.data + h->value.len, + (u_char *) "close", 5 - 1) + != NULL) + { + u->headers_in.connection_close = 1; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset) +{ + ngx_http_upstream_t *u; + + u = r->upstream; + + if (u->headers_in.transfer_encoding) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent duplicate header line: \"%V: %V\", " + "previous value: \"%V: %V\"", + &h->key, &h->value, + &u->headers_in.transfer_encoding->key, + &u->headers_in.transfer_encoding->value); + return NGX_HTTP_UPSTREAM_INVALID_HEADER; + } + + if (u->headers_in.content_length) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent \"Content-Length\" and " + "\"Transfer-Encoding\" headers at the same time"); + return NGX_HTTP_UPSTREAM_INVALID_HEADER; + } + + u->headers_in.transfer_encoding = h; + h->next = NULL; + + if (h->value.len == 7 + && ngx_strncasecmp(h->value.data, (u_char *) "chunked", 7) == 0) + { + u->headers_in.chunked = 1; + + } else { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "upstream sent unknown \"Transfer-Encoding\": \"%V\"", + &h->value); + return NGX_HTTP_UPSTREAM_INVALID_HEADER; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_process_vary(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset) +{ + ngx_table_elt_t **ph; + ngx_http_upstream_t *u; + + u = r->upstream; + ph = &u->headers_in.vary; + + while (*ph) { ph = &(*ph)->next; } + + *ph = h; + h->next = NULL; + +#if (NGX_HTTP_CACHE) + { + u_char *p; + size_t len; + ngx_str_t vary; + + if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_VARY) { + return NGX_OK; + } + + if (r->cache == NULL || !u->cacheable) { + return NGX_OK; + } + + if (h->value.len == 1 && h->value.data[0] == '*') { + u->cacheable = 0; + return NGX_OK; + } + + if (u->headers_in.vary->next) { + + len = 0; + + for (h = u->headers_in.vary; h; h = h->next) { + len += h->value.len + 2; + } + + len -= 2; + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + vary.len = len; + vary.data = p; + + for (h = u->headers_in.vary; h; h = h->next) { + p = ngx_copy(p, h->value.data, h->value.len); + + if (h->next == NULL) { + break; + } + + *p++ = ','; *p++ = ' '; + } + + } else { + vary = h->value; + } + + if (vary.len > NGX_HTTP_CACHE_VARY_LEN) { + u->cacheable = 0; + } + + r->cache->vary = vary; + } +#endif + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_copy_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + ngx_table_elt_t *ho, **ph; + + ho = ngx_list_push(&r->headers_out.headers); + if (ho == NULL) { + return NGX_ERROR; + } + + *ho = *h; + + if (offset) { + ph = (ngx_table_elt_t **) ((char *) &r->headers_out + offset); + *ph = ho; + ho->next = NULL; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_copy_multi_header_lines(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset) +{ + ngx_table_elt_t *ho, **ph; + + ho = ngx_list_push(&r->headers_out.headers); + if (ho == NULL) { + return NGX_ERROR; + } + + *ho = *h; + + ph = (ngx_table_elt_t **) ((char *) &r->headers_out + offset); + + while (*ph) { ph = &(*ph)->next; } + + *ph = ho; + ho->next = NULL; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_copy_content_type(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + u_char *p, *last; + + r->headers_out.content_type_len = h->value.len; + r->headers_out.content_type = h->value; + r->headers_out.content_type_lowcase = NULL; + + for (p = h->value.data; *p; p++) { + + if (*p != ';') { + continue; + } + + last = p; + + while (*++p == ' ') { /* void */ } + + if (*p == '\0') { + return NGX_OK; + } + + if (ngx_strncasecmp(p, (u_char *) "charset=", 8) != 0) { + continue; + } + + p += 8; + + r->headers_out.content_type_len = last - h->value.data; + + if (*p == '"') { + p++; + } + + last = h->value.data + h->value.len; + + if (last > p && *(last - 1) == '"') { + last--; + } + + r->headers_out.charset.len = last - p; + r->headers_out.charset.data = p; + + return NGX_OK; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_copy_last_modified(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + ngx_table_elt_t *ho; + + ho = ngx_list_push(&r->headers_out.headers); + if (ho == NULL) { + return NGX_ERROR; + } + + *ho = *h; + ho->next = NULL; + + r->headers_out.last_modified = ho; + r->headers_out.last_modified_time = + r->upstream->headers_in.last_modified_time; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_rewrite_location(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + ngx_int_t rc; + ngx_table_elt_t *ho; + + ho = ngx_list_push(&r->headers_out.headers); + if (ho == NULL) { + return NGX_ERROR; + } + + *ho = *h; + ho->next = NULL; + + if (r->upstream->rewrite_redirect) { + rc = r->upstream->rewrite_redirect(r, ho, 0); + + if (rc == NGX_DECLINED) { + return NGX_OK; + } + + if (rc == NGX_OK) { + r->headers_out.location = ho; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "rewritten location: \"%V\"", &ho->value); + } + + return rc; + } + + if (ho->value.data[0] != '/') { + r->headers_out.location = ho; + } + + /* + * we do not set r->headers_out.location here to avoid handling + * relative redirects in ngx_http_header_filter() + */ + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_rewrite_refresh(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + u_char *p; + ngx_int_t rc; + ngx_table_elt_t *ho; + + ho = ngx_list_push(&r->headers_out.headers); + if (ho == NULL) { + return NGX_ERROR; + } + + *ho = *h; + ho->next = NULL; + + if (r->upstream->rewrite_redirect) { + + p = ngx_strcasestrn(ho->value.data, "url=", 4 - 1); + + if (p) { + rc = r->upstream->rewrite_redirect(r, ho, p + 4 - ho->value.data); + + } else { + return NGX_OK; + } + + if (rc == NGX_DECLINED) { + return NGX_OK; + } + + if (rc == NGX_OK) { + r->headers_out.refresh = ho; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "rewritten refresh: \"%V\"", &ho->value); + } + + return rc; + } + + r->headers_out.refresh = ho; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_rewrite_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h, + ngx_uint_t offset) +{ + ngx_int_t rc; + ngx_table_elt_t *ho; + + ho = ngx_list_push(&r->headers_out.headers); + if (ho == NULL) { + return NGX_ERROR; + } + + *ho = *h; + ho->next = NULL; + + if (r->upstream->rewrite_cookie) { + rc = r->upstream->rewrite_cookie(r, ho); + + if (rc == NGX_DECLINED) { + return NGX_OK; + } + +#if (NGX_DEBUG) + if (rc == NGX_OK) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "rewritten cookie: \"%V\"", &ho->value); + } +#endif + + return rc; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r, + ngx_table_elt_t *h, ngx_uint_t offset) +{ + ngx_table_elt_t *ho; + + if (r->upstream->conf->force_ranges) { + return NGX_OK; + } + +#if (NGX_HTTP_CACHE) + + if (r->cached) { + r->allow_ranges = 1; + return NGX_OK; + } + + if (r->upstream->cacheable) { + r->allow_ranges = 1; + r->single_range = 1; + return NGX_OK; + } + +#endif + + ho = ngx_list_push(&r->headers_out.headers); + if (ho == NULL) { + return NGX_ERROR; + } + + *ho = *h; + ho->next = NULL; + + r->headers_out.accept_ranges = ho; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_add_variables(ngx_conf_t *cf) +{ + ngx_http_variable_t *var, *v; + + for (v = ngx_http_upstream_vars; v->name.len; v++) { + var = ngx_http_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_addr_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + size_t len; + ngx_uint_t i; + ngx_http_upstream_state_t *state; + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + if (r->upstream_states == NULL || r->upstream_states->nelts == 0) { + v->not_found = 1; + return NGX_OK; + } + + len = 0; + state = r->upstream_states->elts; + + for (i = 0; i < r->upstream_states->nelts; i++) { + if (state[i].peer) { + len += state[i].peer->len + 2; + + } else { + len += 3; + } + } + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + v->data = p; + + i = 0; + + for ( ;; ) { + if (state[i].peer) { + p = ngx_cpymem(p, state[i].peer->data, state[i].peer->len); + } + + if (++i == r->upstream_states->nelts) { + break; + } + + if (state[i].peer) { + *p++ = ','; + *p++ = ' '; + + } else { + *p++ = ' '; + *p++ = ':'; + *p++ = ' '; + + if (++i == r->upstream_states->nelts) { + break; + } + + continue; + } + } + + v->len = p - v->data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_status_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + size_t len; + ngx_uint_t i; + ngx_http_upstream_state_t *state; + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + if (r->upstream_states == NULL || r->upstream_states->nelts == 0) { + v->not_found = 1; + return NGX_OK; + } + + len = r->upstream_states->nelts * (3 + 2); + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + v->data = p; + + i = 0; + state = r->upstream_states->elts; + + for ( ;; ) { + if (state[i].status) { + p = ngx_sprintf(p, "%ui", state[i].status); + + } else { + *p++ = '-'; + } + + if (++i == r->upstream_states->nelts) { + break; + } + + if (state[i].peer) { + *p++ = ','; + *p++ = ' '; + + } else { + *p++ = ' '; + *p++ = ':'; + *p++ = ' '; + + if (++i == r->upstream_states->nelts) { + break; + } + + continue; + } + } + + v->len = p - v->data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_response_time_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + size_t len; + ngx_uint_t i; + ngx_msec_int_t ms; + ngx_http_upstream_state_t *state; + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + if (r->upstream_states == NULL || r->upstream_states->nelts == 0) { + v->not_found = 1; + return NGX_OK; + } + + len = r->upstream_states->nelts * (NGX_TIME_T_LEN + 4 + 2); + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + v->data = p; + + i = 0; + state = r->upstream_states->elts; + + for ( ;; ) { + + if (data == 1) { + ms = state[i].header_time; + + } else if (data == 2) { + ms = state[i].connect_time; + + } else { + ms = state[i].response_time; + } + + if (ms != -1) { + ms = ngx_max(ms, 0); + p = ngx_sprintf(p, "%T.%03M", (time_t) ms / 1000, ms % 1000); + + } else { + *p++ = '-'; + } + + if (++i == r->upstream_states->nelts) { + break; + } + + if (state[i].peer) { + *p++ = ','; + *p++ = ' '; + + } else { + *p++ = ' '; + *p++ = ':'; + *p++ = ' '; + + if (++i == r->upstream_states->nelts) { + break; + } + + continue; + } + } + + v->len = p - v->data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_response_length_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + size_t len; + ngx_uint_t i; + ngx_http_upstream_state_t *state; + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + if (r->upstream_states == NULL || r->upstream_states->nelts == 0) { + v->not_found = 1; + return NGX_OK; + } + + len = r->upstream_states->nelts * (NGX_OFF_T_LEN + 2); + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + v->data = p; + + i = 0; + state = r->upstream_states->elts; + + for ( ;; ) { + + if (data == 1) { + p = ngx_sprintf(p, "%O", state[i].bytes_received); + + } else if (data == 2) { + p = ngx_sprintf(p, "%O", state[i].bytes_sent); + + } else { + p = ngx_sprintf(p, "%O", state[i].response_length); + } + + if (++i == r->upstream_states->nelts) { + break; + } + + if (state[i].peer) { + *p++ = ','; + *p++ = ' '; + + } else { + *p++ = ' '; + *p++ = ':'; + *p++ = ' '; + + if (++i == r->upstream_states->nelts) { + break; + } + + continue; + } + } + + v->len = p - v->data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_header_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->upstream == NULL) { + v->not_found = 1; + return NGX_OK; + } + + return ngx_http_variable_unknown_header(r, v, (ngx_str_t *) data, + &r->upstream->headers_in.headers.part, + sizeof("upstream_http_") - 1); +} + + +static ngx_int_t +ngx_http_upstream_trailer_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->upstream == NULL) { + v->not_found = 1; + return NGX_OK; + } + + return ngx_http_variable_unknown_header(r, v, (ngx_str_t *) data, + &r->upstream->headers_in.trailers.part, + sizeof("upstream_trailer_") - 1); +} + + +static ngx_int_t +ngx_http_upstream_cookie_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_str_t *name = (ngx_str_t *) data; + + ngx_str_t cookie, s; + + if (r->upstream == NULL) { + v->not_found = 1; + return NGX_OK; + } + + s.len = name->len - (sizeof("upstream_cookie_") - 1); + s.data = name->data + sizeof("upstream_cookie_") - 1; + + if (ngx_http_parse_set_cookie_lines(r, r->upstream->headers_in.set_cookie, + &s, &cookie) + == NULL) + { + v->not_found = 1; + return NGX_OK; + } + + v->len = cookie.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = cookie.data; + + return NGX_OK; +} + + +#if (NGX_HTTP_CACHE) + +static ngx_int_t +ngx_http_upstream_cache_status(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_uint_t n; + + if (r->upstream == NULL || r->upstream->cache_status == 0) { + v->not_found = 1; + return NGX_OK; + } + + n = r->upstream->cache_status - 1; + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->len = ngx_http_cache_status[n].len; + v->data = ngx_http_cache_status[n].data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_cache_last_modified(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + if (r->upstream == NULL + || !r->upstream->conf->cache_revalidate + || r->upstream->cache_status != NGX_HTTP_CACHE_EXPIRED + || r->cache->last_modified == -1) + { + v->not_found = 1; + return NGX_OK; + } + + p = ngx_pnalloc(r->pool, sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_http_time(p, r->cache->last_modified) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_upstream_cache_etag(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->upstream == NULL + || !r->upstream->conf->cache_revalidate + || r->upstream->cache_status != NGX_HTTP_CACHE_EXPIRED + || r->cache->etag.len == 0) + { + v->not_found = 1; + return NGX_OK; + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->len = r->cache->etag.len; + v->data = r->cache->etag.data; + + return NGX_OK; +} + +#endif + + +static char * +ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) +{ + char *rv; + void *mconf; + ngx_str_t *value; + ngx_url_t u; + ngx_uint_t m; + ngx_conf_t pcf; + ngx_http_module_t *module; + ngx_http_conf_ctx_t *ctx, *http_ctx; + ngx_http_upstream_srv_conf_t *uscf; + + ngx_memzero(&u, sizeof(ngx_url_t)); + + value = cf->args->elts; + u.host = value[1]; + u.no_resolve = 1; + u.no_port = 1; + + uscf = ngx_http_upstream_add(cf, &u, NGX_HTTP_UPSTREAM_CREATE + |NGX_HTTP_UPSTREAM_MODIFY + |NGX_HTTP_UPSTREAM_WEIGHT + |NGX_HTTP_UPSTREAM_MAX_CONNS + |NGX_HTTP_UPSTREAM_MAX_FAILS + |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT + |NGX_HTTP_UPSTREAM_DOWN + |NGX_HTTP_UPSTREAM_BACKUP); + if (uscf == NULL) { + return NGX_CONF_ERROR; + } + + + ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); + if (ctx == NULL) { + return NGX_CONF_ERROR; + } + + http_ctx = cf->ctx; + ctx->main_conf = http_ctx->main_conf; + + /* the upstream{}'s srv_conf */ + + ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); + if (ctx->srv_conf == NULL) { + return NGX_CONF_ERROR; + } + + ctx->srv_conf[ngx_http_upstream_module.ctx_index] = uscf; + + uscf->srv_conf = ctx->srv_conf; + + + /* the upstream{}'s loc_conf */ + + ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); + if (ctx->loc_conf == NULL) { + return NGX_CONF_ERROR; + } + + for (m = 0; cf->cycle->modules[m]; m++) { + if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) { + continue; + } + + module = cf->cycle->modules[m]->ctx; + + if (module->create_srv_conf) { + mconf = module->create_srv_conf(cf); + if (mconf == NULL) { + return NGX_CONF_ERROR; + } + + ctx->srv_conf[cf->cycle->modules[m]->ctx_index] = mconf; + } + + if (module->create_loc_conf) { + mconf = module->create_loc_conf(cf); + if (mconf == NULL) { + return NGX_CONF_ERROR; + } + + ctx->loc_conf[cf->cycle->modules[m]->ctx_index] = mconf; + } + } + + uscf->servers = ngx_array_create(cf->pool, 4, + sizeof(ngx_http_upstream_server_t)); + if (uscf->servers == NULL) { + return NGX_CONF_ERROR; + } + + + /* parse inside upstream{} */ + + pcf = *cf; + cf->ctx = ctx; + cf->cmd_type = NGX_HTTP_UPS_CONF; + + rv = ngx_conf_parse(cf, NULL); + + *cf = pcf; + + if (rv != NGX_CONF_OK) { + return rv; + } + + if (uscf->servers->nelts == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "no servers are inside upstream"); + return NGX_CONF_ERROR; + } + + return rv; +} + + +static char * +ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_upstream_srv_conf_t *uscf = conf; + + time_t fail_timeout; + ngx_str_t *value, s; + ngx_url_t u; + ngx_int_t weight, max_conns, max_fails; + ngx_uint_t i; +#if (NGX_HTTP_UPSTREAM_ZONE) + ngx_uint_t resolve; +#endif + ngx_http_upstream_server_t *us; + + us = ngx_array_push(uscf->servers); + if (us == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(us, sizeof(ngx_http_upstream_server_t)); + + value = cf->args->elts; + + weight = 1; + max_conns = 0; + max_fails = 1; + fail_timeout = 10; +#if (NGX_HTTP_UPSTREAM_ZONE) + resolve = 0; +#endif + + for (i = 2; i < cf->args->nelts; i++) { + + if (ngx_strncmp(value[i].data, "weight=", 7) == 0) { + + if (!(uscf->flags & NGX_HTTP_UPSTREAM_WEIGHT)) { + goto not_supported; + } + + weight = ngx_atoi(&value[i].data[7], value[i].len - 7); + + if (weight == NGX_ERROR || weight == 0) { + goto invalid; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "max_conns=", 10) == 0) { + + if (!(uscf->flags & NGX_HTTP_UPSTREAM_MAX_CONNS)) { + goto not_supported; + } + + max_conns = ngx_atoi(&value[i].data[10], value[i].len - 10); + + if (max_conns == NGX_ERROR) { + goto invalid; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "max_fails=", 10) == 0) { + + if (!(uscf->flags & NGX_HTTP_UPSTREAM_MAX_FAILS)) { + goto not_supported; + } + + max_fails = ngx_atoi(&value[i].data[10], value[i].len - 10); + + if (max_fails == NGX_ERROR) { + goto invalid; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "fail_timeout=", 13) == 0) { + + if (!(uscf->flags & NGX_HTTP_UPSTREAM_FAIL_TIMEOUT)) { + goto not_supported; + } + + s.len = value[i].len - 13; + s.data = &value[i].data[13]; + + fail_timeout = ngx_parse_time(&s, 1); + + if (fail_timeout == (time_t) NGX_ERROR) { + goto invalid; + } + + continue; + } + + if (ngx_strcmp(value[i].data, "backup") == 0) { + + if (!(uscf->flags & NGX_HTTP_UPSTREAM_BACKUP)) { + goto not_supported; + } + + us->backup = 1; + + continue; + } + + if (ngx_strcmp(value[i].data, "down") == 0) { + + if (!(uscf->flags & NGX_HTTP_UPSTREAM_DOWN)) { + goto not_supported; + } + + us->down = NGX_HTTP_UPSTREAM_FAILED; + + continue; + } + +#if (NGX_HTTP_UPSTREAM_STICKY) + if (ngx_strcmp(value[i].data, "drain") == 0) { + + if (!(uscf->flags & NGX_HTTP_UPSTREAM_DOWN)) { + goto not_supported; + } + + us->down = NGX_HTTP_UPSTREAM_DRAINING; + + continue; + } +#endif + +#if (NGX_HTTP_UPSTREAM_SID) + if (ngx_strncmp(value[i].data, "route=", 6) == 0) { + + us->sid.data = &value[i].data[6]; + us->sid.len = value[i].len - 6; + + if (us->sid.len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "route is empty"); + return NGX_CONF_ERROR; + } + + if (us->sid.len > NGX_HTTP_UPSTREAM_SID_LEN) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "route is longer than %d", + NGX_HTTP_UPSTREAM_SID_LEN); + return NGX_CONF_ERROR; + } + + continue; + } +#endif + +#if (NGX_HTTP_UPSTREAM_ZONE) + if (ngx_strcmp(value[i].data, "resolve") == 0) { + resolve = 1; + continue; + } + + if (ngx_strncmp(value[i].data, "service=", 8) == 0) { + + us->service.len = value[i].len - 8; + us->service.data = &value[i].data[8]; + + if (us->service.len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "service is empty"); + return NGX_CONF_ERROR; + } + + continue; + } +#endif + + goto invalid; + } + + ngx_memzero(&u, sizeof(ngx_url_t)); + + u.url = value[1]; + u.default_port = 80; + +#if (NGX_HTTP_UPSTREAM_ZONE) + if (resolve) { + /* resolve at run time */ + u.no_resolve = 1; + } + + if (us->service.len && !resolve) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "service upstream \"%V\" requires " + "\"resolve\" parameter", + &u.url); + return NGX_CONF_ERROR; + } + +#endif + + if (ngx_parse_url(cf->pool, &u) != NGX_OK) { + if (u.err) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "%s in upstream \"%V\"", u.err, &u.url); + } + + return NGX_CONF_ERROR; + } + + us->name = u.url; + +#if (NGX_HTTP_UPSTREAM_ZONE) + + if (us->service.len && !u.no_port) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "service upstream \"%V\" may not have port", + &us->name); + + return NGX_CONF_ERROR; + } + + if (us->service.len && u.naddrs) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "service upstream \"%V\" requires domain name", + &us->name); + + return NGX_CONF_ERROR; + } + + if (resolve && u.naddrs == 0) { + ngx_addr_t *addr; + + /* save port */ + + addr = ngx_pcalloc(cf->pool, sizeof(ngx_addr_t)); + if (addr == NULL) { + return NGX_CONF_ERROR; + } + + addr->sockaddr = ngx_palloc(cf->pool, u.socklen); + if (addr->sockaddr == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memcpy(addr->sockaddr, &u.sockaddr, u.socklen); + + addr->socklen = u.socklen; + + us->addrs = addr; + us->naddrs = 1; + + us->host = u.host; + + } else { + us->addrs = u.addrs; + us->naddrs = u.naddrs; + } + +#else + + us->addrs = u.addrs; + us->naddrs = u.naddrs; + +#endif + + us->weight = weight; + us->max_conns = max_conns; + us->max_fails = max_fails; + us->fail_timeout = fail_timeout; + + return NGX_CONF_OK; + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + + return NGX_CONF_ERROR; + +not_supported: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "balancing method does not support parameter \"%V\"", + &value[i]); + + return NGX_CONF_ERROR; +} + + +#if (NGX_HTTP_UPSTREAM_ZONE) + +static char * +ngx_http_upstream_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_upstream_srv_conf_t *uscf = conf; + + ngx_str_t *value; + + if (uscf->resolver) { + return "is duplicate"; + } + + value = cf->args->elts; + + uscf->resolver = ngx_resolver_create(cf, &value[1], cf->args->nelts - 1); + if (uscf->resolver == NULL) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + +#endif + + +ngx_http_upstream_srv_conf_t * +ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags) +{ + ngx_uint_t i; + ngx_http_upstream_server_t *us; + ngx_http_upstream_srv_conf_t *uscf, **uscfp; + ngx_http_upstream_main_conf_t *umcf; + + if (!(flags & NGX_HTTP_UPSTREAM_CREATE)) { + + if (ngx_parse_url(cf->pool, u) != NGX_OK) { + if (u->err) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "%s in upstream \"%V\"", u->err, &u->url); + } + + return NULL; + } + } + + umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module); + + uscfp = umcf->upstreams.elts; + + for (i = 0; i < umcf->upstreams.nelts; i++) { + + if (uscfp[i]->host.len != u->host.len + || ngx_strncasecmp(uscfp[i]->host.data, u->host.data, u->host.len) + != 0) + { + continue; + } + + if ((flags & NGX_HTTP_UPSTREAM_CREATE) + && (uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE)) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate upstream \"%V\"", &u->host); + return NULL; + } + + if ((uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE) && !u->no_port) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "upstream \"%V\" may not have port %d", + &u->host, u->port); + return NULL; + } + + if ((flags & NGX_HTTP_UPSTREAM_CREATE) && !uscfp[i]->no_port) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "upstream \"%V\" may not have port %d in %s:%ui", + &u->host, uscfp[i]->port, + uscfp[i]->file_name, uscfp[i]->line); + return NULL; + } + + if (uscfp[i]->port && u->port + && uscfp[i]->port != u->port) + { + continue; + } + + if (flags & NGX_HTTP_UPSTREAM_CREATE) { + uscfp[i]->flags = flags; + uscfp[i]->port = 0; + } + + return uscfp[i]; + } + + uscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_srv_conf_t)); + if (uscf == NULL) { + return NULL; + } + + uscf->flags = flags; + uscf->host = u->host; + uscf->file_name = cf->conf_file->file.name.data; + uscf->line = cf->conf_file->line; + uscf->port = u->port; + uscf->no_port = u->no_port; +#if (NGX_HTTP_UPSTREAM_ZONE) + uscf->resolver_timeout = NGX_CONF_UNSET_MSEC; +#endif + + if (u->naddrs == 1 && (u->port || u->family == AF_UNIX)) { + uscf->servers = ngx_array_create(cf->pool, 1, + sizeof(ngx_http_upstream_server_t)); + if (uscf->servers == NULL) { + return NULL; + } + + us = ngx_array_push(uscf->servers); + if (us == NULL) { + return NULL; + } + + ngx_memzero(us, sizeof(ngx_http_upstream_server_t)); + + us->addrs = u->addrs; + us->naddrs = 1; + } + + uscfp = ngx_array_push(&umcf->upstreams); + if (uscfp == NULL) { + return NULL; + } + + *uscfp = uscf; + + return uscf; +} + + +char * +ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *p = conf; + + ngx_int_t rc; + ngx_str_t *value; + ngx_http_complex_value_t cv; + ngx_http_upstream_local_t **plocal, *local; + ngx_http_compile_complex_value_t ccv; + + plocal = (ngx_http_upstream_local_t **) (p + cmd->offset); + + if (*plocal != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + value = cf->args->elts; + + if (cf->args->nelts == 2 && ngx_strcmp(value[1].data, "off") == 0) { + *plocal = NULL; + return NGX_CONF_OK; + } + + ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &cv; + + if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + local = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_local_t)); + if (local == NULL) { + return NGX_CONF_ERROR; + } + + *plocal = local; + + if (cv.lengths) { + local->value = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t)); + if (local->value == NULL) { + return NGX_CONF_ERROR; + } + + *local->value = cv; + + } else { + local->addr = ngx_palloc(cf->pool, sizeof(ngx_addr_t)); + if (local->addr == NULL) { + return NGX_CONF_ERROR; + } + + rc = ngx_parse_addr_port(cf->pool, local->addr, value[1].data, + value[1].len); + + switch (rc) { + case NGX_OK: + local->addr->name = value[1]; + break; + + case NGX_DECLINED: + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid address \"%V\"", &value[1]); + /* fall through */ + + default: + return NGX_CONF_ERROR; + } + } + + if (cf->args->nelts > 2) { + if (ngx_strcmp(value[2].data, "transparent") == 0) { +#if (NGX_HAVE_TRANSPARENT_PROXY) + ngx_core_conf_t *ccf; + + ccf = (ngx_core_conf_t *) ngx_get_conf(cf->cycle->conf_ctx, + ngx_core_module); + + ccf->transparent = 1; + local->transparent = 1; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "transparent proxying is not supported " + "on this platform, ignored"); +#endif + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + } + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_http_upstream_set_local(ngx_http_request_t *r, ngx_http_upstream_t *u, + ngx_http_upstream_local_t *local) +{ + ngx_int_t rc; + ngx_str_t val; + ngx_addr_t *addr; + + if (local == NULL) { + u->peer.local = NULL; + return NGX_OK; + } + +#if (NGX_HAVE_TRANSPARENT_PROXY) + u->peer.transparent = local->transparent; +#endif + + if (local->value == NULL) { + u->peer.local = local->addr; + return NGX_OK; + } + + if (ngx_http_complex_value(r, local->value, &val) != NGX_OK) { + return NGX_ERROR; + } + + if (val.len == 0) { + u->peer.local = NULL; + return NGX_OK; + } + + addr = ngx_palloc(r->pool, sizeof(ngx_addr_t)); + if (addr == NULL) { + return NGX_ERROR; + } + + rc = ngx_parse_addr_port(r->pool, addr, val.data, val.len); + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc != NGX_OK) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "invalid local address \"%V\"", &val); + u->peer.local = NULL; + return NGX_OK; + } + + addr->name = val; + u->peer.local = addr; + + return NGX_OK; +} + + +char * +ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *p = conf; + + ngx_str_t *value; + ngx_array_t **a; + ngx_http_upstream_param_t *param; + + a = (ngx_array_t **) (p + cmd->offset); + + if (*a == NULL) { + *a = ngx_array_create(cf->pool, 4, sizeof(ngx_http_upstream_param_t)); + if (*a == NULL) { + return NGX_CONF_ERROR; + } + } + + param = ngx_array_push(*a); + if (param == NULL) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + param->key = value[1]; + param->value = value[2]; + param->skip_empty = 0; + + if (cf->args->nelts == 4) { + if (ngx_strcmp(value[3].data, "if_not_empty") != 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[3]); + return NGX_CONF_ERROR; + } + + param->skip_empty = 1; + } + + return NGX_CONF_OK; +} + + +ngx_int_t +ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf, + ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev, + ngx_str_t *default_hide_headers, ngx_hash_init_t *hash) +{ + ngx_str_t *h; + ngx_uint_t i, j; + ngx_array_t hide_headers; + ngx_hash_key_t *hk; + + if (conf->hide_headers == NGX_CONF_UNSET_PTR + && conf->pass_headers == NGX_CONF_UNSET_PTR) + { + conf->hide_headers = prev->hide_headers; + conf->pass_headers = prev->pass_headers; + + conf->hide_headers_hash = prev->hide_headers_hash; + + if (conf->hide_headers_hash.buckets) { + return NGX_OK; + } + + } else { + if (conf->hide_headers == NGX_CONF_UNSET_PTR) { + conf->hide_headers = prev->hide_headers; + } + + if (conf->pass_headers == NGX_CONF_UNSET_PTR) { + conf->pass_headers = prev->pass_headers; + } + } + + if (ngx_array_init(&hide_headers, cf->temp_pool, 4, sizeof(ngx_hash_key_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + for (h = default_hide_headers; h->len; h++) { + hk = ngx_array_push(&hide_headers); + if (hk == NULL) { + return NGX_ERROR; + } + + hk->key = *h; + hk->key_hash = ngx_hash_key_lc(h->data, h->len); + hk->value = (void *) 1; + } + + if (conf->hide_headers != NGX_CONF_UNSET_PTR) { + + h = conf->hide_headers->elts; + + for (i = 0; i < conf->hide_headers->nelts; i++) { + + hk = hide_headers.elts; + + for (j = 0; j < hide_headers.nelts; j++) { + if (ngx_strcasecmp(h[i].data, hk[j].key.data) == 0) { + goto exist; + } + } + + hk = ngx_array_push(&hide_headers); + if (hk == NULL) { + return NGX_ERROR; + } + + hk->key = h[i]; + hk->key_hash = ngx_hash_key_lc(h[i].data, h[i].len); + hk->value = (void *) 1; + + exist: + + continue; + } + } + + if (conf->pass_headers != NGX_CONF_UNSET_PTR) { + + h = conf->pass_headers->elts; + hk = hide_headers.elts; + + for (i = 0; i < conf->pass_headers->nelts; i++) { + for (j = 0; j < hide_headers.nelts; j++) { + + if (hk[j].key.data == NULL) { + continue; + } + + if (ngx_strcasecmp(h[i].data, hk[j].key.data) == 0) { + hk[j].key.data = NULL; + break; + } + } + } + } + + hash->hash = &conf->hide_headers_hash; + hash->key = ngx_hash_key_lc; + hash->pool = cf->pool; + hash->temp_pool = NULL; + + if (ngx_hash_init(hash, hide_headers.elts, hide_headers.nelts) != NGX_OK) { + return NGX_ERROR; + } + + /* + * special handling to preserve conf->hide_headers_hash + * in the "http" section to inherit it to all servers + */ + + if (prev->hide_headers_hash.buckets == NULL + && conf->hide_headers == prev->hide_headers + && conf->pass_headers == prev->pass_headers) + { + prev->hide_headers_hash = conf->hide_headers_hash; + } + + return NGX_OK; +} + + +#if (NGX_HTTP_SSL) + +ngx_int_t +ngx_http_upstream_merge_ssl_passwords(ngx_conf_t *cf, + ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev) +{ + ngx_uint_t preserve; + + ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL); + + if (conf->ssl_certificate == NULL + || conf->ssl_certificate->value.len == 0 + || conf->ssl_certificate_key == NULL) + { + return NGX_OK; + } + + if (conf->ssl_certificate->lengths == NULL + && conf->ssl_certificate_key->lengths == NULL) + { + if (conf->ssl_passwords && conf->ssl_passwords->pool == NULL) { + /* un-preserve empty password list */ + conf->ssl_passwords = NULL; + } + + return NGX_OK; + } + + if (conf->ssl_passwords && conf->ssl_passwords->pool != cf->temp_pool) { + /* already preserved */ + return NGX_OK; + } + + preserve = (conf->ssl_passwords == prev->ssl_passwords) ? 1 : 0; + + conf->ssl_passwords = ngx_ssl_preserve_passwords(cf, conf->ssl_passwords); + if (conf->ssl_passwords == NULL) { + return NGX_ERROR; + } + + /* + * special handling to keep a preserved ssl_passwords copy + * in the previous configuration to inherit it to all children + */ + + if (preserve) { + prev->ssl_passwords = conf->ssl_passwords; + } + + return NGX_OK; +} + +#endif + + +static void * +ngx_http_upstream_create_main_conf(ngx_conf_t *cf) +{ + ngx_http_upstream_main_conf_t *umcf; + + umcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_main_conf_t)); + if (umcf == NULL) { + return NULL; + } + + if (ngx_array_init(&umcf->upstreams, cf->pool, 4, + sizeof(ngx_http_upstream_srv_conf_t *)) + != NGX_OK) + { + return NULL; + } + + return umcf; +} + + +static char * +ngx_http_upstream_init_main_conf(ngx_conf_t *cf, void *conf) +{ + ngx_http_upstream_main_conf_t *umcf = conf; + + ngx_uint_t i; + ngx_array_t headers_in; + ngx_hash_key_t *hk; + ngx_hash_init_t hash; + ngx_http_upstream_init_pt init; + ngx_http_upstream_header_t *header; + ngx_http_upstream_srv_conf_t **uscfp; + + uscfp = umcf->upstreams.elts; + + for (i = 0; i < umcf->upstreams.nelts; i++) { + + init = uscfp[i]->peer.init_upstream ? uscfp[i]->peer.init_upstream: + ngx_http_upstream_init_round_robin; + + if (init(cf, uscfp[i]) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + + /* upstream_headers_in_hash */ + + if (ngx_array_init(&headers_in, cf->temp_pool, 32, sizeof(ngx_hash_key_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + for (header = ngx_http_upstream_headers_in; header->name.len; header++) { + hk = ngx_array_push(&headers_in); + if (hk == NULL) { + return NGX_CONF_ERROR; + } + + hk->key = header->name; + hk->key_hash = ngx_hash_key_lc(header->name.data, header->name.len); + hk->value = header; + } + + hash.hash = &umcf->headers_in_hash; + hash.key = ngx_hash_key_lc; + hash.max_size = 512; + hash.bucket_size = ngx_align(64, ngx_cacheline_size); + hash.name = "upstream_headers_in_hash"; + hash.pool = cf->pool; + hash.temp_pool = NULL; + + if (ngx_hash_init(&hash, headers_in.elts, headers_in.nelts) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/http/ngx_http_upstream.h b/src/deps/src/nginx-1.30.1/src/http/ngx_http_upstream.h new file mode 100644 index 000000000..0d48db788 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/ngx_http_upstream.h @@ -0,0 +1,464 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_HTTP_UPSTREAM_H_INCLUDED_ +#define _NGX_HTTP_UPSTREAM_H_INCLUDED_ + + +#include +#include +#include +#include +#include +#include + + +#define NGX_HTTP_UPSTREAM_FT_ERROR 0x00000002 +#define NGX_HTTP_UPSTREAM_FT_TIMEOUT 0x00000004 +#define NGX_HTTP_UPSTREAM_FT_INVALID_HEADER 0x00000008 +#define NGX_HTTP_UPSTREAM_FT_HTTP_500 0x00000010 +#define NGX_HTTP_UPSTREAM_FT_HTTP_502 0x00000020 +#define NGX_HTTP_UPSTREAM_FT_HTTP_503 0x00000040 +#define NGX_HTTP_UPSTREAM_FT_HTTP_504 0x00000080 +#define NGX_HTTP_UPSTREAM_FT_HTTP_403 0x00000100 +#define NGX_HTTP_UPSTREAM_FT_HTTP_404 0x00000200 +#define NGX_HTTP_UPSTREAM_FT_HTTP_429 0x00000400 +#define NGX_HTTP_UPSTREAM_FT_UPDATING 0x00000800 +#define NGX_HTTP_UPSTREAM_FT_BUSY_LOCK 0x00001000 +#define NGX_HTTP_UPSTREAM_FT_MAX_WAITING 0x00002000 +#define NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT 0x00004000 +#define NGX_HTTP_UPSTREAM_FT_NOLIVE 0x40000000 +#define NGX_HTTP_UPSTREAM_FT_OFF 0x80000000 + +#define NGX_HTTP_UPSTREAM_FT_STATUS (NGX_HTTP_UPSTREAM_FT_HTTP_500 \ + |NGX_HTTP_UPSTREAM_FT_HTTP_502 \ + |NGX_HTTP_UPSTREAM_FT_HTTP_503 \ + |NGX_HTTP_UPSTREAM_FT_HTTP_504 \ + |NGX_HTTP_UPSTREAM_FT_HTTP_403 \ + |NGX_HTTP_UPSTREAM_FT_HTTP_404 \ + |NGX_HTTP_UPSTREAM_FT_HTTP_429) + +#define NGX_HTTP_UPSTREAM_INVALID_HEADER 40 +#define NGX_HTTP_UPSTREAM_EARLY_HINTS 41 + + +#define NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT 0x00000002 +#define NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES 0x00000004 +#define NGX_HTTP_UPSTREAM_IGN_EXPIRES 0x00000008 +#define NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL 0x00000010 +#define NGX_HTTP_UPSTREAM_IGN_SET_COOKIE 0x00000020 +#define NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE 0x00000040 +#define NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING 0x00000080 +#define NGX_HTTP_UPSTREAM_IGN_XA_CHARSET 0x00000100 +#define NGX_HTTP_UPSTREAM_IGN_VARY 0x00000200 + + +#define NGX_HTTP_UPSTREAM_NOTIFY_HEADER 0x1 + + +typedef struct { + ngx_uint_t status; + ngx_msec_t response_time; + ngx_msec_t connect_time; + ngx_msec_t header_time; + ngx_msec_t queue_time; + off_t response_length; + off_t bytes_received; + off_t bytes_sent; + + ngx_str_t *peer; +} ngx_http_upstream_state_t; + + +typedef struct { + ngx_hash_t headers_in_hash; + ngx_array_t upstreams; + /* ngx_http_upstream_srv_conf_t */ +} ngx_http_upstream_main_conf_t; + +typedef struct ngx_http_upstream_srv_conf_s ngx_http_upstream_srv_conf_t; + +typedef ngx_int_t (*ngx_http_upstream_init_pt)(ngx_conf_t *cf, + ngx_http_upstream_srv_conf_t *us); +typedef ngx_int_t (*ngx_http_upstream_init_peer_pt)(ngx_http_request_t *r, + ngx_http_upstream_srv_conf_t *us); + + +typedef struct { + ngx_http_upstream_init_pt init_upstream; + ngx_http_upstream_init_peer_pt init; + void *data; +} ngx_http_upstream_peer_t; + + +typedef struct { + ngx_str_t name; + ngx_addr_t *addrs; + ngx_uint_t naddrs; + ngx_uint_t weight; + ngx_uint_t max_conns; + ngx_uint_t max_fails; + time_t fail_timeout; + ngx_msec_t slow_start; + ngx_uint_t down; + + unsigned backup:1; + +#if (NGX_HTTP_UPSTREAM_ZONE) + ngx_str_t host; + ngx_str_t service; +#endif + +#if (NGX_HTTP_UPSTREAM_SID || NGX_COMPAT) + ngx_str_t sid; +#endif +} ngx_http_upstream_server_t; + + +#define NGX_HTTP_UPSTREAM_CREATE 0x0001 +#define NGX_HTTP_UPSTREAM_WEIGHT 0x0002 +#define NGX_HTTP_UPSTREAM_MAX_FAILS 0x0004 +#define NGX_HTTP_UPSTREAM_FAIL_TIMEOUT 0x0008 +#define NGX_HTTP_UPSTREAM_DOWN 0x0010 +#define NGX_HTTP_UPSTREAM_BACKUP 0x0020 +#define NGX_HTTP_UPSTREAM_MODIFY 0x0040 +#define NGX_HTTP_UPSTREAM_MAX_CONNS 0x0100 + + +struct ngx_http_upstream_srv_conf_s { + ngx_http_upstream_peer_t peer; + void **srv_conf; + + ngx_array_t *servers; /* ngx_http_upstream_server_t */ + + ngx_uint_t flags; + ngx_str_t host; + u_char *file_name; + ngx_uint_t line; + in_port_t port; + ngx_uint_t no_port; /* unsigned no_port:1 */ + +#if (NGX_HTTP_UPSTREAM_ZONE) + ngx_shm_zone_t *shm_zone; + ngx_resolver_t *resolver; + ngx_msec_t resolver_timeout; +#endif +}; + + +typedef struct { + ngx_addr_t *addr; + ngx_http_complex_value_t *value; +#if (NGX_HAVE_TRANSPARENT_PROXY) + ngx_uint_t transparent; /* unsigned transparent:1; */ +#endif +} ngx_http_upstream_local_t; + + +typedef struct { + ngx_http_upstream_srv_conf_t *upstream; + + ngx_msec_t connect_timeout; + ngx_msec_t send_timeout; + ngx_msec_t read_timeout; + ngx_msec_t next_upstream_timeout; + + size_t send_lowat; + size_t buffer_size; + ngx_http_complex_value_t *limit_rate; + + size_t busy_buffers_size; + size_t max_temp_file_size; + size_t temp_file_write_size; + + size_t busy_buffers_size_conf; + size_t max_temp_file_size_conf; + size_t temp_file_write_size_conf; + + ngx_bufs_t bufs; + + ngx_uint_t ignore_headers; + ngx_uint_t next_upstream; + ngx_uint_t store_access; + ngx_uint_t next_upstream_tries; + ngx_flag_t buffering; + ngx_flag_t request_buffering; + ngx_flag_t pass_request_headers; + ngx_flag_t pass_request_body; + ngx_flag_t pass_trailers; + ngx_flag_t pass_early_hints; + + ngx_flag_t ignore_client_abort; + ngx_flag_t intercept_errors; + ngx_flag_t cyclic_temp_file; + ngx_flag_t force_ranges; + + ngx_path_t *temp_path; + + ngx_hash_t hide_headers_hash; + ngx_array_t *hide_headers; + ngx_array_t *pass_headers; + + ngx_http_upstream_local_t *local; + ngx_flag_t socket_keepalive; + +#if (NGX_HTTP_CACHE) + ngx_shm_zone_t *cache_zone; + ngx_http_complex_value_t *cache_value; + + ngx_uint_t cache_min_uses; + ngx_uint_t cache_use_stale; + ngx_uint_t cache_methods; + + off_t cache_max_range_offset; + + ngx_flag_t cache_lock; + ngx_msec_t cache_lock_timeout; + ngx_msec_t cache_lock_age; + + ngx_flag_t cache_revalidate; + ngx_flag_t cache_convert_head; + ngx_flag_t cache_background_update; + + ngx_array_t *cache_valid; + ngx_array_t *cache_bypass; + ngx_array_t *cache_purge; + ngx_array_t *no_cache; +#endif + + ngx_array_t *store_lengths; + ngx_array_t *store_values; + +#if (NGX_HTTP_CACHE) + signed cache:2; +#endif + signed store:2; + unsigned intercept_404:1; + unsigned change_buffering:1; + unsigned preserve_output:1; + +#if (NGX_HTTP_SSL || NGX_COMPAT) + ngx_ssl_t *ssl; + ngx_flag_t ssl_session_reuse; + + ngx_http_complex_value_t *ssl_name; + ngx_flag_t ssl_server_name; + ngx_flag_t ssl_verify; + + ngx_http_complex_value_t *ssl_certificate; + ngx_http_complex_value_t *ssl_certificate_key; + ngx_ssl_cache_t *ssl_certificate_cache; + ngx_array_t *ssl_passwords; +#endif + + ngx_str_t module; + + NGX_COMPAT_BEGIN(6) + NGX_COMPAT_END +} ngx_http_upstream_conf_t; + + +typedef struct { + ngx_str_t name; + ngx_http_header_handler_pt handler; + ngx_uint_t offset; + ngx_http_header_handler_pt copy_handler; + ngx_uint_t conf; + ngx_uint_t redirect; /* unsigned redirect:1; */ +} ngx_http_upstream_header_t; + + +typedef struct { + ngx_list_t headers; + ngx_list_t trailers; + + ngx_uint_t status_n; + ngx_str_t status_line; + + ngx_table_elt_t *status; + ngx_table_elt_t *date; + ngx_table_elt_t *server; + ngx_table_elt_t *connection; + + ngx_table_elt_t *expires; + ngx_table_elt_t *etag; + ngx_table_elt_t *x_accel_expires; + ngx_table_elt_t *x_accel_redirect; + ngx_table_elt_t *x_accel_limit_rate; + + ngx_table_elt_t *content_type; + ngx_table_elt_t *content_length; + + ngx_table_elt_t *last_modified; + ngx_table_elt_t *location; + ngx_table_elt_t *refresh; + ngx_table_elt_t *www_authenticate; + ngx_table_elt_t *transfer_encoding; + ngx_table_elt_t *vary; + + ngx_table_elt_t *cache_control; + ngx_table_elt_t *set_cookie; + + off_t content_length_n; + time_t last_modified_time; + + unsigned connection_close:1; + unsigned chunked:1; + unsigned no_cache:1; + unsigned expired:1; +} ngx_http_upstream_headers_in_t; + + +typedef struct { + ngx_str_t host; + in_port_t port; + ngx_uint_t no_port; /* unsigned no_port:1 */ + + ngx_uint_t naddrs; + ngx_resolver_addr_t *addrs; + + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_str_t name; + + ngx_resolver_ctx_t *ctx; +} ngx_http_upstream_resolved_t; + + +typedef void (*ngx_http_upstream_handler_pt)(ngx_http_request_t *r, + ngx_http_upstream_t *u); + + +struct ngx_http_upstream_s { + ngx_http_upstream_handler_pt read_event_handler; + ngx_http_upstream_handler_pt write_event_handler; + + ngx_peer_connection_t peer; + + ngx_event_pipe_t *pipe; + + ngx_chain_t *request_bufs; + + ngx_output_chain_ctx_t output; + ngx_chain_writer_ctx_t writer; + + ngx_http_upstream_conf_t *conf; + ngx_http_upstream_srv_conf_t *upstream; +#if (NGX_HTTP_CACHE) + ngx_array_t *caches; +#endif + + ngx_http_upstream_headers_in_t headers_in; + + ngx_http_upstream_resolved_t *resolved; + + ngx_buf_t from_client; + + ngx_buf_t buffer; + off_t length; + off_t early_hints_length; + + ngx_chain_t *out_bufs; + ngx_chain_t *busy_bufs; + ngx_chain_t *free_bufs; + + ngx_int_t (*input_filter_init)(void *data); + ngx_int_t (*input_filter)(void *data, ssize_t bytes); + void *input_filter_ctx; + +#if (NGX_HTTP_CACHE) + ngx_int_t (*create_key)(ngx_http_request_t *r); +#endif + ngx_int_t (*create_request)(ngx_http_request_t *r); + ngx_int_t (*reinit_request)(ngx_http_request_t *r); + ngx_int_t (*process_header)(ngx_http_request_t *r); + void (*abort_request)(ngx_http_request_t *r); + void (*finalize_request)(ngx_http_request_t *r, + ngx_int_t rc); + ngx_int_t (*rewrite_redirect)(ngx_http_request_t *r, + ngx_table_elt_t *h, size_t prefix); + ngx_int_t (*rewrite_cookie)(ngx_http_request_t *r, + ngx_table_elt_t *h); + + ngx_msec_t start_time; + + ngx_http_upstream_state_t *state; + + ngx_str_t method; + ngx_str_t schema; + ngx_str_t uri; + +#if (NGX_HTTP_SSL || NGX_COMPAT) + ngx_str_t ssl_name; + ngx_str_t ssl_alpn_protocol; +#endif + + ngx_http_cleanup_pt *cleanup; + + unsigned store:1; + unsigned cacheable:1; + unsigned accel:1; + unsigned ssl:1; +#if (NGX_HTTP_CACHE) + unsigned cache_status:3; +#endif + + unsigned buffering:1; + unsigned keepalive:1; + unsigned upgrade:1; + unsigned error:1; + + unsigned request_sent:1; + unsigned request_body_sent:1; + unsigned request_body_blocked:1; + unsigned header_sent:1; + unsigned response_received:1; +}; + + +typedef struct { + ngx_uint_t status; + ngx_uint_t mask; +} ngx_http_upstream_next_t; + + +typedef struct { + ngx_str_t key; + ngx_str_t value; + ngx_uint_t skip_empty; +} ngx_http_upstream_param_t; + + +ngx_int_t ngx_http_upstream_create(ngx_http_request_t *r); +void ngx_http_upstream_init(ngx_http_request_t *r); +ngx_int_t ngx_http_upstream_non_buffered_filter_init(void *data); +ngx_int_t ngx_http_upstream_non_buffered_filter(void *data, ssize_t bytes); +ngx_http_upstream_srv_conf_t *ngx_http_upstream_add(ngx_conf_t *cf, + ngx_url_t *u, ngx_uint_t flags); +char *ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +char *ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +ngx_int_t ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf, + ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev, + ngx_str_t *default_hide_headers, ngx_hash_init_t *hash); +#if (NGX_HTTP_SSL) +ngx_int_t ngx_http_upstream_merge_ssl_passwords(ngx_conf_t *cf, + ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev); +#endif + + +#define ngx_http_conf_upstream_srv_conf(uscf, module) \ + uscf->srv_conf[module.ctx_index] + + +extern ngx_module_t ngx_http_upstream_module; +extern ngx_conf_bitmask_t ngx_http_upstream_cache_method_mask[]; +extern ngx_conf_bitmask_t ngx_http_upstream_ignore_headers_masks[]; + + +#endif /* _NGX_HTTP_UPSTREAM_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/http/ngx_http_upstream_round_robin.c b/src/deps/src/nginx-1.30.1/src/http/ngx_http_upstream_round_robin.c new file mode 100644 index 000000000..412281767 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/ngx_http_upstream_round_robin.c @@ -0,0 +1,1267 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include + + +#define ngx_http_upstream_tries(p) ((p)->tries \ + + ((p)->next ? (p)->next->tries : 0)) + + +#if (NGX_HTTP_UPSTREAM_SID) +static ngx_int_t ngx_http_upstream_create_sid(ngx_conf_t *cf, + ngx_http_upstream_rr_peer_t *peer, ngx_str_t *route); +#endif + +static ngx_http_upstream_rr_peer_t *ngx_http_upstream_get_peer( + ngx_http_upstream_rr_peer_data_t *rrp, ngx_peer_connection_t *pc); + +#if (NGX_HTTP_SSL) + +static ngx_int_t ngx_http_upstream_empty_set_session(ngx_peer_connection_t *pc, + void *data); +static void ngx_http_upstream_empty_save_session(ngx_peer_connection_t *pc, + void *data); + +#endif + + +ngx_int_t +ngx_http_upstream_init_round_robin(ngx_conf_t *cf, + ngx_http_upstream_srv_conf_t *us) +{ + ngx_url_t u; + ngx_uint_t i, j, n, r, w, t; + ngx_http_upstream_server_t *server; + ngx_http_upstream_rr_peer_t *peer, **peerp; + ngx_http_upstream_rr_peers_t *peers, *backup; +#if (NGX_HTTP_UPSTREAM_ZONE) + ngx_uint_t resolve; + ngx_http_core_loc_conf_t *clcf; + ngx_http_upstream_rr_peer_t **rpeerp; +#endif + + us->peer.init = ngx_http_upstream_init_round_robin_peer; + + if (us->servers) { + server = us->servers->elts; + + n = 0; + r = 0; + w = 0; + t = 0; + +#if (NGX_HTTP_UPSTREAM_ZONE) + resolve = 0; +#endif + + for (i = 0; i < us->servers->nelts; i++) { + +#if (NGX_HTTP_UPSTREAM_ZONE) + if (server[i].host.len) { + resolve = 1; + } +#endif + + if (server[i].backup) { + continue; + } + +#if (NGX_HTTP_UPSTREAM_ZONE) + if (server[i].host.len) { + r++; + continue; + } +#endif + + n += server[i].naddrs; + w += server[i].naddrs * server[i].weight; + + if (!server[i].down) { + t += server[i].naddrs; + } + } + +#if (NGX_HTTP_UPSTREAM_ZONE) + if (us->shm_zone) { + + if (resolve && !(us->flags & NGX_HTTP_UPSTREAM_MODIFY)) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "load balancing method does not support" + " resolving names at run time in" + " upstream \"%V\" in %s:%ui", + &us->host, us->file_name, us->line); + return NGX_ERROR; + } + + clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); + + if (us->resolver == NULL) { + us->resolver = clcf->resolver; + } + + /* + * Without "resolver_timeout" in http{} the merged value is unset. + */ + ngx_conf_merge_msec_value(us->resolver_timeout, + clcf->resolver_timeout, 30000); + + if (resolve + && (us->resolver == NULL + || us->resolver->connections.nelts == 0)) + { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no resolver defined to resolve names" + " at run time in upstream \"%V\" in %s:%ui", + &us->host, us->file_name, us->line); + return NGX_ERROR; + } + + } else if (resolve) { + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "resolving names at run time requires" + " upstream \"%V\" in %s:%ui" + " to be in shared memory", + &us->host, us->file_name, us->line); + return NGX_ERROR; + } +#endif + + if (n + r == 0) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no servers in upstream \"%V\" in %s:%ui", + &us->host, us->file_name, us->line); + return NGX_ERROR; + } + + peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t)); + if (peers == NULL) { + return NGX_ERROR; + } + + peer = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peer_t) + * (n + r)); + if (peer == NULL) { + return NGX_ERROR; + } + + peers->single = (n == 1); + peers->number = n; + peers->weighted = (w != n); + peers->total_weight = w; + peers->tries = t; + peers->name = &us->host; + + n = 0; + peerp = &peers->peer; + +#if (NGX_HTTP_UPSTREAM_ZONE) + rpeerp = &peers->resolve; +#endif + + for (i = 0; i < us->servers->nelts; i++) { + if (server[i].backup) { + continue; + } + +#if (NGX_HTTP_UPSTREAM_ZONE) + if (server[i].host.len) { + + peer[n].host = ngx_pcalloc(cf->pool, + sizeof(ngx_http_upstream_host_t)); + if (peer[n].host == NULL) { + return NGX_ERROR; + } + + peer[n].host->name = server[i].host; + peer[n].host->service = server[i].service; + + peer[n].sockaddr = server[i].addrs[0].sockaddr; + peer[n].socklen = server[i].addrs[0].socklen; + peer[n].name = server[i].addrs[0].name; + peer[n].weight = server[i].weight; + peer[n].effective_weight = server[i].weight; + peer[n].current_weight = 0; + peer[n].max_conns = server[i].max_conns; + peer[n].max_fails = server[i].max_fails; + peer[n].fail_timeout = server[i].fail_timeout; + peer[n].down = server[i].down; + peer[n].server = server[i].name; + +#if (NGX_HTTP_UPSTREAM_SID) + if (ngx_http_upstream_create_sid(cf, &peer[n], &server[i].sid) + != NGX_OK) + { + return NGX_ERROR; + } +#endif + + *rpeerp = &peer[n]; + rpeerp = &peer[n].next; + n++; + + continue; + } +#endif + + for (j = 0; j < server[i].naddrs; j++) { + peer[n].sockaddr = server[i].addrs[j].sockaddr; + peer[n].socklen = server[i].addrs[j].socklen; + peer[n].name = server[i].addrs[j].name; + peer[n].weight = server[i].weight; + peer[n].effective_weight = server[i].weight; + peer[n].current_weight = 0; + peer[n].max_conns = server[i].max_conns; + peer[n].max_fails = server[i].max_fails; + peer[n].fail_timeout = server[i].fail_timeout; + peer[n].down = server[i].down; + peer[n].server = server[i].name; + +#if (NGX_HTTP_UPSTREAM_SID) + if (ngx_http_upstream_create_sid(cf, &peer[n], &server[i].sid) + != NGX_OK) + { + return NGX_ERROR; + } +#endif + + *peerp = &peer[n]; + peerp = &peer[n].next; + n++; + } + } + + us->peer.data = peers; + + /* backup servers */ + + n = 0; + r = 0; + w = 0; + t = 0; + + for (i = 0; i < us->servers->nelts; i++) { + if (!server[i].backup) { + continue; + } + +#if (NGX_HTTP_UPSTREAM_ZONE) + if (server[i].host.len) { + r++; + continue; + } +#endif + + n += server[i].naddrs; + w += server[i].naddrs * server[i].weight; + + if (!server[i].down) { + t += server[i].naddrs; + } + } + + if (n == 0 +#if (NGX_HTTP_UPSTREAM_ZONE) + && !resolve +#endif + ) { + return NGX_OK; + } + + if (n + r == 0 && !(us->flags & NGX_HTTP_UPSTREAM_BACKUP)) { + return NGX_OK; + } + + backup = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t)); + if (backup == NULL) { + return NGX_ERROR; + } + + peer = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peer_t) + * (n + r)); + if (peer == NULL) { + return NGX_ERROR; + } + + if (n > 0) { + peers->single = 0; + } + + backup->single = 0; + backup->number = n; + backup->weighted = (w != n); + backup->total_weight = w; + backup->tries = t; + backup->name = &us->host; + + n = 0; + peerp = &backup->peer; + +#if (NGX_HTTP_UPSTREAM_ZONE) + rpeerp = &backup->resolve; +#endif + + for (i = 0; i < us->servers->nelts; i++) { + if (!server[i].backup) { + continue; + } + +#if (NGX_HTTP_UPSTREAM_ZONE) + if (server[i].host.len) { + + peer[n].host = ngx_pcalloc(cf->pool, + sizeof(ngx_http_upstream_host_t)); + if (peer[n].host == NULL) { + return NGX_ERROR; + } + + peer[n].host->name = server[i].host; + peer[n].host->service = server[i].service; + + peer[n].sockaddr = server[i].addrs[0].sockaddr; + peer[n].socklen = server[i].addrs[0].socklen; + peer[n].name = server[i].addrs[0].name; + peer[n].weight = server[i].weight; + peer[n].effective_weight = server[i].weight; + peer[n].current_weight = 0; + peer[n].max_conns = server[i].max_conns; + peer[n].max_fails = server[i].max_fails; + peer[n].fail_timeout = server[i].fail_timeout; + peer[n].down = server[i].down; + peer[n].server = server[i].name; + +#if (NGX_HTTP_UPSTREAM_SID) + if (ngx_http_upstream_create_sid(cf, &peer[n], &server[i].sid) + != NGX_OK) + { + return NGX_ERROR; + } +#endif + + *rpeerp = &peer[n]; + rpeerp = &peer[n].next; + n++; + + continue; + } +#endif + + for (j = 0; j < server[i].naddrs; j++) { + peer[n].sockaddr = server[i].addrs[j].sockaddr; + peer[n].socklen = server[i].addrs[j].socklen; + peer[n].name = server[i].addrs[j].name; + peer[n].weight = server[i].weight; + peer[n].effective_weight = server[i].weight; + peer[n].current_weight = 0; + peer[n].max_conns = server[i].max_conns; + peer[n].max_fails = server[i].max_fails; + peer[n].fail_timeout = server[i].fail_timeout; + peer[n].down = server[i].down; + peer[n].server = server[i].name; + +#if (NGX_HTTP_UPSTREAM_SID) + if (ngx_http_upstream_create_sid(cf, &peer[n], &server[i].sid) + != NGX_OK) + { + return NGX_ERROR; + } +#endif + + *peerp = &peer[n]; + peerp = &peer[n].next; + n++; + } + } + + peers->next = backup; + + return NGX_OK; + } + + + /* an upstream implicitly defined by proxy_pass, etc. */ + + if (us->port == 0) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no port in upstream \"%V\" in %s:%ui", + &us->host, us->file_name, us->line); + return NGX_ERROR; + } + + ngx_memzero(&u, sizeof(ngx_url_t)); + + u.host = us->host; + u.port = us->port; + + if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) { + if (u.err) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "%s in upstream \"%V\" in %s:%ui", + u.err, &us->host, us->file_name, us->line); + } + + return NGX_ERROR; + } + + n = u.naddrs; + + peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t)); + if (peers == NULL) { + return NGX_ERROR; + } + + peer = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peer_t) * n); + if (peer == NULL) { + return NGX_ERROR; + } + + peers->single = (n == 1); + peers->number = n; + peers->weighted = 0; + peers->total_weight = n; + peers->tries = n; + peers->name = &us->host; + + peerp = &peers->peer; + + for (i = 0; i < u.naddrs; i++) { + peer[i].sockaddr = u.addrs[i].sockaddr; + peer[i].socklen = u.addrs[i].socklen; + peer[i].name = u.addrs[i].name; + peer[i].weight = 1; + peer[i].effective_weight = 1; + peer[i].current_weight = 0; + peer[i].max_conns = 0; + peer[i].max_fails = 1; + peer[i].fail_timeout = 10; + *peerp = &peer[i]; + peerp = &peer[i].next; + } + + us->peer.data = peers; + + /* implicitly defined upstream has no backup servers */ + + return NGX_OK; +} + + +#if (NGX_HTTP_UPSTREAM_SID) + +static ngx_int_t +ngx_http_upstream_create_sid(ngx_conf_t *cf, ngx_http_upstream_rr_peer_t *peer, + ngx_str_t *route) +{ + if (route->len) { + peer->route = 1; + peer->sid = *route; + return NGX_OK; + } + + peer->sid.data = ngx_pnalloc(cf->pool, NGX_HTTP_UPSTREAM_SID_LEN); + if (peer->sid.data == NULL) { + return NGX_ERROR; + } + + ngx_http_upstream_init_round_robin_sid(peer, NULL); + + return NGX_OK; +} + + +void +ngx_http_upstream_init_round_robin_sid(ngx_http_upstream_rr_peer_t *peer, + ngx_str_t *route) +{ + u_char hash[16]; + ngx_md5_t md5; + + if (route && route->len) { + peer->route = 1; + peer->sid.len = route->len; + ngx_memcpy(peer->sid.data, route->data, route->len); + return; + } + + peer->route = 0; + + /* SID is the MD5 hash of a printable socket address */ + + if (peer->name.len == 0) { + peer->sid.len = 0; + return; + } + + ngx_md5_init(&md5); + ngx_md5_update(&md5, peer->name.data, peer->name.len); + ngx_md5_final(hash, &md5); + + ngx_hex_dump(peer->sid.data, hash, 16); + peer->sid.len = NGX_HTTP_UPSTREAM_SID_LEN; +} + +#endif + + +ngx_int_t +ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r, + ngx_http_upstream_srv_conf_t *us) +{ + ngx_uint_t n; + ngx_http_upstream_rr_peer_data_t *rrp; + + rrp = r->upstream->peer.data; + + if (rrp == NULL) { + rrp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_rr_peer_data_t)); + if (rrp == NULL) { + return NGX_ERROR; + } + + r->upstream->peer.data = rrp; + } + + rrp->peers = us->peer.data; + rrp->current = NULL; + + ngx_http_upstream_rr_peers_rlock(rrp->peers); + +#if (NGX_HTTP_UPSTREAM_ZONE) + rrp->config = rrp->peers->config ? *rrp->peers->config : 0; +#endif + + n = rrp->peers->number; + + if (rrp->peers->next && rrp->peers->next->number > n) { + n = rrp->peers->next->number; + } + + r->upstream->peer.tries = ngx_http_upstream_tries(rrp->peers); + + ngx_http_upstream_rr_peers_unlock(rrp->peers); + + if (n <= 8 * sizeof(uintptr_t)) { + rrp->tried = &rrp->data; + rrp->data = 0; + + } else { + n = (n + (8 * sizeof(uintptr_t) - 1)) / (8 * sizeof(uintptr_t)); + + rrp->tried = ngx_pcalloc(r->pool, n * sizeof(uintptr_t)); + if (rrp->tried == NULL) { + return NGX_ERROR; + } + } + + r->upstream->peer.get = ngx_http_upstream_get_round_robin_peer; + r->upstream->peer.free = ngx_http_upstream_free_round_robin_peer; +#if (NGX_HTTP_SSL) + r->upstream->peer.set_session = + ngx_http_upstream_set_round_robin_peer_session; + r->upstream->peer.save_session = + ngx_http_upstream_save_round_robin_peer_session; +#endif + + return NGX_OK; +} + + +ngx_int_t +ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r, + ngx_http_upstream_resolved_t *ur) +{ + u_char *p; + size_t len; + socklen_t socklen; + ngx_uint_t i, n; + struct sockaddr *sockaddr; + ngx_http_upstream_rr_peer_t *peer, **peerp; + ngx_http_upstream_rr_peers_t *peers; + ngx_http_upstream_rr_peer_data_t *rrp; + + rrp = r->upstream->peer.data; + + if (rrp == NULL) { + rrp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_rr_peer_data_t)); + if (rrp == NULL) { + return NGX_ERROR; + } + + r->upstream->peer.data = rrp; + } + + peers = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_rr_peers_t)); + if (peers == NULL) { + return NGX_ERROR; + } + + peer = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_rr_peer_t) + * ur->naddrs); + if (peer == NULL) { + return NGX_ERROR; + } + + peers->single = (ur->naddrs == 1); + peers->number = ur->naddrs; + peers->tries = ur->naddrs; + peers->name = &ur->host; + + if (ur->sockaddr) { + peer[0].sockaddr = ur->sockaddr; + peer[0].socklen = ur->socklen; + peer[0].name = ur->name.data ? ur->name : ur->host; + peer[0].weight = 1; + peer[0].effective_weight = 1; + peer[0].current_weight = 0; + peer[0].max_conns = 0; + peer[0].max_fails = 1; + peer[0].fail_timeout = 10; + peers->peer = peer; + + } else { + peerp = &peers->peer; + + for (i = 0; i < ur->naddrs; i++) { + + socklen = ur->addrs[i].socklen; + + sockaddr = ngx_palloc(r->pool, socklen); + if (sockaddr == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen); + ngx_inet_set_port(sockaddr, ur->port); + + p = ngx_pnalloc(r->pool, NGX_SOCKADDR_STRLEN); + if (p == NULL) { + return NGX_ERROR; + } + + len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1); + + peer[i].sockaddr = sockaddr; + peer[i].socklen = socklen; + peer[i].name.len = len; + peer[i].name.data = p; + peer[i].weight = 1; + peer[i].effective_weight = 1; + peer[i].current_weight = 0; + peer[i].max_conns = 0; + peer[i].max_fails = 1; + peer[i].fail_timeout = 10; + *peerp = &peer[i]; + peerp = &peer[i].next; + } + } + + rrp->peers = peers; + rrp->current = NULL; + rrp->config = 0; + + if (rrp->peers->number <= 8 * sizeof(uintptr_t)) { + rrp->tried = &rrp->data; + rrp->data = 0; + + } else { + n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1)) + / (8 * sizeof(uintptr_t)); + + rrp->tried = ngx_pcalloc(r->pool, n * sizeof(uintptr_t)); + if (rrp->tried == NULL) { + return NGX_ERROR; + } + } + + r->upstream->peer.get = ngx_http_upstream_get_round_robin_peer; + r->upstream->peer.free = ngx_http_upstream_free_round_robin_peer; + r->upstream->peer.tries = ngx_http_upstream_tries(rrp->peers); +#if (NGX_HTTP_SSL) + r->upstream->peer.set_session = ngx_http_upstream_empty_set_session; + r->upstream->peer.save_session = ngx_http_upstream_empty_save_session; +#endif + + return NGX_OK; +} + + +ngx_int_t +ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data) +{ + ngx_http_upstream_rr_peer_data_t *rrp = data; + + ngx_int_t rc; + ngx_uint_t i, n; + ngx_http_upstream_rr_peer_t *peer; + ngx_http_upstream_rr_peers_t *peers; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "get rr peer, try: %ui", pc->tries); + + pc->cached = 0; + pc->connection = NULL; + + peers = rrp->peers; + ngx_http_upstream_rr_peers_wlock(peers); + +#if (NGX_HTTP_UPSTREAM_ZONE) + if (peers->config && rrp->config != *peers->config) { + goto busy; + } +#endif + + if (peers->single) { +#if (NGX_HTTP_UPSTREAM_SID) + peer = ngx_http_upstream_get_rr_peer_by_sid(rrp, pc->hint, &i, 0); + + if (peer == NULL) { +#endif + peer = peers->peer; + + if (peer->down) { + goto failed; + } + + if (peer->max_conns && peer->conns >= peer->max_conns) { + goto failed; + } +#if (NGX_HTTP_UPSTREAM_SID) + } +#endif + + rrp->current = peer; + ngx_http_upstream_rr_peer_ref(peers, peer); + + } else { + + /* there are several peers */ + + peer = ngx_http_upstream_get_peer(rrp, pc); + + if (peer == NULL) { + goto failed; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "get rr peer, current: %p %i", + peer, peer->current_weight); + } + + pc->sockaddr = peer->sockaddr; + pc->socklen = peer->socklen; + pc->name = &peer->name; + +#if (NGX_HTTP_UPSTREAM_SID) + pc->sid = &peer->sid; +#endif + + peer->conns++; + + ngx_http_upstream_rr_peers_unlock(peers); + + return NGX_OK; + +failed: + + if (peers->next) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, "backup servers"); + + rrp->peers = peers->next; + + n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1)) + / (8 * sizeof(uintptr_t)); + + for (i = 0; i < n; i++) { + rrp->tried[i] = 0; + } + + ngx_http_upstream_rr_peers_unlock(peers); + + rc = ngx_http_upstream_get_round_robin_peer(pc, rrp); + + if (rc != NGX_BUSY) { + return rc; + } + + ngx_http_upstream_rr_peers_wlock(peers); + } + +#if (NGX_HTTP_UPSTREAM_ZONE) +busy: +#endif + + ngx_http_upstream_rr_peers_unlock(peers); + + pc->name = peers->name; + + return NGX_BUSY; +} + + +static ngx_http_upstream_rr_peer_t * +ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp, + ngx_peer_connection_t *pc) +{ + time_t now; + uintptr_t m; + ngx_int_t total; + ngx_uint_t i, n, p; + ngx_http_upstream_rr_peer_t *peer, *best; + +#if (NGX_HTTP_UPSTREAM_SID) + ngx_int_t low_limit; + ngx_uint_t st_p; + ngx_http_upstream_rr_peer_t *st_peer; +#endif + + now = ngx_time(); + + best = NULL; + total = 0; + +#if (NGX_SUPPRESS_WARN) + p = 0; +#endif + +#if (NGX_HTTP_UPSTREAM_SID) + st_peer = ngx_http_upstream_get_rr_peer_by_sid(rrp, pc->hint, &p, 0); + + if (st_peer) { + + low_limit = -((ngx_int_t)(rrp->peers->total_weight - st_peer->weight)); + + /* + * note: current code accounts only one sticky request in a row, if it + * is required to account more, multiply low_limit by N below + */ + if (st_peer->current_weight <= low_limit) { + + /* do not update weights if the limit exceeded */ + best = st_peer; + goto best_chosen; + } + /* else: proceed to reweight with existing st_peer */ + } + + st_p = p; +#endif + + for (peer = rrp->peers->peer, i = 0; + peer; + peer = peer->next, i++) + { + n = i / (8 * sizeof(uintptr_t)); + m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); + + if (rrp->tried[n] & m) { + continue; + } + + if (peer->down) { + continue; + } + + if (peer->max_fails + && peer->fails >= peer->max_fails + && now - peer->checked <= peer->fail_timeout) + { + continue; + } + + if (peer->max_conns && peer->conns >= peer->max_conns) { + continue; + } + + peer->current_weight += peer->effective_weight; + total += peer->effective_weight; + + if (peer->effective_weight < peer->weight) { + peer->effective_weight++; + } + + if (best == NULL || peer->current_weight > best->current_weight) { + best = peer; + p = i; + } + } + +#if (NGX_HTTP_UPSTREAM_SID) + /* prefer peer chosen by sticky to best from RR */ + + if (st_peer) { + best = st_peer; + p = st_p; + } +#endif + + if (best == NULL) { + return NULL; + } + + best->current_weight -= total; + +#if (NGX_HTTP_UPSTREAM_SID) +best_chosen: +#endif + + rrp->current = best; + ngx_http_upstream_rr_peer_ref(rrp->peers, best); + + n = p / (8 * sizeof(uintptr_t)); + m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t)); + + rrp->tried[n] |= m; + + if (now - best->checked > best->fail_timeout) { + best->checked = now; + } + + return best; +} + + +#if (NGX_HTTP_UPSTREAM_SID) + +ngx_http_upstream_rr_peer_t * +ngx_http_upstream_get_rr_peer_by_sid(ngx_http_upstream_rr_peer_data_t *rrp, + ngx_str_t *hint, ngx_uint_t *p, ngx_uint_t lock) +{ + uintptr_t m; + ngx_uint_t i, n; + ngx_http_upstream_rr_peer_t *peer; + + if (hint == NULL) { + return NULL; + } + + for (peer = rrp->peers->peer, i = 0; + peer; + peer = peer->next, i++) + { + + if (peer->sid.len == hint->len + && ngx_memcmp(peer->sid.data, hint->data, hint->len) == 0) + { + goto found; + } + } + + return NULL; + +found: + + n = i / (8 * sizeof(uintptr_t)); + m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); + + if (rrp->tried[n] & m) { + return NULL; + } + + if (lock) { + ngx_http_upstream_rr_peer_lock(rrp->peers, peer); + } + + if (peer->down +#if (NGX_HTTP_UPSTREAM_STICKY) + & ~NGX_HTTP_UPSTREAM_DRAINING +#endif + ) { + goto failed; + } + + if (peer->max_fails + && peer->fails >= peer->max_fails + && ngx_time() - peer->checked <= peer->fail_timeout) + { + goto failed; + } + + if (peer->max_conns && peer->conns >= peer->max_conns) { + goto failed; + } + + *p = i; + return peer; + +failed: + + if (lock) { + ngx_http_upstream_rr_peer_unlock(rrp->peers, peer); + } + + return NULL; +} + +#endif + + +void +ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data, + ngx_uint_t state) +{ + ngx_http_upstream_rr_peer_data_t *rrp = data; + + time_t now; + ngx_http_upstream_rr_peer_t *peer; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "free rr peer %ui %ui", pc->tries, state); + + /* TODO: NGX_PEER_KEEPALIVE */ + + peer = rrp->current; + + ngx_http_upstream_rr_peers_rlock(rrp->peers); + ngx_http_upstream_rr_peer_lock(rrp->peers, peer); + + if (rrp->peers->single) { + + if (peer->fails) { + peer->fails = 0; + } + + peer->conns--; + + if (ngx_http_upstream_rr_peer_unref(rrp->peers, peer) == NGX_OK) { + ngx_http_upstream_rr_peer_unlock(rrp->peers, peer); + } + + ngx_http_upstream_rr_peers_unlock(rrp->peers); + + pc->tries = 0; + return; + } + + if (state & NGX_PEER_FAILED) { + now = ngx_time(); + + peer->fails++; + peer->accessed = now; + peer->checked = now; + + if (peer->max_fails) { + peer->effective_weight -= peer->weight / peer->max_fails; + + if (peer->fails >= peer->max_fails) { + ngx_log_error(NGX_LOG_WARN, pc->log, 0, + "upstream server temporarily disabled"); + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "free rr peer failed: %p %i", + peer, peer->effective_weight); + + if (peer->effective_weight < 0) { + peer->effective_weight = 0; + } + + } else { + + /* mark peer live if check passed */ + + if (peer->accessed < peer->checked) { + peer->fails = 0; + } + } + + peer->conns--; + + if (ngx_http_upstream_rr_peer_unref(rrp->peers, peer) == NGX_OK) { + ngx_http_upstream_rr_peer_unlock(rrp->peers, peer); + } + + ngx_http_upstream_rr_peers_unlock(rrp->peers); + + if (pc->tries) { + pc->tries--; + } +} + + +#if (NGX_HTTP_SSL) + +ngx_int_t +ngx_http_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc, + void *data) +{ + ngx_http_upstream_rr_peer_data_t *rrp = data; + + ngx_int_t rc; + ngx_ssl_session_t *ssl_session; + ngx_http_upstream_rr_peer_t *peer; +#if (NGX_HTTP_UPSTREAM_ZONE) + int len; + const u_char *p; + ngx_http_upstream_rr_peers_t *peers; +#endif + + peer = rrp->current; + +#if (NGX_HTTP_UPSTREAM_ZONE) + peers = rrp->peers; + + if (peers->shpool) { + ngx_http_upstream_rr_peers_rlock(peers); + ngx_http_upstream_rr_peer_lock(peers, peer); + + if (peer->ssl_session == NULL) { + ngx_http_upstream_rr_peer_unlock(peers, peer); + ngx_http_upstream_rr_peers_unlock(peers); + return NGX_OK; + } + + len = peer->ssl_session_len; + + ngx_memcpy(ngx_ssl_session_buffer, peer->ssl_session, len); + + ngx_http_upstream_rr_peer_unlock(peers, peer); + ngx_http_upstream_rr_peers_unlock(peers); + + p = ngx_ssl_session_buffer; + ssl_session = d2i_SSL_SESSION(NULL, &p, len); + + rc = ngx_ssl_set_session(pc->connection, ssl_session); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "set session: %p", ssl_session); + + ngx_ssl_free_session(ssl_session); + + return rc; + } +#endif + + ssl_session = peer->ssl_session; + + rc = ngx_ssl_set_session(pc->connection, ssl_session); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "set session: %p", ssl_session); + + return rc; +} + + +void +ngx_http_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc, + void *data) +{ + ngx_http_upstream_rr_peer_data_t *rrp = data; + + ngx_ssl_session_t *old_ssl_session, *ssl_session; + ngx_http_upstream_rr_peer_t *peer; +#if (NGX_HTTP_UPSTREAM_ZONE) + int len; + u_char *p; + ngx_http_upstream_rr_peers_t *peers; +#endif + +#if (NGX_HTTP_UPSTREAM_ZONE) + peers = rrp->peers; + + if (peers->shpool) { + + ssl_session = ngx_ssl_get0_session(pc->connection); + + if (ssl_session == NULL) { + return; + } + + len = i2d_SSL_SESSION(ssl_session, NULL); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "save session: %p:%d", ssl_session, len); + + /* do not cache too big session */ + + if (len > NGX_SSL_MAX_SESSION_SIZE) { + return; + } + + p = ngx_ssl_session_buffer; + (void) i2d_SSL_SESSION(ssl_session, &p); + + peer = rrp->current; + + ngx_http_upstream_rr_peers_rlock(peers); + ngx_http_upstream_rr_peer_lock(peers, peer); + + if (len > peer->ssl_session_len) { + ngx_shmtx_lock(&peers->shpool->mutex); + + if (peer->ssl_session) { + ngx_slab_free_locked(peers->shpool, peer->ssl_session); + } + + peer->ssl_session = ngx_slab_alloc_locked(peers->shpool, len); + + ngx_shmtx_unlock(&peers->shpool->mutex); + + if (peer->ssl_session == NULL) { + peer->ssl_session_len = 0; + + ngx_http_upstream_rr_peer_unlock(peers, peer); + ngx_http_upstream_rr_peers_unlock(peers); + return; + } + + peer->ssl_session_len = len; + } + + ngx_memcpy(peer->ssl_session, ngx_ssl_session_buffer, len); + + ngx_http_upstream_rr_peer_unlock(peers, peer); + ngx_http_upstream_rr_peers_unlock(peers); + + return; + } +#endif + + ssl_session = ngx_ssl_get_session(pc->connection); + + if (ssl_session == NULL) { + return; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "save session: %p", ssl_session); + + peer = rrp->current; + + old_ssl_session = peer->ssl_session; + peer->ssl_session = ssl_session; + + if (old_ssl_session) { + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, + "old session: %p", old_ssl_session); + + ngx_ssl_free_session(old_ssl_session); + } +} + + +static ngx_int_t +ngx_http_upstream_empty_set_session(ngx_peer_connection_t *pc, void *data) +{ + return NGX_OK; +} + + +static void +ngx_http_upstream_empty_save_session(ngx_peer_connection_t *pc, void *data) +{ + return; +} + +#endif diff --git a/src/deps/src/nginx-1.30.1/src/http/ngx_http_upstream_round_robin.h b/src/deps/src/nginx-1.30.1/src/http/ngx_http_upstream_round_robin.h new file mode 100644 index 000000000..95c7ae190 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/ngx_http_upstream_round_robin.h @@ -0,0 +1,275 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_ +#define _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_ + + +#include +#include +#include + + +#if (NGX_HTTP_UPSTREAM_SID) +#define NGX_HTTP_UPSTREAM_SID_LEN 32 /* md5 in hex */ +#endif + +#define NGX_HTTP_UPSTREAM_FAILED 1 + +#if (NGX_HTTP_UPSTREAM_STICKY) +#define NGX_HTTP_UPSTREAM_DRAINING 8 +#endif + + +typedef struct ngx_http_upstream_rr_peers_s ngx_http_upstream_rr_peers_t; +typedef struct ngx_http_upstream_rr_peer_s ngx_http_upstream_rr_peer_t; + + +#if (NGX_HTTP_UPSTREAM_ZONE) + +typedef struct { + ngx_event_t event; /* must be first */ + ngx_uint_t worker; + ngx_str_t name; + ngx_str_t service; + time_t valid; + ngx_http_upstream_rr_peers_t *peers; + ngx_http_upstream_rr_peer_t *peer; +} ngx_http_upstream_host_t; + +#endif + + +struct ngx_http_upstream_rr_peer_s { + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_str_t name; + ngx_str_t server; + + ngx_int_t current_weight; + ngx_int_t effective_weight; + ngx_int_t weight; + + ngx_uint_t conns; + ngx_uint_t max_conns; + + ngx_uint_t fails; + time_t accessed; + time_t checked; + + ngx_uint_t max_fails; + time_t fail_timeout; + ngx_msec_t slow_start; + ngx_msec_t start_time; + + ngx_uint_t down; + +#if (NGX_HTTP_SSL || NGX_COMPAT) + void *ssl_session; + int ssl_session_len; +#endif + +#if (NGX_HTTP_UPSTREAM_SID || NGX_COMPAT) + unsigned route:1; +#endif + +#if (NGX_HTTP_UPSTREAM_ZONE) + unsigned zombie:1; + + ngx_atomic_t lock; + ngx_uint_t refs; + ngx_http_upstream_host_t *host; +#endif + +#if (NGX_HTTP_UPSTREAM_SID || NGX_COMPAT) + ngx_str_t sid; +#endif + + ngx_http_upstream_rr_peer_t *next; + + NGX_COMPAT_BEGIN(13) + NGX_COMPAT_END +}; + + +struct ngx_http_upstream_rr_peers_s { + ngx_uint_t number; + +#if (NGX_HTTP_UPSTREAM_ZONE) + ngx_slab_pool_t *shpool; + ngx_atomic_t rwlock; + ngx_uint_t *config; + ngx_http_upstream_rr_peer_t *resolve; + ngx_http_upstream_rr_peers_t *zone_next; +#endif + + ngx_uint_t total_weight; + ngx_uint_t tries; + + unsigned single:1; + unsigned weighted:1; + + ngx_str_t *name; + + ngx_http_upstream_rr_peers_t *next; + + ngx_http_upstream_rr_peer_t *peer; +}; + + +#if (NGX_HTTP_UPSTREAM_ZONE) + +#define ngx_http_upstream_rr_peers_rlock(peers) \ + \ + if (peers->shpool) { \ + ngx_rwlock_rlock(&peers->rwlock); \ + } + +#define ngx_http_upstream_rr_peers_wlock(peers) \ + \ + if (peers->shpool) { \ + ngx_rwlock_wlock(&peers->rwlock); \ + } + +#define ngx_http_upstream_rr_peers_unlock(peers) \ + \ + if (peers->shpool) { \ + ngx_rwlock_unlock(&peers->rwlock); \ + } + + +#define ngx_http_upstream_rr_peer_lock(peers, peer) \ + \ + if (peers->shpool) { \ + ngx_rwlock_wlock(&peer->lock); \ + } + +#define ngx_http_upstream_rr_peer_unlock(peers, peer) \ + \ + if (peers->shpool) { \ + ngx_rwlock_unlock(&peer->lock); \ + } + + +#define ngx_http_upstream_rr_peer_ref(peers, peer) \ + (peer)->refs++; + + +static ngx_inline void +ngx_http_upstream_rr_peer_free_locked(ngx_http_upstream_rr_peers_t *peers, + ngx_http_upstream_rr_peer_t *peer) +{ + if (peer->refs) { + peer->zombie = 1; + return; + } + + ngx_slab_free_locked(peers->shpool, peer->sockaddr); + ngx_slab_free_locked(peers->shpool, peer->name.data); +#if (NGX_HTTP_UPSTREAM_SID) + ngx_slab_free_locked(peers->shpool, peer->sid.data); +#endif + + if (peer->server.data) { + ngx_slab_free_locked(peers->shpool, peer->server.data); + } + +#if (NGX_HTTP_SSL) + if (peer->ssl_session) { + ngx_slab_free_locked(peers->shpool, peer->ssl_session); + } +#endif + + ngx_slab_free_locked(peers->shpool, peer); +} + + +static ngx_inline void +ngx_http_upstream_rr_peer_free(ngx_http_upstream_rr_peers_t *peers, + ngx_http_upstream_rr_peer_t *peer) +{ + ngx_shmtx_lock(&peers->shpool->mutex); + ngx_http_upstream_rr_peer_free_locked(peers, peer); + ngx_shmtx_unlock(&peers->shpool->mutex); +} + + +static ngx_inline ngx_int_t +ngx_http_upstream_rr_peer_unref(ngx_http_upstream_rr_peers_t *peers, + ngx_http_upstream_rr_peer_t *peer) +{ + peer->refs--; + + if (peers->shpool == NULL) { + return NGX_OK; + } + + if (peer->refs == 0 && peer->zombie) { + ngx_http_upstream_rr_peer_free(peers, peer); + return NGX_DONE; + } + + return NGX_OK; +} + +#else + +#define ngx_http_upstream_rr_peers_rlock(peers) +#define ngx_http_upstream_rr_peers_wlock(peers) +#define ngx_http_upstream_rr_peers_unlock(peers) +#define ngx_http_upstream_rr_peer_lock(peers, peer) +#define ngx_http_upstream_rr_peer_unlock(peers, peer) +#define ngx_http_upstream_rr_peer_ref(peers, peer) +#define ngx_http_upstream_rr_peer_unref(peers, peer) NGX_OK + +#endif + + +typedef struct { + ngx_uint_t config; + ngx_http_upstream_rr_peers_t *peers; + ngx_http_upstream_rr_peer_t *current; + uintptr_t *tried; + uintptr_t data; +} ngx_http_upstream_rr_peer_data_t; + + +ngx_int_t ngx_http_upstream_init_round_robin(ngx_conf_t *cf, + ngx_http_upstream_srv_conf_t *us); +ngx_int_t ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r, + ngx_http_upstream_srv_conf_t *us); +ngx_int_t ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r, + ngx_http_upstream_resolved_t *ur); +ngx_int_t ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, + void *data); +void ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, + void *data, ngx_uint_t state); + +#if (NGX_HTTP_SSL) +ngx_int_t + ngx_http_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc, + void *data); +void ngx_http_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc, + void *data); +#endif + +#if (NGX_HTTP_UPSTREAM_SID) + +#define ngx_http_upstream_copy_round_robin_sid(dst, src) \ + ngx_http_upstream_init_round_robin_sid(dst, \ + (src)->route ? &(src)->sid : NULL) + +void ngx_http_upstream_init_round_robin_sid(ngx_http_upstream_rr_peer_t *peer, + ngx_str_t *route); +ngx_http_upstream_rr_peer_t *ngx_http_upstream_get_rr_peer_by_sid( + ngx_http_upstream_rr_peer_data_t *rrp, ngx_str_t *hint, ngx_uint_t *p, + ngx_uint_t lock); + +#endif + + +#endif /* _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/http/ngx_http_variables.c b/src/deps/src/nginx-1.30.1/src/http/ngx_http_variables.c new file mode 100644 index 000000000..37cd0d287 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/ngx_http_variables.c @@ -0,0 +1,2890 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include + + +static ngx_http_variable_t *ngx_http_add_prefix_variable(ngx_conf_t *cf, + ngx_str_t *name, ngx_uint_t flags); + +static ngx_int_t ngx_http_variable_request(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +#if 0 +static void ngx_http_variable_request_set(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +#endif +static ngx_int_t ngx_http_variable_request_get_size(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_header(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + +static ngx_int_t ngx_http_variable_cookies(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_headers_internal(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data, u_char sep); + +static ngx_int_t ngx_http_variable_unknown_header_in(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_unknown_trailer_out(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_request_line(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_cookie(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_argument(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +#if (NGX_HAVE_TCP_INFO) +static ngx_int_t ngx_http_variable_tcpinfo(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +#endif + +static ngx_int_t ngx_http_variable_content_length(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_host(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_binary_remote_addr(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_remote_addr(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_remote_port(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_proxy_protocol_addr(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_proxy_protocol_port(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_scheme(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_https(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_request_port(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_is_request_port(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static void ngx_http_variable_set_args(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_is_args(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_document_root(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_realpath_root(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_request_filename(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_server_name(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_request_method(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_remote_user(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_bytes_sent(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_body_bytes_sent(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_pipe(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_request_completion(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_request_body(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_request_body_file(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_request_length(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_request_time(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_request_id(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_status(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + +static ngx_int_t ngx_http_variable_sent_content_type(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_sent_content_length(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_sent_location(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_sent_last_modified(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_sent_connection(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_sent_keep_alive(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_sent_transfer_encoding(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static void ngx_http_variable_set_limit_rate(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + +static ngx_int_t ngx_http_variable_connection(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_connection_requests(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_connection_time(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + +static ngx_int_t ngx_http_variable_nginx_version(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_hostname(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_pid(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_msec(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_time_iso8601(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_time_local(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + +/* + * TODO: + * Apache CGI: AUTH_TYPE, PATH_INFO (null), PATH_TRANSLATED + * REMOTE_HOST (null), REMOTE_IDENT (null), + * SERVER_SOFTWARE + * + * Apache SSI: DOCUMENT_NAME, LAST_MODIFIED, USER_NAME (file owner) + */ + +/* + * the $http_host, $http_user_agent, $http_referer, and $http_via + * variables may be handled by generic + * ngx_http_variable_unknown_header_in(), but for performance reasons + * they are handled using dedicated entries + */ + +static ngx_http_variable_t ngx_http_core_variables[] = { + + { ngx_string("http_host"), NULL, ngx_http_variable_header, + offsetof(ngx_http_request_t, headers_in.host), 0, 0 }, + + { ngx_string("http_user_agent"), NULL, ngx_http_variable_header, + offsetof(ngx_http_request_t, headers_in.user_agent), 0, 0 }, + + { ngx_string("http_referer"), NULL, ngx_http_variable_header, + offsetof(ngx_http_request_t, headers_in.referer), 0, 0 }, + +#if (NGX_HTTP_GZIP) + { ngx_string("http_via"), NULL, ngx_http_variable_header, + offsetof(ngx_http_request_t, headers_in.via), 0, 0 }, +#endif + +#if (NGX_HTTP_X_FORWARDED_FOR) + { ngx_string("http_x_forwarded_for"), NULL, ngx_http_variable_header, + offsetof(ngx_http_request_t, headers_in.x_forwarded_for), 0, 0 }, +#endif + + { ngx_string("http_cookie"), NULL, ngx_http_variable_cookies, + offsetof(ngx_http_request_t, headers_in.cookie), 0, 0 }, + + { ngx_string("content_length"), NULL, ngx_http_variable_content_length, + 0, 0, 0 }, + + { ngx_string("content_type"), NULL, ngx_http_variable_header, + offsetof(ngx_http_request_t, headers_in.content_type), 0, 0 }, + + { ngx_string("host"), NULL, ngx_http_variable_host, 0, 0, 0 }, + + { ngx_string("binary_remote_addr"), NULL, + ngx_http_variable_binary_remote_addr, 0, 0, 0 }, + + { ngx_string("remote_addr"), NULL, ngx_http_variable_remote_addr, 0, 0, 0 }, + + { ngx_string("remote_port"), NULL, ngx_http_variable_remote_port, 0, 0, 0 }, + + { ngx_string("proxy_protocol_addr"), NULL, + ngx_http_variable_proxy_protocol_addr, + offsetof(ngx_proxy_protocol_t, src_addr), 0, 0 }, + + { ngx_string("proxy_protocol_port"), NULL, + ngx_http_variable_proxy_protocol_port, + offsetof(ngx_proxy_protocol_t, src_port), 0, 0 }, + + { ngx_string("proxy_protocol_server_addr"), NULL, + ngx_http_variable_proxy_protocol_addr, + offsetof(ngx_proxy_protocol_t, dst_addr), 0, 0 }, + + { ngx_string("proxy_protocol_server_port"), NULL, + ngx_http_variable_proxy_protocol_port, + offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 }, + + { ngx_string("proxy_protocol_tlv_"), NULL, + ngx_http_variable_proxy_protocol_tlv, + 0, NGX_HTTP_VAR_PREFIX, 0 }, + + { ngx_string("server_addr"), NULL, ngx_http_variable_server_addr, 0, 0, 0 }, + + { ngx_string("server_port"), NULL, ngx_http_variable_server_port, 0, 0, 0 }, + + { ngx_string("server_protocol"), NULL, ngx_http_variable_request, + offsetof(ngx_http_request_t, http_protocol), 0, 0 }, + + { ngx_string("scheme"), NULL, ngx_http_variable_scheme, 0, 0, 0 }, + + { ngx_string("https"), NULL, ngx_http_variable_https, 0, 0, 0 }, + + { ngx_string("request_port"), NULL, + ngx_http_variable_request_port, 0, 0, 0 }, + + { ngx_string("is_request_port"), NULL, + ngx_http_variable_is_request_port, 0, 0, 0 }, + + { ngx_string("request_uri"), NULL, ngx_http_variable_request, + offsetof(ngx_http_request_t, unparsed_uri), 0, 0 }, + + { ngx_string("uri"), NULL, ngx_http_variable_request, + offsetof(ngx_http_request_t, uri), + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("document_uri"), NULL, ngx_http_variable_request, + offsetof(ngx_http_request_t, uri), + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("request"), NULL, ngx_http_variable_request_line, 0, 0, 0 }, + + { ngx_string("document_root"), NULL, + ngx_http_variable_document_root, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("realpath_root"), NULL, + ngx_http_variable_realpath_root, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("query_string"), NULL, ngx_http_variable_request, + offsetof(ngx_http_request_t, args), + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("args"), + ngx_http_variable_set_args, + ngx_http_variable_request, + offsetof(ngx_http_request_t, args), + NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("is_args"), NULL, ngx_http_variable_is_args, + 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("request_filename"), NULL, + ngx_http_variable_request_filename, 0, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("server_name"), NULL, ngx_http_variable_server_name, 0, 0, 0 }, + + { ngx_string("request_method"), NULL, + ngx_http_variable_request_method, 0, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("remote_user"), NULL, ngx_http_variable_remote_user, 0, 0, 0 }, + + { ngx_string("bytes_sent"), NULL, ngx_http_variable_bytes_sent, + 0, 0, 0 }, + + { ngx_string("body_bytes_sent"), NULL, ngx_http_variable_body_bytes_sent, + 0, 0, 0 }, + + { ngx_string("pipe"), NULL, ngx_http_variable_pipe, + 0, 0, 0 }, + + { ngx_string("request_completion"), NULL, + ngx_http_variable_request_completion, + 0, 0, 0 }, + + { ngx_string("request_body"), NULL, + ngx_http_variable_request_body, + 0, 0, 0 }, + + { ngx_string("request_body_file"), NULL, + ngx_http_variable_request_body_file, + 0, 0, 0 }, + + { ngx_string("request_length"), NULL, ngx_http_variable_request_length, + 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("request_time"), NULL, ngx_http_variable_request_time, + 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("request_id"), NULL, + ngx_http_variable_request_id, + 0, 0, 0 }, + + { ngx_string("status"), NULL, + ngx_http_variable_status, 0, + NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("sent_http_content_type"), NULL, + ngx_http_variable_sent_content_type, 0, 0, 0 }, + + { ngx_string("sent_http_content_length"), NULL, + ngx_http_variable_sent_content_length, 0, 0, 0 }, + + { ngx_string("sent_http_location"), NULL, + ngx_http_variable_sent_location, 0, 0, 0 }, + + { ngx_string("sent_http_last_modified"), NULL, + ngx_http_variable_sent_last_modified, 0, 0, 0 }, + + { ngx_string("sent_http_connection"), NULL, + ngx_http_variable_sent_connection, 0, 0, 0 }, + + { ngx_string("sent_http_keep_alive"), NULL, + ngx_http_variable_sent_keep_alive, 0, 0, 0 }, + + { ngx_string("sent_http_transfer_encoding"), NULL, + ngx_http_variable_sent_transfer_encoding, 0, 0, 0 }, + + { ngx_string("sent_http_cache_control"), NULL, ngx_http_variable_header, + offsetof(ngx_http_request_t, headers_out.cache_control), 0, 0 }, + + { ngx_string("sent_http_link"), NULL, ngx_http_variable_header, + offsetof(ngx_http_request_t, headers_out.link), 0, 0 }, + + { ngx_string("limit_rate"), ngx_http_variable_set_limit_rate, + ngx_http_variable_request_get_size, + offsetof(ngx_http_request_t, limit_rate), + NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("connection"), NULL, + ngx_http_variable_connection, 0, 0, 0 }, + + { ngx_string("connection_requests"), NULL, + ngx_http_variable_connection_requests, 0, 0, 0 }, + + { ngx_string("connection_time"), NULL, ngx_http_variable_connection_time, + 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("nginx_version"), NULL, ngx_http_variable_nginx_version, + 0, 0, 0 }, + + { ngx_string("hostname"), NULL, ngx_http_variable_hostname, + 0, 0, 0 }, + + { ngx_string("pid"), NULL, ngx_http_variable_pid, + 0, 0, 0 }, + + { ngx_string("msec"), NULL, ngx_http_variable_msec, + 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("time_iso8601"), NULL, ngx_http_variable_time_iso8601, + 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("time_local"), NULL, ngx_http_variable_time_local, + 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + +#if (NGX_HAVE_TCP_INFO) + { ngx_string("tcpinfo_rtt"), NULL, ngx_http_variable_tcpinfo, + 0, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("tcpinfo_rttvar"), NULL, ngx_http_variable_tcpinfo, + 1, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("tcpinfo_snd_cwnd"), NULL, ngx_http_variable_tcpinfo, + 2, NGX_HTTP_VAR_NOCACHEABLE, 0 }, + + { ngx_string("tcpinfo_rcv_space"), NULL, ngx_http_variable_tcpinfo, + 3, NGX_HTTP_VAR_NOCACHEABLE, 0 }, +#endif + + { ngx_string("http_"), NULL, ngx_http_variable_unknown_header_in, + 0, NGX_HTTP_VAR_PREFIX, 0 }, + + { ngx_string("sent_http_"), NULL, ngx_http_variable_unknown_header_out, + 0, NGX_HTTP_VAR_PREFIX, 0 }, + + { ngx_string("sent_trailer_"), NULL, ngx_http_variable_unknown_trailer_out, + 0, NGX_HTTP_VAR_PREFIX, 0 }, + + { ngx_string("cookie_"), NULL, ngx_http_variable_cookie, + 0, NGX_HTTP_VAR_PREFIX, 0 }, + + { ngx_string("arg_"), NULL, ngx_http_variable_argument, + 0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 }, + + ngx_http_null_variable +}; + + +ngx_http_variable_value_t ngx_http_variable_null_value = + ngx_http_variable(""); +ngx_http_variable_value_t ngx_http_variable_true_value = + ngx_http_variable("1"); + + +static ngx_uint_t ngx_http_variable_depth = 100; + + +ngx_http_variable_t * +ngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags) +{ + ngx_int_t rc; + ngx_uint_t i; + ngx_hash_key_t *key; + ngx_http_variable_t *v; + ngx_http_core_main_conf_t *cmcf; + + if (name->len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"$\""); + return NULL; + } + + if (flags & NGX_HTTP_VAR_PREFIX) { + return ngx_http_add_prefix_variable(cf, name, flags); + } + + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + + key = cmcf->variables_keys->keys.elts; + for (i = 0; i < cmcf->variables_keys->keys.nelts; i++) { + if (name->len != key[i].key.len + || ngx_strncasecmp(name->data, key[i].key.data, name->len) != 0) + { + continue; + } + + v = key[i].value; + + if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the duplicate \"%V\" variable", name); + return NULL; + } + + if (!(flags & NGX_HTTP_VAR_WEAK)) { + v->flags &= ~NGX_HTTP_VAR_WEAK; + } + + return v; + } + + v = ngx_palloc(cf->pool, sizeof(ngx_http_variable_t)); + if (v == NULL) { + return NULL; + } + + v->name.len = name->len; + v->name.data = ngx_pnalloc(cf->pool, name->len); + if (v->name.data == NULL) { + return NULL; + } + + ngx_strlow(v->name.data, name->data, name->len); + + v->set_handler = NULL; + v->get_handler = NULL; + v->data = 0; + v->flags = flags; + v->index = 0; + + rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v, 0); + + if (rc == NGX_ERROR) { + return NULL; + } + + if (rc == NGX_BUSY) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "conflicting variable name \"%V\"", name); + return NULL; + } + + return v; +} + + +static ngx_http_variable_t * +ngx_http_add_prefix_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags) +{ + ngx_uint_t i; + ngx_http_variable_t *v; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + + v = cmcf->prefix_variables.elts; + for (i = 0; i < cmcf->prefix_variables.nelts; i++) { + if (name->len != v[i].name.len + || ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0) + { + continue; + } + + v = &v[i]; + + if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the duplicate \"%V\" variable", name); + return NULL; + } + + if (!(flags & NGX_HTTP_VAR_WEAK)) { + v->flags &= ~NGX_HTTP_VAR_WEAK; + } + + return v; + } + + v = ngx_array_push(&cmcf->prefix_variables); + if (v == NULL) { + return NULL; + } + + v->name.len = name->len; + v->name.data = ngx_pnalloc(cf->pool, name->len); + if (v->name.data == NULL) { + return NULL; + } + + ngx_strlow(v->name.data, name->data, name->len); + + v->set_handler = NULL; + v->get_handler = NULL; + v->data = 0; + v->flags = flags; + v->index = 0; + + return v; +} + + +ngx_int_t +ngx_http_get_variable_index(ngx_conf_t *cf, ngx_str_t *name) +{ + ngx_uint_t i; + ngx_http_variable_t *v; + ngx_http_core_main_conf_t *cmcf; + + if (name->len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"$\""); + return NGX_ERROR; + } + + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + + v = cmcf->variables.elts; + + if (v == NULL) { + if (ngx_array_init(&cmcf->variables, cf->pool, 4, + sizeof(ngx_http_variable_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + } else { + for (i = 0; i < cmcf->variables.nelts; i++) { + if (name->len != v[i].name.len + || ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0) + { + continue; + } + + return i; + } + } + + v = ngx_array_push(&cmcf->variables); + if (v == NULL) { + return NGX_ERROR; + } + + v->name.len = name->len; + v->name.data = ngx_pnalloc(cf->pool, name->len); + if (v->name.data == NULL) { + return NGX_ERROR; + } + + ngx_strlow(v->name.data, name->data, name->len); + + v->set_handler = NULL; + v->get_handler = NULL; + v->data = 0; + v->flags = 0; + v->index = cmcf->variables.nelts - 1; + + return v->index; +} + + +ngx_http_variable_value_t * +ngx_http_get_indexed_variable(ngx_http_request_t *r, ngx_uint_t index) +{ + ngx_http_variable_t *v; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + if (cmcf->variables.nelts <= index) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "unknown variable index: %ui", index); + return NULL; + } + + if (r->variables[index].not_found || r->variables[index].valid) { + return &r->variables[index]; + } + + v = cmcf->variables.elts; + + if (ngx_http_variable_depth == 0) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "cycle while evaluating variable \"%V\"", + &v[index].name); + return NULL; + } + + ngx_http_variable_depth--; + + if (v[index].get_handler(r, &r->variables[index], v[index].data) + == NGX_OK) + { + ngx_http_variable_depth++; + + if (v[index].flags & NGX_HTTP_VAR_NOCACHEABLE) { + r->variables[index].no_cacheable = 1; + } + + return &r->variables[index]; + } + + ngx_http_variable_depth++; + + r->variables[index].valid = 0; + r->variables[index].not_found = 1; + + return NULL; +} + + +ngx_http_variable_value_t * +ngx_http_get_flushed_variable(ngx_http_request_t *r, ngx_uint_t index) +{ + ngx_http_variable_value_t *v; + + v = &r->variables[index]; + + if (v->valid || v->not_found) { + if (!v->no_cacheable) { + return v; + } + + v->valid = 0; + v->not_found = 0; + } + + return ngx_http_get_indexed_variable(r, index); +} + + +ngx_http_variable_value_t * +ngx_http_get_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_uint_t key) +{ + size_t len; + ngx_uint_t i, n; + ngx_http_variable_t *v; + ngx_http_variable_value_t *vv; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + v = ngx_hash_find(&cmcf->variables_hash, key, name->data, name->len); + + if (v) { + if (v->flags & NGX_HTTP_VAR_INDEXED) { + return ngx_http_get_flushed_variable(r, v->index); + } + + if (ngx_http_variable_depth == 0) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "cycle while evaluating variable \"%V\"", name); + return NULL; + } + + ngx_http_variable_depth--; + + vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t)); + + if (vv && v->get_handler(r, vv, v->data) == NGX_OK) { + ngx_http_variable_depth++; + return vv; + } + + ngx_http_variable_depth++; + return NULL; + } + + vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t)); + if (vv == NULL) { + return NULL; + } + + len = 0; + + v = cmcf->prefix_variables.elts; + n = cmcf->prefix_variables.nelts; + + for (i = 0; i < cmcf->prefix_variables.nelts; i++) { + if (name->len >= v[i].name.len && name->len > len + && ngx_strncmp(name->data, v[i].name.data, v[i].name.len) == 0) + { + len = v[i].name.len; + n = i; + } + } + + if (n != cmcf->prefix_variables.nelts) { + if (v[n].get_handler(r, vv, (uintptr_t) name) == NGX_OK) { + return vv; + } + + return NULL; + } + + vv->not_found = 1; + + return vv; +} + + +static ngx_int_t +ngx_http_variable_request(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + ngx_str_t *s; + + s = (ngx_str_t *) ((char *) r + data); + + if (s->data) { + v->len = s->len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = s->data; + + } else { + v->not_found = 1; + } + + return NGX_OK; +} + + +#if 0 + +static void +ngx_http_variable_request_set(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_str_t *s; + + s = (ngx_str_t *) ((char *) r + data); + + s->len = v->len; + s->data = v->data; +} + +#endif + + +static ngx_int_t +ngx_http_variable_request_get_size(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + size_t *sp; + + sp = (size_t *) ((char *) r + data); + + v->data = ngx_pnalloc(r->pool, NGX_SIZE_T_LEN); + if (v->data == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(v->data, "%uz", *sp) - v->data; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_header(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + return ngx_http_variable_headers_internal(r, v, data, ','); +} + + +static ngx_int_t +ngx_http_variable_cookies(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + return ngx_http_variable_headers_internal(r, v, data, ';'); +} + + +static ngx_int_t +ngx_http_variable_headers_internal(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data, u_char sep) +{ + size_t len; + u_char *p, *end; + ngx_table_elt_t *h, *th; + + h = *(ngx_table_elt_t **) ((char *) r + data); + + len = 0; + + for (th = h; th; th = th->next) { + + if (th->hash == 0) { + continue; + } + + len += th->value.len + 2; + } + + if (len == 0) { + v->not_found = 1; + return NGX_OK; + } + + len -= 2; + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + if (h->next == NULL) { + v->len = h->value.len; + v->data = h->value.data; + + return NGX_OK; + } + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = len; + v->data = p; + + end = p + len; + + for (th = h; th; th = th->next) { + + if (th->hash == 0) { + continue; + } + + p = ngx_copy(p, th->value.data, th->value.len); + + if (p == end) { + break; + } + + *p++ = sep; *p++ = ' '; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_unknown_header_in(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + return ngx_http_variable_unknown_header(r, v, (ngx_str_t *) data, + &r->headers_in.headers.part, + sizeof("http_") - 1); +} + + +static ngx_int_t +ngx_http_variable_unknown_header_out(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + return ngx_http_variable_unknown_header(r, v, (ngx_str_t *) data, + &r->headers_out.headers.part, + sizeof("sent_http_") - 1); +} + + +static ngx_int_t +ngx_http_variable_unknown_trailer_out(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + return ngx_http_variable_unknown_header(r, v, (ngx_str_t *) data, + &r->headers_out.trailers.part, + sizeof("sent_trailer_") - 1); +} + + +ngx_int_t +ngx_http_variable_unknown_header(ngx_http_request_t *r, + ngx_http_variable_value_t *v, ngx_str_t *var, + ngx_list_part_t *part, size_t prefix) +{ + u_char *p, ch; + size_t len; + ngx_uint_t i, n; + ngx_table_elt_t *header, *h, **ph; + + ph = &h; +#if (NGX_SUPPRESS_WARN) + len = 0; +#endif + + header = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + if (header[i].key.len != var->len - prefix) { + continue; + } + + for (n = 0; n < var->len - prefix; n++) { + ch = header[i].key.data[n]; + + if (ch >= 'A' && ch <= 'Z') { + ch |= 0x20; + + } else if (ch == '-') { + ch = '_'; + } + + if (var->data[n + prefix] != ch) { + break; + } + } + + if (n != var->len - prefix) { + continue; + } + + len += header[i].value.len + 2; + + *ph = &header[i]; + ph = &header[i].next; + } + + *ph = NULL; + + if (h == NULL) { + v->not_found = 1; + return NGX_OK; + } + + len -= 2; + + if (h->next == NULL) { + + v->len = h->value.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = h->value.data; + + return NGX_OK; + } + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + for ( ;; ) { + + p = ngx_copy(p, h->value.data, h->value.len); + + if (h->next == NULL) { + break; + } + + *p++ = ','; *p++ = ' '; + + h = h->next; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_request_line(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p, *s; + + s = r->request_line.data; + + if (s == NULL) { + s = r->request_start; + + if (s == NULL) { + v->not_found = 1; + return NGX_OK; + } + + for (p = s; p < r->header_in->last; p++) { + if (*p == CR || *p == LF) { + break; + } + } + + r->request_line.len = p - s; + r->request_line.data = s; + } + + v->len = r->request_line.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = s; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_cookie(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + ngx_str_t *name = (ngx_str_t *) data; + + ngx_str_t cookie, s; + + s.len = name->len - (sizeof("cookie_") - 1); + s.data = name->data + sizeof("cookie_") - 1; + + if (ngx_http_parse_cookie_lines(r, r->headers_in.cookie, &s, &cookie) + == NULL) + { + v->not_found = 1; + return NGX_OK; + } + + v->len = cookie.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = cookie.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_argument(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + ngx_str_t *name = (ngx_str_t *) data; + + u_char *arg; + size_t len; + ngx_str_t value; + + len = name->len - (sizeof("arg_") - 1); + arg = name->data + sizeof("arg_") - 1; + + if (len == 0 || ngx_http_arg(r, arg, len, &value) != NGX_OK) { + v->not_found = 1; + return NGX_OK; + } + + v->data = value.data; + v->len = value.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; +} + + +#if (NGX_HAVE_TCP_INFO) + +static ngx_int_t +ngx_http_variable_tcpinfo(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + struct tcp_info ti; + socklen_t len; + uint32_t value; + + len = sizeof(struct tcp_info); + if (getsockopt(r->connection->fd, IPPROTO_TCP, TCP_INFO, &ti, &len) == -1) { + v->not_found = 1; + return NGX_OK; + } + + v->data = ngx_pnalloc(r->pool, NGX_INT32_LEN); + if (v->data == NULL) { + return NGX_ERROR; + } + + switch (data) { + case 0: + value = ti.tcpi_rtt; + break; + + case 1: + value = ti.tcpi_rttvar; + break; + + case 2: + value = ti.tcpi_snd_cwnd; + break; + + case 3: + value = ti.tcpi_rcv_space; + break; + + /* suppress warning */ + default: + value = 0; + break; + } + + v->len = ngx_sprintf(v->data, "%uD", value) - v->data; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; +} + +#endif + + +static ngx_int_t +ngx_http_variable_content_length(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + if (r->headers_in.content_length) { + v->len = r->headers_in.content_length->value.len; + v->data = r->headers_in.content_length->value.data; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + } else if (r->reading_body) { + v->not_found = 1; + v->no_cacheable = 1; + + } else if (r->headers_in.content_length_n >= 0) { + p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(p, "%O", r->headers_in.content_length_n) - p; + v->data = p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + } else if (r->headers_in.chunked) { + v->not_found = 1; + v->no_cacheable = 1; + + } else { + v->not_found = 1; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + ngx_http_core_srv_conf_t *cscf; + + if (r->headers_in.server.len) { + v->len = r->headers_in.server.len; + v->data = r->headers_in.server.data; + + } else { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + v->len = cscf->server_name.len; + v->data = cscf->server_name.data; + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_binary_remote_addr(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + switch (r->connection->sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) r->connection->sockaddr; + + v->len = sizeof(struct in6_addr); + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = sin6->sin6_addr.s6_addr; + + break; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + + v->len = r->connection->addr_text.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = r->connection->addr_text.data; + + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) r->connection->sockaddr; + + v->len = sizeof(in_addr_t); + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) &sin->sin_addr; + + break; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_remote_addr(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + v->len = r->connection->addr_text.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = r->connection->addr_text.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_remote_port(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_uint_t port; + + v->len = 0; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1); + if (v->data == NULL) { + return NGX_ERROR; + } + + port = ngx_inet_get_port(r->connection->sockaddr); + + if (port > 0 && port < 65536) { + v->len = ngx_sprintf(v->data, "%ui", port) - v->data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_proxy_protocol_addr(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_str_t *addr; + ngx_proxy_protocol_t *pp; + + pp = r->connection->proxy_protocol; + if (pp == NULL) { + v->not_found = 1; + return NGX_OK; + } + + addr = (ngx_str_t *) ((char *) pp + data); + + v->len = addr->len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = addr->data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_proxy_protocol_port(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_uint_t port; + ngx_proxy_protocol_t *pp; + + pp = r->connection->proxy_protocol; + if (pp == NULL) { + v->not_found = 1; + return NGX_OK; + } + + v->len = 0; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1); + if (v->data == NULL) { + return NGX_ERROR; + } + + port = *(in_port_t *) ((char *) pp + data); + + if (port > 0 && port < 65536) { + v->len = ngx_sprintf(v->data, "%ui", port) - v->data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_str_t *name = (ngx_str_t *) data; + + ngx_int_t rc; + ngx_str_t tlv, value; + + tlv.len = name->len - (sizeof("proxy_protocol_tlv_") - 1); + tlv.data = name->data + sizeof("proxy_protocol_tlv_") - 1; + + rc = ngx_proxy_protocol_get_tlv(r->connection, &tlv, &value); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_DECLINED) { + v->not_found = 1; + return NGX_OK; + } + + v->len = value.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = value.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_server_addr(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_str_t s; + u_char addr[NGX_SOCKADDR_STRLEN]; + + s.len = NGX_SOCKADDR_STRLEN; + s.data = addr; + + if (ngx_connection_local_sockaddr(r->connection, &s, 0) != NGX_OK) { + return NGX_ERROR; + } + + s.data = ngx_pnalloc(r->pool, s.len); + if (s.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s.data, addr, s.len); + + v->len = s.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = s.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_server_port(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_uint_t port; + + v->len = 0; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + if (ngx_connection_local_sockaddr(r->connection, NULL, 0) != NGX_OK) { + return NGX_ERROR; + } + + v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1); + if (v->data == NULL) { + return NGX_ERROR; + } + + port = ngx_inet_get_port(r->connection->local_sockaddr); + + if (port > 0 && port < 65536) { + v->len = ngx_sprintf(v->data, "%ui", port) - v->data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_scheme(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ +#if (NGX_HTTP_SSL) + + if (r->connection->ssl) { + v->len = sizeof("https") - 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) "https"; + + return NGX_OK; + } + +#endif + + v->len = sizeof("http") - 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) "http"; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_https(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ +#if (NGX_HTTP_SSL) + + if (r->connection->ssl) { + v->len = sizeof("on") - 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) "on"; + + return NGX_OK; + } + +#endif + + *v = ngx_http_variable_null_value; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_request_port(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_uint_t port; + + v->len = 0; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1); + if (v->data == NULL) { + return NGX_ERROR; + } + + port = r->port; + + if (port > 0 && port < 65536) { + v->len = ngx_sprintf(v->data, "%ui", port) - v->data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_is_request_port(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->port == 0) { + *v = ngx_http_variable_null_value; + return NGX_OK; + } + + v->len = 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) ":"; + + return NGX_OK; +} + + +static void +ngx_http_variable_set_args(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + r->args.len = v->len; + r->args.data = v->data; + r->valid_unparsed_uri = 0; +} + + +static ngx_int_t +ngx_http_variable_is_args(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->args.len == 0) { + *v = ngx_http_variable_null_value; + return NGX_OK; + } + + v->len = 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) "?"; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_document_root(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_str_t path; + ngx_http_core_loc_conf_t *clcf; + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->root_lengths == NULL) { + v->len = clcf->root.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = clcf->root.data; + + } else { + if (ngx_http_script_run(r, &path, clcf->root_lengths->elts, 0, + clcf->root_values->elts) + == NULL) + { + return NGX_ERROR; + } + + if (ngx_get_full_name(r->pool, (ngx_str_t *) &ngx_cycle->prefix, &path) + != NGX_OK) + { + return NGX_ERROR; + } + + v->len = path.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = path.data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_realpath_root(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *real; + size_t len; + ngx_str_t path; + ngx_http_core_loc_conf_t *clcf; +#if (NGX_HAVE_MAX_PATH) + u_char buffer[NGX_MAX_PATH]; +#endif + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->root_lengths == NULL) { + path = clcf->root; + + } else { + if (ngx_http_script_run(r, &path, clcf->root_lengths->elts, 1, + clcf->root_values->elts) + == NULL) + { + return NGX_ERROR; + } + + path.data[path.len - 1] = '\0'; + + if (ngx_get_full_name(r->pool, (ngx_str_t *) &ngx_cycle->prefix, &path) + != NGX_OK) + { + return NGX_ERROR; + } + } + +#if (NGX_HAVE_MAX_PATH) + real = buffer; +#else + real = NULL; +#endif + + real = ngx_realpath(path.data, real); + + if (real == NULL) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, + ngx_realpath_n " \"%s\" failed", path.data); + return NGX_ERROR; + } + + len = ngx_strlen(real); + + v->data = ngx_pnalloc(r->pool, len); + if (v->data == NULL) { +#if !(NGX_HAVE_MAX_PATH) + ngx_free(real); +#endif + return NGX_ERROR; + } + + v->len = len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + ngx_memcpy(v->data, real, len); + +#if !(NGX_HAVE_MAX_PATH) + ngx_free(real); +#endif + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_request_filename(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + size_t root; + ngx_str_t path; + + if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) { + return NGX_ERROR; + } + + /* ngx_http_map_uri_to_path() allocates memory for terminating '\0' */ + + v->len = path.len - 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = path.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_server_name(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_http_core_srv_conf_t *cscf; + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + v->len = cscf->server_name.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = cscf->server_name.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_request_method(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->main->method_name.data) { + v->len = r->main->method_name.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = r->main->method_name.data; + + } else { + v->not_found = 1; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_remote_user(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_int_t rc; + + rc = ngx_http_auth_basic_user(r); + + if (rc == NGX_DECLINED) { + v->not_found = 1; + return NGX_OK; + } + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + v->len = r->headers_in.user.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = r->headers_in.user.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_bytes_sent(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(p, "%O", r->connection->sent) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_body_bytes_sent(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + off_t sent; + u_char *p; + + sent = r->connection->sent - r->header_size; + + if (sent < 0) { + sent = 0; + } + + p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(p, "%O", sent) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_pipe(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + v->data = (u_char *) (r->pipeline ? "p" : "."); + v->len = 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_status(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_uint_t status; + + v->data = ngx_pnalloc(r->pool, NGX_INT_T_LEN); + if (v->data == NULL) { + return NGX_ERROR; + } + + if (r->err_status) { + status = r->err_status; + + } else if (r->headers_out.status) { + status = r->headers_out.status; + + } else if (r->http_version == NGX_HTTP_VERSION_9) { + status = 9; + + } else { + status = 0; + } + + v->len = ngx_sprintf(v->data, "%03ui", status) - v->data; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_sent_content_type(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->headers_out.content_type.len) { + v->len = r->headers_out.content_type.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = r->headers_out.content_type.data; + + } else { + v->not_found = 1; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_sent_content_length(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + if (r->headers_out.content_length) { + v->len = r->headers_out.content_length->value.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = r->headers_out.content_length->value.data; + + return NGX_OK; + } + + if (r->headers_out.content_length_n >= 0) { + p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(p, "%O", r->headers_out.content_length_n) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; + } + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_sent_location(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_str_t name; + + if (r->headers_out.location) { + v->len = r->headers_out.location->value.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = r->headers_out.location->value.data; + + return NGX_OK; + } + + ngx_str_set(&name, "sent_http_location"); + + return ngx_http_variable_unknown_header(r, v, &name, + &r->headers_out.headers.part, + sizeof("sent_http_") - 1); +} + + +static ngx_int_t +ngx_http_variable_sent_last_modified(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + if (r->headers_out.last_modified) { + v->len = r->headers_out.last_modified->value.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = r->headers_out.last_modified->value.data; + + return NGX_OK; + } + + if (r->headers_out.last_modified_time >= 0) { + p = ngx_pnalloc(r->pool, sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_http_time(p, r->headers_out.last_modified_time) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; + } + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_sent_connection(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + size_t len; + char *p; + + if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) { + len = sizeof("upgrade") - 1; + p = "upgrade"; + + } else if (r->keepalive) { + len = sizeof("keep-alive") - 1; + p = "keep-alive"; + + } else { + len = sizeof("close") - 1; + p = "close"; + } + + v->len = len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_sent_keep_alive(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + ngx_http_core_loc_conf_t *clcf; + + if (r->keepalive) { + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->keepalive_header) { + + p = ngx_pnalloc(r->pool, sizeof("timeout=") - 1 + NGX_TIME_T_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(p, "timeout=%T", clcf->keepalive_header) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; + } + } + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_sent_transfer_encoding(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->chunked) { + v->len = sizeof("chunked") - 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) "chunked"; + + } else { + v->not_found = 1; + } + + return NGX_OK; +} + + +static void +ngx_http_variable_set_limit_rate(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ssize_t s; + ngx_str_t val; + + val.len = v->len; + val.data = v->data; + + s = ngx_parse_size(&val); + + if (s == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "invalid $limit_rate \"%V\"", &val); + return; + } + + r->limit_rate = s; + r->limit_rate_set = 1; +} + + +static ngx_int_t +ngx_http_variable_request_completion(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->request_complete) { + v->len = 2; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) "OK"; + + return NGX_OK; + } + + *v = ngx_http_variable_null_value; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_request_body(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + size_t len; + ngx_buf_t *buf; + ngx_chain_t *cl; + + if (r->request_body == NULL + || r->request_body->bufs == NULL + || r->request_body->temp_file) + { + v->not_found = 1; + + return NGX_OK; + } + + cl = r->request_body->bufs; + buf = cl->buf; + + if (cl->next == NULL) { + v->len = buf->last - buf->pos; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = buf->pos; + + return NGX_OK; + } + + len = buf->last - buf->pos; + cl = cl->next; + + for ( /* void */ ; cl; cl = cl->next) { + buf = cl->buf; + len += buf->last - buf->pos; + } + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + v->data = p; + cl = r->request_body->bufs; + + for ( /* void */ ; cl; cl = cl->next) { + buf = cl->buf; + p = ngx_cpymem(p, buf->pos, buf->last - buf->pos); + } + + v->len = len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_request_body_file(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + if (r->request_body == NULL || r->request_body->temp_file == NULL) { + v->not_found = 1; + + return NGX_OK; + } + + v->len = r->request_body->temp_file->file.name.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = r->request_body->temp_file->file.name.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_request_length(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(p, "%O", r->request_length) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_request_time(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + ngx_time_t *tp; + ngx_msec_int_t ms; + + p = ngx_pnalloc(r->pool, NGX_TIME_T_LEN + 4); + if (p == NULL) { + return NGX_ERROR; + } + + tp = ngx_timeofday(); + + ms = (ngx_msec_int_t) + ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec)); + ms = ngx_max(ms, 0); + + v->len = ngx_sprintf(p, "%T.%03M", (time_t) ms / 1000, ms % 1000) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_request_id(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *id; + +#if (NGX_OPENSSL) + u_char random_bytes[16]; +#endif + + id = ngx_pnalloc(r->pool, 32); + if (id == NULL) { + return NGX_ERROR; + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + v->len = 32; + v->data = id; + +#if (NGX_OPENSSL) + + if (RAND_bytes(random_bytes, 16) == 1) { + ngx_hex_dump(id, random_bytes, 16); + return NGX_OK; + } + + ngx_ssl_error(NGX_LOG_ERR, r->connection->log, 0, "RAND_bytes() failed"); + +#endif + + ngx_sprintf(id, "%08xD%08xD%08xD%08xD", + (uint32_t) ngx_random(), (uint32_t) ngx_random(), + (uint32_t) ngx_random(), (uint32_t) ngx_random()); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_connection(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + p = ngx_pnalloc(r->pool, NGX_ATOMIC_T_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(p, "%uA", r->connection->number) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_connection_requests(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + p = ngx_pnalloc(r->pool, NGX_INT_T_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(p, "%ui", r->connection->requests) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_connection_time(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + ngx_msec_int_t ms; + + p = ngx_pnalloc(r->pool, NGX_TIME_T_LEN + 4); + if (p == NULL) { + return NGX_ERROR; + } + + ms = ngx_current_msec - r->connection->start_time; + ms = ngx_max(ms, 0); + + v->len = ngx_sprintf(p, "%T.%03M", (time_t) ms / 1000, ms % 1000) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_nginx_version(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + v->len = sizeof(NGINX_VERSION) - 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) NGINX_VERSION; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_hostname(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + v->len = ngx_cycle->hostname.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = ngx_cycle->hostname.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_pid(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + p = ngx_pnalloc(r->pool, NGX_INT64_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(p, "%P", ngx_pid) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_msec(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + ngx_time_t *tp; + + p = ngx_pnalloc(r->pool, NGX_TIME_T_LEN + 4); + if (p == NULL) { + return NGX_ERROR; + } + + tp = ngx_timeofday(); + + v->len = ngx_sprintf(p, "%T.%03M", tp->sec, tp->msec) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_time_iso8601(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + p = ngx_pnalloc(r->pool, ngx_cached_http_log_iso8601.len); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, ngx_cached_http_log_iso8601.data, + ngx_cached_http_log_iso8601.len); + + v->len = ngx_cached_http_log_iso8601.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_time_local(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + p = ngx_pnalloc(r->pool, ngx_cached_http_log_time.len); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, ngx_cached_http_log_time.data, ngx_cached_http_log_time.len); + + v->len = ngx_cached_http_log_time.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +void * +ngx_http_map_find(ngx_http_request_t *r, ngx_http_map_t *map, ngx_str_t *match) +{ + void *value; + u_char *low; + size_t len; + ngx_uint_t key; + + len = match->len; + + if (len) { + low = ngx_pnalloc(r->pool, len); + if (low == NULL) { + return NULL; + } + + } else { + low = NULL; + } + + key = ngx_hash_strlow(low, match->data, len); + + value = ngx_hash_find_combined(&map->hash, key, low, len); + if (value) { + return value; + } + +#if (NGX_PCRE) + + if (len && map->nregex) { + ngx_int_t n; + ngx_uint_t i; + ngx_http_map_regex_t *reg; + + reg = map->regex; + + for (i = 0; i < map->nregex; i++) { + + n = ngx_http_regex_exec(r, reg[i].regex, match); + + if (n == NGX_OK) { + return reg[i].value; + } + + if (n == NGX_DECLINED) { + continue; + } + + /* NGX_ERROR */ + + return NULL; + } + } + +#endif + + return NULL; +} + + +#if (NGX_PCRE) + +static ngx_int_t +ngx_http_variable_not_found(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + v->not_found = 1; + return NGX_OK; +} + + +ngx_http_regex_t * +ngx_http_regex_compile(ngx_conf_t *cf, ngx_regex_compile_t *rc) +{ + u_char *p; + size_t size; + ngx_str_t name; + ngx_uint_t i, n; + ngx_http_variable_t *v; + ngx_http_regex_t *re; + ngx_http_regex_variable_t *rv; + ngx_http_core_main_conf_t *cmcf; + + rc->pool = cf->pool; + + if (ngx_regex_compile(rc) != NGX_OK) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc->err); + return NULL; + } + + re = ngx_pcalloc(cf->pool, sizeof(ngx_http_regex_t)); + if (re == NULL) { + return NULL; + } + + re->regex = rc->regex; + re->ncaptures = rc->captures; + re->name = rc->pattern; + + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + cmcf->ncaptures = ngx_max(cmcf->ncaptures, re->ncaptures); + + n = (ngx_uint_t) rc->named_captures; + + if (n == 0) { + return re; + } + + rv = ngx_palloc(rc->pool, n * sizeof(ngx_http_regex_variable_t)); + if (rv == NULL) { + return NULL; + } + + re->variables = rv; + re->nvariables = n; + + size = rc->name_size; + p = rc->names; + + for (i = 0; i < n; i++) { + rv[i].capture = 2 * ((p[0] << 8) + p[1]); + + name.data = &p[2]; + name.len = ngx_strlen(name.data); + + v = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE); + if (v == NULL) { + return NULL; + } + + rv[i].index = ngx_http_get_variable_index(cf, &name); + if (rv[i].index == NGX_ERROR) { + return NULL; + } + + v->get_handler = ngx_http_variable_not_found; + + p += size; + } + + return re; +} + + +ngx_int_t +ngx_http_regex_exec(ngx_http_request_t *r, ngx_http_regex_t *re, ngx_str_t *s) +{ + ngx_int_t rc, index; + ngx_uint_t i, n, len; + ngx_http_variable_value_t *vv; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + if (re->ncaptures) { + len = cmcf->ncaptures; + + if (r->captures == NULL || r->realloc_captures) { + r->realloc_captures = 0; + + r->captures = ngx_palloc(r->pool, len * sizeof(int)); + if (r->captures == NULL) { + return NGX_ERROR; + } + } + + } else { + len = 0; + } + + rc = ngx_regex_exec(re->regex, s, r->captures, len); + + if (rc == NGX_REGEX_NO_MATCHED) { + return NGX_DECLINED; + } + + if (rc < 0) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"", + rc, s, &re->name); + return NGX_ERROR; + } + + for (i = 0; i < re->nvariables; i++) { + + n = re->variables[i].capture; + index = re->variables[i].index; + vv = &r->variables[index]; + + vv->len = r->captures[n + 1] - r->captures[n]; + vv->valid = 1; + vv->no_cacheable = 0; + vv->not_found = 0; + vv->data = &s->data[r->captures[n]]; + +#if (NGX_DEBUG) + { + ngx_http_variable_t *v; + + v = cmcf->variables.elts; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http regex set $%V to \"%v\"", &v[index].name, vv); + } +#endif + } + + r->ncaptures = rc * 2; + r->captures_data = s->data; + + return NGX_OK; +} + +#endif + + +ngx_int_t +ngx_http_variables_add_core_vars(ngx_conf_t *cf) +{ + ngx_http_variable_t *cv, *v; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + + cmcf->variables_keys = ngx_pcalloc(cf->temp_pool, + sizeof(ngx_hash_keys_arrays_t)); + if (cmcf->variables_keys == NULL) { + return NGX_ERROR; + } + + cmcf->variables_keys->pool = cf->pool; + cmcf->variables_keys->temp_pool = cf->pool; + + if (ngx_hash_keys_array_init(cmcf->variables_keys, NGX_HASH_SMALL) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_array_init(&cmcf->prefix_variables, cf->pool, 8, + sizeof(ngx_http_variable_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + for (cv = ngx_http_core_variables; cv->name.len; cv++) { + v = ngx_http_add_variable(cf, &cv->name, cv->flags); + if (v == NULL) { + return NGX_ERROR; + } + + *v = *cv; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_variables_init_vars(ngx_conf_t *cf) +{ + size_t len; + ngx_uint_t i, n; + ngx_hash_key_t *key; + ngx_hash_init_t hash; + ngx_http_variable_t *v, *av, *pv; + ngx_http_core_main_conf_t *cmcf; + + /* set the handlers for the indexed http variables */ + + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + + v = cmcf->variables.elts; + pv = cmcf->prefix_variables.elts; + key = cmcf->variables_keys->keys.elts; + + for (i = 0; i < cmcf->variables.nelts; i++) { + + for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) { + + av = key[n].value; + + if (v[i].name.len == key[n].key.len + && ngx_strncmp(v[i].name.data, key[n].key.data, v[i].name.len) + == 0) + { + v[i].get_handler = av->get_handler; + v[i].data = av->data; + + av->flags |= NGX_HTTP_VAR_INDEXED; + v[i].flags = av->flags; + + av->index = i; + + if (av->get_handler == NULL + || (av->flags & NGX_HTTP_VAR_WEAK)) + { + break; + } + + goto next; + } + } + + len = 0; + av = NULL; + + for (n = 0; n < cmcf->prefix_variables.nelts; n++) { + if (v[i].name.len >= pv[n].name.len && v[i].name.len > len + && ngx_strncmp(v[i].name.data, pv[n].name.data, pv[n].name.len) + == 0) + { + av = &pv[n]; + len = pv[n].name.len; + } + } + + if (av) { + v[i].get_handler = av->get_handler; + v[i].data = (uintptr_t) &v[i].name; + v[i].flags = av->flags; + + goto next; + } + + if (v[i].get_handler == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "unknown \"%V\" variable", &v[i].name); + + return NGX_ERROR; + } + + next: + continue; + } + + + for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) { + av = key[n].value; + + if (av->flags & NGX_HTTP_VAR_NOHASH) { + key[n].key.data = NULL; + } + } + + + hash.hash = &cmcf->variables_hash; + hash.key = ngx_hash_key; + hash.max_size = cmcf->variables_hash_max_size; + hash.bucket_size = cmcf->variables_hash_bucket_size; + hash.name = "variables_hash"; + hash.pool = cf->pool; + hash.temp_pool = NULL; + + if (ngx_hash_init(&hash, cmcf->variables_keys->keys.elts, + cmcf->variables_keys->keys.nelts) + != NGX_OK) + { + return NGX_ERROR; + } + + cmcf->variables_keys = NULL; + + return NGX_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/http/ngx_http_variables.h b/src/deps/src/nginx-1.30.1/src/http/ngx_http_variables.h new file mode 100644 index 000000000..6a661a256 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/ngx_http_variables.h @@ -0,0 +1,117 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_HTTP_VARIABLES_H_INCLUDED_ +#define _NGX_HTTP_VARIABLES_H_INCLUDED_ + + +#include +#include +#include + + +typedef ngx_variable_value_t ngx_http_variable_value_t; + +#define ngx_http_variable(v) { sizeof(v) - 1, 1, 0, 0, 0, (u_char *) v } + +typedef struct ngx_http_variable_s ngx_http_variable_t; + +typedef void (*ngx_http_set_variable_pt) (ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +typedef ngx_int_t (*ngx_http_get_variable_pt) (ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + + +#define NGX_HTTP_VAR_CHANGEABLE 1 +#define NGX_HTTP_VAR_NOCACHEABLE 2 +#define NGX_HTTP_VAR_INDEXED 4 +#define NGX_HTTP_VAR_NOHASH 8 +#define NGX_HTTP_VAR_WEAK 16 +#define NGX_HTTP_VAR_PREFIX 32 + + +struct ngx_http_variable_s { + ngx_str_t name; /* must be first to build the hash */ + ngx_http_set_variable_pt set_handler; + ngx_http_get_variable_pt get_handler; + uintptr_t data; + ngx_uint_t flags; + ngx_uint_t index; +}; + +#define ngx_http_null_variable { ngx_null_string, NULL, NULL, 0, 0, 0 } + + +ngx_http_variable_t *ngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name, + ngx_uint_t flags); +ngx_int_t ngx_http_get_variable_index(ngx_conf_t *cf, ngx_str_t *name); +ngx_http_variable_value_t *ngx_http_get_indexed_variable(ngx_http_request_t *r, + ngx_uint_t index); +ngx_http_variable_value_t *ngx_http_get_flushed_variable(ngx_http_request_t *r, + ngx_uint_t index); + +ngx_http_variable_value_t *ngx_http_get_variable(ngx_http_request_t *r, + ngx_str_t *name, ngx_uint_t key); + +ngx_int_t ngx_http_variable_unknown_header(ngx_http_request_t *r, + ngx_http_variable_value_t *v, ngx_str_t *var, ngx_list_part_t *part, + size_t prefix); + + +#if (NGX_PCRE) + +typedef struct { + ngx_uint_t capture; + ngx_int_t index; +} ngx_http_regex_variable_t; + + +typedef struct { + ngx_regex_t *regex; + ngx_uint_t ncaptures; + ngx_http_regex_variable_t *variables; + ngx_uint_t nvariables; + ngx_str_t name; +} ngx_http_regex_t; + + +typedef struct { + ngx_http_regex_t *regex; + void *value; +} ngx_http_map_regex_t; + + +ngx_http_regex_t *ngx_http_regex_compile(ngx_conf_t *cf, + ngx_regex_compile_t *rc); +ngx_int_t ngx_http_regex_exec(ngx_http_request_t *r, ngx_http_regex_t *re, + ngx_str_t *s); + +#endif + + +typedef struct { + ngx_hash_combined_t hash; +#if (NGX_PCRE) + ngx_http_map_regex_t *regex; + ngx_uint_t nregex; +#endif +} ngx_http_map_t; + + +void *ngx_http_map_find(ngx_http_request_t *r, ngx_http_map_t *map, + ngx_str_t *match); + + +ngx_int_t ngx_http_variables_add_core_vars(ngx_conf_t *cf); +ngx_int_t ngx_http_variables_init_vars(ngx_conf_t *cf); + + +extern ngx_http_variable_value_t ngx_http_variable_null_value; +extern ngx_http_variable_value_t ngx_http_variable_true_value; + + +#endif /* _NGX_HTTP_VARIABLES_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/http/ngx_http_write_filter_module.c b/src/deps/src/nginx-1.30.1/src/http/ngx_http_write_filter_module.c new file mode 100644 index 000000000..9188ee948 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/ngx_http_write_filter_module.c @@ -0,0 +1,371 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +static ngx_int_t ngx_http_write_filter_init(ngx_conf_t *cf); + + +static ngx_http_module_t ngx_http_write_filter_module_ctx = { + NULL, /* preconfiguration */ + ngx_http_write_filter_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + NULL, /* create location configuration */ + NULL, /* merge location configuration */ +}; + + +ngx_module_t ngx_http_write_filter_module = { + NGX_MODULE_V1, + &ngx_http_write_filter_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +ngx_int_t +ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + off_t size, sent, nsent, limit; + ngx_uint_t last, flush, sync; + ngx_msec_t delay; + ngx_chain_t *cl, *ln, **ll, *chain; + ngx_connection_t *c; + ngx_http_core_loc_conf_t *clcf; + + c = r->connection; + + if (c->error) { + return NGX_ERROR; + } + + size = 0; + flush = 0; + sync = 0; + last = 0; + ll = &r->out; + + /* find the size, the flush point and the last link of the saved chain */ + + for (cl = r->out; cl; cl = cl->next) { + ll = &cl->next; + + ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, + "write old buf t:%d f:%d %p, pos %p, size: %z " + "file: %O, size: %O", + cl->buf->temporary, cl->buf->in_file, + cl->buf->start, cl->buf->pos, + cl->buf->last - cl->buf->pos, + cl->buf->file_pos, + cl->buf->file_last - cl->buf->file_pos); + + if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "zero size buf in writer " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + cl->buf->temporary, + cl->buf->recycled, + cl->buf->in_file, + cl->buf->start, + cl->buf->pos, + cl->buf->last, + cl->buf->file, + cl->buf->file_pos, + cl->buf->file_last); + + ngx_debug_point(); + return NGX_ERROR; + } + + if (ngx_buf_size(cl->buf) < 0) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "negative size buf in writer " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + cl->buf->temporary, + cl->buf->recycled, + cl->buf->in_file, + cl->buf->start, + cl->buf->pos, + cl->buf->last, + cl->buf->file, + cl->buf->file_pos, + cl->buf->file_last); + + ngx_debug_point(); + return NGX_ERROR; + } + + size += ngx_buf_size(cl->buf); + + if (cl->buf->flush || cl->buf->recycled) { + flush = 1; + } + + if (cl->buf->sync) { + sync = 1; + } + + if (cl->buf->last_buf) { + last = 1; + } + } + + /* add the new chain to the existent one */ + + for (ln = in; ln; ln = ln->next) { + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = ln->buf; + *ll = cl; + ll = &cl->next; + + ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, + "write new buf t:%d f:%d %p, pos %p, size: %z " + "file: %O, size: %O", + cl->buf->temporary, cl->buf->in_file, + cl->buf->start, cl->buf->pos, + cl->buf->last - cl->buf->pos, + cl->buf->file_pos, + cl->buf->file_last - cl->buf->file_pos); + + if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "zero size buf in writer " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + cl->buf->temporary, + cl->buf->recycled, + cl->buf->in_file, + cl->buf->start, + cl->buf->pos, + cl->buf->last, + cl->buf->file, + cl->buf->file_pos, + cl->buf->file_last); + + ngx_debug_point(); + return NGX_ERROR; + } + + if (ngx_buf_size(cl->buf) < 0) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "negative size buf in writer " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + cl->buf->temporary, + cl->buf->recycled, + cl->buf->in_file, + cl->buf->start, + cl->buf->pos, + cl->buf->last, + cl->buf->file, + cl->buf->file_pos, + cl->buf->file_last); + + ngx_debug_point(); + return NGX_ERROR; + } + + size += ngx_buf_size(cl->buf); + + if (cl->buf->flush || cl->buf->recycled) { + flush = 1; + } + + if (cl->buf->sync) { + sync = 1; + } + + if (cl->buf->last_buf) { + last = 1; + } + } + + *ll = NULL; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http write filter: l:%ui f:%ui s:%O", last, flush, size); + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + /* + * avoid the output if there are no last buf, no flush point, + * there are the incoming bufs and the size of all bufs + * is smaller than "postpone_output" directive + */ + + if (!last && !flush && in && size < (off_t) clcf->postpone_output) { + return NGX_OK; + } + + if (c->write->delayed) { + c->buffered |= NGX_HTTP_WRITE_BUFFERED; + return NGX_AGAIN; + } + + if (size == 0 + && !(c->buffered & NGX_LOWLEVEL_BUFFERED) + && !(last && c->need_last_buf) + && !(flush && c->need_flush_buf)) + { + if (last || flush || sync) { + for (cl = r->out; cl; /* void */) { + ln = cl; + cl = cl->next; + ngx_free_chain(r->pool, ln); + } + + r->out = NULL; + c->buffered &= ~NGX_HTTP_WRITE_BUFFERED; + + if (last) { + r->response_sent = 1; + } + + return NGX_OK; + } + + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "the http output chain is empty"); + + ngx_debug_point(); + + return NGX_ERROR; + } + + if (!r->limit_rate_set) { + r->limit_rate = ngx_http_complex_value_size(r, clcf->limit_rate, 0); + r->limit_rate_set = 1; + } + + if (r->limit_rate) { + + if (!r->limit_rate_after_set) { + r->limit_rate_after = ngx_http_complex_value_size(r, + clcf->limit_rate_after, 0); + r->limit_rate_after_set = 1; + } + + limit = (off_t) r->limit_rate * (ngx_time() - r->start_sec + 1) + - (c->sent - r->limit_rate_after); + + if (limit <= 0) { + c->write->delayed = 1; + delay = (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1); + ngx_add_timer(c->write, delay); + + c->buffered |= NGX_HTTP_WRITE_BUFFERED; + + return NGX_AGAIN; + } + + if (clcf->sendfile_max_chunk + && (off_t) clcf->sendfile_max_chunk < limit) + { + limit = clcf->sendfile_max_chunk; + } + + } else { + limit = clcf->sendfile_max_chunk; + } + + sent = c->sent; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http write filter limit %O", limit); + + chain = c->send_chain(c, r->out, limit); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http write filter %p", chain); + + if (chain == NGX_CHAIN_ERROR) { + c->error = 1; + return NGX_ERROR; + } + + if (r->limit_rate) { + + nsent = c->sent; + + if (r->limit_rate_after) { + + sent -= r->limit_rate_after; + if (sent < 0) { + sent = 0; + } + + nsent -= r->limit_rate_after; + if (nsent < 0) { + nsent = 0; + } + } + + delay = (ngx_msec_t) ((nsent - sent) * 1000 / r->limit_rate); + + if (delay > 0) { + c->write->delayed = 1; + ngx_add_timer(c->write, delay); + } + } + + if (chain && c->write->ready && !c->write->delayed) { + ngx_post_event(c->write, &ngx_posted_next_events); + } + + for (cl = r->out; cl && cl != chain; /* void */) { + ln = cl; + cl = cl->next; + ngx_free_chain(r->pool, ln); + } + + r->out = chain; + + if (chain) { + c->buffered |= NGX_HTTP_WRITE_BUFFERED; + return NGX_AGAIN; + } + + c->buffered &= ~NGX_HTTP_WRITE_BUFFERED; + + if (last) { + r->response_sent = 1; + } + + if ((c->buffered & NGX_LOWLEVEL_BUFFERED) && r->postponed == NULL) { + return NGX_AGAIN; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_write_filter_init(ngx_conf_t *cf) +{ + ngx_http_top_body_filter = ngx_http_write_filter; + + return NGX_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/http/v2/ngx_http_v2.c b/src/deps/src/nginx-1.30.1/src/http/v2/ngx_http_v2.c new file mode 100644 index 000000000..efe22903f --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/v2/ngx_http_v2.c @@ -0,0 +1,5030 @@ + +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Valentin V. Bartenev + */ + + +#include +#include +#include +#include + + +/* errors */ +#define NGX_HTTP_V2_NO_ERROR 0x0 +#define NGX_HTTP_V2_PROTOCOL_ERROR 0x1 +#define NGX_HTTP_V2_INTERNAL_ERROR 0x2 +#define NGX_HTTP_V2_FLOW_CTRL_ERROR 0x3 +#define NGX_HTTP_V2_SETTINGS_TIMEOUT 0x4 +#define NGX_HTTP_V2_STREAM_CLOSED 0x5 +#define NGX_HTTP_V2_SIZE_ERROR 0x6 +#define NGX_HTTP_V2_REFUSED_STREAM 0x7 +#define NGX_HTTP_V2_CANCEL 0x8 +#define NGX_HTTP_V2_COMP_ERROR 0x9 +#define NGX_HTTP_V2_CONNECT_ERROR 0xa +#define NGX_HTTP_V2_ENHANCE_YOUR_CALM 0xb +#define NGX_HTTP_V2_INADEQUATE_SECURITY 0xc +#define NGX_HTTP_V2_HTTP_1_1_REQUIRED 0xd + +/* frame sizes */ +#define NGX_HTTP_V2_SETTINGS_ACK_SIZE 0 +#define NGX_HTTP_V2_RST_STREAM_SIZE 4 +#define NGX_HTTP_V2_PRIORITY_SIZE 5 +#define NGX_HTTP_V2_PING_SIZE 8 +#define NGX_HTTP_V2_GOAWAY_SIZE 8 +#define NGX_HTTP_V2_WINDOW_UPDATE_SIZE 4 + +#define NGX_HTTP_V2_SETTINGS_PARAM_SIZE 6 + +/* settings fields */ +#define NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING 0x1 +#define NGX_HTTP_V2_ENABLE_PUSH_SETTING 0x2 +#define NGX_HTTP_V2_MAX_STREAMS_SETTING 0x3 +#define NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING 0x4 +#define NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING 0x5 + +#define NGX_HTTP_V2_FRAME_BUFFER_SIZE 24 + +#define NGX_HTTP_V2_ROOT (void *) -1 + + +static void ngx_http_v2_read_handler(ngx_event_t *rev); +static void ngx_http_v2_write_handler(ngx_event_t *wev); +static void ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c); +static void ngx_http_v2_lingering_close(ngx_connection_t *c); +static void ngx_http_v2_lingering_close_handler(ngx_event_t *rev); + +static u_char *ngx_http_v2_state_preface(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_preface_end(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_head(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_header_block(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_field_len(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_field_huff(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_field_raw(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_field_skip(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_header_complete(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_handle_continuation(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end, ngx_http_v2_handler_pt handler); +static u_char *ngx_http_v2_state_priority(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_rst_stream(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_settings(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_push_promise(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_ping(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_goaway(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_window_update(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_continuation(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_complete(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_skip_padded(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_skip(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); +static u_char *ngx_http_v2_state_save(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end, ngx_http_v2_handler_pt handler); +static u_char *ngx_http_v2_state_headers_save(ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end, ngx_http_v2_handler_pt handler); +static u_char *ngx_http_v2_connection_error(ngx_http_v2_connection_t *h2c, + ngx_uint_t err); + +static ngx_int_t ngx_http_v2_parse_int(ngx_http_v2_connection_t *h2c, + u_char **pos, u_char *end, ngx_uint_t prefix); + +static ngx_http_v2_stream_t *ngx_http_v2_create_stream( + ngx_http_v2_connection_t *h2c); +static ngx_http_v2_node_t *ngx_http_v2_get_node_by_id( + ngx_http_v2_connection_t *h2c, ngx_uint_t sid, ngx_uint_t alloc); +static ngx_http_v2_node_t *ngx_http_v2_get_closed_node( + ngx_http_v2_connection_t *h2c); +#define ngx_http_v2_index_size(h2scf) (h2scf->streams_index_mask + 1) +#define ngx_http_v2_index(h2scf, sid) ((sid >> 1) & h2scf->streams_index_mask) + +static ngx_int_t ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c); +static ngx_int_t ngx_http_v2_settings_frame_handler( + ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame); +static ngx_int_t ngx_http_v2_send_window_update(ngx_http_v2_connection_t *h2c, + ngx_uint_t sid, size_t window); +static ngx_int_t ngx_http_v2_send_rst_stream(ngx_http_v2_connection_t *h2c, + ngx_uint_t sid, ngx_uint_t status); +static ngx_int_t ngx_http_v2_send_goaway(ngx_http_v2_connection_t *h2c, + ngx_uint_t status); + +static ngx_http_v2_out_frame_t *ngx_http_v2_get_frame( + ngx_http_v2_connection_t *h2c, size_t length, ngx_uint_t type, + u_char flags, ngx_uint_t sid); +static ngx_int_t ngx_http_v2_frame_handler(ngx_http_v2_connection_t *h2c, + ngx_http_v2_out_frame_t *frame); + +static ngx_int_t ngx_http_v2_validate_header(ngx_http_request_t *r, + ngx_http_v2_header_t *header); +static ngx_int_t ngx_http_v2_pseudo_header(ngx_http_request_t *r, + ngx_http_v2_header_t *header); +static ngx_int_t ngx_http_v2_parse_path(ngx_http_request_t *r, + ngx_str_t *value); +static ngx_int_t ngx_http_v2_parse_method(ngx_http_request_t *r, + ngx_str_t *value); +static ngx_int_t ngx_http_v2_parse_scheme(ngx_http_request_t *r, + ngx_str_t *value); +static ngx_int_t ngx_http_v2_parse_authority(ngx_http_request_t *r, + ngx_str_t *value); +static ngx_int_t ngx_http_v2_construct_request_line(ngx_http_request_t *r); +static ngx_int_t ngx_http_v2_cookie(ngx_http_request_t *r, + ngx_http_v2_header_t *header); +static ngx_int_t ngx_http_v2_construct_cookie_header(ngx_http_request_t *r); +static ngx_int_t ngx_http_v2_construct_host_header(ngx_http_request_t *r); +static void ngx_http_v2_run_request(ngx_http_request_t *r); +static ngx_int_t ngx_http_v2_process_request_body(ngx_http_request_t *r, + u_char *pos, size_t size, ngx_uint_t last, ngx_uint_t flush); +static ngx_int_t ngx_http_v2_filter_request_body(ngx_http_request_t *r); +static void ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r); + +static ngx_int_t ngx_http_v2_terminate_stream(ngx_http_v2_connection_t *h2c, + ngx_http_v2_stream_t *stream, ngx_uint_t status); +static void ngx_http_v2_close_stream_handler(ngx_event_t *ev); +static void ngx_http_v2_retry_close_stream_handler(ngx_event_t *ev); +static void ngx_http_v2_handle_connection_handler(ngx_event_t *rev); +static void ngx_http_v2_idle_handler(ngx_event_t *rev); +static void ngx_http_v2_finalize_connection(ngx_http_v2_connection_t *h2c, + ngx_uint_t status); + +static ngx_int_t ngx_http_v2_adjust_windows(ngx_http_v2_connection_t *h2c, + ssize_t delta); +static void ngx_http_v2_set_dependency(ngx_http_v2_connection_t *h2c, + ngx_http_v2_node_t *node, ngx_uint_t depend, ngx_uint_t exclusive); +static void ngx_http_v2_node_children_update(ngx_http_v2_node_t *node); + +static void ngx_http_v2_pool_cleanup(void *data); + + +static ngx_http_v2_handler_pt ngx_http_v2_frame_states[] = { + ngx_http_v2_state_data, /* NGX_HTTP_V2_DATA_FRAME */ + ngx_http_v2_state_headers, /* NGX_HTTP_V2_HEADERS_FRAME */ + ngx_http_v2_state_priority, /* NGX_HTTP_V2_PRIORITY_FRAME */ + ngx_http_v2_state_rst_stream, /* NGX_HTTP_V2_RST_STREAM_FRAME */ + ngx_http_v2_state_settings, /* NGX_HTTP_V2_SETTINGS_FRAME */ + ngx_http_v2_state_push_promise, /* NGX_HTTP_V2_PUSH_PROMISE_FRAME */ + ngx_http_v2_state_ping, /* NGX_HTTP_V2_PING_FRAME */ + ngx_http_v2_state_goaway, /* NGX_HTTP_V2_GOAWAY_FRAME */ + ngx_http_v2_state_window_update, /* NGX_HTTP_V2_WINDOW_UPDATE_FRAME */ + ngx_http_v2_state_continuation /* NGX_HTTP_V2_CONTINUATION_FRAME */ +}; + +#define NGX_HTTP_V2_FRAME_STATES \ + (sizeof(ngx_http_v2_frame_states) / sizeof(ngx_http_v2_handler_pt)) + + +void +ngx_http_v2_init(ngx_event_t *rev) +{ + u_char *p, *end; + ngx_connection_t *c; + ngx_pool_cleanup_t *cln; + ngx_http_connection_t *hc; + ngx_http_v2_srv_conf_t *h2scf; + ngx_http_v2_main_conf_t *h2mcf; + ngx_http_v2_connection_t *h2c; + ngx_http_core_srv_conf_t *cscf; + + c = rev->data; + hc = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "init http2 connection"); + + c->log->action = "processing HTTP/2 connection"; + + h2mcf = ngx_http_get_module_main_conf(hc->conf_ctx, ngx_http_v2_module); + + if (h2mcf->recv_buffer == NULL) { + h2mcf->recv_buffer = ngx_palloc(ngx_cycle->pool, + h2mcf->recv_buffer_size); + if (h2mcf->recv_buffer == NULL) { + ngx_http_close_connection(c); + return; + } + } + + h2c = ngx_pcalloc(c->pool, sizeof(ngx_http_v2_connection_t)); + if (h2c == NULL) { + ngx_http_close_connection(c); + return; + } + + h2c->connection = c; + h2c->http_connection = hc; + + h2c->send_window = NGX_HTTP_V2_DEFAULT_WINDOW; + h2c->recv_window = NGX_HTTP_V2_MAX_WINDOW; + + h2c->init_window = NGX_HTTP_V2_DEFAULT_WINDOW; + + h2c->frame_size = NGX_HTTP_V2_DEFAULT_FRAME_SIZE; + + h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module); + + h2c->priority_limit = ngx_max(h2scf->concurrent_streams, 100); + + h2c->pool = ngx_create_pool(h2scf->pool_size, h2c->connection->log); + if (h2c->pool == NULL) { + ngx_http_close_connection(c); + return; + } + + cln = ngx_pool_cleanup_add(c->pool, 0); + if (cln == NULL) { + ngx_http_close_connection(c); + return; + } + + cln->handler = ngx_http_v2_pool_cleanup; + cln->data = h2c; + + h2c->streams_index = ngx_pcalloc(c->pool, ngx_http_v2_index_size(h2scf) + * sizeof(ngx_http_v2_node_t *)); + if (h2c->streams_index == NULL) { + ngx_http_close_connection(c); + return; + } + + if (ngx_http_v2_send_settings(h2c) == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } + + if (ngx_http_v2_send_window_update(h2c, 0, NGX_HTTP_V2_MAX_WINDOW + - NGX_HTTP_V2_DEFAULT_WINDOW) + == NGX_ERROR) + { + ngx_http_close_connection(c); + return; + } + + h2c->state.handler = ngx_http_v2_state_preface; + + ngx_queue_init(&h2c->waiting); + ngx_queue_init(&h2c->dependencies); + ngx_queue_init(&h2c->closed); + + c->data = h2c; + + if (ngx_exiting) { + ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_NO_ERROR); + return; + } + + rev->handler = ngx_http_v2_read_handler; + c->write->handler = ngx_http_v2_write_handler; + + if (!rev->timer_set) { + cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, + ngx_http_core_module); + ngx_add_timer(rev, cscf->client_header_timeout); + } + + c->idle = 1; + ngx_reusable_connection(c, 0); + + if (c->buffer) { + p = c->buffer->pos; + end = c->buffer->last; + + do { + p = h2c->state.handler(h2c, p, end); + + if (p == NULL) { + return; + } + + } while (p != end); + + h2c->total_bytes += p - c->buffer->pos; + c->buffer->pos = p; + } + + ngx_http_v2_read_handler(rev); +} + + +static void +ngx_http_v2_read_handler(ngx_event_t *rev) +{ + u_char *p, *end; + size_t available; + ssize_t n; + ngx_connection_t *c; + ngx_http_v2_main_conf_t *h2mcf; + ngx_http_v2_connection_t *h2c; + + c = rev->data; + h2c = c->data; + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http2 read handler"); + + h2c->blocked = 1; + h2c->new_streams = 0; + + if (c->close) { + c->close = 0; + + if (c->error) { + ngx_http_v2_finalize_connection(h2c, 0); + return; + } + + if (!h2c->processing) { + ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_NO_ERROR); + return; + } + + if (!h2c->goaway) { + h2c->goaway = 1; + + if (ngx_http_v2_send_goaway(h2c, NGX_HTTP_V2_NO_ERROR) + == NGX_ERROR) + { + ngx_http_v2_finalize_connection(h2c, 0); + return; + } + + if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) { + ngx_http_v2_finalize_connection(h2c, 0); + return; + } + } + + h2c->blocked = 0; + + return; + } + + h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx, + ngx_http_v2_module); + + available = h2mcf->recv_buffer_size - NGX_HTTP_V2_STATE_BUFFER_SIZE; + + do { + p = h2mcf->recv_buffer; + end = ngx_cpymem(p, h2c->state.buffer, h2c->state.buffer_used); + + n = c->recv(c, end, available); + + if (n == NGX_AGAIN) { + break; + } + + if (n == 0 && (h2c->state.incomplete || h2c->processing)) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client prematurely closed connection"); + } + + if (n == 0 || n == NGX_ERROR) { + c->error = 1; + ngx_http_v2_finalize_connection(h2c, 0); + return; + } + + end += n; + + h2c->state.buffer_used = 0; + h2c->state.incomplete = 0; + + do { + p = h2c->state.handler(h2c, p, end); + + if (p == NULL) { + return; + } + + } while (p != end); + + h2c->total_bytes += n; + + if (h2c->total_bytes / 8 > h2c->payload_bytes + 1048576) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "http2 flood detected"); + ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_NO_ERROR); + return; + } + + } while (rev->ready); + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_INTERNAL_ERROR); + return; + } + + if (h2c->last_out && ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) { + ngx_http_v2_finalize_connection(h2c, 0); + return; + } + + h2c->blocked = 0; + + ngx_http_v2_handle_connection(h2c); +} + + +static void +ngx_http_v2_write_handler(ngx_event_t *wev) +{ + ngx_int_t rc; + ngx_connection_t *c; + ngx_http_v2_connection_t *h2c; + + c = wev->data; + h2c = c->data; + + if (wev->timedout) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http2 write event timed out"); + c->error = 1; + c->timedout = 1; + ngx_http_v2_finalize_connection(h2c, 0); + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http2 write handler"); + + if (h2c->last_out == NULL && !c->buffered) { + + if (wev->timer_set) { + ngx_del_timer(wev); + } + + ngx_http_v2_handle_connection(h2c); + return; + } + + h2c->blocked = 1; + + rc = ngx_http_v2_send_output_queue(h2c); + + if (rc == NGX_ERROR) { + ngx_http_v2_finalize_connection(h2c, 0); + return; + } + + h2c->blocked = 0; + + if (rc == NGX_AGAIN) { + return; + } + + ngx_http_v2_handle_connection(h2c); +} + + +ngx_int_t +ngx_http_v2_send_output_queue(ngx_http_v2_connection_t *h2c) +{ + int tcp_nodelay; + ngx_chain_t *cl; + ngx_event_t *wev; + ngx_connection_t *c; + ngx_http_v2_out_frame_t *out, *frame, *fn; + ngx_http_core_loc_conf_t *clcf; + + c = h2c->connection; + wev = c->write; + + if (c->error) { + goto error; + } + + if (!wev->ready) { + return NGX_AGAIN; + } + + cl = NULL; + out = NULL; + + for (frame = h2c->last_out; frame; frame = fn) { + frame->last->next = cl; + cl = frame->first; + + fn = frame->next; + frame->next = out; + out = frame; + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http2 frame out: %p sid:%ui bl:%d len:%uz", + out, out->stream ? out->stream->node->id : 0, + out->blocked, out->length); + } + + cl = c->send_chain(c, cl, 0); + + if (cl == NGX_CHAIN_ERROR) { + goto error; + } + + clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx, + ngx_http_core_module); + + if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { + goto error; + } + + if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) { + if (ngx_tcp_push(c->fd) == -1) { + ngx_connection_error(c, ngx_socket_errno, ngx_tcp_push_n " failed"); + goto error; + } + + c->tcp_nopush = NGX_TCP_NOPUSH_UNSET; + tcp_nodelay = ngx_tcp_nodelay_and_tcp_nopush ? 1 : 0; + + } else { + tcp_nodelay = 1; + } + + if (tcp_nodelay && clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) { + goto error; + } + + for ( /* void */ ; out; out = fn) { + fn = out->next; + + if (out->handler(h2c, out) != NGX_OK) { + out->blocked = 1; + break; + } + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http2 frame sent: %p sid:%ui bl:%d len:%uz", + out, out->stream ? out->stream->node->id : 0, + out->blocked, out->length); + } + + frame = NULL; + + for ( /* void */ ; out; out = fn) { + fn = out->next; + out->next = frame; + frame = out; + } + + h2c->last_out = frame; + + if (!wev->ready) { + ngx_add_timer(wev, clcf->send_timeout); + return NGX_AGAIN; + } + + if (wev->timer_set) { + ngx_del_timer(wev); + } + + return NGX_OK; + +error: + + c->error = 1; + + if (!h2c->blocked) { + ngx_post_event(wev, &ngx_posted_events); + } + + return NGX_ERROR; +} + + +static void +ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c) +{ + ngx_int_t rc; + ngx_connection_t *c; + ngx_http_core_loc_conf_t *clcf; + + if (h2c->last_out || h2c->processing) { + return; + } + + c = h2c->connection; + + if (c->error) { + ngx_http_close_connection(c); + return; + } + + if (c->buffered) { + h2c->blocked = 1; + + rc = ngx_http_v2_send_output_queue(h2c); + + h2c->blocked = 0; + + if (rc == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } + + if (rc == NGX_AGAIN) { + return; + } + + /* rc == NGX_OK */ + } + + if (h2c->goaway) { + ngx_http_v2_lingering_close(c); + return; + } + + clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx, + ngx_http_core_module); + + if (!c->read->timer_set) { + ngx_add_timer(c->read, clcf->keepalive_timeout); + } + + ngx_reusable_connection(c, 1); + + if (h2c->state.incomplete) { + return; + } + + ngx_destroy_pool(h2c->pool); + + h2c->pool = NULL; + h2c->free_frames = NULL; + h2c->frames = 0; + h2c->free_fake_connections = NULL; + +#if (NGX_HTTP_SSL) + if (c->ssl) { + ngx_ssl_free_buffer(c); + } +#endif + + c->destroyed = 1; + + c->write->handler = ngx_http_empty_handler; + c->read->handler = ngx_http_v2_idle_handler; + + if (c->write->timer_set) { + ngx_del_timer(c->write); + } +} + + +static void +ngx_http_v2_lingering_close(ngx_connection_t *c) +{ + ngx_event_t *rev, *wev; + ngx_http_v2_connection_t *h2c; + ngx_http_core_loc_conf_t *clcf; + + h2c = c->data; + + clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx, + ngx_http_core_module); + + if (clcf->lingering_close == NGX_HTTP_LINGERING_OFF) { + ngx_http_close_connection(c); + return; + } + + if (h2c->lingering_time == 0) { + h2c->lingering_time = ngx_time() + + (time_t) (clcf->lingering_time / 1000); + } + +#if (NGX_HTTP_SSL) + if (c->ssl) { + ngx_int_t rc; + + rc = ngx_ssl_shutdown(c); + + if (rc == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } + + if (rc == NGX_AGAIN) { + c->ssl->handler = ngx_http_v2_lingering_close; + return; + } + } +#endif + + rev = c->read; + rev->handler = ngx_http_v2_lingering_close_handler; + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + + wev = c->write; + wev->handler = ngx_http_empty_handler; + + if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) { + if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + } + + if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) { + ngx_connection_error(c, ngx_socket_errno, + ngx_shutdown_socket_n " failed"); + ngx_http_close_connection(c); + return; + } + + c->close = 0; + ngx_reusable_connection(c, 1); + + ngx_add_timer(rev, clcf->lingering_timeout); + + if (rev->ready) { + ngx_http_v2_lingering_close_handler(rev); + } +} + + +static void +ngx_http_v2_lingering_close_handler(ngx_event_t *rev) +{ + ssize_t n; + ngx_msec_t timer; + ngx_connection_t *c; + ngx_http_core_loc_conf_t *clcf; + ngx_http_v2_connection_t *h2c; + u_char buffer[NGX_HTTP_LINGERING_BUFFER_SIZE]; + + c = rev->data; + h2c = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http2 lingering close handler"); + + if (rev->timedout || c->close) { + ngx_http_close_connection(c); + return; + } + + timer = (ngx_msec_t) h2c->lingering_time - (ngx_msec_t) ngx_time(); + if ((ngx_msec_int_t) timer <= 0) { + ngx_http_close_connection(c); + return; + } + + do { + n = c->recv(c, buffer, NGX_HTTP_LINGERING_BUFFER_SIZE); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "lingering read: %z", n); + + if (n == NGX_AGAIN) { + break; + } + + if (n == NGX_ERROR || n == 0) { + ngx_http_close_connection(c); + return; + } + + } while (rev->ready); + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + + clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx, + ngx_http_core_module); + timer *= 1000; + + if (timer > clcf->lingering_timeout) { + timer = clcf->lingering_timeout; + } + + ngx_add_timer(rev, timer); +} + + +static u_char * +ngx_http_v2_state_preface(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ + static const u_char preface[] = NGX_HTTP_V2_PREFACE_START; + + if ((size_t) (end - pos) < sizeof(preface) - 1) { + return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_preface); + } + + if (ngx_memcmp(pos, preface, sizeof(preface) - 1) != 0) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "invalid connection preface"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + + return ngx_http_v2_state_preface_end(h2c, pos + sizeof(preface) - 1, end); +} + + +static u_char * +ngx_http_v2_state_preface_end(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ + static const u_char preface[] = NGX_HTTP_V2_PREFACE_END; + + if ((size_t) (end - pos) < sizeof(preface) - 1) { + return ngx_http_v2_state_save(h2c, pos, end, + ngx_http_v2_state_preface_end); + } + + if (ngx_memcmp(pos, preface, sizeof(preface) - 1) != 0) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "invalid connection preface"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 preface verified"); + + return ngx_http_v2_state_head(h2c, pos + sizeof(preface) - 1, end); +} + + +static u_char * +ngx_http_v2_state_head(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) +{ + uint32_t head; + ngx_uint_t type; + + if (end - pos < NGX_HTTP_V2_FRAME_HEADER_SIZE) { + return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_head); + } + + head = ngx_http_v2_parse_uint32(pos); + + h2c->state.length = ngx_http_v2_parse_length(head); + h2c->state.flags = pos[4]; + + h2c->state.sid = ngx_http_v2_parse_sid(&pos[5]); + + pos += NGX_HTTP_V2_FRAME_HEADER_SIZE; + + type = ngx_http_v2_parse_type(head); + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 frame type:%ui f:%Xd l:%uz sid:%ui", + type, h2c->state.flags, h2c->state.length, h2c->state.sid); + + if (type >= NGX_HTTP_V2_FRAME_STATES) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent frame with unknown type %ui", type); + return ngx_http_v2_state_skip(h2c, pos, end); + } + + return ngx_http_v2_frame_states[type](h2c, pos, end); +} + + +static u_char * +ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) +{ + size_t size; + ngx_http_v2_node_t *node; + ngx_http_v2_stream_t *stream; + + size = h2c->state.length; + + if (h2c->state.flags & NGX_HTTP_V2_PADDED_FLAG) { + + if (h2c->state.length == 0) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent padded DATA frame " + "with incorrect length: 0"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); + } + + if (end - pos == 0) { + return ngx_http_v2_state_save(h2c, pos, end, + ngx_http_v2_state_data); + } + + h2c->state.padding = *pos++; + + if (h2c->state.padding >= size) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent padded DATA frame " + "with incorrect length: %uz, padding: %uz", + size, h2c->state.padding); + + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_PROTOCOL_ERROR); + } + + h2c->state.length -= 1 + h2c->state.padding; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 DATA frame"); + + if (h2c->state.sid == 0) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent DATA frame with incorrect identifier"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + + if (size > h2c->recv_window) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client violated connection flow control: " + "received DATA frame length %uz, available window %uz", + size, h2c->recv_window); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_FLOW_CTRL_ERROR); + } + + h2c->recv_window -= size; + + if (h2c->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4) { + + if (ngx_http_v2_send_window_update(h2c, 0, NGX_HTTP_V2_MAX_WINDOW + - h2c->recv_window) + == NGX_ERROR) + { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + h2c->recv_window = NGX_HTTP_V2_MAX_WINDOW; + } + + node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 0); + + if (node == NULL || node->stream == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "unknown http2 stream"); + + return ngx_http_v2_state_skip_padded(h2c, pos, end); + } + + stream = node->stream; + + if (size > stream->recv_window) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client violated flow control for stream %ui: " + "received DATA frame length %uz, available window %uz", + node->id, size, stream->recv_window); + + if (ngx_http_v2_terminate_stream(h2c, stream, + NGX_HTTP_V2_FLOW_CTRL_ERROR) + == NGX_ERROR) + { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + return ngx_http_v2_state_skip_padded(h2c, pos, end); + } + + stream->recv_window -= size; + + if (stream->no_flow_control + && stream->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4) + { + if (ngx_http_v2_send_window_update(h2c, node->id, + NGX_HTTP_V2_MAX_WINDOW + - stream->recv_window) + == NGX_ERROR) + { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + stream->recv_window = NGX_HTTP_V2_MAX_WINDOW; + } + + if (stream->in_closed) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent DATA frame for half-closed stream %ui", + node->id); + + if (ngx_http_v2_terminate_stream(h2c, stream, + NGX_HTTP_V2_STREAM_CLOSED) + == NGX_ERROR) + { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + return ngx_http_v2_state_skip_padded(h2c, pos, end); + } + + h2c->state.stream = stream; + + return ngx_http_v2_state_read_data(h2c, pos, end); +} + + +static u_char * +ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ + size_t size; + ngx_buf_t *buf; + ngx_int_t rc; + ngx_connection_t *fc; + ngx_http_request_t *r; + ngx_http_v2_stream_t *stream; + ngx_http_v2_srv_conf_t *h2scf; + + stream = h2c->state.stream; + + if (stream == NULL) { + return ngx_http_v2_state_skip_padded(h2c, pos, end); + } + + if (stream->skip_data) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "skipping http2 DATA frame"); + + return ngx_http_v2_state_skip_padded(h2c, pos, end); + } + + r = stream->request; + fc = r->connection; + + if (r->reading_body && !r->request_body_no_buffering) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "skipping http2 DATA frame"); + + return ngx_http_v2_state_skip_padded(h2c, pos, end); + } + + if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "skipping http2 DATA frame"); + + return ngx_http_v2_state_skip_padded(h2c, pos, end); + } + + size = end - pos; + + if (size >= h2c->state.length) { + size = h2c->state.length; + stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG; + } + + h2c->payload_bytes += size; + + if (r->request_body) { + rc = ngx_http_v2_process_request_body(r, pos, size, + stream->in_closed, 0); + + if (rc != NGX_OK && rc != NGX_AGAIN) { + stream->skip_data = 1; + ngx_http_finalize_request(r, rc); + } + + ngx_http_run_posted_requests(fc); + + } else if (size) { + buf = stream->preread; + + if (buf == NULL) { + h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module); + + buf = ngx_create_temp_buf(r->pool, h2scf->preread_size); + if (buf == NULL) { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + stream->preread = buf; + } + + if (size > (size_t) (buf->end - buf->last)) { + ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0, + "http2 preread buffer overflow"); + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + buf->last = ngx_cpymem(buf->last, pos, size); + } + + pos += size; + h2c->state.length -= size; + + if (h2c->state.length) { + return ngx_http_v2_state_save(h2c, pos, end, + ngx_http_v2_state_read_data); + } + + if (h2c->state.padding) { + return ngx_http_v2_state_skip_padded(h2c, pos, end); + } + + return ngx_http_v2_state_complete(h2c, pos, end); +} + + +static u_char * +ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ + size_t size; + ngx_uint_t padded, priority, depend, dependency, excl, + weight; + ngx_uint_t status; + ngx_http_v2_node_t *node; + ngx_http_v2_stream_t *stream; + ngx_http_v2_srv_conf_t *h2scf; + ngx_http_core_srv_conf_t *cscf; + ngx_http_core_loc_conf_t *clcf; + + padded = h2c->state.flags & NGX_HTTP_V2_PADDED_FLAG; + priority = h2c->state.flags & NGX_HTTP_V2_PRIORITY_FLAG; + + size = 0; + + if (padded) { + size++; + } + + if (priority) { + size += sizeof(uint32_t) + 1; + } + + if (h2c->state.length < size) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent HEADERS frame with incorrect length %uz", + h2c->state.length); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); + } + + if (h2c->state.length == size) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent HEADERS frame with empty header block"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); + } + + if (h2c->goaway) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "skipping http2 HEADERS frame"); + return ngx_http_v2_state_skip(h2c, pos, end); + } + + if ((size_t) (end - pos) < size) { + return ngx_http_v2_state_save(h2c, pos, end, + ngx_http_v2_state_headers); + } + + h2c->state.length -= size; + + if (padded) { + h2c->state.padding = *pos++; + + if (h2c->state.padding > h2c->state.length) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent padded HEADERS frame " + "with incorrect length: %uz, padding: %uz", + h2c->state.length, h2c->state.padding); + + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_PROTOCOL_ERROR); + } + + h2c->state.length -= h2c->state.padding; + } + + depend = 0; + excl = 0; + weight = NGX_HTTP_V2_DEFAULT_WEIGHT; + + if (priority) { + dependency = ngx_http_v2_parse_uint32(pos); + + depend = dependency & 0x7fffffff; + excl = dependency >> 31; + weight = pos[4] + 1; + + pos += sizeof(uint32_t) + 1; + } + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 HEADERS frame sid:%ui " + "depends on %ui excl:%ui weight:%ui", + h2c->state.sid, depend, excl, weight); + + if (h2c->state.sid % 2 == 0 || h2c->state.sid <= h2c->last_sid) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent HEADERS frame with incorrect identifier " + "%ui, the last was %ui", h2c->state.sid, h2c->last_sid); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + + if (depend == h2c->state.sid) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent HEADERS frame for stream %ui " + "with incorrect dependency", h2c->state.sid); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + + h2c->last_sid = h2c->state.sid; + + h2c->state.pool = ngx_create_pool(1024, h2c->connection->log); + if (h2c->state.pool == NULL) { + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); + } + + cscf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, + ngx_http_core_module); + + h2c->state.header_limit = cscf->large_client_header_buffers.size + * cscf->large_client_header_buffers.num; + + h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, + ngx_http_v2_module); + + if (h2c->processing >= h2scf->concurrent_streams) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "concurrent streams exceeded %ui", h2c->processing); + + status = NGX_HTTP_V2_REFUSED_STREAM; + goto rst_stream; + } + + if (h2c->new_streams++ >= 2 * h2scf->concurrent_streams) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent too many streams at once"); + + status = NGX_HTTP_V2_REFUSED_STREAM; + goto rst_stream; + } + + if (!h2c->settings_ack + && !(h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG) + && h2scf->preread_size < NGX_HTTP_V2_DEFAULT_WINDOW) + { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent stream with data " + "before settings were acknowledged"); + + status = NGX_HTTP_V2_REFUSED_STREAM; + goto rst_stream; + } + + node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 1); + + if (node == NULL) { + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); + } + + if (node->parent) { + ngx_queue_remove(&node->reuse); + h2c->closed_nodes--; + } + + stream = ngx_http_v2_create_stream(h2c); + if (stream == NULL) { + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); + } + + h2c->state.stream = stream; + + stream->pool = h2c->state.pool; + h2c->state.keep_pool = 1; + + stream->request->request_length = h2c->state.length; + + stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG; + stream->node = node; + + node->stream = stream; + + if (priority || node->parent == NULL) { + node->weight = weight; + ngx_http_v2_set_dependency(h2c, node, depend, excl); + } + + clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx, + ngx_http_core_module); + + if (clcf->keepalive_timeout == 0 + || h2c->connection->requests >= clcf->keepalive_requests + || ngx_current_msec - h2c->connection->start_time + > clcf->keepalive_time) + { + h2c->goaway = 1; + + if (ngx_http_v2_send_goaway(h2c, NGX_HTTP_V2_NO_ERROR) == NGX_ERROR) { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + } + + return ngx_http_v2_state_header_block(h2c, pos, end); + +rst_stream: + + if (h2c->refused_streams++ > ngx_max(h2scf->concurrent_streams, 100)) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent too many refused streams"); + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_NO_ERROR); + } + + if (ngx_http_v2_send_rst_stream(h2c, h2c->state.sid, status) != NGX_OK) { + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); + } + + return ngx_http_v2_state_header_block(h2c, pos, end); +} + + +static u_char * +ngx_http_v2_state_header_block(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ + u_char ch; + ngx_int_t value; + ngx_uint_t indexed, size_update, prefix; + + if (end - pos < 1) { + return ngx_http_v2_state_headers_save(h2c, pos, end, + ngx_http_v2_state_header_block); + } + + if (!(h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) + && h2c->state.length < NGX_HTTP_V2_INT_OCTETS) + { + return ngx_http_v2_handle_continuation(h2c, pos, end, + ngx_http_v2_state_header_block); + } + + size_update = 0; + indexed = 0; + + ch = *pos; + + if (ch >= (1 << 7)) { + /* indexed header field */ + indexed = 1; + prefix = ngx_http_v2_prefix(7); + + } else if (ch >= (1 << 6)) { + /* literal header field with incremental indexing */ + h2c->state.index = 1; + prefix = ngx_http_v2_prefix(6); + + } else if (ch >= (1 << 5)) { + /* dynamic table size update */ + size_update = 1; + prefix = ngx_http_v2_prefix(5); + + } else if (ch >= (1 << 4)) { + /* literal header field never indexed */ + prefix = ngx_http_v2_prefix(4); + + } else { + /* literal header field without indexing */ + prefix = ngx_http_v2_prefix(4); + } + + value = ngx_http_v2_parse_int(h2c, &pos, end, prefix); + + if (value < 0) { + if (value == NGX_AGAIN) { + return ngx_http_v2_state_headers_save(h2c, pos, end, + ngx_http_v2_state_header_block); + } + + if (value == NGX_DECLINED) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent header block with too long %s value", + size_update ? "size update" : "header index"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR); + } + + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent header block with incorrect length"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); + } + + if (indexed) { + if (ngx_http_v2_get_indexed_header(h2c, value, 0) != NGX_OK) { + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR); + } + + return ngx_http_v2_state_process_header(h2c, pos, end); + } + + if (size_update) { + if (ngx_http_v2_table_size(h2c, value) != NGX_OK) { + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR); + } + + return ngx_http_v2_state_header_complete(h2c, pos, end); + } + + if (value == 0) { + h2c->state.parse_name = 1; + + } else if (ngx_http_v2_get_indexed_header(h2c, value, 1) != NGX_OK) { + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR); + } + + h2c->state.parse_value = 1; + + return ngx_http_v2_state_field_len(h2c, pos, end); +} + + +static u_char * +ngx_http_v2_state_field_len(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ + size_t alloc; + ngx_int_t len; + ngx_uint_t huff; + ngx_http_core_srv_conf_t *cscf; + + if (!(h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) + && h2c->state.length < NGX_HTTP_V2_INT_OCTETS) + { + return ngx_http_v2_handle_continuation(h2c, pos, end, + ngx_http_v2_state_field_len); + } + + if (h2c->state.length < 1) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent header block with incorrect length"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); + } + + if (end - pos < 1) { + return ngx_http_v2_state_headers_save(h2c, pos, end, + ngx_http_v2_state_field_len); + } + + huff = *pos >> 7; + len = ngx_http_v2_parse_int(h2c, &pos, end, ngx_http_v2_prefix(7)); + + if (len < 0) { + if (len == NGX_AGAIN) { + return ngx_http_v2_state_headers_save(h2c, pos, end, + ngx_http_v2_state_field_len); + } + + if (len == NGX_DECLINED) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent header field with too long length value"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR); + } + + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent header block with incorrect length"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 %s string, len:%i", + huff ? "encoded" : "raw", len); + + cscf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, + ngx_http_core_module); + + if ((size_t) len > cscf->large_client_header_buffers.size) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent too large header field"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_ENHANCE_YOUR_CALM); + } + + h2c->state.field_rest = len; + + if (h2c->state.stream == NULL && !h2c->state.index) { + return ngx_http_v2_state_field_skip(h2c, pos, end); + } + + alloc = (huff ? len * 8 / 5 : len) + 1; + + h2c->state.field_start = ngx_pnalloc(h2c->state.pool, alloc); + if (h2c->state.field_start == NULL) { + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); + } + + h2c->state.field_end = h2c->state.field_start; + + if (huff) { + return ngx_http_v2_state_field_huff(h2c, pos, end); + } + + return ngx_http_v2_state_field_raw(h2c, pos, end); +} + + +static u_char * +ngx_http_v2_state_field_huff(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ + size_t size; + + size = end - pos; + + if (size > h2c->state.field_rest) { + size = h2c->state.field_rest; + } + + if (size > h2c->state.length) { + size = h2c->state.length; + } + + h2c->state.length -= size; + h2c->state.field_rest -= size; + + if (ngx_http_huff_decode(&h2c->state.field_state, pos, size, + &h2c->state.field_end, + h2c->state.field_rest == 0, + h2c->connection->log) + != NGX_OK) + { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent invalid encoded header field"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR); + } + + pos += size; + + if (h2c->state.field_rest == 0) { + *h2c->state.field_end = '\0'; + return ngx_http_v2_state_process_header(h2c, pos, end); + } + + if (h2c->state.length) { + return ngx_http_v2_state_headers_save(h2c, pos, end, + ngx_http_v2_state_field_huff); + } + + if (h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent header field with incorrect length"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); + } + + return ngx_http_v2_handle_continuation(h2c, pos, end, + ngx_http_v2_state_field_huff); +} + + +static u_char * +ngx_http_v2_state_field_raw(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ + size_t size; + + size = end - pos; + + if (size > h2c->state.field_rest) { + size = h2c->state.field_rest; + } + + if (size > h2c->state.length) { + size = h2c->state.length; + } + + h2c->state.length -= size; + h2c->state.field_rest -= size; + + h2c->state.field_end = ngx_cpymem(h2c->state.field_end, pos, size); + + pos += size; + + if (h2c->state.field_rest == 0) { + *h2c->state.field_end = '\0'; + return ngx_http_v2_state_process_header(h2c, pos, end); + } + + if (h2c->state.length) { + return ngx_http_v2_state_headers_save(h2c, pos, end, + ngx_http_v2_state_field_raw); + } + + if (h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent header field with incorrect length"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); + } + + return ngx_http_v2_handle_continuation(h2c, pos, end, + ngx_http_v2_state_field_raw); +} + + +static u_char * +ngx_http_v2_state_field_skip(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ + size_t size; + + size = end - pos; + + if (size > h2c->state.field_rest) { + size = h2c->state.field_rest; + } + + if (size > h2c->state.length) { + size = h2c->state.length; + } + + h2c->state.length -= size; + h2c->state.field_rest -= size; + + pos += size; + + if (h2c->state.field_rest == 0) { + return ngx_http_v2_state_process_header(h2c, pos, end); + } + + if (h2c->state.length) { + return ngx_http_v2_state_save(h2c, pos, end, + ngx_http_v2_state_field_skip); + } + + if (h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent header field with incorrect length"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); + } + + return ngx_http_v2_handle_continuation(h2c, pos, end, + ngx_http_v2_state_field_skip); +} + + +static u_char * +ngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ + size_t len; + ngx_int_t rc; + ngx_table_elt_t *h; + ngx_connection_t *fc; + ngx_http_header_t *hh; + ngx_http_request_t *r; + ngx_http_v2_header_t *header; + ngx_http_core_srv_conf_t *cscf; + ngx_http_core_main_conf_t *cmcf; + + static ngx_str_t cookie = ngx_string("cookie"); + + header = &h2c->state.header; + + if (h2c->state.parse_name) { + h2c->state.parse_name = 0; + + header->name.len = h2c->state.field_end - h2c->state.field_start; + header->name.data = h2c->state.field_start; + + if (header->name.len == 0) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent zero header name length"); + + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_PROTOCOL_ERROR); + } + + return ngx_http_v2_state_field_len(h2c, pos, end); + } + + if (h2c->state.parse_value) { + h2c->state.parse_value = 0; + + header->value.len = h2c->state.field_end - h2c->state.field_start; + header->value.data = h2c->state.field_start; + } + + len = header->name.len + header->value.len; + + if (len > h2c->state.header_limit) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent too large header"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_ENHANCE_YOUR_CALM); + } + + h2c->state.header_limit -= len; + + if (h2c->state.index) { + if (ngx_http_v2_add_header(h2c, header) != NGX_OK) { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + h2c->state.index = 0; + } + + if (h2c->state.stream == NULL) { + return ngx_http_v2_state_header_complete(h2c, pos, end); + } + + r = h2c->state.stream->request; + fc = r->connection; + + /* TODO Optimization: validate headers while parsing. */ + if (ngx_http_v2_validate_header(r, header) != NGX_OK) { + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + goto error; + } + + if (header->name.data[0] == ':') { + rc = ngx_http_v2_pseudo_header(r, header); + + if (rc == NGX_OK) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http2 header: \":%V: %V\"", + &header->name, &header->value); + + return ngx_http_v2_state_header_complete(h2c, pos, end); + } + + if (rc == NGX_ABORT) { + goto error; + } + + if (rc == NGX_DECLINED) { + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + goto error; + } + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); + } + + if (r->invalid_header) { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + if (cscf->ignore_invalid_headers) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid header: \"%V\"", &header->name); + + return ngx_http_v2_state_header_complete(h2c, pos, end); + } + } + + if (header->name.len == cookie.len + && ngx_memcmp(header->name.data, cookie.data, cookie.len) == 0) + { + if (ngx_http_v2_cookie(r, header) != NGX_OK) { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + } else { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + if (r->headers_in.count++ >= cscf->max_headers) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent too many header lines"); + ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); + goto error; + } + + h = ngx_list_push(&r->headers_in.headers); + if (h == NULL) { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + h->key.len = header->name.len; + h->key.data = header->name.data; + + /* + * TODO Optimization: precalculate hash + * and handler for indexed headers. + */ + h->hash = ngx_hash_key(h->key.data, h->key.len); + + h->value.len = header->value.len; + h->value.data = header->value.data; + + h->lowcase_key = h->key.data; + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, + h->lowcase_key, h->key.len); + + if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { + goto error; + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http2 header: \"%V: %V\"", + &header->name, &header->value); + + return ngx_http_v2_state_header_complete(h2c, pos, end); + +error: + + h2c->state.stream = NULL; + + ngx_http_run_posted_requests(fc); + + return ngx_http_v2_state_header_complete(h2c, pos, end); +} + + +static u_char * +ngx_http_v2_state_header_complete(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ + ngx_http_v2_stream_t *stream; + + if (h2c->state.length) { + if (end - pos > 0) { + h2c->state.handler = ngx_http_v2_state_header_block; + return pos; + } + + return ngx_http_v2_state_headers_save(h2c, pos, end, + ngx_http_v2_state_header_block); + } + + if (!(h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG)) { + return ngx_http_v2_handle_continuation(h2c, pos, end, + ngx_http_v2_state_header_complete); + } + + stream = h2c->state.stream; + + if (stream) { + ngx_http_v2_run_request(stream->request); + } + + if (!h2c->state.keep_pool) { + ngx_destroy_pool(h2c->state.pool); + } + + h2c->state.pool = NULL; + h2c->state.keep_pool = 0; + + if (h2c->state.padding) { + return ngx_http_v2_state_skip_padded(h2c, pos, end); + } + + return ngx_http_v2_state_complete(h2c, pos, end); +} + + +static u_char * +ngx_http_v2_handle_continuation(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end, ngx_http_v2_handler_pt handler) +{ + u_char *p; + size_t len, skip; + uint32_t head; + + len = h2c->state.length; + + if (h2c->state.padding && (size_t) (end - pos) > len) { + skip = ngx_min(h2c->state.padding, (end - pos) - len); + + h2c->state.padding -= skip; + + p = pos; + pos += skip; + ngx_memmove(pos, p, len); + } + + if ((size_t) (end - pos) < len + NGX_HTTP_V2_FRAME_HEADER_SIZE) { + return ngx_http_v2_state_headers_save(h2c, pos, end, handler); + } + + p = pos + len; + + head = ngx_http_v2_parse_uint32(p); + + if (ngx_http_v2_parse_type(head) != NGX_HTTP_V2_CONTINUATION_FRAME) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent inappropriate frame while CONTINUATION was expected"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + + h2c->state.flags |= p[4]; + + if (h2c->state.sid != ngx_http_v2_parse_sid(&p[5])) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent CONTINUATION frame with incorrect identifier"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + + p = pos; + pos += NGX_HTTP_V2_FRAME_HEADER_SIZE; + + ngx_memcpy(pos, p, len); + + len = ngx_http_v2_parse_length(head); + + h2c->state.length += len; + + if (h2c->state.stream) { + h2c->state.stream->request->request_length += len; + } + + h2c->state.handler = handler; + return pos; +} + + +static u_char * +ngx_http_v2_state_priority(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ + ngx_uint_t depend, dependency, excl, weight; + ngx_http_v2_node_t *node; + + if (h2c->state.length != NGX_HTTP_V2_PRIORITY_SIZE) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent PRIORITY frame with incorrect length %uz", + h2c->state.length); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); + } + + if (--h2c->priority_limit == 0) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent too many PRIORITY frames"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_ENHANCE_YOUR_CALM); + } + + if (end - pos < NGX_HTTP_V2_PRIORITY_SIZE) { + return ngx_http_v2_state_save(h2c, pos, end, + ngx_http_v2_state_priority); + } + + dependency = ngx_http_v2_parse_uint32(pos); + + depend = dependency & 0x7fffffff; + excl = dependency >> 31; + weight = pos[4] + 1; + + pos += NGX_HTTP_V2_PRIORITY_SIZE; + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 PRIORITY frame sid:%ui " + "depends on %ui excl:%ui weight:%ui", + h2c->state.sid, depend, excl, weight); + + if (h2c->state.sid == 0) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent PRIORITY frame with incorrect identifier"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + + if (depend == h2c->state.sid) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent PRIORITY frame for stream %ui " + "with incorrect dependency", h2c->state.sid); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + + node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 1); + + if (node == NULL) { + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); + } + + node->weight = weight; + + if (node->stream == NULL) { + if (node->parent == NULL) { + h2c->closed_nodes++; + + } else { + ngx_queue_remove(&node->reuse); + } + + ngx_queue_insert_tail(&h2c->closed, &node->reuse); + } + + ngx_http_v2_set_dependency(h2c, node, depend, excl); + + return ngx_http_v2_state_complete(h2c, pos, end); +} + + +static u_char * +ngx_http_v2_state_rst_stream(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ + ngx_uint_t status; + ngx_event_t *ev; + ngx_connection_t *fc; + ngx_http_v2_node_t *node; + ngx_http_v2_stream_t *stream; + + if (h2c->state.length != NGX_HTTP_V2_RST_STREAM_SIZE) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent RST_STREAM frame with incorrect length %uz", + h2c->state.length); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); + } + + if (end - pos < NGX_HTTP_V2_RST_STREAM_SIZE) { + return ngx_http_v2_state_save(h2c, pos, end, + ngx_http_v2_state_rst_stream); + } + + status = ngx_http_v2_parse_uint32(pos); + + pos += NGX_HTTP_V2_RST_STREAM_SIZE; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 RST_STREAM frame, sid:%ui status:%ui", + h2c->state.sid, status); + + if (h2c->state.sid == 0) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent RST_STREAM frame with incorrect identifier"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + + node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 0); + + if (node == NULL || node->stream == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "unknown http2 stream"); + + return ngx_http_v2_state_complete(h2c, pos, end); + } + + stream = node->stream; + + stream->in_closed = 1; + stream->out_closed = 1; + + fc = stream->request->connection; + fc->error = 1; + + switch (status) { + + case NGX_HTTP_V2_CANCEL: + ngx_log_error(NGX_LOG_INFO, fc->log, 0, + "client canceled stream %ui", h2c->state.sid); + break; + + case NGX_HTTP_V2_INTERNAL_ERROR: + ngx_log_error(NGX_LOG_INFO, fc->log, 0, + "client terminated stream %ui due to internal error", + h2c->state.sid); + break; + + default: + ngx_log_error(NGX_LOG_INFO, fc->log, 0, + "client terminated stream %ui with status %ui", + h2c->state.sid, status); + break; + } + + ev = fc->read; + ev->handler(ev); + + return ngx_http_v2_state_complete(h2c, pos, end); +} + + +static u_char * +ngx_http_v2_state_settings(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 SETTINGS frame"); + + if (h2c->state.sid) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent SETTINGS frame with incorrect identifier"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + + if (h2c->state.flags == NGX_HTTP_V2_ACK_FLAG) { + + if (h2c->state.length != 0) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent SETTINGS frame with the ACK flag " + "and nonzero length"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); + } + + h2c->settings_ack = 1; + + return ngx_http_v2_state_complete(h2c, pos, end); + } + + if (h2c->state.length % NGX_HTTP_V2_SETTINGS_PARAM_SIZE) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent SETTINGS frame with incorrect length %uz", + h2c->state.length); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); + } + + return ngx_http_v2_state_settings_params(h2c, pos, end); +} + + +static u_char * +ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ + ssize_t window_delta; + ngx_uint_t id, value; + ngx_http_v2_out_frame_t *frame; + + window_delta = 0; + + while (h2c->state.length) { + if (end - pos < NGX_HTTP_V2_SETTINGS_PARAM_SIZE) { + return ngx_http_v2_state_save(h2c, pos, end, + ngx_http_v2_state_settings_params); + } + + h2c->state.length -= NGX_HTTP_V2_SETTINGS_PARAM_SIZE; + + id = ngx_http_v2_parse_uint16(pos); + value = ngx_http_v2_parse_uint32(&pos[2]); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 setting %ui:%ui", id, value); + + switch (id) { + + case NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING: + + if (value > NGX_HTTP_V2_MAX_WINDOW) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent SETTINGS frame with incorrect " + "INITIAL_WINDOW_SIZE value %ui", value); + + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_FLOW_CTRL_ERROR); + } + + window_delta = value - h2c->init_window; + break; + + case NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING: + + if (value > NGX_HTTP_V2_MAX_FRAME_SIZE + || value < NGX_HTTP_V2_DEFAULT_FRAME_SIZE) + { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent SETTINGS frame with incorrect " + "MAX_FRAME_SIZE value %ui", value); + + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_PROTOCOL_ERROR); + } + + h2c->frame_size = value; + break; + + case NGX_HTTP_V2_ENABLE_PUSH_SETTING: + + if (value > 1) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent SETTINGS frame with incorrect " + "ENABLE_PUSH value %ui", value); + + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_PROTOCOL_ERROR); + } + + break; + + case NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING: + + h2c->table_update = 1; + break; + + default: + break; + } + + pos += NGX_HTTP_V2_SETTINGS_PARAM_SIZE; + } + + frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_SETTINGS_ACK_SIZE, + NGX_HTTP_V2_SETTINGS_FRAME, + NGX_HTTP_V2_ACK_FLAG, 0); + if (frame == NULL) { + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); + } + + ngx_http_v2_queue_ordered_frame(h2c, frame); + + if (window_delta) { + h2c->init_window += window_delta; + + if (ngx_http_v2_adjust_windows(h2c, window_delta) != NGX_OK) { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + } + + return ngx_http_v2_state_complete(h2c, pos, end); +} + + +static u_char * +ngx_http_v2_state_push_promise(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent PUSH_PROMISE frame"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); +} + + +static u_char * +ngx_http_v2_state_ping(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) +{ + ngx_buf_t *buf; + ngx_http_v2_out_frame_t *frame; + + if (h2c->state.length != NGX_HTTP_V2_PING_SIZE) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent PING frame with incorrect length %uz", + h2c->state.length); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); + } + + if (end - pos < NGX_HTTP_V2_PING_SIZE) { + return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_ping); + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 PING frame"); + + if (h2c->state.sid) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent PING frame with incorrect identifier"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + + if (h2c->state.flags & NGX_HTTP_V2_ACK_FLAG) { + return ngx_http_v2_state_skip(h2c, pos, end); + } + + frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_PING_SIZE, + NGX_HTTP_V2_PING_FRAME, + NGX_HTTP_V2_ACK_FLAG, 0); + if (frame == NULL) { + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); + } + + buf = frame->first->buf; + + buf->last = ngx_cpymem(buf->last, pos, NGX_HTTP_V2_PING_SIZE); + + ngx_http_v2_queue_blocked_frame(h2c, frame); + + return ngx_http_v2_state_complete(h2c, pos + NGX_HTTP_V2_PING_SIZE, end); +} + + +static u_char * +ngx_http_v2_state_goaway(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ +#if (NGX_DEBUG) + ngx_uint_t last_sid, error; +#endif + + if (h2c->state.length < NGX_HTTP_V2_GOAWAY_SIZE) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent GOAWAY frame " + "with incorrect length %uz", h2c->state.length); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); + } + + if (end - pos < NGX_HTTP_V2_GOAWAY_SIZE) { + return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_goaway); + } + + if (h2c->state.sid) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent GOAWAY frame with incorrect identifier"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + +#if (NGX_DEBUG) + h2c->state.length -= NGX_HTTP_V2_GOAWAY_SIZE; + + last_sid = ngx_http_v2_parse_sid(pos); + error = ngx_http_v2_parse_uint32(&pos[4]); + + pos += NGX_HTTP_V2_GOAWAY_SIZE; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 GOAWAY frame: last sid %ui, error %ui", + last_sid, error); +#endif + + return ngx_http_v2_state_skip(h2c, pos, end); +} + + +static u_char * +ngx_http_v2_state_window_update(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ + size_t window; + ngx_event_t *wev; + ngx_queue_t *q; + ngx_http_v2_node_t *node; + ngx_http_v2_stream_t *stream; + + if (h2c->state.length != NGX_HTTP_V2_WINDOW_UPDATE_SIZE) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent WINDOW_UPDATE frame " + "with incorrect length %uz", h2c->state.length); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR); + } + + if (end - pos < NGX_HTTP_V2_WINDOW_UPDATE_SIZE) { + return ngx_http_v2_state_save(h2c, pos, end, + ngx_http_v2_state_window_update); + } + + window = ngx_http_v2_parse_window(pos); + + pos += NGX_HTTP_V2_WINDOW_UPDATE_SIZE; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 WINDOW_UPDATE frame sid:%ui window:%uz", + h2c->state.sid, window); + + if (window == 0) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent WINDOW_UPDATE frame " + "with incorrect window increment 0"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); + } + + if (h2c->state.sid) { + node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 0); + + if (node == NULL || node->stream == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "unknown http2 stream"); + + return ngx_http_v2_state_complete(h2c, pos, end); + } + + stream = node->stream; + + if (window > (size_t) (NGX_HTTP_V2_MAX_WINDOW - stream->send_window)) { + + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client violated flow control for stream %ui: " + "received WINDOW_UPDATE frame " + "with window increment %uz " + "not allowed for window %z", + h2c->state.sid, window, stream->send_window); + + if (ngx_http_v2_terminate_stream(h2c, stream, + NGX_HTTP_V2_FLOW_CTRL_ERROR) + == NGX_ERROR) + { + return ngx_http_v2_connection_error(h2c, + NGX_HTTP_V2_INTERNAL_ERROR); + } + + return ngx_http_v2_state_complete(h2c, pos, end); + } + + stream->send_window += window; + + if (stream->exhausted) { + stream->exhausted = 0; + + wev = stream->request->connection->write; + + wev->active = 0; + wev->ready = 1; + + if (!wev->delayed) { + wev->handler(wev); + } + } + + return ngx_http_v2_state_complete(h2c, pos, end); + } + + if (window > NGX_HTTP_V2_MAX_WINDOW - h2c->send_window) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client violated connection flow control: " + "received WINDOW_UPDATE frame " + "with window increment %uz " + "not allowed for window %uz", + window, h2c->send_window); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_FLOW_CTRL_ERROR); + } + + h2c->send_window += window; + + while (!ngx_queue_empty(&h2c->waiting)) { + q = ngx_queue_head(&h2c->waiting); + + ngx_queue_remove(q); + + stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue); + + stream->waiting = 0; + + wev = stream->request->connection->write; + + wev->active = 0; + wev->ready = 1; + + if (!wev->delayed) { + wev->handler(wev); + + if (h2c->send_window == 0) { + break; + } + } + } + + return ngx_http_v2_state_complete(h2c, pos, end); +} + + +static u_char * +ngx_http_v2_state_continuation(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent unexpected CONTINUATION frame"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); +} + + +static u_char * +ngx_http_v2_state_complete(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 frame complete pos:%p end:%p", pos, end); + + if (pos > end) { + ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0, + "receive buffer overrun"); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); + } + + h2c->state.stream = NULL; + h2c->state.handler = ngx_http_v2_state_head; + + return pos; +} + + +static u_char * +ngx_http_v2_state_skip_padded(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end) +{ + h2c->state.length += h2c->state.padding; + h2c->state.padding = 0; + + return ngx_http_v2_state_skip(h2c, pos, end); +} + + +static u_char * +ngx_http_v2_state_skip(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end) +{ + size_t size; + + size = end - pos; + + if (size < h2c->state.length) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 frame skip %uz of %uz", size, h2c->state.length); + + h2c->state.length -= size; + return ngx_http_v2_state_save(h2c, end, end, ngx_http_v2_state_skip); + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 frame skip %uz", h2c->state.length); + + return ngx_http_v2_state_complete(h2c, pos + h2c->state.length, end); +} + + +static u_char * +ngx_http_v2_state_save(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end, + ngx_http_v2_handler_pt handler) +{ + size_t size; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 frame state save pos:%p end:%p handler:%p", + pos, end, handler); + + size = end - pos; + + if (size > NGX_HTTP_V2_STATE_BUFFER_SIZE) { + ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0, + "state buffer overflow: %uz bytes required", size); + + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); + } + + ngx_memcpy(h2c->state.buffer, pos, size); + + h2c->state.buffer_used = size; + h2c->state.handler = handler; + h2c->state.incomplete = 1; + + return end; +} + + +static u_char * +ngx_http_v2_state_headers_save(ngx_http_v2_connection_t *h2c, u_char *pos, + u_char *end, ngx_http_v2_handler_pt handler) +{ + ngx_event_t *rev; + ngx_http_request_t *r; + ngx_http_core_srv_conf_t *cscf; + + if (h2c->state.stream) { + r = h2c->state.stream->request; + rev = r->connection->read; + + if (!rev->timer_set) { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + ngx_add_timer(rev, cscf->client_header_timeout); + } + } + + return ngx_http_v2_state_save(h2c, pos, end, handler); +} + + +static u_char * +ngx_http_v2_connection_error(ngx_http_v2_connection_t *h2c, + ngx_uint_t err) +{ + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 state connection error"); + + ngx_http_v2_finalize_connection(h2c, err); + + return NULL; +} + + +static ngx_int_t +ngx_http_v2_parse_int(ngx_http_v2_connection_t *h2c, u_char **pos, u_char *end, + ngx_uint_t prefix) +{ + u_char *start, *p; + ngx_uint_t value, octet, shift; + + start = *pos; + p = start; + + value = *p++ & prefix; + + if (value != prefix) { + if (h2c->state.length == 0) { + return NGX_ERROR; + } + + h2c->state.length--; + + *pos = p; + return value; + } + + if (end - start > NGX_HTTP_V2_INT_OCTETS) { + end = start + NGX_HTTP_V2_INT_OCTETS; + } + + for (shift = 0; p != end; shift += 7) { + octet = *p++; + + value += (octet & 0x7f) << shift; + + if (octet < 128) { + if ((size_t) (p - start) > h2c->state.length) { + return NGX_ERROR; + } + + h2c->state.length -= p - start; + + *pos = p; + return value; + } + } + + if ((size_t) (end - start) >= h2c->state.length) { + return NGX_ERROR; + } + + if (end == start + NGX_HTTP_V2_INT_OCTETS) { + return NGX_DECLINED; + } + + return NGX_AGAIN; +} + + +static ngx_int_t +ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c) +{ + size_t len; + ngx_buf_t *buf; + ngx_chain_t *cl; + ngx_http_v2_srv_conf_t *h2scf; + ngx_http_v2_out_frame_t *frame; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send SETTINGS frame"); + + frame = ngx_palloc(h2c->pool, sizeof(ngx_http_v2_out_frame_t)); + if (frame == NULL) { + return NGX_ERROR; + } + + cl = ngx_alloc_chain_link(h2c->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + len = NGX_HTTP_V2_SETTINGS_PARAM_SIZE * 3; + + buf = ngx_create_temp_buf(h2c->pool, NGX_HTTP_V2_FRAME_HEADER_SIZE + len); + if (buf == NULL) { + return NGX_ERROR; + } + + buf->last_buf = 1; + + cl->buf = buf; + cl->next = NULL; + + frame->first = cl; + frame->last = cl; + frame->handler = ngx_http_v2_settings_frame_handler; + frame->stream = NULL; +#if (NGX_DEBUG) + frame->length = len; +#endif + frame->blocked = 0; + + buf->last = ngx_http_v2_write_len_and_type(buf->last, len, + NGX_HTTP_V2_SETTINGS_FRAME); + + *buf->last++ = NGX_HTTP_V2_NO_FLAG; + + buf->last = ngx_http_v2_write_sid(buf->last, 0); + + h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, + ngx_http_v2_module); + + buf->last = ngx_http_v2_write_uint16(buf->last, + NGX_HTTP_V2_MAX_STREAMS_SETTING); + buf->last = ngx_http_v2_write_uint32(buf->last, + h2scf->concurrent_streams); + + buf->last = ngx_http_v2_write_uint16(buf->last, + NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING); + buf->last = ngx_http_v2_write_uint32(buf->last, h2scf->preread_size); + + buf->last = ngx_http_v2_write_uint16(buf->last, + NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING); + buf->last = ngx_http_v2_write_uint32(buf->last, + NGX_HTTP_V2_MAX_FRAME_SIZE); + + ngx_http_v2_queue_blocked_frame(h2c, frame); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v2_settings_frame_handler(ngx_http_v2_connection_t *h2c, + ngx_http_v2_out_frame_t *frame) +{ + ngx_buf_t *buf; + + buf = frame->first->buf; + + if (buf->pos != buf->last) { + return NGX_AGAIN; + } + + ngx_free_chain(h2c->pool, frame->first); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v2_send_window_update(ngx_http_v2_connection_t *h2c, ngx_uint_t sid, + size_t window) +{ + ngx_buf_t *buf; + ngx_http_v2_out_frame_t *frame; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send WINDOW_UPDATE frame sid:%ui, window:%uz", + sid, window); + + frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_WINDOW_UPDATE_SIZE, + NGX_HTTP_V2_WINDOW_UPDATE_FRAME, + NGX_HTTP_V2_NO_FLAG, sid); + if (frame == NULL) { + return NGX_ERROR; + } + + buf = frame->first->buf; + + buf->last = ngx_http_v2_write_uint32(buf->last, window); + + ngx_http_v2_queue_blocked_frame(h2c, frame); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v2_send_rst_stream(ngx_http_v2_connection_t *h2c, ngx_uint_t sid, + ngx_uint_t status) +{ + ngx_buf_t *buf; + ngx_http_v2_out_frame_t *frame; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send RST_STREAM frame sid:%ui, status:%ui", + sid, status); + + frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_RST_STREAM_SIZE, + NGX_HTTP_V2_RST_STREAM_FRAME, + NGX_HTTP_V2_NO_FLAG, sid); + if (frame == NULL) { + return NGX_ERROR; + } + + buf = frame->first->buf; + + buf->last = ngx_http_v2_write_uint32(buf->last, status); + + ngx_http_v2_queue_blocked_frame(h2c, frame); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v2_send_goaway(ngx_http_v2_connection_t *h2c, ngx_uint_t status) +{ + ngx_buf_t *buf; + ngx_http_v2_out_frame_t *frame; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 send GOAWAY frame: last sid %ui, error %ui", + h2c->last_sid, status); + + frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_GOAWAY_SIZE, + NGX_HTTP_V2_GOAWAY_FRAME, + NGX_HTTP_V2_NO_FLAG, 0); + if (frame == NULL) { + return NGX_ERROR; + } + + buf = frame->first->buf; + + buf->last = ngx_http_v2_write_sid(buf->last, h2c->last_sid); + buf->last = ngx_http_v2_write_uint32(buf->last, status); + + ngx_http_v2_queue_blocked_frame(h2c, frame); + + return NGX_OK; +} + + +static ngx_http_v2_out_frame_t * +ngx_http_v2_get_frame(ngx_http_v2_connection_t *h2c, size_t length, + ngx_uint_t type, u_char flags, ngx_uint_t sid) +{ + ngx_buf_t *buf; + ngx_pool_t *pool; + ngx_http_v2_out_frame_t *frame; + + frame = h2c->free_frames; + + if (frame) { + h2c->free_frames = frame->next; + + buf = frame->first->buf; + buf->pos = buf->start; + + frame->blocked = 0; + + } else if (h2c->frames < 10000) { + pool = h2c->pool ? h2c->pool : h2c->connection->pool; + + frame = ngx_pcalloc(pool, sizeof(ngx_http_v2_out_frame_t)); + if (frame == NULL) { + return NULL; + } + + frame->first = ngx_alloc_chain_link(pool); + if (frame->first == NULL) { + return NULL; + } + + buf = ngx_create_temp_buf(pool, NGX_HTTP_V2_FRAME_BUFFER_SIZE); + if (buf == NULL) { + return NULL; + } + + buf->last_buf = 1; + + frame->first->buf = buf; + frame->last = frame->first; + + frame->handler = ngx_http_v2_frame_handler; + + h2c->frames++; + + } else { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "http2 flood detected"); + + h2c->connection->error = 1; + return NULL; + } + +#if (NGX_DEBUG) + if (length > NGX_HTTP_V2_FRAME_BUFFER_SIZE - NGX_HTTP_V2_FRAME_HEADER_SIZE) + { + ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0, + "requested control frame is too large: %uz", length); + return NULL; + } +#endif + + frame->length = length; + + buf->last = ngx_http_v2_write_len_and_type(buf->pos, length, type); + + *buf->last++ = flags; + + buf->last = ngx_http_v2_write_sid(buf->last, sid); + + return frame; +} + + +static ngx_int_t +ngx_http_v2_frame_handler(ngx_http_v2_connection_t *h2c, + ngx_http_v2_out_frame_t *frame) +{ + ngx_buf_t *buf; + + buf = frame->first->buf; + + if (buf->pos != buf->last) { + return NGX_AGAIN; + } + + frame->next = h2c->free_frames; + h2c->free_frames = frame; + + h2c->total_bytes += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length; + + return NGX_OK; +} + + +static ngx_http_v2_stream_t * +ngx_http_v2_create_stream(ngx_http_v2_connection_t *h2c) +{ + ngx_log_t *log; + ngx_event_t *rev, *wev; + ngx_connection_t *fc; + ngx_http_log_ctx_t *ctx; + ngx_http_request_t *r; + ngx_http_v2_stream_t *stream; + ngx_http_v2_srv_conf_t *h2scf; + ngx_http_core_srv_conf_t *cscf; + + fc = h2c->free_fake_connections; + + if (fc) { + h2c->free_fake_connections = fc->data; + + rev = fc->read; + wev = fc->write; + log = fc->log; + ctx = log->data; + + } else { + fc = ngx_palloc(h2c->pool, sizeof(ngx_connection_t)); + if (fc == NULL) { + return NULL; + } + + rev = ngx_palloc(h2c->pool, sizeof(ngx_event_t)); + if (rev == NULL) { + return NULL; + } + + wev = ngx_palloc(h2c->pool, sizeof(ngx_event_t)); + if (wev == NULL) { + return NULL; + } + + log = ngx_palloc(h2c->pool, sizeof(ngx_log_t)); + if (log == NULL) { + return NULL; + } + + ctx = ngx_palloc(h2c->pool, sizeof(ngx_http_log_ctx_t)); + if (ctx == NULL) { + return NULL; + } + + ctx->connection = fc; + ctx->request = NULL; + ctx->current_request = NULL; + } + + ngx_memcpy(log, h2c->connection->log, sizeof(ngx_log_t)); + + log->data = ctx; + log->action = "reading client request headers"; + + ngx_memzero(rev, sizeof(ngx_event_t)); + + rev->data = fc; + rev->ready = 1; + rev->handler = ngx_http_v2_close_stream_handler; + rev->log = log; + + ngx_memcpy(wev, rev, sizeof(ngx_event_t)); + + wev->write = 1; + + ngx_memcpy(fc, h2c->connection, sizeof(ngx_connection_t)); + + fc->data = h2c->http_connection; + fc->read = rev; + fc->write = wev; + fc->sent = 0; + fc->log = log; + fc->buffered = 0; + fc->sndlowat = 1; + fc->tcp_nodelay = NGX_TCP_NODELAY_DISABLED; + + r = ngx_http_create_request(fc); + if (r == NULL) { + return NULL; + } + + ngx_str_set(&r->http_protocol, "HTTP/2.0"); + + r->http_version = NGX_HTTP_VERSION_20; + r->valid_location = 1; + + fc->data = r; + h2c->connection->requests++; + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + r->header_in = ngx_create_temp_buf(r->pool, + cscf->client_header_buffer_size); + if (r->header_in == NULL) { + ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NULL; + } + + if (ngx_list_init(&r->headers_in.headers, r->pool, 20, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NULL; + } + + r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; + + stream = ngx_pcalloc(r->pool, sizeof(ngx_http_v2_stream_t)); + if (stream == NULL) { + ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NULL; + } + + r->stream = stream; + + stream->request = r; + stream->connection = h2c; + + h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module); + + stream->send_window = h2c->init_window; + stream->recv_window = h2scf->preread_size; + + h2c->processing++; + + h2c->priority_limit += h2scf->concurrent_streams; + + if (h2c->connection->read->timer_set) { + ngx_del_timer(h2c->connection->read); + } + + return stream; +} + + +static ngx_http_v2_node_t * +ngx_http_v2_get_node_by_id(ngx_http_v2_connection_t *h2c, ngx_uint_t sid, + ngx_uint_t alloc) +{ + ngx_uint_t index; + ngx_http_v2_node_t *node; + ngx_http_v2_srv_conf_t *h2scf; + + h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, + ngx_http_v2_module); + + index = ngx_http_v2_index(h2scf, sid); + + for (node = h2c->streams_index[index]; node; node = node->index) { + + if (node->id == sid) { + return node; + } + } + + if (!alloc) { + return NULL; + } + + if (h2c->closed_nodes < 32) { + node = ngx_pcalloc(h2c->connection->pool, sizeof(ngx_http_v2_node_t)); + if (node == NULL) { + return NULL; + } + + } else { + node = ngx_http_v2_get_closed_node(h2c); + } + + node->id = sid; + + ngx_queue_init(&node->children); + + node->index = h2c->streams_index[index]; + h2c->streams_index[index] = node; + + return node; +} + + +static ngx_http_v2_node_t * +ngx_http_v2_get_closed_node(ngx_http_v2_connection_t *h2c) +{ + ngx_uint_t weight; + ngx_queue_t *q, *children; + ngx_http_v2_node_t *node, **next, *n, *parent, *child; + ngx_http_v2_srv_conf_t *h2scf; + + h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, + ngx_http_v2_module); + + h2c->closed_nodes--; + + q = ngx_queue_head(&h2c->closed); + + ngx_queue_remove(q); + + node = ngx_queue_data(q, ngx_http_v2_node_t, reuse); + + next = &h2c->streams_index[ngx_http_v2_index(h2scf, node->id)]; + + for ( ;; ) { + n = *next; + + if (n == node) { + *next = n->index; + break; + } + + next = &n->index; + } + + ngx_queue_remove(&node->queue); + + weight = 0; + + for (q = ngx_queue_head(&node->children); + q != ngx_queue_sentinel(&node->children); + q = ngx_queue_next(q)) + { + child = ngx_queue_data(q, ngx_http_v2_node_t, queue); + weight += child->weight; + } + + parent = node->parent; + + for (q = ngx_queue_head(&node->children); + q != ngx_queue_sentinel(&node->children); + q = ngx_queue_next(q)) + { + child = ngx_queue_data(q, ngx_http_v2_node_t, queue); + child->parent = parent; + child->weight = node->weight * child->weight / weight; + + if (child->weight == 0) { + child->weight = 1; + } + } + + if (parent == NGX_HTTP_V2_ROOT) { + node->rank = 0; + node->rel_weight = 1.0; + + children = &h2c->dependencies; + + } else { + node->rank = parent->rank; + node->rel_weight = parent->rel_weight; + + children = &parent->children; + } + + ngx_http_v2_node_children_update(node); + ngx_queue_add(children, &node->children); + + ngx_memzero(node, sizeof(ngx_http_v2_node_t)); + + return node; +} + + +static ngx_int_t +ngx_http_v2_validate_header(ngx_http_request_t *r, ngx_http_v2_header_t *header) +{ + u_char ch; + ngx_uint_t i; + ngx_http_core_srv_conf_t *cscf; + + r->invalid_header = 0; + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + for (i = (header->name.data[0] == ':'); i != header->name.len; i++) { + ch = header->name.data[i]; + + if ((ch >= 'a' && ch <= 'z') + || (ch == '-') + || (ch >= '0' && ch <= '9') + || (ch == '_' && cscf->underscores_in_headers)) + { + continue; + } + + if (ch <= 0x20 || ch == 0x7f || ch == ':' + || (ch >= 'A' && ch <= 'Z')) + { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid header name: \"%V\"", + &header->name); + + return NGX_ERROR; + } + + r->invalid_header = 1; + } + + for (i = 0; i != header->value.len; i++) { + ch = header->value.data[i]; + + if (ch == '\0' || ch == LF || ch == CR) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent header \"%V\" with " + "invalid value: \"%V\"", + &header->name, &header->value); + + return NGX_ERROR; + } + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v2_pseudo_header(ngx_http_request_t *r, ngx_http_v2_header_t *header) +{ + header->name.len--; + header->name.data++; + + switch (header->name.len) { + case 4: + if (ngx_memcmp(header->name.data, "path", sizeof("path") - 1) + == 0) + { + return ngx_http_v2_parse_path(r, &header->value); + } + + break; + + case 6: + if (ngx_memcmp(header->name.data, "method", sizeof("method") - 1) + == 0) + { + return ngx_http_v2_parse_method(r, &header->value); + } + + if (ngx_memcmp(header->name.data, "scheme", sizeof("scheme") - 1) + == 0) + { + return ngx_http_v2_parse_scheme(r, &header->value); + } + + break; + + case 9: + if (ngx_memcmp(header->name.data, "authority", sizeof("authority") - 1) + == 0) + { + return ngx_http_v2_parse_authority(r, &header->value); + } + + break; + } + + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent unknown pseudo-header \":%V\"", + &header->name); + + return NGX_DECLINED; +} + + +static ngx_int_t +ngx_http_v2_parse_path(ngx_http_request_t *r, ngx_str_t *value) +{ + if (r->unparsed_uri.len) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent duplicate :path header"); + + return NGX_DECLINED; + } + + if (value->len == 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent empty :path header"); + + return NGX_DECLINED; + } + + r->uri_start = value->data; + r->uri_end = value->data + value->len; + + if (ngx_http_parse_uri(r) != NGX_OK) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid :path header: \"%V\"", value); + + return NGX_DECLINED; + } + + if (ngx_http_process_request_uri(r) != NGX_OK) { + /* + * request has been finalized already + * in ngx_http_process_request_uri() + */ + return NGX_ABORT; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v2_parse_method(ngx_http_request_t *r, ngx_str_t *value) +{ + size_t k, len; + ngx_uint_t n; + const u_char *p, *m; + + /* + * This array takes less than 256 sequential bytes, + * and if typical CPU cache line size is 64 bytes, + * it is prefetched for 4 load operations. + */ + static const struct { + u_char len; + const u_char method[11]; + uint32_t value; + } tests[] = { + { 3, "GET", NGX_HTTP_GET }, + { 4, "POST", NGX_HTTP_POST }, + { 4, "HEAD", NGX_HTTP_HEAD }, + { 7, "OPTIONS", NGX_HTTP_OPTIONS }, + { 8, "PROPFIND", NGX_HTTP_PROPFIND }, + { 3, "PUT", NGX_HTTP_PUT }, + { 5, "MKCOL", NGX_HTTP_MKCOL }, + { 6, "DELETE", NGX_HTTP_DELETE }, + { 4, "COPY", NGX_HTTP_COPY }, + { 4, "MOVE", NGX_HTTP_MOVE }, + { 9, "PROPPATCH", NGX_HTTP_PROPPATCH }, + { 4, "LOCK", NGX_HTTP_LOCK }, + { 6, "UNLOCK", NGX_HTTP_UNLOCK }, + { 5, "PATCH", NGX_HTTP_PATCH }, + { 5, "TRACE", NGX_HTTP_TRACE }, + { 7, "CONNECT", NGX_HTTP_CONNECT } + }, *test; + + if (r->method_name.len) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent duplicate :method header"); + + return NGX_DECLINED; + } + + if (value->len == 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent empty :method header"); + + return NGX_DECLINED; + } + + r->method_name.len = value->len; + r->method_name.data = value->data; + + len = r->method_name.len; + n = sizeof(tests) / sizeof(tests[0]); + test = tests; + + do { + if (len == test->len) { + p = r->method_name.data; + m = test->method; + k = len; + + do { + if (*p++ != *m++) { + goto next; + } + } while (--k); + + r->method = test->value; + return NGX_OK; + } + + next: + test++; + + } while (--n); + + p = r->method_name.data; + + do { + if ((*p < 'A' || *p > 'Z') && *p != '_' && *p != '-') { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid method: \"%V\"", + &r->method_name); + + return NGX_DECLINED; + } + + p++; + + } while (--len); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v2_parse_scheme(ngx_http_request_t *r, ngx_str_t *value) +{ + u_char c, ch; + ngx_uint_t i; + + if (r->schema.len) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent duplicate :scheme header"); + + return NGX_DECLINED; + } + + if (value->len == 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent empty :scheme header"); + + return NGX_DECLINED; + } + + for (i = 0; i < value->len; i++) { + ch = value->data[i]; + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'z') { + continue; + } + + if (((ch >= '0' && ch <= '9') || ch == '+' || ch == '-' || ch == '.') + && i > 0) + { + continue; + } + + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid :scheme header: \"%V\"", value); + + return NGX_DECLINED; + } + + r->schema = *value; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v2_parse_authority(ngx_http_request_t *r, ngx_str_t *value) +{ + ngx_int_t rc; + in_port_t port; + + if (r->host_start) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent duplicate \":authority\" header"); + return NGX_DECLINED; + } + + r->host_start = value->data; + r->host_end = value->data + value->len; + + rc = ngx_http_validate_host(value, &port, r->pool, 0); + + if (rc == NGX_DECLINED) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid \":authority\" header"); + return NGX_DECLINED; + } + + if (rc == NGX_ERROR) { + ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ABORT; + } + + if (ngx_http_set_virtual_server(r, value) == NGX_ERROR) { + /* + * request has been finalized already + * in ngx_http_set_virtual_server() + */ + return NGX_ABORT; + } + + r->headers_in.server = *value; + r->port = port; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v2_construct_request_line(ngx_http_request_t *r) +{ + u_char *p; + + static const u_char ending[] = " HTTP/2.0"; + + if (r->method_name.len == 0 + || r->schema.len == 0 + || r->unparsed_uri.len == 0) + { + if (r->method_name.len == 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent no :method header"); + + } else if (r->schema.len == 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent no :scheme header"); + + } else { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent no :path header"); + } + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; + } + + r->request_line.len = r->method_name.len + 1 + + r->unparsed_uri.len + + sizeof(ending) - 1; + + p = ngx_pnalloc(r->pool, r->request_line.len + 1); + if (p == NULL) { + ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + r->request_line.data = p; + + p = ngx_cpymem(p, r->method_name.data, r->method_name.len); + + *p++ = ' '; + + p = ngx_cpymem(p, r->unparsed_uri.data, r->unparsed_uri.len); + + ngx_memcpy(p, ending, sizeof(ending)); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http2 request line: \"%V\"", &r->request_line); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v2_cookie(ngx_http_request_t *r, ngx_http_v2_header_t *header) +{ + ngx_str_t *val; + ngx_array_t *cookies; + + cookies = r->stream->cookies; + + if (cookies == NULL) { + cookies = ngx_array_create(r->pool, 2, sizeof(ngx_str_t)); + if (cookies == NULL) { + return NGX_ERROR; + } + + r->stream->cookies = cookies; + } + + val = ngx_array_push(cookies); + if (val == NULL) { + return NGX_ERROR; + } + + val->len = header->value.len; + val->data = header->value.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v2_construct_cookie_header(ngx_http_request_t *r) +{ + u_char *buf, *p, *end; + size_t len; + ngx_str_t *vals; + ngx_uint_t i; + ngx_array_t *cookies; + ngx_table_elt_t *h; + ngx_http_header_t *hh; + ngx_http_core_main_conf_t *cmcf; + + static ngx_str_t cookie = ngx_string("cookie"); + + cookies = r->stream->cookies; + + if (cookies == NULL) { + return NGX_OK; + } + + vals = cookies->elts; + + i = 0; + len = 0; + + do { + len += vals[i].len + 2; + } while (++i != cookies->nelts); + + len -= 2; + + buf = ngx_pnalloc(r->pool, len + 1); + if (buf == NULL) { + ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + p = buf; + end = buf + len; + + for (i = 0; /* void */ ; i++) { + + p = ngx_cpymem(p, vals[i].data, vals[i].len); + + if (p == end) { + *p = '\0'; + break; + } + + *p++ = ';'; *p++ = ' '; + } + + h = ngx_list_push(&r->headers_in.headers); + if (h == NULL) { + ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash( + ngx_hash('c', 'o'), 'o'), 'k'), 'i'), 'e'); + + h->key.len = cookie.len; + h->key.data = cookie.data; + + h->value.len = len; + h->value.data = buf; + + h->lowcase_key = cookie.data; + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, + h->lowcase_key, h->key.len); + + if (hh == NULL) { + ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + if (hh->handler(r, h, hh->offset) != NGX_OK) { + /* + * request has been finalized already + * in ngx_http_process_header_line() + */ + return NGX_ERROR; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v2_construct_host_header(ngx_http_request_t *r) +{ + ngx_table_elt_t *h; + ngx_http_header_t *hh; + ngx_http_core_main_conf_t *cmcf; + + static ngx_str_t host = ngx_string("host"); + + h = ngx_list_push(&r->headers_in.headers); + if (h == NULL) { + ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + h->hash = ngx_hash(ngx_hash(ngx_hash('h', 'o'), 's'), 't'); + + h->key.len = host.len; + h->key.data = host.data; + + h->value.len = r->host_end - r->host_start; + h->value.data = r->host_start; + + h->lowcase_key = host.data; + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, + h->lowcase_key, h->key.len); + + if (hh == NULL) { + ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + if (hh->handler(r, h, hh->offset) != NGX_OK) { + /* + * request has been finalized already + * in ngx_http_process_host() + */ + return NGX_ERROR; + } + + return NGX_OK; +} + + +static void +ngx_http_v2_run_request(ngx_http_request_t *r) +{ + ngx_str_t host; + ngx_connection_t *fc; + ngx_http_v2_srv_conf_t *h2scf; + ngx_http_v2_connection_t *h2c; + + fc = r->connection; + + h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module); + + if (!h2scf->enable && !r->http_connection->addr_conf->http2) { + ngx_log_error(NGX_LOG_INFO, fc->log, 0, + "client attempted to request the server name " + "for which the negotiated protocol is disabled"); + + ngx_http_finalize_request(r, NGX_HTTP_MISDIRECTED_REQUEST); + goto failed; + } + + if (ngx_http_v2_construct_request_line(r) != NGX_OK) { + goto failed; + } + + if (ngx_http_v2_construct_cookie_header(r) != NGX_OK) { + goto failed; + } + + r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE; + + if (r->headers_in.server.len == 0) { + ngx_log_error(NGX_LOG_INFO, fc->log, 0, + "client sent neither \":authority\" nor \"Host\" header"); + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + goto failed; + } + + if (r->host_end) { + + host.len = r->host_end - r->host_start; + host.data = r->host_start; + + if (r->headers_in.host) { + if (r->headers_in.host->value.len != host.len + || ngx_memcmp(r->headers_in.host->value.data, host.data, + host.len) + != 0) + { + ngx_log_error(NGX_LOG_INFO, fc->log, 0, + "client sent \":authority\" and \"Host\" headers " + "with different values"); + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + goto failed; + } + + } else { + /* compatibility for $http_host */ + + if (ngx_http_v2_construct_host_header(r) != NGX_OK) { + goto failed; + } + } + } + + if (r->headers_in.content_length) { + r->headers_in.content_length_n = + ngx_atoof(r->headers_in.content_length->value.data, + r->headers_in.content_length->value.len); + + if (r->headers_in.content_length_n == NGX_ERROR) { + ngx_log_error(NGX_LOG_INFO, fc->log, 0, + "client sent invalid \"Content-Length\" header"); + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + goto failed; + } + + if (r->headers_in.content_length_n > 0 && r->stream->in_closed) { + ngx_log_error(NGX_LOG_INFO, fc->log, 0, + "client prematurely closed stream"); + + r->stream->skip_data = 1; + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + goto failed; + } + + } else if (!r->stream->in_closed) { + r->headers_in.chunked = 1; + } + + if (r->method == NGX_HTTP_CONNECT) { + ngx_log_error(NGX_LOG_INFO, fc->log, 0, "client sent CONNECT method"); + ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED); + goto failed; + } + + if (r->method == NGX_HTTP_TRACE) { + ngx_log_error(NGX_LOG_INFO, fc->log, 0, "client sent TRACE method"); + ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED); + goto failed; + } + + h2c = r->stream->connection; + + h2c->payload_bytes += r->request_length; + + ngx_http_process_request(r); + +failed: + + ngx_http_run_posted_requests(fc); +} + + +ngx_int_t +ngx_http_v2_read_request_body(ngx_http_request_t *r) +{ + off_t len; + size_t size; + ngx_buf_t *buf; + ngx_int_t rc; + ngx_http_v2_stream_t *stream; + ngx_http_v2_srv_conf_t *h2scf; + ngx_http_request_body_t *rb; + ngx_http_core_loc_conf_t *clcf; + ngx_http_v2_connection_t *h2c; + + stream = r->stream; + rb = r->request_body; + + if (stream->skip_data) { + r->request_body_no_buffering = 0; + rb->post_handler(r); + return NGX_OK; + } + + rb->rest = 1; + + /* set rb->filter_need_buffering */ + + rc = ngx_http_top_request_body_filter(r, NULL); + + if (rc != NGX_OK) { + stream->skip_data = 1; + return rc; + } + + h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module); + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + len = r->headers_in.content_length_n; + + if (len < 0 || len > (off_t) clcf->client_body_buffer_size) { + len = clcf->client_body_buffer_size; + + } else { + len++; + } + + if (r->request_body_no_buffering || rb->filter_need_buffering) { + + /* + * We need a room to store data up to the stream's initial window size, + * at least until this window will be exhausted. + */ + + if (len < (off_t) h2scf->preread_size) { + len = h2scf->preread_size; + } + + if (len > NGX_HTTP_V2_MAX_WINDOW) { + len = NGX_HTTP_V2_MAX_WINDOW; + } + } + + rb->buf = ngx_create_temp_buf(r->pool, (size_t) len); + + if (rb->buf == NULL) { + stream->skip_data = 1; + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + buf = stream->preread; + + if (stream->in_closed) { + if (!rb->filter_need_buffering) { + r->request_body_no_buffering = 0; + } + + if (buf) { + rc = ngx_http_v2_process_request_body(r, buf->pos, + buf->last - buf->pos, 1, 0); + ngx_pfree(r->pool, buf->start); + + } else { + rc = ngx_http_v2_process_request_body(r, NULL, 0, 1, 0); + } + + if (rc != NGX_AGAIN) { + return rc; + } + + r->read_event_handler = ngx_http_v2_read_client_request_body_handler; + r->write_event_handler = ngx_http_request_empty_handler; + + return NGX_AGAIN; + } + + if (buf) { + rc = ngx_http_v2_process_request_body(r, buf->pos, + buf->last - buf->pos, 0, 0); + + ngx_pfree(r->pool, buf->start); + + if (rc != NGX_OK && rc != NGX_AGAIN) { + stream->skip_data = 1; + return rc; + } + } + + if (r->request_body_no_buffering || rb->filter_need_buffering) { + size = (size_t) len - h2scf->preread_size; + + } else { + stream->no_flow_control = 1; + size = NGX_HTTP_V2_MAX_WINDOW - stream->recv_window; + } + + if (size) { + if (ngx_http_v2_send_window_update(stream->connection, + stream->node->id, size) + == NGX_ERROR) + { + stream->skip_data = 1; + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + h2c = stream->connection; + + if (!h2c->blocked) { + if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) { + stream->skip_data = 1; + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + } + + stream->recv_window += size; + } + + if (!buf) { + ngx_add_timer(r->connection->read, clcf->client_body_timeout); + } + + r->read_event_handler = ngx_http_v2_read_client_request_body_handler; + r->write_event_handler = ngx_http_request_empty_handler; + + return NGX_AGAIN; +} + + +static ngx_int_t +ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos, + size_t size, ngx_uint_t last, ngx_uint_t flush) +{ + size_t n; + ngx_int_t rc; + ngx_connection_t *fc; + ngx_http_request_body_t *rb; + ngx_http_core_loc_conf_t *clcf; + + fc = r->connection; + rb = r->request_body; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 process request body"); + + if (size == 0 && !last && !flush) { + return NGX_AGAIN; + } + + for ( ;; ) { + for ( ;; ) { + if (rb->buf->last == rb->buf->end && size) { + + if (r->request_body_no_buffering) { + + /* should never happen due to flow control */ + + ngx_log_error(NGX_LOG_ALERT, fc->log, 0, + "no space in http2 body buffer"); + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + /* update chains */ + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 body update chains"); + + rc = ngx_http_v2_filter_request_body(r); + + if (rc != NGX_OK) { + return rc; + } + + if (rb->busy != NULL) { + ngx_log_error(NGX_LOG_ALERT, fc->log, 0, + "busy buffers after request body flush"); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + rb->buf->pos = rb->buf->start; + rb->buf->last = rb->buf->start; + } + + /* copy body data to the buffer */ + + n = rb->buf->end - rb->buf->last; + + if (n > size) { + n = size; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 request body recv %uz", n); + + if (n > 0) { + rb->buf->last = ngx_cpymem(rb->buf->last, pos, n); + pos += n; + size -= n; + } + + if (size == 0 && last) { + rb->rest = 0; + } + + if (size == 0) { + break; + } + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 request body rest %O", rb->rest); + + if (flush) { + rc = ngx_http_v2_filter_request_body(r); + + if (rc != NGX_OK) { + return rc; + } + } + + if (rb->rest == 0 && rb->last_saved) { + break; + } + + if (size == 0) { + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + ngx_add_timer(fc->read, clcf->client_body_timeout); + + if (!flush) { + ngx_post_event(fc->read, &ngx_posted_events); + } + + return NGX_AGAIN; + } + } + + if (fc->read->timer_set) { + ngx_del_timer(fc->read); + } + + if (r->request_body_no_buffering) { + if (!flush) { + ngx_post_event(fc->read, &ngx_posted_events); + } + + return NGX_OK; + } + + if (r->headers_in.chunked) { + r->headers_in.content_length_n = rb->received; + } + + r->read_event_handler = ngx_http_block_reading; + rb->post_handler(r); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v2_filter_request_body(ngx_http_request_t *r) +{ + ngx_buf_t *b, *buf; + ngx_int_t rc; + ngx_chain_t *cl; + ngx_http_request_body_t *rb; + ngx_http_core_loc_conf_t *clcf; + + rb = r->request_body; + buf = rb->buf; + + if (buf->pos == buf->last && (rb->rest || rb->last_sent)) { + cl = NULL; + goto update; + } + + cl = ngx_chain_get_free_buf(r->pool, &rb->free); + if (cl == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + b = cl->buf; + + ngx_memzero(b, sizeof(ngx_buf_t)); + + if (buf->pos != buf->last) { + r->request_length += buf->last - buf->pos; + rb->received += buf->last - buf->pos; + + if (r->headers_in.content_length_n != -1) { + if (rb->received > r->headers_in.content_length_n) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client intended to send body data " + "larger than declared"); + + return NGX_HTTP_BAD_REQUEST; + } + + } else { + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->client_max_body_size + && rb->received > clcf->client_max_body_size) + { + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "client intended to send too large chunked body: " + "%O bytes", rb->received); + + return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; + } + } + + b->temporary = 1; + b->pos = buf->pos; + b->last = buf->last; + b->start = b->pos; + b->end = b->last; + + buf->pos = buf->last; + } + + if (!rb->rest) { + if (r->headers_in.content_length_n != -1 + && r->headers_in.content_length_n != rb->received) + { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client prematurely closed stream: " + "only %O out of %O bytes of request body received", + rb->received, r->headers_in.content_length_n); + + return NGX_HTTP_BAD_REQUEST; + } + + b->last_buf = 1; + rb->last_sent = 1; + } + + b->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_request_body; + b->flush = r->request_body_no_buffering; + +update: + + rc = ngx_http_top_request_body_filter(r, cl); + + ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &cl, + (ngx_buf_tag_t) &ngx_http_v2_filter_request_body); + + return rc; +} + + +static void +ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r) +{ + size_t window; + ngx_buf_t *buf; + ngx_int_t rc; + ngx_connection_t *fc; + ngx_http_v2_stream_t *stream; + ngx_http_v2_connection_t *h2c; + + fc = r->connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 read client request body handler"); + + if (fc->read->timedout) { + ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, "client timed out"); + + fc->timedout = 1; + r->stream->skip_data = 1; + + ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT); + return; + } + + if (fc->error) { + ngx_log_error(NGX_LOG_INFO, fc->log, 0, + "client prematurely closed stream"); + + r->stream->skip_data = 1; + + ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST); + return; + } + + rc = ngx_http_v2_process_request_body(r, NULL, 0, r->stream->in_closed, 1); + + if (rc != NGX_OK && rc != NGX_AGAIN) { + r->stream->skip_data = 1; + ngx_http_finalize_request(r, rc); + return; + } + + if (rc == NGX_OK) { + return; + } + + if (r->stream->no_flow_control) { + return; + } + + if (r->request_body->rest == 0) { + return; + } + + if (r->request_body->busy != NULL) { + return; + } + + stream = r->stream; + h2c = stream->connection; + + buf = r->request_body->buf; + + buf->pos = buf->start; + buf->last = buf->start; + + window = buf->end - buf->start; + + if (h2c->state.stream == stream) { + window -= h2c->state.length; + } + + if (window <= stream->recv_window) { + if (window < stream->recv_window) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "http2 negative window update"); + + stream->skip_data = 1; + + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + return; + } + + if (ngx_http_v2_send_window_update(h2c, stream->node->id, + window - stream->recv_window) + == NGX_ERROR) + { + stream->skip_data = 1; + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + stream->recv_window = window; + + if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) { + stream->skip_data = 1; + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } +} + + +ngx_int_t +ngx_http_v2_read_unbuffered_request_body(ngx_http_request_t *r) +{ + size_t window; + ngx_buf_t *buf; + ngx_int_t rc; + ngx_connection_t *fc; + ngx_http_v2_stream_t *stream; + ngx_http_v2_connection_t *h2c; + + stream = r->stream; + fc = r->connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 read unbuffered request body"); + + if (fc->read->timedout) { + if (stream->recv_window) { + stream->skip_data = 1; + fc->timedout = 1; + + return NGX_HTTP_REQUEST_TIME_OUT; + } + + fc->read->timedout = 0; + } + + if (fc->error) { + stream->skip_data = 1; + return NGX_HTTP_BAD_REQUEST; + } + + rc = ngx_http_v2_process_request_body(r, NULL, 0, r->stream->in_closed, 1); + + if (rc != NGX_OK && rc != NGX_AGAIN) { + stream->skip_data = 1; + return rc; + } + + if (rc == NGX_OK) { + return NGX_OK; + } + + if (r->request_body->rest == 0) { + return NGX_AGAIN; + } + + if (r->request_body->busy != NULL) { + return NGX_AGAIN; + } + + buf = r->request_body->buf; + + buf->pos = buf->start; + buf->last = buf->start; + + window = buf->end - buf->start; + h2c = stream->connection; + + if (h2c->state.stream == stream) { + window -= h2c->state.length; + } + + if (window <= stream->recv_window) { + if (window < stream->recv_window) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "http2 negative window update"); + stream->skip_data = 1; + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + return NGX_AGAIN; + } + + if (ngx_http_v2_send_window_update(h2c, stream->node->id, + window - stream->recv_window) + == NGX_ERROR) + { + stream->skip_data = 1; + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) { + stream->skip_data = 1; + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + stream->recv_window = window; + + return NGX_AGAIN; +} + + +static ngx_int_t +ngx_http_v2_terminate_stream(ngx_http_v2_connection_t *h2c, + ngx_http_v2_stream_t *stream, ngx_uint_t status) +{ + ngx_event_t *rev; + ngx_connection_t *fc; + + if (stream->rst_sent) { + return NGX_OK; + } + + if (ngx_http_v2_send_rst_stream(h2c, stream->node->id, status) + == NGX_ERROR) + { + return NGX_ERROR; + } + + stream->rst_sent = 1; + stream->skip_data = 1; + + fc = stream->request->connection; + fc->error = 1; + + rev = fc->read; + rev->handler(rev); + + return NGX_OK; +} + + +void +ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc) +{ + ngx_pool_t *pool; + ngx_event_t *ev; + ngx_connection_t *fc; + ngx_http_v2_node_t *node; + ngx_http_v2_connection_t *h2c; + + h2c = stream->connection; + node = stream->node; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 close stream %ui, queued %ui, processing %ui", + node->id, stream->queued, h2c->processing); + + fc = stream->request->connection; + + if (stream->queued) { + fc->error = 1; + fc->write->handler = ngx_http_v2_retry_close_stream_handler; + fc->read->handler = ngx_http_v2_retry_close_stream_handler; + return; + } + + if (!stream->rst_sent && !h2c->connection->error) { + + if (!stream->out_closed) { + if (ngx_http_v2_send_rst_stream(h2c, node->id, + fc->timedout ? NGX_HTTP_V2_PROTOCOL_ERROR + : NGX_HTTP_V2_INTERNAL_ERROR) + != NGX_OK) + { + h2c->connection->error = 1; + } + + } else if (!stream->in_closed) { + if (ngx_http_v2_send_rst_stream(h2c, node->id, NGX_HTTP_V2_NO_ERROR) + != NGX_OK) + { + h2c->connection->error = 1; + } + } + } + + if (h2c->state.stream == stream) { + h2c->state.stream = NULL; + } + + node->stream = NULL; + + ngx_queue_insert_tail(&h2c->closed, &node->reuse); + h2c->closed_nodes++; + + /* + * This pool keeps decoded request headers which can be used by log phase + * handlers in ngx_http_free_request(). + * + * The pointer is stored into local variable because the stream object + * will be destroyed after a call to ngx_http_free_request(). + */ + pool = stream->pool; + + h2c->frames -= stream->frames; + + ngx_http_free_request(stream->request, rc); + + if (pool != h2c->state.pool) { + ngx_destroy_pool(pool); + + } else { + /* pool will be destroyed when the complete header is parsed */ + h2c->state.keep_pool = 0; + } + + ev = fc->read; + + if (ev->timer_set) { + ngx_del_timer(ev); + } + + if (ev->posted) { + ngx_delete_posted_event(ev); + } + + ev = fc->write; + + if (ev->timer_set) { + ngx_del_timer(ev); + } + + if (ev->posted) { + ngx_delete_posted_event(ev); + } + + fc->data = h2c->free_fake_connections; + h2c->free_fake_connections = fc; + + h2c->processing--; + + if (h2c->processing || h2c->blocked) { + return; + } + + ev = h2c->connection->read; + + ev->handler = ngx_http_v2_handle_connection_handler; + ngx_post_event(ev, &ngx_posted_events); +} + + +static void +ngx_http_v2_close_stream_handler(ngx_event_t *ev) +{ + ngx_connection_t *fc; + ngx_http_request_t *r; + + fc = ev->data; + r = fc->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 close stream handler"); + + if (ev->timedout) { + ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, "client timed out"); + + fc->timedout = 1; + + ngx_http_v2_close_stream(r->stream, NGX_HTTP_REQUEST_TIME_OUT); + return; + } + + ngx_http_v2_close_stream(r->stream, 0); +} + + +static void +ngx_http_v2_retry_close_stream_handler(ngx_event_t *ev) +{ + ngx_connection_t *fc; + ngx_http_request_t *r; + + fc = ev->data; + r = fc->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 retry close stream handler"); + + ngx_http_v2_close_stream(r->stream, 0); +} + + +static void +ngx_http_v2_handle_connection_handler(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_http_v2_connection_t *h2c; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "http2 handle connection handler"); + + c = rev->data; + h2c = c->data; + + if (c->error) { + ngx_http_v2_finalize_connection(h2c, 0); + return; + } + + rev->handler = ngx_http_v2_read_handler; + + if (rev->ready) { + ngx_http_v2_read_handler(rev); + return; + } + + if (h2c->last_out && ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) { + ngx_http_v2_finalize_connection(h2c, 0); + return; + } + + ngx_http_v2_handle_connection(c->data); +} + + +static void +ngx_http_v2_idle_handler(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_http_v2_srv_conf_t *h2scf; + ngx_http_v2_connection_t *h2c; + ngx_http_core_loc_conf_t *clcf; + + c = rev->data; + h2c = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http2 idle handler"); + + if (rev->timedout || c->close) { + ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_NO_ERROR); + return; + } + +#if (NGX_HAVE_KQUEUE) + + if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { + if (rev->pending_eof) { + c->log->handler = NULL; + ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno, + "kevent() reported that client %V closed " + "idle connection", &c->addr_text); +#if (NGX_HTTP_SSL) + if (c->ssl) { + c->ssl->no_send_shutdown = 1; + } +#endif + ngx_http_close_connection(c); + return; + } + } + +#endif + + clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx, + ngx_http_core_module); + + if (h2c->idle++ > 10 * clcf->keepalive_requests) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "http2 flood detected"); + ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_NO_ERROR); + return; + } + + c->destroyed = 0; + ngx_reusable_connection(c, 0); + + h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, + ngx_http_v2_module); + + h2c->pool = ngx_create_pool(h2scf->pool_size, h2c->connection->log); + if (h2c->pool == NULL) { + ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_INTERNAL_ERROR); + return; + } + + c->write->handler = ngx_http_v2_write_handler; + + rev->handler = ngx_http_v2_read_handler; + ngx_http_v2_read_handler(rev); +} + + +static void +ngx_http_v2_finalize_connection(ngx_http_v2_connection_t *h2c, + ngx_uint_t status) +{ + ngx_uint_t i, size; + ngx_event_t *ev; + ngx_connection_t *c, *fc; + ngx_http_request_t *r; + ngx_http_v2_node_t *node; + ngx_http_v2_stream_t *stream; + ngx_http_v2_srv_conf_t *h2scf; + + c = h2c->connection; + + h2c->blocked = 1; + + if (!c->error && !h2c->goaway) { + h2c->goaway = 1; + + if (ngx_http_v2_send_goaway(h2c, status) != NGX_ERROR) { + (void) ngx_http_v2_send_output_queue(h2c); + } + } + + if (!h2c->processing) { + goto done; + } + + c->read->handler = ngx_http_empty_handler; + c->write->handler = ngx_http_empty_handler; + + h2c->last_out = NULL; + + h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, + ngx_http_v2_module); + + size = ngx_http_v2_index_size(h2scf); + + for (i = 0; i < size; i++) { + + for (node = h2c->streams_index[i]; node; node = node->index) { + stream = node->stream; + + if (stream == NULL) { + continue; + } + + stream->waiting = 0; + + r = stream->request; + fc = r->connection; + + fc->error = 1; + + if (stream->queued) { + stream->queued = 0; + + ev = fc->write; + ev->active = 0; + ev->ready = 1; + + } else { + ev = fc->read; + } + + ev->eof = 1; + ev->handler(ev); + } + } + + h2c->blocked = 0; + + if (h2c->processing) { + c->error = 1; + return; + } + +done: + + if (c->error) { + ngx_http_close_connection(c); + return; + } + + ngx_http_v2_lingering_close(c); +} + + +static ngx_int_t +ngx_http_v2_adjust_windows(ngx_http_v2_connection_t *h2c, ssize_t delta) +{ + ngx_uint_t i, size; + ngx_event_t *wev; + ngx_http_v2_node_t *node; + ngx_http_v2_stream_t *stream; + ngx_http_v2_srv_conf_t *h2scf; + + h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, + ngx_http_v2_module); + + size = ngx_http_v2_index_size(h2scf); + + for (i = 0; i < size; i++) { + + for (node = h2c->streams_index[i]; node; node = node->index) { + stream = node->stream; + + if (stream == NULL) { + continue; + } + + if (delta > 0 + && stream->send_window + > (ssize_t) (NGX_HTTP_V2_MAX_WINDOW - delta)) + { + if (ngx_http_v2_terminate_stream(h2c, stream, + NGX_HTTP_V2_FLOW_CTRL_ERROR) + == NGX_ERROR) + { + return NGX_ERROR; + } + + continue; + } + + stream->send_window += delta; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2:%ui adjusted window: %z", + node->id, stream->send_window); + + if (stream->send_window > 0 && stream->exhausted) { + stream->exhausted = 0; + + wev = stream->request->connection->write; + + wev->active = 0; + wev->ready = 1; + + if (!wev->delayed) { + wev->handler(wev); + } + } + } + } + + return NGX_OK; +} + + +static void +ngx_http_v2_set_dependency(ngx_http_v2_connection_t *h2c, + ngx_http_v2_node_t *node, ngx_uint_t depend, ngx_uint_t exclusive) +{ + ngx_queue_t *children, *q; + ngx_http_v2_node_t *parent, *child, *next; + + parent = depend ? ngx_http_v2_get_node_by_id(h2c, depend, 0) : NULL; + + if (parent == NULL) { + parent = NGX_HTTP_V2_ROOT; + + if (depend != 0) { + exclusive = 0; + } + + node->rank = 1; + node->rel_weight = (1.0 / 256) * node->weight; + + children = &h2c->dependencies; + + } else { + if (node->parent != NULL) { + + for (next = parent->parent; + next != NGX_HTTP_V2_ROOT && next->rank >= node->rank; + next = next->parent) + { + if (next != node) { + continue; + } + + ngx_queue_remove(&parent->queue); + ngx_queue_insert_after(&node->queue, &parent->queue); + + parent->parent = node->parent; + + if (node->parent == NGX_HTTP_V2_ROOT) { + parent->rank = 1; + parent->rel_weight = (1.0 / 256) * parent->weight; + + } else { + parent->rank = node->parent->rank + 1; + parent->rel_weight = (node->parent->rel_weight / 256) + * parent->weight; + } + + if (!exclusive) { + ngx_http_v2_node_children_update(parent); + } + + break; + } + } + + node->rank = parent->rank + 1; + node->rel_weight = (parent->rel_weight / 256) * node->weight; + + if (parent->stream == NULL) { + ngx_queue_remove(&parent->reuse); + ngx_queue_insert_tail(&h2c->closed, &parent->reuse); + } + + children = &parent->children; + } + + if (exclusive) { + for (q = ngx_queue_head(children); + q != ngx_queue_sentinel(children); + q = ngx_queue_next(q)) + { + child = ngx_queue_data(q, ngx_http_v2_node_t, queue); + child->parent = node; + } + + ngx_queue_add(&node->children, children); + ngx_queue_init(children); + } + + if (node->parent != NULL) { + ngx_queue_remove(&node->queue); + } + + ngx_queue_insert_tail(children, &node->queue); + + node->parent = parent; + + ngx_http_v2_node_children_update(node); +} + + +static void +ngx_http_v2_node_children_update(ngx_http_v2_node_t *node) +{ + ngx_queue_t *q; + ngx_http_v2_node_t *child; + + for (q = ngx_queue_head(&node->children); + q != ngx_queue_sentinel(&node->children); + q = ngx_queue_next(q)) + { + child = ngx_queue_data(q, ngx_http_v2_node_t, queue); + + child->rank = node->rank + 1; + child->rel_weight = (node->rel_weight / 256) * child->weight; + + ngx_http_v2_node_children_update(child); + } +} + + +static void +ngx_http_v2_pool_cleanup(void *data) +{ + ngx_http_v2_connection_t *h2c = data; + + if (h2c->state.pool) { + ngx_destroy_pool(h2c->state.pool); + } + + if (h2c->pool) { + ngx_destroy_pool(h2c->pool); + } +} diff --git a/src/deps/src/nginx-1.30.1/src/http/v2/ngx_http_v2.h b/src/deps/src/nginx-1.30.1/src/http/v2/ngx_http_v2.h new file mode 100644 index 000000000..9605c8a23 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/v2/ngx_http_v2.h @@ -0,0 +1,423 @@ +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Valentin V. Bartenev + */ + + +#ifndef _NGX_HTTP_V2_H_INCLUDED_ +#define _NGX_HTTP_V2_H_INCLUDED_ + + +#include +#include +#include + + +#define NGX_HTTP_V2_ALPN_PROTO "\x02h2" + +#define NGX_HTTP_V2_STATE_BUFFER_SIZE 16 + +#define NGX_HTTP_V2_DEFAULT_FRAME_SIZE (1 << 14) +#define NGX_HTTP_V2_MAX_FRAME_SIZE ((1 << 24) - 1) + +#define NGX_HTTP_V2_INT_OCTETS 4 +#define NGX_HTTP_V2_MAX_FIELD \ + (127 + (1 << (NGX_HTTP_V2_INT_OCTETS - 1) * 7) - 1) + +#define NGX_HTTP_V2_FRAME_HEADER_SIZE 9 + +/* frame types */ +#define NGX_HTTP_V2_DATA_FRAME 0x0 +#define NGX_HTTP_V2_HEADERS_FRAME 0x1 +#define NGX_HTTP_V2_PRIORITY_FRAME 0x2 +#define NGX_HTTP_V2_RST_STREAM_FRAME 0x3 +#define NGX_HTTP_V2_SETTINGS_FRAME 0x4 +#define NGX_HTTP_V2_PUSH_PROMISE_FRAME 0x5 +#define NGX_HTTP_V2_PING_FRAME 0x6 +#define NGX_HTTP_V2_GOAWAY_FRAME 0x7 +#define NGX_HTTP_V2_WINDOW_UPDATE_FRAME 0x8 +#define NGX_HTTP_V2_CONTINUATION_FRAME 0x9 + +/* frame flags */ +#define NGX_HTTP_V2_NO_FLAG 0x00 +#define NGX_HTTP_V2_ACK_FLAG 0x01 +#define NGX_HTTP_V2_END_STREAM_FLAG 0x01 +#define NGX_HTTP_V2_END_HEADERS_FLAG 0x04 +#define NGX_HTTP_V2_PADDED_FLAG 0x08 +#define NGX_HTTP_V2_PRIORITY_FLAG 0x20 + +#define NGX_HTTP_V2_MAX_WINDOW ((1U << 31) - 1) +#define NGX_HTTP_V2_DEFAULT_WINDOW 65535 + +#define NGX_HTTP_V2_DEFAULT_WEIGHT 16 + + +typedef struct ngx_http_v2_connection_s ngx_http_v2_connection_t; +typedef struct ngx_http_v2_node_s ngx_http_v2_node_t; +typedef struct ngx_http_v2_out_frame_s ngx_http_v2_out_frame_t; + + +typedef u_char *(*ngx_http_v2_handler_pt) (ngx_http_v2_connection_t *h2c, + u_char *pos, u_char *end); + + +typedef struct { + ngx_flag_t enable; + size_t pool_size; + ngx_uint_t concurrent_streams; + size_t preread_size; + ngx_uint_t streams_index_mask; +} ngx_http_v2_srv_conf_t; + + +typedef struct { + ngx_str_t name; + ngx_str_t value; +} ngx_http_v2_header_t; + + +typedef struct { + ngx_uint_t sid; + size_t length; + size_t padding; + unsigned flags:8; + + unsigned incomplete:1; + unsigned keep_pool:1; + + /* HPACK */ + unsigned parse_name:1; + unsigned parse_value:1; + unsigned index:1; + ngx_http_v2_header_t header; + size_t header_limit; + u_char field_state; + u_char *field_start; + u_char *field_end; + size_t field_rest; + ngx_pool_t *pool; + + ngx_http_v2_stream_t *stream; + + u_char buffer[NGX_HTTP_V2_STATE_BUFFER_SIZE]; + size_t buffer_used; + ngx_http_v2_handler_pt handler; +} ngx_http_v2_state_t; + + + +typedef struct { + ngx_http_v2_header_t **entries; + + ngx_uint_t added; + ngx_uint_t deleted; + ngx_uint_t reused; + ngx_uint_t allocated; + + size_t size; + size_t free; + u_char *storage; + u_char *pos; +} ngx_http_v2_hpack_t; + + +struct ngx_http_v2_connection_s { + ngx_connection_t *connection; + ngx_http_connection_t *http_connection; + + off_t total_bytes; + off_t payload_bytes; + + ngx_uint_t processing; + ngx_uint_t frames; + ngx_uint_t idle; + ngx_uint_t new_streams; + ngx_uint_t refused_streams; + ngx_uint_t priority_limit; + + size_t send_window; + size_t recv_window; + size_t init_window; + + size_t frame_size; + + ngx_queue_t waiting; + + ngx_http_v2_state_t state; + + ngx_http_v2_hpack_t hpack; + + ngx_pool_t *pool; + + ngx_http_v2_out_frame_t *free_frames; + ngx_connection_t *free_fake_connections; + + ngx_http_v2_node_t **streams_index; + + ngx_http_v2_out_frame_t *last_out; + + ngx_queue_t dependencies; + ngx_queue_t closed; + + ngx_uint_t closed_nodes; + ngx_uint_t last_sid; + + time_t lingering_time; + + unsigned settings_ack:1; + unsigned table_update:1; + unsigned blocked:1; + unsigned goaway:1; +}; + + +struct ngx_http_v2_node_s { + ngx_uint_t id; + ngx_http_v2_node_t *index; + ngx_http_v2_node_t *parent; + ngx_queue_t queue; + ngx_queue_t children; + ngx_queue_t reuse; + ngx_uint_t rank; + ngx_uint_t weight; + double rel_weight; + ngx_http_v2_stream_t *stream; +}; + + +struct ngx_http_v2_stream_s { + ngx_http_request_t *request; + ngx_http_v2_connection_t *connection; + ngx_http_v2_node_t *node; + + ngx_uint_t queued; + + /* + * A change to SETTINGS_INITIAL_WINDOW_SIZE could cause the + * send_window to become negative, hence it's signed. + */ + ssize_t send_window; + size_t recv_window; + + ngx_buf_t *preread; + + ngx_uint_t frames; + + ngx_http_v2_out_frame_t *free_frames; + ngx_chain_t *free_frame_headers; + ngx_chain_t *free_bufs; + + ngx_queue_t queue; + + ngx_array_t *cookies; + + ngx_pool_t *pool; + + unsigned initialized:1; + unsigned waiting:1; + unsigned blocked:1; + unsigned exhausted:1; + unsigned in_closed:1; + unsigned out_closed:1; + unsigned rst_sent:1; + unsigned no_flow_control:1; + unsigned skip_data:1; +}; + + +struct ngx_http_v2_out_frame_s { + ngx_http_v2_out_frame_t *next; + ngx_chain_t *first; + ngx_chain_t *last; + ngx_int_t (*handler)(ngx_http_v2_connection_t *h2c, + ngx_http_v2_out_frame_t *frame); + + ngx_http_v2_stream_t *stream; + size_t length; + + unsigned blocked:1; + unsigned fin:1; +}; + + +static ngx_inline void +ngx_http_v2_queue_frame(ngx_http_v2_connection_t *h2c, + ngx_http_v2_out_frame_t *frame) +{ + ngx_http_v2_out_frame_t **out; + + for (out = &h2c->last_out; *out; out = &(*out)->next) { + + if ((*out)->blocked || (*out)->stream == NULL) { + break; + } + + if ((*out)->stream->node->rank < frame->stream->node->rank + || ((*out)->stream->node->rank == frame->stream->node->rank + && (*out)->stream->node->rel_weight + >= frame->stream->node->rel_weight)) + { + break; + } + } + + frame->next = *out; + *out = frame; +} + + +static ngx_inline void +ngx_http_v2_queue_blocked_frame(ngx_http_v2_connection_t *h2c, + ngx_http_v2_out_frame_t *frame) +{ + ngx_http_v2_out_frame_t **out; + + for (out = &h2c->last_out; *out; out = &(*out)->next) { + + if ((*out)->blocked || (*out)->stream == NULL) { + break; + } + } + + frame->next = *out; + *out = frame; +} + + +static ngx_inline void +ngx_http_v2_queue_ordered_frame(ngx_http_v2_connection_t *h2c, + ngx_http_v2_out_frame_t *frame) +{ + frame->next = h2c->last_out; + h2c->last_out = frame; +} + + +void ngx_http_v2_init(ngx_event_t *rev); + +ngx_int_t ngx_http_v2_read_request_body(ngx_http_request_t *r); +ngx_int_t ngx_http_v2_read_unbuffered_request_body(ngx_http_request_t *r); + +void ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc); + +ngx_int_t ngx_http_v2_send_output_queue(ngx_http_v2_connection_t *h2c); + + +ngx_str_t *ngx_http_v2_get_static_name(ngx_uint_t index); +ngx_str_t *ngx_http_v2_get_static_value(ngx_uint_t index); + +ngx_int_t ngx_http_v2_get_indexed_header(ngx_http_v2_connection_t *h2c, + ngx_uint_t index, ngx_uint_t name_only); +ngx_int_t ngx_http_v2_add_header(ngx_http_v2_connection_t *h2c, + ngx_http_v2_header_t *header); +ngx_int_t ngx_http_v2_table_size(ngx_http_v2_connection_t *h2c, size_t size); + + +#define ngx_http_v2_prefix(bits) ((1 << (bits)) - 1) + + +#if (NGX_HAVE_NONALIGNED) + +#define ngx_http_v2_parse_uint16(p) ntohs(*(uint16_t *) (p)) +#define ngx_http_v2_parse_uint32(p) ntohl(*(uint32_t *) (p)) + +#else + +#define ngx_http_v2_parse_uint16(p) ((p)[0] << 8 | (p)[1]) +#define ngx_http_v2_parse_uint32(p) \ + ((uint32_t) (p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3]) + +#endif + +#define ngx_http_v2_parse_length(p) ((p) >> 8) +#define ngx_http_v2_parse_type(p) ((p) & 0xff) +#define ngx_http_v2_parse_sid(p) (ngx_http_v2_parse_uint32(p) & 0x7fffffff) +#define ngx_http_v2_parse_window(p) (ngx_http_v2_parse_uint32(p) & 0x7fffffff) + + +#define ngx_http_v2_write_uint16_aligned(p, s) \ + (*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t)) +#define ngx_http_v2_write_uint32_aligned(p, s) \ + (*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t)) + +#if (NGX_HAVE_NONALIGNED) + +#define ngx_http_v2_write_uint16 ngx_http_v2_write_uint16_aligned +#define ngx_http_v2_write_uint32 ngx_http_v2_write_uint32_aligned + +#else + +#define ngx_http_v2_write_uint16(p, s) \ + ((p)[0] = (u_char) ((s) >> 8), \ + (p)[1] = (u_char) (s), \ + (p) + sizeof(uint16_t)) + +#define ngx_http_v2_write_uint32(p, s) \ + ((p)[0] = (u_char) ((s) >> 24), \ + (p)[1] = (u_char) ((s) >> 16), \ + (p)[2] = (u_char) ((s) >> 8), \ + (p)[3] = (u_char) (s), \ + (p) + sizeof(uint32_t)) + +#endif + +#define ngx_http_v2_write_len_and_type(p, l, t) \ + ngx_http_v2_write_uint32_aligned(p, (l) << 8 | (t)) + +#define ngx_http_v2_write_sid ngx_http_v2_write_uint32 + + +#define ngx_http_v2_indexed(i) (128 + (i)) +#define ngx_http_v2_inc_indexed(i) (64 + (i)) + +#define ngx_http_v2_write_name(dst, src, len, tmp) \ + ngx_http_v2_string_encode(dst, src, len, tmp, 1) +#define ngx_http_v2_write_value(dst, src, len, tmp) \ + ngx_http_v2_string_encode(dst, src, len, tmp, 0) + +#define NGX_HTTP_V2_ENCODE_RAW 0 +#define NGX_HTTP_V2_ENCODE_HUFF 0x80 + +#define NGX_HTTP_V2_AUTHORITY_INDEX 1 + +#define NGX_HTTP_V2_METHOD_INDEX 2 +#define NGX_HTTP_V2_METHOD_GET_INDEX 2 +#define NGX_HTTP_V2_METHOD_POST_INDEX 3 + +#define NGX_HTTP_V2_PATH_INDEX 4 +#define NGX_HTTP_V2_PATH_ROOT_INDEX 4 + +#define NGX_HTTP_V2_SCHEME_HTTP_INDEX 6 +#define NGX_HTTP_V2_SCHEME_HTTPS_INDEX 7 + +#define NGX_HTTP_V2_STATUS_INDEX 8 +#define NGX_HTTP_V2_STATUS_200_INDEX 8 +#define NGX_HTTP_V2_STATUS_204_INDEX 9 +#define NGX_HTTP_V2_STATUS_206_INDEX 10 +#define NGX_HTTP_V2_STATUS_304_INDEX 11 +#define NGX_HTTP_V2_STATUS_400_INDEX 12 +#define NGX_HTTP_V2_STATUS_404_INDEX 13 +#define NGX_HTTP_V2_STATUS_500_INDEX 14 + +#define NGX_HTTP_V2_CONTENT_LENGTH_INDEX 28 +#define NGX_HTTP_V2_CONTENT_TYPE_INDEX 31 +#define NGX_HTTP_V2_DATE_INDEX 33 +#define NGX_HTTP_V2_LAST_MODIFIED_INDEX 44 +#define NGX_HTTP_V2_LOCATION_INDEX 46 +#define NGX_HTTP_V2_SERVER_INDEX 54 +#define NGX_HTTP_V2_VARY_INDEX 59 + +#define NGX_HTTP_V2_PREFACE_START "PRI * HTTP/2.0\r\n" +#define NGX_HTTP_V2_PREFACE_END "\r\nSM\r\n\r\n" +#define NGX_HTTP_V2_PREFACE NGX_HTTP_V2_PREFACE_START \ + NGX_HTTP_V2_PREFACE_END + + +u_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, + u_char *tmp, ngx_uint_t lower); + + +extern ngx_module_t ngx_http_v2_module; + + +#endif /* _NGX_HTTP_V2_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/http/v2/ngx_http_v2_encode.c b/src/deps/src/nginx-1.30.1/src/http/v2/ngx_http_v2_encode.c new file mode 100644 index 000000000..8798aa9ae --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/v2/ngx_http_v2_encode.c @@ -0,0 +1,62 @@ + +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Valentin V. Bartenev + */ + + +#include +#include +#include + + +static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, + ngx_uint_t value); + + +u_char * +ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp, + ngx_uint_t lower) +{ + size_t hlen; + + hlen = ngx_http_huff_encode(src, len, tmp, lower); + + if (hlen > 0) { + *dst = NGX_HTTP_V2_ENCODE_HUFF; + dst = ngx_http_v2_write_int(dst, ngx_http_v2_prefix(7), hlen); + return ngx_cpymem(dst, tmp, hlen); + } + + *dst = NGX_HTTP_V2_ENCODE_RAW; + dst = ngx_http_v2_write_int(dst, ngx_http_v2_prefix(7), len); + + if (lower) { + ngx_strlow(dst, src, len); + return dst + len; + } + + return ngx_cpymem(dst, src, len); +} + + +static u_char * +ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value) +{ + if (value < prefix) { + *pos++ |= value; + return pos; + } + + *pos++ |= prefix; + value -= prefix; + + while (value >= 128) { + *pos++ = value % 128 + 128; + value /= 128; + } + + *pos++ = (u_char) value; + + return pos; +} diff --git a/src/deps/src/nginx-1.30.1/src/http/v2/ngx_http_v2_filter_module.c b/src/deps/src/nginx-1.30.1/src/http/v2/ngx_http_v2_filter_module.c new file mode 100644 index 000000000..6b73b1e68 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/v2/ngx_http_v2_filter_module.c @@ -0,0 +1,1769 @@ + +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Valentin V. Bartenev + * Copyright (C) Ruslan Ermilov + */ + + +#include +#include +#include +#include +#include + + +/* + * This returns precise number of octets for values in range 0..253 + * and estimate number for the rest, but not smaller than required. + */ + +#define ngx_http_v2_integer_octets(v) (1 + (v) / 127) + +#define ngx_http_v2_literal_size(h) \ + (ngx_http_v2_integer_octets(sizeof(h) - 1) + sizeof(h) - 1) + + +#define NGX_HTTP_V2_NO_TRAILERS (ngx_http_v2_out_frame_t *) -1 + + +static ngx_int_t ngx_http_v2_header_filter(ngx_http_request_t *r); +static ngx_int_t ngx_http_v2_early_hints_filter(ngx_http_request_t *r); +static ngx_int_t ngx_http_v2_init_stream(ngx_http_request_t *r); + +static ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame( + ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin, + ngx_uint_t flush); +static ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame( + ngx_http_request_t *r); + +static ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc, + ngx_chain_t *in, off_t limit); + +static ngx_chain_t *ngx_http_v2_filter_get_shadow( + ngx_http_v2_stream_t *stream, ngx_buf_t *buf, off_t offset, off_t size); +static ngx_http_v2_out_frame_t *ngx_http_v2_filter_get_data_frame( + ngx_http_v2_stream_t *stream, size_t len, ngx_chain_t *first, + ngx_chain_t *last); + +static ngx_inline ngx_int_t ngx_http_v2_flow_control( + ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream); +static void ngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c, + ngx_http_v2_stream_t *stream); + +static ngx_inline ngx_int_t ngx_http_v2_filter_send( + ngx_connection_t *fc, ngx_http_v2_stream_t *stream); + +static ngx_int_t ngx_http_v2_headers_frame_handler( + ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame); +static ngx_int_t ngx_http_v2_data_frame_handler( + ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame); +static ngx_inline void ngx_http_v2_handle_frame( + ngx_http_v2_stream_t *stream, ngx_http_v2_out_frame_t *frame); +static ngx_inline void ngx_http_v2_handle_stream( + ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream); + +static void ngx_http_v2_filter_cleanup(void *data); + +static ngx_int_t ngx_http_v2_filter_init(ngx_conf_t *cf); + + +static ngx_http_module_t ngx_http_v2_filter_module_ctx = { + NULL, /* preconfiguration */ + ngx_http_v2_filter_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + NULL, /* create location configuration */ + NULL /* merge location configuration */ +}; + + +ngx_module_t ngx_http_v2_filter_module = { + NGX_MODULE_V1, + &ngx_http_v2_filter_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_http_output_header_filter_pt ngx_http_next_header_filter; +static ngx_http_output_header_filter_pt ngx_http_next_early_hints_filter; + + +static ngx_int_t +ngx_http_v2_header_filter(ngx_http_request_t *r) +{ + u_char status, *pos, *start, *p, *tmp; + size_t len, tmp_len; + ngx_str_t host, location; + ngx_uint_t i, port, fin; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_connection_t *fc; + ngx_http_v2_stream_t *stream; + ngx_http_v2_out_frame_t *frame; + ngx_http_v2_connection_t *h2c; + ngx_http_core_loc_conf_t *clcf; + ngx_http_core_srv_conf_t *cscf; + u_char addr[NGX_SOCKADDR_STRLEN]; + + static const u_char nginx[5] = { 0x84, 0xaa, 0x63, 0x55, 0xe7 }; +#if (NGX_HTTP_GZIP) + static const u_char accept_encoding[12] = { + 0x8b, 0x84, 0x84, 0x2d, 0x69, 0x5b, 0x05, 0x44, 0x3c, 0x86, 0xaa, 0x6f + }; +#endif + + static size_t nginx_ver_len = ngx_http_v2_literal_size(NGINX_VER); + static u_char nginx_ver[ngx_http_v2_literal_size(NGINX_VER)]; + + static size_t nginx_ver_build_len = + ngx_http_v2_literal_size(NGINX_VER_BUILD); + static u_char nginx_ver_build[ngx_http_v2_literal_size(NGINX_VER_BUILD)]; + + stream = r->stream; + + if (!stream) { + return ngx_http_next_header_filter(r); + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http2 header filter"); + + if (r->header_sent) { + return NGX_OK; + } + + r->header_sent = 1; + + if (r != r->main) { + return NGX_OK; + } + + fc = r->connection; + + if (fc->error) { + return NGX_ERROR; + } + + if (r->method == NGX_HTTP_HEAD) { + r->header_only = 1; + } + + switch (r->headers_out.status) { + + case NGX_HTTP_OK: + status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_200_INDEX); + break; + + case NGX_HTTP_NO_CONTENT: + r->header_only = 1; + + ngx_str_null(&r->headers_out.content_type); + + r->headers_out.content_length = NULL; + r->headers_out.content_length_n = -1; + + r->headers_out.last_modified_time = -1; + r->headers_out.last_modified = NULL; + + status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_204_INDEX); + break; + + case NGX_HTTP_PARTIAL_CONTENT: + status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_206_INDEX); + break; + + case NGX_HTTP_NOT_MODIFIED: + r->header_only = 1; + status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_304_INDEX); + break; + + default: + r->headers_out.last_modified_time = -1; + r->headers_out.last_modified = NULL; + + switch (r->headers_out.status) { + + case NGX_HTTP_BAD_REQUEST: + status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_400_INDEX); + break; + + case NGX_HTTP_NOT_FOUND: + status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_404_INDEX); + break; + + case NGX_HTTP_INTERNAL_SERVER_ERROR: + status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_500_INDEX); + break; + + default: + status = 0; + } + } + + h2c = stream->connection; + + len = h2c->table_update ? 1 : 0; + + len += status ? 1 : 1 + ngx_http_v2_literal_size("418"); + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (r->headers_out.server == NULL) { + + if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { + len += 1 + nginx_ver_len; + + } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { + len += 1 + nginx_ver_build_len; + + } else { + len += 1 + sizeof(nginx); + } + } + + if (r->headers_out.date == NULL) { + len += 1 + ngx_http_v2_literal_size("Wed, 31 Dec 1986 18:00:00 GMT"); + } + + if (r->headers_out.content_type.len) { + len += 1 + NGX_HTTP_V2_INT_OCTETS + r->headers_out.content_type.len; + + if (r->headers_out.content_type_len == r->headers_out.content_type.len + && r->headers_out.charset.len) + { + len += sizeof("; charset=") - 1 + r->headers_out.charset.len; + } + } + + if (r->headers_out.content_length == NULL + && r->headers_out.content_length_n >= 0) + { + len += 1 + ngx_http_v2_integer_octets(NGX_OFF_T_LEN) + NGX_OFF_T_LEN; + } + + if (r->headers_out.last_modified == NULL + && r->headers_out.last_modified_time != -1) + { + len += 1 + ngx_http_v2_literal_size("Wed, 31 Dec 1986 18:00:00 GMT"); + } + + if (r->headers_out.location && r->headers_out.location->value.len) { + + if (r->headers_out.location->value.data[0] == '/' + && clcf->absolute_redirect) + { + if (clcf->server_name_in_redirect) { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + host = cscf->server_name; + + } else if (r->headers_in.server.len) { + host = r->headers_in.server; + + } else { + host.len = NGX_SOCKADDR_STRLEN; + host.data = addr; + + if (ngx_connection_local_sockaddr(fc, &host, 0) != NGX_OK) { + return NGX_ERROR; + } + } + + port = ngx_inet_get_port(fc->local_sockaddr); + + location.len = sizeof("https://") - 1 + host.len + + r->headers_out.location->value.len; + + if (clcf->port_in_redirect) { + +#if (NGX_HTTP_SSL) + if (fc->ssl) + port = (port == 443) ? 0 : port; + else +#endif + port = (port == 80) ? 0 : port; + + } else { + port = 0; + } + + if (port) { + location.len += sizeof(":65535") - 1; + } + + location.data = ngx_pnalloc(r->pool, location.len); + if (location.data == NULL) { + return NGX_ERROR; + } + + p = ngx_cpymem(location.data, "http", sizeof("http") - 1); + +#if (NGX_HTTP_SSL) + if (fc->ssl) { + *p++ = 's'; + } +#endif + + *p++ = ':'; *p++ = '/'; *p++ = '/'; + p = ngx_cpymem(p, host.data, host.len); + + if (port) { + p = ngx_sprintf(p, ":%ui", port); + } + + p = ngx_cpymem(p, r->headers_out.location->value.data, + r->headers_out.location->value.len); + + /* update r->headers_out.location->value for possible logging */ + + r->headers_out.location->value.len = p - location.data; + r->headers_out.location->value.data = location.data; + ngx_str_set(&r->headers_out.location->key, "Location"); + } + + r->headers_out.location->hash = 0; + + len += 1 + NGX_HTTP_V2_INT_OCTETS + r->headers_out.location->value.len; + } + + tmp_len = len; + +#if (NGX_HTTP_GZIP) + if (r->gzip_vary) { + if (clcf->gzip_vary) { + len += 1 + sizeof(accept_encoding); + + } else { + r->gzip_vary = 0; + } + } +#endif + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { + ngx_log_error(NGX_LOG_CRIT, fc->log, 0, + "too long response header name: \"%V\"", + &header[i].key); + return NGX_ERROR; + } + + if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { + ngx_log_error(NGX_LOG_CRIT, fc->log, 0, + "too long response header value: \"%V: %V\"", + &header[i].key, &header[i].value); + return NGX_ERROR; + } + + len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len + + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; + + if (header[i].key.len > tmp_len) { + tmp_len = header[i].key.len; + } + + if (header[i].value.len > tmp_len) { + tmp_len = header[i].value.len; + } + } + + tmp = ngx_palloc(r->pool, tmp_len); + pos = ngx_pnalloc(r->pool, len); + + if (pos == NULL || tmp == NULL) { + return NGX_ERROR; + } + + start = pos; + + if (h2c->table_update) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 table size update: 0"); + *pos++ = (1 << 5) | 0; + h2c->table_update = 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 output header: \":status: %03ui\"", + r->headers_out.status); + + if (status) { + *pos++ = status; + + } else { + *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX); + *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3; + pos = ngx_sprintf(pos, "%03ui", r->headers_out.status); + } + + if (r->headers_out.server == NULL) { + + if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 output header: \"server: %s\"", + NGINX_VER); + + } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 output header: \"server: %s\"", + NGINX_VER_BUILD); + + } else { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 output header: \"server: nginx\""); + } + + *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX); + + if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { + if (nginx_ver[0] == '\0') { + p = ngx_http_v2_write_value(nginx_ver, (u_char *) NGINX_VER, + sizeof(NGINX_VER) - 1, tmp); + nginx_ver_len = p - nginx_ver; + } + + pos = ngx_cpymem(pos, nginx_ver, nginx_ver_len); + + } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { + if (nginx_ver_build[0] == '\0') { + p = ngx_http_v2_write_value(nginx_ver_build, + (u_char *) NGINX_VER_BUILD, + sizeof(NGINX_VER_BUILD) - 1, tmp); + nginx_ver_build_len = p - nginx_ver_build; + } + + pos = ngx_cpymem(pos, nginx_ver_build, nginx_ver_build_len); + + } else { + pos = ngx_cpymem(pos, nginx, sizeof(nginx)); + } + } + + if (r->headers_out.date == NULL) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 output header: \"date: %V\"", + &ngx_cached_http_time); + + *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX); + pos = ngx_http_v2_write_value(pos, ngx_cached_http_time.data, + ngx_cached_http_time.len, tmp); + } + + if (r->headers_out.content_type.len) { + *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX); + + if (r->headers_out.content_type_len == r->headers_out.content_type.len + && r->headers_out.charset.len) + { + len = r->headers_out.content_type.len + sizeof("; charset=") - 1 + + r->headers_out.charset.len; + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + p = ngx_cpymem(p, r->headers_out.content_type.data, + r->headers_out.content_type.len); + + p = ngx_cpymem(p, "; charset=", sizeof("; charset=") - 1); + + p = ngx_cpymem(p, r->headers_out.charset.data, + r->headers_out.charset.len); + + /* updated r->headers_out.content_type is also needed for logging */ + + r->headers_out.content_type.len = len; + r->headers_out.content_type.data = p - len; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 output header: \"content-type: %V\"", + &r->headers_out.content_type); + + pos = ngx_http_v2_write_value(pos, r->headers_out.content_type.data, + r->headers_out.content_type.len, tmp); + } + + if (r->headers_out.content_length == NULL + && r->headers_out.content_length_n >= 0) + { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 output header: \"content-length: %O\"", + r->headers_out.content_length_n); + + *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX); + + p = pos; + pos = ngx_sprintf(pos + 1, "%O", r->headers_out.content_length_n); + *p = NGX_HTTP_V2_ENCODE_RAW | (u_char) (pos - p - 1); + } + + if (r->headers_out.last_modified == NULL + && r->headers_out.last_modified_time != -1) + { + *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX); + + ngx_http_time(pos, r->headers_out.last_modified_time); + len = sizeof("Wed, 31 Dec 1986 18:00:00 GMT") - 1; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 output header: \"last-modified: %*s\"", + len, pos); + + /* + * Date will always be encoded using huffman in the temporary buffer, + * so it's safe here to use src and dst pointing to the same address. + */ + pos = ngx_http_v2_write_value(pos, pos, len, tmp); + } + + if (r->headers_out.location && r->headers_out.location->value.len) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 output header: \"location: %V\"", + &r->headers_out.location->value); + + *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX); + pos = ngx_http_v2_write_value(pos, r->headers_out.location->value.data, + r->headers_out.location->value.len, tmp); + } + +#if (NGX_HTTP_GZIP) + if (r->gzip_vary) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 output header: \"vary: Accept-Encoding\""); + + *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX); + pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding)); + } +#endif + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + +#if (NGX_DEBUG) + if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) { + ngx_strlow(tmp, header[i].key.data, header[i].key.len); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 output header: \"%*s: %V\"", + header[i].key.len, tmp, &header[i].value); + } +#endif + + *pos++ = 0; + + pos = ngx_http_v2_write_name(pos, header[i].key.data, + header[i].key.len, tmp); + + pos = ngx_http_v2_write_value(pos, header[i].value.data, + header[i].value.len, tmp); + } + + fin = r->header_only + || (r->headers_out.content_length_n == 0 && !r->expect_trailers); + + frame = ngx_http_v2_create_headers_frame(r, start, pos, fin, 0); + if (frame == NULL) { + return NGX_ERROR; + } + + ngx_http_v2_queue_blocked_frame(h2c, frame); + + stream->queued++; + + if (ngx_http_v2_init_stream(r) != NGX_OK) { + return NGX_ERROR; + } + + return ngx_http_v2_filter_send(fc, stream); +} + + +static ngx_int_t +ngx_http_v2_early_hints_filter(ngx_http_request_t *r) +{ + u_char *pos, *start, *tmp; + size_t len, tmp_len; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_connection_t *fc; + ngx_http_v2_stream_t *stream; + ngx_http_v2_out_frame_t *frame; + ngx_http_v2_connection_t *h2c; + + stream = r->stream; + + if (!stream) { + return ngx_http_next_early_hints_filter(r); + } + + if (r != r->main) { + return NGX_OK; + } + + fc = r->connection; + + if (fc->error) { + return NGX_ERROR; + } + + len = 0; + tmp_len = 0; + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { + ngx_log_error(NGX_LOG_CRIT, fc->log, 0, + "too long response header name: \"%V\"", + &header[i].key); + return NGX_ERROR; + } + + if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { + ngx_log_error(NGX_LOG_CRIT, fc->log, 0, + "too long response header value: \"%V: %V\"", + &header[i].key, &header[i].value); + return NGX_ERROR; + } + + len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len + + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; + + if (header[i].key.len > tmp_len) { + tmp_len = header[i].key.len; + } + + if (header[i].value.len > tmp_len) { + tmp_len = header[i].value.len; + } + } + + if (len == 0) { + return NGX_OK; + } + + h2c = stream->connection; + + len += h2c->table_update ? 1 : 0; + len += 1 + ngx_http_v2_literal_size("418"); + + tmp = ngx_palloc(r->pool, tmp_len); + pos = ngx_pnalloc(r->pool, len); + + if (pos == NULL || tmp == NULL) { + return NGX_ERROR; + } + + start = pos; + + if (h2c->table_update) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 table size update: 0"); + *pos++ = (1 << 5) | 0; + h2c->table_update = 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 output header: \":status: %03ui\"", + (ngx_uint_t) NGX_HTTP_EARLY_HINTS); + + *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX); + *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3; + pos = ngx_sprintf(pos, "%03ui", (ngx_uint_t) NGX_HTTP_EARLY_HINTS); + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + +#if (NGX_DEBUG) + if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) { + ngx_strlow(tmp, header[i].key.data, header[i].key.len); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 output header: \"%*s: %V\"", + header[i].key.len, tmp, &header[i].value); + } +#endif + + *pos++ = 0; + + pos = ngx_http_v2_write_name(pos, header[i].key.data, + header[i].key.len, tmp); + + pos = ngx_http_v2_write_value(pos, header[i].value.data, + header[i].value.len, tmp); + } + + frame = ngx_http_v2_create_headers_frame(r, start, pos, 0, 1); + if (frame == NULL) { + return NGX_ERROR; + } + + ngx_http_v2_queue_blocked_frame(h2c, frame); + + stream->queued++; + + if (ngx_http_v2_init_stream(r) != NGX_OK) { + return NGX_ERROR; + } + + return ngx_http_v2_filter_send(fc, stream); +} + + +static ngx_int_t +ngx_http_v2_init_stream(ngx_http_request_t *r) +{ + ngx_connection_t *fc; + ngx_http_cleanup_t *cln; + ngx_http_v2_stream_t *stream; + + stream = r->stream; + fc = r->connection; + + if (stream->initialized) { + return NGX_OK; + } + + stream->initialized = 1; + + cln = ngx_http_cleanup_add(r, 0); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->handler = ngx_http_v2_filter_cleanup; + cln->data = stream; + + fc->send_chain = ngx_http_v2_send_chain; + fc->need_last_buf = 1; + fc->need_flush_buf = 1; + + return NGX_OK; +} + + +static ngx_http_v2_out_frame_t * +ngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos, + u_char *end, ngx_uint_t fin, ngx_uint_t flush) +{ + u_char type, flags; + size_t rest, frame_size; + ngx_buf_t *b; + ngx_chain_t *cl, **ll; + ngx_http_v2_stream_t *stream; + ngx_http_v2_out_frame_t *frame; + + stream = r->stream; + rest = end - pos; + + frame = ngx_palloc(r->pool, sizeof(ngx_http_v2_out_frame_t)); + if (frame == NULL) { + return NULL; + } + + frame->handler = ngx_http_v2_headers_frame_handler; + frame->stream = stream; + frame->length = rest; + frame->blocked = 1; + frame->fin = fin; + + ll = &frame->first; + + type = NGX_HTTP_V2_HEADERS_FRAME; + flags = fin ? NGX_HTTP_V2_END_STREAM_FLAG : NGX_HTTP_V2_NO_FLAG; + frame_size = stream->connection->frame_size; + + for ( ;; ) { + if (rest <= frame_size) { + frame_size = rest; + flags |= NGX_HTTP_V2_END_HEADERS_FLAG; + } + + b = ngx_create_temp_buf(r->pool, NGX_HTTP_V2_FRAME_HEADER_SIZE); + if (b == NULL) { + return NULL; + } + + b->last = ngx_http_v2_write_len_and_type(b->last, frame_size, type); + *b->last++ = flags; + b->last = ngx_http_v2_write_sid(b->last, stream->node->id); + + b->tag = (ngx_buf_tag_t) &ngx_http_v2_module; + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NULL; + } + + cl->buf = b; + + *ll = cl; + ll = &cl->next; + + b = ngx_calloc_buf(r->pool); + if (b == NULL) { + return NULL; + } + + b->pos = pos; + + pos += frame_size; + + b->last = pos; + b->start = b->pos; + b->end = b->last; + b->temporary = 1; + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NULL; + } + + cl->buf = b; + + *ll = cl; + ll = &cl->next; + + rest -= frame_size; + + if (rest) { + frame->length += NGX_HTTP_V2_FRAME_HEADER_SIZE; + + type = NGX_HTTP_V2_CONTINUATION_FRAME; + flags = NGX_HTTP_V2_NO_FLAG; + continue; + } + + b->last_buf = fin; + b->flush = flush; + cl->next = NULL; + frame->last = cl; + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http2:%ui create HEADERS frame %p: len:%uz fin:%ui", + stream->node->id, frame, frame->length, fin); + + return frame; + } +} + + +static ngx_http_v2_out_frame_t * +ngx_http_v2_create_trailers_frame(ngx_http_request_t *r) +{ + u_char *pos, *start, *tmp; + size_t len, tmp_len; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_connection_t *fc; + + fc = r->connection; + len = 0; + tmp_len = 0; + + part = &r->headers_out.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) { + ngx_log_error(NGX_LOG_CRIT, fc->log, 0, + "too long response trailer name: \"%V\"", + &header[i].key); + return NULL; + } + + if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) { + ngx_log_error(NGX_LOG_CRIT, fc->log, 0, + "too long response trailer value: \"%V: %V\"", + &header[i].key, &header[i].value); + return NULL; + } + + len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len + + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; + + if (header[i].key.len > tmp_len) { + tmp_len = header[i].key.len; + } + + if (header[i].value.len > tmp_len) { + tmp_len = header[i].value.len; + } + } + + if (len == 0) { + return NGX_HTTP_V2_NO_TRAILERS; + } + + tmp = ngx_palloc(r->pool, tmp_len); + pos = ngx_pnalloc(r->pool, len); + + if (pos == NULL || tmp == NULL) { + return NULL; + } + + start = pos; + + part = &r->headers_out.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + +#if (NGX_DEBUG) + if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) { + ngx_strlow(tmp, header[i].key.data, header[i].key.len); + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 output trailer: \"%*s: %V\"", + header[i].key.len, tmp, &header[i].value); + } +#endif + + *pos++ = 0; + + pos = ngx_http_v2_write_name(pos, header[i].key.data, + header[i].key.len, tmp); + + pos = ngx_http_v2_write_value(pos, header[i].value.data, + header[i].value.len, tmp); + } + + return ngx_http_v2_create_headers_frame(r, start, pos, 1, 0); +} + + +static ngx_chain_t * +ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit) +{ + off_t size, offset; + size_t rest, frame_size; + ngx_chain_t *cl, *out, **ln; + ngx_http_request_t *r; + ngx_http_v2_stream_t *stream; + ngx_http_v2_loc_conf_t *h2lcf; + ngx_http_v2_out_frame_t *frame, *trailers; + ngx_http_v2_connection_t *h2c; + + r = fc->data; + stream = r->stream; + +#if (NGX_SUPPRESS_WARN) + size = 0; +#endif + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, + "http2 send chain: %p", in); + + while (in) { + size = ngx_buf_size(in->buf); + + if (size || in->buf->last_buf) { + break; + } + + in = in->next; + } + + if (in == NULL || stream->out_closed) { + + if (size) { + ngx_log_error(NGX_LOG_ERR, fc->log, 0, + "output on closed stream"); + return NGX_CHAIN_ERROR; + } + + if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) { + return NGX_CHAIN_ERROR; + } + + return NULL; + } + + h2c = stream->connection; + + if (size && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) { + + if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) { + return NGX_CHAIN_ERROR; + } + + if (ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) { + fc->write->active = 1; + fc->write->ready = 0; + return in; + } + } + + if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) { + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_CHAIN_ERROR; + } + + cl->buf = in->buf; + in->buf = cl->buf->shadow; + + offset = ngx_buf_in_memory(in->buf) + ? (cl->buf->pos - in->buf->pos) + : (cl->buf->file_pos - in->buf->file_pos); + + cl->next = stream->free_bufs; + stream->free_bufs = cl; + + } else { + offset = 0; + } + + if (limit == 0 || limit > (off_t) h2c->send_window) { + limit = h2c->send_window; + } + + if (limit > stream->send_window) { + limit = (stream->send_window > 0) ? stream->send_window : 0; + } + + h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module); + + frame_size = (h2lcf->chunk_size < h2c->frame_size) + ? h2lcf->chunk_size : h2c->frame_size; + + trailers = NGX_HTTP_V2_NO_TRAILERS; + +#if (NGX_SUPPRESS_WARN) + cl = NULL; +#endif + + for ( ;; ) { + if ((off_t) frame_size > limit) { + frame_size = (size_t) limit; + } + + ln = &out; + rest = frame_size; + + while ((off_t) rest >= size) { + + if (offset) { + cl = ngx_http_v2_filter_get_shadow(stream, in->buf, + offset, size); + if (cl == NULL) { + return NGX_CHAIN_ERROR; + } + + offset = 0; + + } else { + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_CHAIN_ERROR; + } + + cl->buf = in->buf; + } + + *ln = cl; + ln = &cl->next; + + rest -= (size_t) size; + in = in->next; + + if (in == NULL) { + frame_size -= rest; + rest = 0; + break; + } + + size = ngx_buf_size(in->buf); + } + + if (rest) { + cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, rest); + if (cl == NULL) { + return NGX_CHAIN_ERROR; + } + + cl->buf->flush = 0; + cl->buf->last_buf = 0; + + *ln = cl; + + offset += rest; + size -= rest; + } + + if (cl->buf->last_buf) { + trailers = ngx_http_v2_create_trailers_frame(r); + if (trailers == NULL) { + return NGX_CHAIN_ERROR; + } + + if (trailers != NGX_HTTP_V2_NO_TRAILERS) { + cl->buf->last_buf = 0; + } + } + + if (frame_size || cl->buf->last_buf) { + frame = ngx_http_v2_filter_get_data_frame(stream, frame_size, + out, cl); + if (frame == NULL) { + return NGX_CHAIN_ERROR; + } + + ngx_http_v2_queue_frame(h2c, frame); + + h2c->send_window -= frame_size; + + stream->send_window -= frame_size; + stream->queued++; + } + + if (in == NULL) { + + if (trailers != NGX_HTTP_V2_NO_TRAILERS) { + ngx_http_v2_queue_frame(h2c, trailers); + stream->queued++; + } + + break; + } + + limit -= frame_size; + + if (limit == 0) { + break; + } + } + + if (offset) { + cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, size); + if (cl == NULL) { + return NGX_CHAIN_ERROR; + } + + in->buf = cl->buf; + ngx_free_chain(r->pool, cl); + } + + if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) { + return NGX_CHAIN_ERROR; + } + + if (in && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) { + fc->write->active = 1; + fc->write->ready = 0; + } + + return in; +} + + +static ngx_chain_t * +ngx_http_v2_filter_get_shadow(ngx_http_v2_stream_t *stream, ngx_buf_t *buf, + off_t offset, off_t size) +{ + ngx_buf_t *chunk; + ngx_chain_t *cl; + + cl = ngx_chain_get_free_buf(stream->request->pool, &stream->free_bufs); + if (cl == NULL) { + return NULL; + } + + chunk = cl->buf; + + ngx_memcpy(chunk, buf, sizeof(ngx_buf_t)); + + chunk->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow; + chunk->shadow = buf; + + if (ngx_buf_in_memory(chunk)) { + chunk->pos += offset; + chunk->last = chunk->pos + size; + } + + if (chunk->in_file) { + chunk->file_pos += offset; + chunk->file_last = chunk->file_pos + size; + } + + return cl; +} + + +static ngx_http_v2_out_frame_t * +ngx_http_v2_filter_get_data_frame(ngx_http_v2_stream_t *stream, + size_t len, ngx_chain_t *first, ngx_chain_t *last) +{ + u_char flags; + ngx_buf_t *buf; + ngx_chain_t *cl; + ngx_http_v2_out_frame_t *frame; + ngx_http_v2_connection_t *h2c; + + frame = stream->free_frames; + h2c = stream->connection; + + if (frame) { + stream->free_frames = frame->next; + + } else if (h2c->frames < 10000) { + frame = ngx_palloc(stream->request->pool, + sizeof(ngx_http_v2_out_frame_t)); + if (frame == NULL) { + return NULL; + } + + stream->frames++; + h2c->frames++; + + } else { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "http2 flood detected"); + + h2c->connection->error = 1; + return NULL; + } + + flags = last->buf->last_buf ? NGX_HTTP_V2_END_STREAM_FLAG : 0; + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0, + "http2:%ui create DATA frame %p: len:%uz flags:%ui", + stream->node->id, frame, len, (ngx_uint_t) flags); + + cl = ngx_chain_get_free_buf(stream->request->pool, + &stream->free_frame_headers); + if (cl == NULL) { + return NULL; + } + + buf = cl->buf; + + if (buf->start == NULL) { + buf->start = ngx_palloc(stream->request->pool, + NGX_HTTP_V2_FRAME_HEADER_SIZE); + if (buf->start == NULL) { + return NULL; + } + + buf->end = buf->start + NGX_HTTP_V2_FRAME_HEADER_SIZE; + buf->last = buf->end; + + buf->tag = (ngx_buf_tag_t) &ngx_http_v2_module; + buf->memory = 1; + } + + buf->pos = buf->start; + buf->last = buf->pos; + + buf->last = ngx_http_v2_write_len_and_type(buf->last, len, + NGX_HTTP_V2_DATA_FRAME); + *buf->last++ = flags; + + buf->last = ngx_http_v2_write_sid(buf->last, stream->node->id); + + cl->next = first; + first = cl; + + last->buf->flush = 1; + + frame->first = first; + frame->last = last; + frame->handler = ngx_http_v2_data_frame_handler; + frame->stream = stream; + frame->length = len; + frame->blocked = 0; + frame->fin = last->buf->last_buf; + + return frame; +} + + +static ngx_inline ngx_int_t +ngx_http_v2_flow_control(ngx_http_v2_connection_t *h2c, + ngx_http_v2_stream_t *stream) +{ + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2:%ui windows: conn:%uz stream:%z", + stream->node->id, h2c->send_window, stream->send_window); + + if (stream->send_window <= 0) { + stream->exhausted = 1; + return NGX_DECLINED; + } + + if (h2c->send_window == 0) { + ngx_http_v2_waiting_queue(h2c, stream); + return NGX_DECLINED; + } + + return NGX_OK; +} + + +static void +ngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c, + ngx_http_v2_stream_t *stream) +{ + ngx_queue_t *q; + ngx_http_v2_stream_t *s; + + if (stream->waiting) { + return; + } + + stream->waiting = 1; + + for (q = ngx_queue_last(&h2c->waiting); + q != ngx_queue_sentinel(&h2c->waiting); + q = ngx_queue_prev(q)) + { + s = ngx_queue_data(q, ngx_http_v2_stream_t, queue); + + if (s->node->rank < stream->node->rank + || (s->node->rank == stream->node->rank + && s->node->rel_weight >= stream->node->rel_weight)) + { + break; + } + } + + ngx_queue_insert_after(q, &stream->queue); +} + + +static ngx_inline ngx_int_t +ngx_http_v2_filter_send(ngx_connection_t *fc, ngx_http_v2_stream_t *stream) +{ + ngx_connection_t *c; + + c = stream->connection->connection; + + if (stream->queued == 0 && !c->buffered) { + fc->buffered &= ~NGX_HTTP_V2_BUFFERED; + return NGX_OK; + } + + stream->blocked = 1; + + if (ngx_http_v2_send_output_queue(stream->connection) == NGX_ERROR) { + fc->error = 1; + return NGX_ERROR; + } + + stream->blocked = 0; + + if (stream->queued) { + fc->buffered |= NGX_HTTP_V2_BUFFERED; + fc->write->active = 1; + fc->write->ready = 0; + return NGX_AGAIN; + } + + fc->buffered &= ~NGX_HTTP_V2_BUFFERED; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v2_headers_frame_handler(ngx_http_v2_connection_t *h2c, + ngx_http_v2_out_frame_t *frame) +{ + ngx_chain_t *cl, *ln; + ngx_http_v2_stream_t *stream; + + stream = frame->stream; + cl = frame->first; + + for ( ;; ) { + if (cl->buf->pos != cl->buf->last) { + frame->first = cl; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2:%ui HEADERS frame %p was sent partially", + stream->node->id, frame); + + return NGX_AGAIN; + } + + ln = cl->next; + + if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) { + cl->next = stream->free_frame_headers; + stream->free_frame_headers = cl; + + } else { + cl->next = stream->free_bufs; + stream->free_bufs = cl; + } + + if (cl == frame->last) { + break; + } + + cl = ln; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2:%ui HEADERS frame %p was sent", + stream->node->id, frame); + + stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE + + frame->length; + + h2c->payload_bytes += frame->length; + + ngx_http_v2_handle_frame(stream, frame); + + ngx_http_v2_handle_stream(h2c, stream); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v2_data_frame_handler(ngx_http_v2_connection_t *h2c, + ngx_http_v2_out_frame_t *frame) +{ + ngx_buf_t *buf; + ngx_chain_t *cl, *ln; + ngx_http_v2_stream_t *stream; + + stream = frame->stream; + cl = frame->first; + + if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) { + + if (cl->buf->pos != cl->buf->last) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2:%ui DATA frame %p was sent partially", + stream->node->id, frame); + + return NGX_AGAIN; + } + + ln = cl->next; + + cl->next = stream->free_frame_headers; + stream->free_frame_headers = cl; + + if (cl == frame->last) { + goto done; + } + + cl = ln; + } + + for ( ;; ) { + if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) { + buf = cl->buf->shadow; + + if (ngx_buf_in_memory(buf)) { + buf->pos = cl->buf->pos; + } + + if (buf->in_file) { + buf->file_pos = cl->buf->file_pos; + } + } + + if (ngx_buf_size(cl->buf) != 0) { + + if (cl != frame->first) { + frame->first = cl; + ngx_http_v2_handle_stream(h2c, stream); + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2:%ui DATA frame %p was sent partially", + stream->node->id, frame); + + return NGX_AGAIN; + } + + ln = cl->next; + + if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) { + cl->next = stream->free_bufs; + stream->free_bufs = cl; + + } else { + ngx_free_chain(stream->request->pool, cl); + } + + if (cl == frame->last) { + goto done; + } + + cl = ln; + } + +done: + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2:%ui DATA frame %p was sent", + stream->node->id, frame); + + stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE; + + h2c->payload_bytes += frame->length; + + ngx_http_v2_handle_frame(stream, frame); + + ngx_http_v2_handle_stream(h2c, stream); + + return NGX_OK; +} + + +static ngx_inline void +ngx_http_v2_handle_frame(ngx_http_v2_stream_t *stream, + ngx_http_v2_out_frame_t *frame) +{ + ngx_http_request_t *r; + ngx_http_v2_connection_t *h2c; + + r = stream->request; + + r->connection->sent += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length; + + h2c = stream->connection; + + h2c->total_bytes += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length; + + if (frame->fin) { + stream->out_closed = 1; + } + + frame->next = stream->free_frames; + stream->free_frames = frame; + + stream->queued--; +} + + +static ngx_inline void +ngx_http_v2_handle_stream(ngx_http_v2_connection_t *h2c, + ngx_http_v2_stream_t *stream) +{ + ngx_event_t *wev; + ngx_connection_t *fc; + + if (stream->waiting || stream->blocked) { + return; + } + + fc = stream->request->connection; + + if (!fc->error && stream->exhausted) { + return; + } + + wev = fc->write; + + wev->active = 0; + wev->ready = 1; + + if (!fc->error && wev->delayed) { + return; + } + + ngx_post_event(wev, &ngx_posted_events); +} + + +static void +ngx_http_v2_filter_cleanup(void *data) +{ + ngx_http_v2_stream_t *stream = data; + + size_t window; + ngx_event_t *wev; + ngx_queue_t *q; + ngx_http_v2_out_frame_t *frame, **fn; + ngx_http_v2_connection_t *h2c; + + if (stream->waiting) { + stream->waiting = 0; + ngx_queue_remove(&stream->queue); + } + + if (stream->queued == 0) { + return; + } + + window = 0; + h2c = stream->connection; + fn = &h2c->last_out; + + for ( ;; ) { + frame = *fn; + + if (frame == NULL) { + break; + } + + if (frame->stream == stream && !frame->blocked) { + *fn = frame->next; + + window += frame->length; + + if (--stream->queued == 0) { + break; + } + + continue; + } + + fn = &frame->next; + } + + if (h2c->send_window == 0 && window) { + + while (!ngx_queue_empty(&h2c->waiting)) { + q = ngx_queue_head(&h2c->waiting); + + ngx_queue_remove(q); + + stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue); + + stream->waiting = 0; + + wev = stream->request->connection->write; + + wev->active = 0; + wev->ready = 1; + + if (!wev->delayed) { + ngx_post_event(wev, &ngx_posted_events); + } + } + } + + h2c->send_window += window; +} + + +static ngx_int_t +ngx_http_v2_filter_init(ngx_conf_t *cf) +{ + ngx_http_next_header_filter = ngx_http_top_header_filter; + ngx_http_top_header_filter = ngx_http_v2_header_filter; + + ngx_http_next_early_hints_filter = ngx_http_top_early_hints_filter; + ngx_http_top_early_hints_filter = ngx_http_v2_early_hints_filter; + + return NGX_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/http/v2/ngx_http_v2_module.c b/src/deps/src/nginx-1.30.1/src/http/v2/ngx_http_v2_module.c new file mode 100644 index 000000000..d64488c20 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/v2/ngx_http_v2_module.c @@ -0,0 +1,498 @@ + +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Valentin V. Bartenev + */ + + +#include +#include +#include +#include + + +static ngx_int_t ngx_http_v2_add_variables(ngx_conf_t *cf); + +static ngx_int_t ngx_http_v2_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); + +static ngx_int_t ngx_http_v2_module_init(ngx_cycle_t *cycle); + +static void *ngx_http_v2_create_main_conf(ngx_conf_t *cf); +static char *ngx_http_v2_init_main_conf(ngx_conf_t *cf, void *conf); +static void *ngx_http_v2_create_srv_conf(ngx_conf_t *cf); +static char *ngx_http_v2_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); +static void *ngx_http_v2_create_loc_conf(ngx_conf_t *cf); +static char *ngx_http_v2_merge_loc_conf(ngx_conf_t *cf, void *parent, + void *child); + +static char *ngx_http_v2_recv_buffer_size(ngx_conf_t *cf, void *post, + void *data); +static char *ngx_http_v2_pool_size(ngx_conf_t *cf, void *post, void *data); +static char *ngx_http_v2_preread_size(ngx_conf_t *cf, void *post, void *data); +static char *ngx_http_v2_streams_index_mask(ngx_conf_t *cf, void *post, + void *data); +static char *ngx_http_v2_chunk_size(ngx_conf_t *cf, void *post, void *data); +static char *ngx_http_v2_obsolete(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + + +static ngx_conf_deprecated_t ngx_http_v2_recv_timeout_deprecated = { + ngx_conf_deprecated, "http2_recv_timeout", "client_header_timeout" +}; + +static ngx_conf_deprecated_t ngx_http_v2_idle_timeout_deprecated = { + ngx_conf_deprecated, "http2_idle_timeout", "keepalive_timeout" +}; + +static ngx_conf_deprecated_t ngx_http_v2_max_requests_deprecated = { + ngx_conf_deprecated, "http2_max_requests", "keepalive_requests" +}; + +static ngx_conf_deprecated_t ngx_http_v2_max_field_size_deprecated = { + ngx_conf_deprecated, "http2_max_field_size", "large_client_header_buffers" +}; + +static ngx_conf_deprecated_t ngx_http_v2_max_header_size_deprecated = { + ngx_conf_deprecated, "http2_max_header_size", "large_client_header_buffers" +}; + + +static ngx_conf_post_t ngx_http_v2_recv_buffer_size_post = + { ngx_http_v2_recv_buffer_size }; +static ngx_conf_post_t ngx_http_v2_pool_size_post = + { ngx_http_v2_pool_size }; +static ngx_conf_post_t ngx_http_v2_preread_size_post = + { ngx_http_v2_preread_size }; +static ngx_conf_post_t ngx_http_v2_streams_index_mask_post = + { ngx_http_v2_streams_index_mask }; +static ngx_conf_post_t ngx_http_v2_chunk_size_post = + { ngx_http_v2_chunk_size }; + + +static ngx_command_t ngx_http_v2_commands[] = { + + { ngx_string("http2"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v2_srv_conf_t, enable), + NULL }, + + { ngx_string("http2_recv_buffer_size"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_MAIN_CONF_OFFSET, + offsetof(ngx_http_v2_main_conf_t, recv_buffer_size), + &ngx_http_v2_recv_buffer_size_post }, + + { ngx_string("http2_pool_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v2_srv_conf_t, pool_size), + &ngx_http_v2_pool_size_post }, + + { ngx_string("http2_max_concurrent_streams"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v2_srv_conf_t, concurrent_streams), + NULL }, + + { ngx_string("http2_max_concurrent_pushes"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_http_v2_obsolete, + 0, + 0, + NULL }, + + { ngx_string("http2_max_requests"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_http_v2_obsolete, + 0, + 0, + &ngx_http_v2_max_requests_deprecated }, + + { ngx_string("http2_max_field_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_http_v2_obsolete, + 0, + 0, + &ngx_http_v2_max_field_size_deprecated }, + + { ngx_string("http2_max_header_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_http_v2_obsolete, + 0, + 0, + &ngx_http_v2_max_header_size_deprecated }, + + { ngx_string("http2_body_preread_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v2_srv_conf_t, preread_size), + &ngx_http_v2_preread_size_post }, + + { ngx_string("http2_streams_index_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v2_srv_conf_t, streams_index_mask), + &ngx_http_v2_streams_index_mask_post }, + + { ngx_string("http2_recv_timeout"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_http_v2_obsolete, + 0, + 0, + &ngx_http_v2_recv_timeout_deprecated }, + + { ngx_string("http2_idle_timeout"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_http_v2_obsolete, + 0, + 0, + &ngx_http_v2_idle_timeout_deprecated }, + + { ngx_string("http2_chunk_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_v2_loc_conf_t, chunk_size), + &ngx_http_v2_chunk_size_post }, + + { ngx_string("http2_push_preload"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, + ngx_http_v2_obsolete, + 0, + 0, + NULL }, + + { ngx_string("http2_push"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_v2_obsolete, + 0, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_v2_module_ctx = { + ngx_http_v2_add_variables, /* preconfiguration */ + NULL, /* postconfiguration */ + + ngx_http_v2_create_main_conf, /* create main configuration */ + ngx_http_v2_init_main_conf, /* init main configuration */ + + ngx_http_v2_create_srv_conf, /* create server configuration */ + ngx_http_v2_merge_srv_conf, /* merge server configuration */ + + ngx_http_v2_create_loc_conf, /* create location configuration */ + ngx_http_v2_merge_loc_conf /* merge location configuration */ +}; + + +ngx_module_t ngx_http_v2_module = { + NGX_MODULE_V1, + &ngx_http_v2_module_ctx, /* module context */ + ngx_http_v2_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + ngx_http_v2_module_init, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_http_variable_t ngx_http_v2_vars[] = { + + { ngx_string("http2"), NULL, + ngx_http_v2_variable, 0, 0, 0 }, + + ngx_http_null_variable +}; + + +static ngx_int_t +ngx_http_v2_add_variables(ngx_conf_t *cf) +{ + ngx_http_variable_t *var, *v; + + for (v = ngx_http_v2_vars; v->name.len; v++) { + var = ngx_http_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v2_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + + if (r->stream) { +#if (NGX_HTTP_SSL) + + if (r->connection->ssl) { + v->len = sizeof("h2") - 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) "h2"; + + return NGX_OK; + } + +#endif + v->len = sizeof("h2c") - 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) "h2c"; + + return NGX_OK; + } + + *v = ngx_http_variable_null_value; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v2_module_init(ngx_cycle_t *cycle) +{ + return NGX_OK; +} + + +static void * +ngx_http_v2_create_main_conf(ngx_conf_t *cf) +{ + ngx_http_v2_main_conf_t *h2mcf; + + h2mcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v2_main_conf_t)); + if (h2mcf == NULL) { + return NULL; + } + + h2mcf->recv_buffer_size = NGX_CONF_UNSET_SIZE; + + return h2mcf; +} + + +static char * +ngx_http_v2_init_main_conf(ngx_conf_t *cf, void *conf) +{ + ngx_http_v2_main_conf_t *h2mcf = conf; + + ngx_conf_init_size_value(h2mcf->recv_buffer_size, 256 * 1024); + + return NGX_CONF_OK; +} + + +static void * +ngx_http_v2_create_srv_conf(ngx_conf_t *cf) +{ + ngx_http_v2_srv_conf_t *h2scf; + + h2scf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v2_srv_conf_t)); + if (h2scf == NULL) { + return NULL; + } + + h2scf->enable = NGX_CONF_UNSET; + + h2scf->pool_size = NGX_CONF_UNSET_SIZE; + + h2scf->concurrent_streams = NGX_CONF_UNSET_UINT; + + h2scf->preread_size = NGX_CONF_UNSET_SIZE; + + h2scf->streams_index_mask = NGX_CONF_UNSET_UINT; + + return h2scf; +} + + +static char * +ngx_http_v2_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_http_v2_srv_conf_t *prev = parent; + ngx_http_v2_srv_conf_t *conf = child; + + ngx_conf_merge_value(conf->enable, prev->enable, 0); + + ngx_conf_merge_size_value(conf->pool_size, prev->pool_size, 4096); + + ngx_conf_merge_uint_value(conf->concurrent_streams, + prev->concurrent_streams, 128); + + ngx_conf_merge_size_value(conf->preread_size, prev->preread_size, 65536); + + ngx_conf_merge_uint_value(conf->streams_index_mask, + prev->streams_index_mask, 32 - 1); + + return NGX_CONF_OK; +} + + +static void * +ngx_http_v2_create_loc_conf(ngx_conf_t *cf) +{ + ngx_http_v2_loc_conf_t *h2lcf; + + h2lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v2_loc_conf_t)); + if (h2lcf == NULL) { + return NULL; + } + + h2lcf->chunk_size = NGX_CONF_UNSET_SIZE; + + return h2lcf; +} + + +static char * +ngx_http_v2_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_http_v2_loc_conf_t *prev = parent; + ngx_http_v2_loc_conf_t *conf = child; + + ngx_conf_merge_size_value(conf->chunk_size, prev->chunk_size, 8 * 1024); + + return NGX_CONF_OK; +} + + +static char * +ngx_http_v2_recv_buffer_size(ngx_conf_t *cf, void *post, void *data) +{ + size_t *sp = data; + + if (*sp <= NGX_HTTP_V2_STATE_BUFFER_SIZE) { + return "value is too small"; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_v2_pool_size(ngx_conf_t *cf, void *post, void *data) +{ + size_t *sp = data; + + if (*sp < NGX_MIN_POOL_SIZE) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the pool size must be no less than %uz", + NGX_MIN_POOL_SIZE); + + return NGX_CONF_ERROR; + } + + if (*sp % NGX_POOL_ALIGNMENT) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the pool size must be a multiple of %uz", + NGX_POOL_ALIGNMENT); + + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_v2_preread_size(ngx_conf_t *cf, void *post, void *data) +{ + size_t *sp = data; + + if (*sp > NGX_HTTP_V2_MAX_WINDOW) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the maximum body preread buffer size is %uz", + NGX_HTTP_V2_MAX_WINDOW); + + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_v2_streams_index_mask(ngx_conf_t *cf, void *post, void *data) +{ + ngx_uint_t *np = data; + + ngx_uint_t mask; + + mask = *np - 1; + + if (*np == 0 || (*np & mask)) { + return "must be a power of two"; + } + + *np = mask; + + return NGX_CONF_OK; +} + + +static char * +ngx_http_v2_chunk_size(ngx_conf_t *cf, void *post, void *data) +{ + size_t *sp = data; + + if (*sp == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the http2 chunk size cannot be zero"); + + return NGX_CONF_ERROR; + } + + if (*sp > NGX_HTTP_V2_MAX_FRAME_SIZE) { + *sp = NGX_HTTP_V2_MAX_FRAME_SIZE; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_v2_obsolete(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_conf_deprecated_t *d = cmd->post; + + if (d) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "the \"%s\" directive is obsolete, " + "use the \"%s\" directive instead", + d->old_name, d->new_name); + + } else { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "the \"%V\" directive is obsolete, ignored", + &cmd->name); + } + + return NGX_CONF_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/http/v2/ngx_http_v2_module.h b/src/deps/src/nginx-1.30.1/src/http/v2/ngx_http_v2_module.h new file mode 100644 index 000000000..07a595cfd --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/v2/ngx_http_v2_module.h @@ -0,0 +1,28 @@ + +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Valentin V. Bartenev + */ + + +#ifndef _NGX_HTTP_V2_MODULE_H_INCLUDED_ +#define _NGX_HTTP_V2_MODULE_H_INCLUDED_ + + +#include +#include +#include + + +typedef struct { + size_t recv_buffer_size; + u_char *recv_buffer; +} ngx_http_v2_main_conf_t; + + +typedef struct { + size_t chunk_size; +} ngx_http_v2_loc_conf_t; + + +#endif /* _NGX_HTTP_V2_MODULE_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/http/v2/ngx_http_v2_table.c b/src/deps/src/nginx-1.30.1/src/http/v2/ngx_http_v2_table.c new file mode 100644 index 000000000..7d49803fd --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/v2/ngx_http_v2_table.c @@ -0,0 +1,363 @@ + +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Valentin V. Bartenev + */ + + +#include +#include +#include + + +#define NGX_HTTP_V2_TABLE_SIZE 4096 + + +static ngx_int_t ngx_http_v2_table_account(ngx_http_v2_connection_t *h2c, + size_t size); + + +static ngx_http_v2_header_t ngx_http_v2_static_table[] = { + { ngx_string(":authority"), ngx_string("") }, + { ngx_string(":method"), ngx_string("GET") }, + { ngx_string(":method"), ngx_string("POST") }, + { ngx_string(":path"), ngx_string("/") }, + { ngx_string(":path"), ngx_string("/index.html") }, + { ngx_string(":scheme"), ngx_string("http") }, + { ngx_string(":scheme"), ngx_string("https") }, + { ngx_string(":status"), ngx_string("200") }, + { ngx_string(":status"), ngx_string("204") }, + { ngx_string(":status"), ngx_string("206") }, + { ngx_string(":status"), ngx_string("304") }, + { ngx_string(":status"), ngx_string("400") }, + { ngx_string(":status"), ngx_string("404") }, + { ngx_string(":status"), ngx_string("500") }, + { ngx_string("accept-charset"), ngx_string("") }, + { ngx_string("accept-encoding"), ngx_string("gzip, deflate") }, + { ngx_string("accept-language"), ngx_string("") }, + { ngx_string("accept-ranges"), ngx_string("") }, + { ngx_string("accept"), ngx_string("") }, + { ngx_string("access-control-allow-origin"), ngx_string("") }, + { ngx_string("age"), ngx_string("") }, + { ngx_string("allow"), ngx_string("") }, + { ngx_string("authorization"), ngx_string("") }, + { ngx_string("cache-control"), ngx_string("") }, + { ngx_string("content-disposition"), ngx_string("") }, + { ngx_string("content-encoding"), ngx_string("") }, + { ngx_string("content-language"), ngx_string("") }, + { ngx_string("content-length"), ngx_string("") }, + { ngx_string("content-location"), ngx_string("") }, + { ngx_string("content-range"), ngx_string("") }, + { ngx_string("content-type"), ngx_string("") }, + { ngx_string("cookie"), ngx_string("") }, + { ngx_string("date"), ngx_string("") }, + { ngx_string("etag"), ngx_string("") }, + { ngx_string("expect"), ngx_string("") }, + { ngx_string("expires"), ngx_string("") }, + { ngx_string("from"), ngx_string("") }, + { ngx_string("host"), ngx_string("") }, + { ngx_string("if-match"), ngx_string("") }, + { ngx_string("if-modified-since"), ngx_string("") }, + { ngx_string("if-none-match"), ngx_string("") }, + { ngx_string("if-range"), ngx_string("") }, + { ngx_string("if-unmodified-since"), ngx_string("") }, + { ngx_string("last-modified"), ngx_string("") }, + { ngx_string("link"), ngx_string("") }, + { ngx_string("location"), ngx_string("") }, + { ngx_string("max-forwards"), ngx_string("") }, + { ngx_string("proxy-authenticate"), ngx_string("") }, + { ngx_string("proxy-authorization"), ngx_string("") }, + { ngx_string("range"), ngx_string("") }, + { ngx_string("referer"), ngx_string("") }, + { ngx_string("refresh"), ngx_string("") }, + { ngx_string("retry-after"), ngx_string("") }, + { ngx_string("server"), ngx_string("") }, + { ngx_string("set-cookie"), ngx_string("") }, + { ngx_string("strict-transport-security"), ngx_string("") }, + { ngx_string("transfer-encoding"), ngx_string("") }, + { ngx_string("user-agent"), ngx_string("") }, + { ngx_string("vary"), ngx_string("") }, + { ngx_string("via"), ngx_string("") }, + { ngx_string("www-authenticate"), ngx_string("") }, +}; + +#define NGX_HTTP_V2_STATIC_TABLE_ENTRIES \ + (sizeof(ngx_http_v2_static_table) \ + / sizeof(ngx_http_v2_header_t)) + + +ngx_str_t * +ngx_http_v2_get_static_name(ngx_uint_t index) +{ + return &ngx_http_v2_static_table[index - 1].name; +} + + +ngx_str_t * +ngx_http_v2_get_static_value(ngx_uint_t index) +{ + return &ngx_http_v2_static_table[index - 1].value; +} + + +ngx_int_t +ngx_http_v2_get_indexed_header(ngx_http_v2_connection_t *h2c, ngx_uint_t index, + ngx_uint_t name_only) +{ + u_char *p; + size_t rest; + ngx_http_v2_header_t *entry; + + if (index == 0) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent invalid hpack table index 0"); + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 get indexed %s: %ui", + name_only ? "name" : "header", index); + + index--; + + if (index < NGX_HTTP_V2_STATIC_TABLE_ENTRIES) { + h2c->state.header = ngx_http_v2_static_table[index]; + return NGX_OK; + } + + index -= NGX_HTTP_V2_STATIC_TABLE_ENTRIES; + + if (index < h2c->hpack.added - h2c->hpack.deleted) { + index = (h2c->hpack.added - index - 1) % h2c->hpack.allocated; + entry = h2c->hpack.entries[index]; + + p = ngx_pnalloc(h2c->state.pool, entry->name.len + 1); + if (p == NULL) { + return NGX_ERROR; + } + + h2c->state.header.name.len = entry->name.len; + h2c->state.header.name.data = p; + + rest = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - entry->name.data; + + if (entry->name.len > rest) { + p = ngx_cpymem(p, entry->name.data, rest); + p = ngx_cpymem(p, h2c->hpack.storage, entry->name.len - rest); + + } else { + p = ngx_cpymem(p, entry->name.data, entry->name.len); + } + + *p = '\0'; + + if (name_only) { + return NGX_OK; + } + + p = ngx_pnalloc(h2c->state.pool, entry->value.len + 1); + if (p == NULL) { + return NGX_ERROR; + } + + h2c->state.header.value.len = entry->value.len; + h2c->state.header.value.data = p; + + rest = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - entry->value.data; + + if (entry->value.len > rest) { + p = ngx_cpymem(p, entry->value.data, rest); + p = ngx_cpymem(p, h2c->hpack.storage, entry->value.len - rest); + + } else { + p = ngx_cpymem(p, entry->value.data, entry->value.len); + } + + *p = '\0'; + + return NGX_OK; + } + + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent out of bound hpack table index: %ui", index); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_http_v2_add_header(ngx_http_v2_connection_t *h2c, + ngx_http_v2_header_t *header) +{ + size_t avail; + ngx_uint_t index; + ngx_http_v2_header_t *entry, **entries; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 table add: \"%V: %V\"", + &header->name, &header->value); + + if (h2c->hpack.entries == NULL) { + h2c->hpack.allocated = 64; + h2c->hpack.size = NGX_HTTP_V2_TABLE_SIZE; + h2c->hpack.free = NGX_HTTP_V2_TABLE_SIZE; + + h2c->hpack.entries = ngx_palloc(h2c->connection->pool, + sizeof(ngx_http_v2_header_t *) + * h2c->hpack.allocated); + if (h2c->hpack.entries == NULL) { + return NGX_ERROR; + } + + h2c->hpack.storage = ngx_palloc(h2c->connection->pool, + h2c->hpack.free); + if (h2c->hpack.storage == NULL) { + return NGX_ERROR; + } + + h2c->hpack.pos = h2c->hpack.storage; + } + + if (ngx_http_v2_table_account(h2c, header->name.len + header->value.len) + != NGX_OK) + { + return NGX_OK; + } + + if (h2c->hpack.reused == h2c->hpack.deleted) { + entry = ngx_palloc(h2c->connection->pool, sizeof(ngx_http_v2_header_t)); + if (entry == NULL) { + return NGX_ERROR; + } + + } else { + entry = h2c->hpack.entries[h2c->hpack.reused++ % h2c->hpack.allocated]; + } + + avail = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - h2c->hpack.pos; + + entry->name.len = header->name.len; + entry->name.data = h2c->hpack.pos; + + if (avail >= header->name.len) { + h2c->hpack.pos = ngx_cpymem(h2c->hpack.pos, header->name.data, + header->name.len); + } else { + ngx_memcpy(h2c->hpack.pos, header->name.data, avail); + h2c->hpack.pos = ngx_cpymem(h2c->hpack.storage, + header->name.data + avail, + header->name.len - avail); + avail = NGX_HTTP_V2_TABLE_SIZE; + } + + avail -= header->name.len; + + entry->value.len = header->value.len; + entry->value.data = h2c->hpack.pos; + + if (avail >= header->value.len) { + h2c->hpack.pos = ngx_cpymem(h2c->hpack.pos, header->value.data, + header->value.len); + } else { + ngx_memcpy(h2c->hpack.pos, header->value.data, avail); + h2c->hpack.pos = ngx_cpymem(h2c->hpack.storage, + header->value.data + avail, + header->value.len - avail); + } + + if (h2c->hpack.allocated == h2c->hpack.added - h2c->hpack.deleted) { + + entries = ngx_palloc(h2c->connection->pool, + sizeof(ngx_http_v2_header_t *) + * (h2c->hpack.allocated + 64)); + if (entries == NULL) { + return NGX_ERROR; + } + + index = h2c->hpack.deleted % h2c->hpack.allocated; + + ngx_memcpy(entries, &h2c->hpack.entries[index], + (h2c->hpack.allocated - index) + * sizeof(ngx_http_v2_header_t *)); + + ngx_memcpy(&entries[h2c->hpack.allocated - index], h2c->hpack.entries, + index * sizeof(ngx_http_v2_header_t *)); + + (void) ngx_pfree(h2c->connection->pool, h2c->hpack.entries); + + h2c->hpack.entries = entries; + + h2c->hpack.added = h2c->hpack.allocated; + h2c->hpack.deleted = 0; + h2c->hpack.reused = 0; + h2c->hpack.allocated += 64; + } + + h2c->hpack.entries[h2c->hpack.added++ % h2c->hpack.allocated] = entry; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v2_table_account(ngx_http_v2_connection_t *h2c, size_t size) +{ + ngx_http_v2_header_t *entry; + + size += 32; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 table account: %uz free:%uz", + size, h2c->hpack.free); + + if (size <= h2c->hpack.free) { + h2c->hpack.free -= size; + return NGX_OK; + } + + if (size > h2c->hpack.size) { + h2c->hpack.deleted = h2c->hpack.added; + h2c->hpack.free = h2c->hpack.size; + return NGX_DECLINED; + } + + do { + entry = h2c->hpack.entries[h2c->hpack.deleted++ % h2c->hpack.allocated]; + h2c->hpack.free += 32 + entry->name.len + entry->value.len; + } while (size > h2c->hpack.free); + + h2c->hpack.free -= size; + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v2_table_size(ngx_http_v2_connection_t *h2c, size_t size) +{ + ssize_t needed; + ngx_http_v2_header_t *entry; + + if (size > NGX_HTTP_V2_TABLE_SIZE) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent invalid table size update: %uz", size); + + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, + "http2 new hpack table size: %uz was:%uz", + size, h2c->hpack.size); + + needed = h2c->hpack.size - size; + + while (needed > (ssize_t) h2c->hpack.free) { + entry = h2c->hpack.entries[h2c->hpack.deleted++ % h2c->hpack.allocated]; + h2c->hpack.free += 32 + entry->name.len + entry->value.len; + } + + h2c->hpack.size = size; + h2c->hpack.free -= needed; + + return NGX_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3.c b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3.c new file mode 100644 index 000000000..d8597ec5c --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3.c @@ -0,0 +1,111 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +static void ngx_http_v3_keepalive_handler(ngx_event_t *ev); +static void ngx_http_v3_cleanup_session(void *data); + + +ngx_int_t +ngx_http_v3_init_session(ngx_connection_t *c) +{ + ngx_pool_cleanup_t *cln; + ngx_http_connection_t *hc; + ngx_http_v3_session_t *h3c; + + hc = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init session"); + + h3c = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_session_t)); + if (h3c == NULL) { + goto failed; + } + + h3c->http_connection = hc; + + ngx_queue_init(&h3c->blocked); + + h3c->keepalive.log = c->log; + h3c->keepalive.data = c; + h3c->keepalive.handler = ngx_http_v3_keepalive_handler; + + h3c->table.send_insert_count.log = c->log; + h3c->table.send_insert_count.data = c; + h3c->table.send_insert_count.handler = ngx_http_v3_inc_insert_count_handler; + + cln = ngx_pool_cleanup_add(c->pool, 0); + if (cln == NULL) { + goto failed; + } + + cln->handler = ngx_http_v3_cleanup_session; + cln->data = h3c; + + c->data = h3c; + + return NGX_OK; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to create http3 session"); + return NGX_ERROR; +} + + +static void +ngx_http_v3_keepalive_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; + + c = ev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 keepalive handler"); + + ngx_http_v3_shutdown_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, + "keepalive timeout"); +} + + +static void +ngx_http_v3_cleanup_session(void *data) +{ + ngx_http_v3_session_t *h3c = data; + + ngx_http_v3_cleanup_table(h3c); + + if (h3c->keepalive.timer_set) { + ngx_del_timer(&h3c->keepalive); + } + + if (h3c->table.send_insert_count.posted) { + ngx_delete_posted_event(&h3c->table.send_insert_count); + } +} + + +ngx_int_t +ngx_http_v3_check_flood(ngx_connection_t *c) +{ + ngx_http_v3_session_t *h3c; + + h3c = ngx_http_v3_get_session(c); + + if (h3c->total_bytes / 8 > h3c->payload_bytes + 1048576) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "http3 flood detected"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, + "HTTP/3 flood detected"); + return NGX_ERROR; + } + + return NGX_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3.h b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3.h new file mode 100644 index 000000000..8fd212c1f --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3.h @@ -0,0 +1,160 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_HTTP_V3_H_INCLUDED_ +#define _NGX_HTTP_V3_H_INCLUDED_ + + +#include +#include +#include + +#include +#include +#include +#include + + +#define NGX_HTTP_V3_ALPN_PROTO "\x02h3" +#define NGX_HTTP_V3_HQ_ALPN_PROTO "\x0Ahq-interop" +#define NGX_HTTP_V3_HQ_PROTO "hq-interop" + +#define NGX_HTTP_V3_VARLEN_INT_LEN 8 +#define NGX_HTTP_V3_PREFIX_INT_LEN 11 + +#define NGX_HTTP_V3_STREAM_CONTROL 0x00 +#define NGX_HTTP_V3_STREAM_PUSH 0x01 +#define NGX_HTTP_V3_STREAM_ENCODER 0x02 +#define NGX_HTTP_V3_STREAM_DECODER 0x03 + +#define NGX_HTTP_V3_FRAME_DATA 0x00 +#define NGX_HTTP_V3_FRAME_HEADERS 0x01 +#define NGX_HTTP_V3_FRAME_CANCEL_PUSH 0x03 +#define NGX_HTTP_V3_FRAME_SETTINGS 0x04 +#define NGX_HTTP_V3_FRAME_PUSH_PROMISE 0x05 +#define NGX_HTTP_V3_FRAME_GOAWAY 0x07 +#define NGX_HTTP_V3_FRAME_MAX_PUSH_ID 0x0d + +#define NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY 0x01 +#define NGX_HTTP_V3_PARAM_MAX_FIELD_SECTION_SIZE 0x06 +#define NGX_HTTP_V3_PARAM_BLOCKED_STREAMS 0x07 + +#define NGX_HTTP_V3_MAX_TABLE_CAPACITY 4096 + +#define NGX_HTTP_V3_STREAM_CLIENT_CONTROL 0 +#define NGX_HTTP_V3_STREAM_SERVER_CONTROL 1 +#define NGX_HTTP_V3_STREAM_CLIENT_ENCODER 2 +#define NGX_HTTP_V3_STREAM_SERVER_ENCODER 3 +#define NGX_HTTP_V3_STREAM_CLIENT_DECODER 4 +#define NGX_HTTP_V3_STREAM_SERVER_DECODER 5 +#define NGX_HTTP_V3_MAX_KNOWN_STREAM 6 +#define NGX_HTTP_V3_MAX_UNI_STREAMS 3 + +/* HTTP/3 errors */ +#define NGX_HTTP_V3_ERR_NO_ERROR 0x100 +#define NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR 0x101 +#define NGX_HTTP_V3_ERR_INTERNAL_ERROR 0x102 +#define NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR 0x103 +#define NGX_HTTP_V3_ERR_CLOSED_CRITICAL_STREAM 0x104 +#define NGX_HTTP_V3_ERR_FRAME_UNEXPECTED 0x105 +#define NGX_HTTP_V3_ERR_FRAME_ERROR 0x106 +#define NGX_HTTP_V3_ERR_EXCESSIVE_LOAD 0x107 +#define NGX_HTTP_V3_ERR_ID_ERROR 0x108 +#define NGX_HTTP_V3_ERR_SETTINGS_ERROR 0x109 +#define NGX_HTTP_V3_ERR_MISSING_SETTINGS 0x10a +#define NGX_HTTP_V3_ERR_REQUEST_REJECTED 0x10b +#define NGX_HTTP_V3_ERR_REQUEST_CANCELLED 0x10c +#define NGX_HTTP_V3_ERR_REQUEST_INCOMPLETE 0x10d +#define NGX_HTTP_V3_ERR_CONNECT_ERROR 0x10f +#define NGX_HTTP_V3_ERR_VERSION_FALLBACK 0x110 + +/* QPACK errors */ +#define NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED 0x200 +#define NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR 0x201 +#define NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR 0x202 + + +#define ngx_http_v3_get_session(c) \ + ((ngx_http_v3_session_t *) ((c)->quic ? (c)->quic->parent->data \ + : (c)->data)) + +#define ngx_http_quic_get_connection(c) \ + (ngx_http_v3_get_session(c)->http_connection) + +#define ngx_http_v3_get_module_loc_conf(c, module) \ + ngx_http_get_module_loc_conf(ngx_http_quic_get_connection(c)->conf_ctx, \ + module) + +#define ngx_http_v3_get_module_srv_conf(c, module) \ + ngx_http_get_module_srv_conf(ngx_http_quic_get_connection(c)->conf_ctx, \ + module) + +#define ngx_http_v3_finalize_connection(c, code, reason) \ + ngx_quic_finalize_connection((c)->quic ? (c)->quic->parent : (c), \ + code, reason) + +#define ngx_http_v3_shutdown_connection(c, code, reason) \ + ngx_quic_shutdown_connection((c)->quic ? (c)->quic->parent : (c), \ + code, reason) + + +typedef struct { + ngx_flag_t enable; + ngx_flag_t enable_hq; + size_t max_table_capacity; + ngx_uint_t max_blocked_streams; + ngx_uint_t max_concurrent_streams; + ngx_quic_conf_t quic; +} ngx_http_v3_srv_conf_t; + + +struct ngx_http_v3_parse_s { + size_t header_limit; + ngx_http_v3_parse_headers_t headers; + ngx_http_v3_parse_data_t body; + ngx_array_t *cookies; +}; + + +struct ngx_http_v3_session_s { + ngx_http_connection_t *http_connection; + + ngx_http_v3_dynamic_table_t table; + + ngx_event_t keepalive; + ngx_uint_t nrequests; + + ngx_queue_t blocked; + ngx_uint_t nblocked; + + uint64_t next_request_id; + + off_t total_bytes; + off_t payload_bytes; + + unsigned goaway:1; + unsigned hq:1; + + ngx_connection_t *known_streams[NGX_HTTP_V3_MAX_KNOWN_STREAM]; +}; + + +void ngx_http_v3_init_stream(ngx_connection_t *c); +void ngx_http_v3_reset_stream(ngx_connection_t *c); +ngx_int_t ngx_http_v3_init_session(ngx_connection_t *c); +ngx_int_t ngx_http_v3_check_flood(ngx_connection_t *c); +ngx_int_t ngx_http_v3_init(ngx_connection_t *c); +void ngx_http_v3_shutdown(ngx_connection_t *c); + +ngx_int_t ngx_http_v3_read_request_body(ngx_http_request_t *r); +ngx_int_t ngx_http_v3_read_unbuffered_request_body(ngx_http_request_t *r); + + +extern ngx_module_t ngx_http_v3_module; + + +#endif /* _NGX_HTTP_V3_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_encode.c b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_encode.c new file mode 100644 index 000000000..fb089c413 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_encode.c @@ -0,0 +1,304 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +uintptr_t +ngx_http_v3_encode_varlen_int(u_char *p, uint64_t value) +{ + if (value <= 63) { + if (p == NULL) { + return 1; + } + + *p++ = value; + return (uintptr_t) p; + } + + if (value <= 16383) { + if (p == NULL) { + return 2; + } + + *p++ = 0x40 | (value >> 8); + *p++ = value; + return (uintptr_t) p; + } + + if (value <= 1073741823) { + if (p == NULL) { + return 4; + } + + *p++ = 0x80 | (value >> 24); + *p++ = (value >> 16); + *p++ = (value >> 8); + *p++ = value; + return (uintptr_t) p; + } + + if (p == NULL) { + return 8; + } + + *p++ = 0xc0 | (value >> 56); + *p++ = (value >> 48); + *p++ = (value >> 40); + *p++ = (value >> 32); + *p++ = (value >> 24); + *p++ = (value >> 16); + *p++ = (value >> 8); + *p++ = value; + return (uintptr_t) p; +} + + +uintptr_t +ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, ngx_uint_t prefix) +{ + ngx_uint_t thresh, n; + + thresh = (1 << prefix) - 1; + + if (value < thresh) { + if (p == NULL) { + return 1; + } + + *p++ |= value; + return (uintptr_t) p; + } + + value -= thresh; + + if (p == NULL) { + for (n = 2; value >= 128; n++) { + value >>= 7; + } + + return n; + } + + *p++ |= thresh; + + while (value >= 128) { + *p++ = 0x80 | value; + value >>= 7; + } + + *p++ = value; + + return (uintptr_t) p; +} + + +uintptr_t +ngx_http_v3_encode_field_section_prefix(u_char *p, ngx_uint_t insert_count, + ngx_uint_t sign, ngx_uint_t delta_base) +{ + if (p == NULL) { + return ngx_http_v3_encode_prefix_int(NULL, insert_count, 8) + + ngx_http_v3_encode_prefix_int(NULL, delta_base, 7); + } + + *p = 0; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, insert_count, 8); + + *p = sign ? 0x80 : 0; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, delta_base, 7); + + return (uintptr_t) p; +} + + +uintptr_t +ngx_http_v3_encode_field_ri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index) +{ + /* Indexed Field Line */ + + if (p == NULL) { + return ngx_http_v3_encode_prefix_int(NULL, index, 6); + } + + *p = dynamic ? 0x80 : 0xc0; + + return ngx_http_v3_encode_prefix_int(p, index, 6); +} + + +uintptr_t +ngx_http_v3_encode_field_lri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index, + u_char *data, size_t len) +{ + size_t hlen; + u_char *p1, *p2; + + /* Literal Field Line With Name Reference */ + + if (p == NULL) { + return ngx_http_v3_encode_prefix_int(NULL, index, 4) + + ngx_http_v3_encode_prefix_int(NULL, len, 7) + + len; + } + + *p = dynamic ? 0x40 : 0x50; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, index, 4); + + p1 = p; + *p = 0; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, len, 7); + + if (data) { + p2 = p; + hlen = ngx_http_huff_encode(data, len, p, 0); + + if (hlen) { + p = p1; + *p = 0x80; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, hlen, 7); + + if (p != p2) { + ngx_memmove(p, p2, hlen); + } + + p += hlen; + + } else { + p = ngx_cpymem(p, data, len); + } + } + + return (uintptr_t) p; +} + + +uintptr_t +ngx_http_v3_encode_field_l(u_char *p, ngx_str_t *name, ngx_str_t *value) +{ + size_t hlen; + u_char *p1, *p2; + + /* Literal Field Line With Literal Name */ + + if (p == NULL) { + return ngx_http_v3_encode_prefix_int(NULL, name->len, 3) + + name->len + + ngx_http_v3_encode_prefix_int(NULL, value->len, 7) + + value->len; + } + + p1 = p; + *p = 0x20; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, name->len, 3); + + p2 = p; + hlen = ngx_http_huff_encode(name->data, name->len, p, 1); + + if (hlen) { + p = p1; + *p = 0x28; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, hlen, 3); + + if (p != p2) { + ngx_memmove(p, p2, hlen); + } + + p += hlen; + + } else { + ngx_strlow(p, name->data, name->len); + p += name->len; + } + + p1 = p; + *p = 0; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, value->len, 7); + + p2 = p; + hlen = ngx_http_huff_encode(value->data, value->len, p, 0); + + if (hlen) { + p = p1; + *p = 0x80; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, hlen, 7); + + if (p != p2) { + ngx_memmove(p, p2, hlen); + } + + p += hlen; + + } else { + p = ngx_cpymem(p, value->data, value->len); + } + + return (uintptr_t) p; +} + + +uintptr_t +ngx_http_v3_encode_field_pbi(u_char *p, ngx_uint_t index) +{ + /* Indexed Field Line With Post-Base Index */ + + if (p == NULL) { + return ngx_http_v3_encode_prefix_int(NULL, index, 4); + } + + *p = 0x10; + + return ngx_http_v3_encode_prefix_int(p, index, 4); +} + + +uintptr_t +ngx_http_v3_encode_field_lpbi(u_char *p, ngx_uint_t index, u_char *data, + size_t len) +{ + size_t hlen; + u_char *p1, *p2; + + /* Literal Field Line With Post-Base Name Reference */ + + if (p == NULL) { + return ngx_http_v3_encode_prefix_int(NULL, index, 3) + + ngx_http_v3_encode_prefix_int(NULL, len, 7) + + len; + } + + *p = 0; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, index, 3); + + p1 = p; + *p = 0; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, len, 7); + + if (data) { + p2 = p; + hlen = ngx_http_huff_encode(data, len, p, 0); + + if (hlen) { + p = p1; + *p = 0x80; + p = (u_char *) ngx_http_v3_encode_prefix_int(p, hlen, 7); + + if (p != p2) { + ngx_memmove(p, p2, hlen); + } + + p += hlen; + + } else { + p = ngx_cpymem(p, data, len); + } + } + + return (uintptr_t) p; +} diff --git a/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_encode.h b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_encode.h new file mode 100644 index 000000000..fca376da5 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_encode.h @@ -0,0 +1,34 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_HTTP_V3_ENCODE_H_INCLUDED_ +#define _NGX_HTTP_V3_ENCODE_H_INCLUDED_ + + +#include +#include +#include + + +uintptr_t ngx_http_v3_encode_varlen_int(u_char *p, uint64_t value); +uintptr_t ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, + ngx_uint_t prefix); + +uintptr_t ngx_http_v3_encode_field_section_prefix(u_char *p, + ngx_uint_t insert_count, ngx_uint_t sign, ngx_uint_t delta_base); +uintptr_t ngx_http_v3_encode_field_ri(u_char *p, ngx_uint_t dynamic, + ngx_uint_t index); +uintptr_t ngx_http_v3_encode_field_lri(u_char *p, ngx_uint_t dynamic, + ngx_uint_t index, u_char *data, size_t len); +uintptr_t ngx_http_v3_encode_field_l(u_char *p, ngx_str_t *name, + ngx_str_t *value); +uintptr_t ngx_http_v3_encode_field_pbi(u_char *p, ngx_uint_t index); +uintptr_t ngx_http_v3_encode_field_lpbi(u_char *p, ngx_uint_t index, + u_char *data, size_t len); + + +#endif /* _NGX_HTTP_V3_ENCODE_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_filter_module.c b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_filter_module.c new file mode 100644 index 000000000..e3f15368f --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_filter_module.c @@ -0,0 +1,1002 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +/* static table indices */ +#define NGX_HTTP_V3_HEADER_AUTHORITY 0 +#define NGX_HTTP_V3_HEADER_PATH_ROOT 1 +#define NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO 4 +#define NGX_HTTP_V3_HEADER_DATE 6 +#define NGX_HTTP_V3_HEADER_LAST_MODIFIED 10 +#define NGX_HTTP_V3_HEADER_LOCATION 12 +#define NGX_HTTP_V3_HEADER_METHOD_GET 17 +#define NGX_HTTP_V3_HEADER_SCHEME_HTTP 22 +#define NGX_HTTP_V3_HEADER_SCHEME_HTTPS 23 +#define NGX_HTTP_V3_HEADER_STATUS_103 24 +#define NGX_HTTP_V3_HEADER_STATUS_200 25 +#define NGX_HTTP_V3_HEADER_ACCEPT_ENCODING 31 +#define NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN 53 +#define NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING 59 +#define NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE 72 +#define NGX_HTTP_V3_HEADER_SERVER 92 +#define NGX_HTTP_V3_HEADER_USER_AGENT 95 + + +typedef struct { + ngx_chain_t *free; + ngx_chain_t *busy; +} ngx_http_v3_filter_ctx_t; + + +static ngx_int_t ngx_http_v3_header_filter(ngx_http_request_t *r); +static ngx_int_t ngx_http_v3_early_hints_filter(ngx_http_request_t *r); +static ngx_int_t ngx_http_v3_body_filter(ngx_http_request_t *r, + ngx_chain_t *in); +static ngx_chain_t *ngx_http_v3_create_trailers(ngx_http_request_t *r, + ngx_http_v3_filter_ctx_t *ctx); +static ngx_int_t ngx_http_v3_filter_init(ngx_conf_t *cf); + + +static ngx_http_module_t ngx_http_v3_filter_module_ctx = { + NULL, /* preconfiguration */ + ngx_http_v3_filter_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + NULL, /* create location configuration */ + NULL /* merge location configuration */ +}; + + +ngx_module_t ngx_http_v3_filter_module = { + NGX_MODULE_V1, + &ngx_http_v3_filter_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_http_output_header_filter_pt ngx_http_next_header_filter; +static ngx_http_output_header_filter_pt ngx_http_next_early_hints_filter; +static ngx_http_output_body_filter_pt ngx_http_next_body_filter; + + +static ngx_int_t +ngx_http_v3_header_filter(ngx_http_request_t *r) +{ + u_char *p; + size_t len, n; + ngx_buf_t *b; + ngx_str_t host, location; + ngx_uint_t i, port; + ngx_chain_t *out, *hl, *cl, **ll; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_connection_t *c; + ngx_http_v3_session_t *h3c; + ngx_http_v3_filter_ctx_t *ctx; + ngx_http_core_loc_conf_t *clcf; + ngx_http_core_srv_conf_t *cscf; + u_char addr[NGX_SOCKADDR_STRLEN]; + + if (r->http_version != NGX_HTTP_VERSION_30) { + return ngx_http_next_header_filter(r); + } + + if (r->header_sent) { + return NGX_OK; + } + + r->header_sent = 1; + + if (r != r->main) { + return NGX_OK; + } + + h3c = ngx_http_v3_get_session(r->connection); + + if (r->method == NGX_HTTP_HEAD) { + r->header_only = 1; + } + + if (r->headers_out.last_modified_time != -1) { + if (r->headers_out.status != NGX_HTTP_OK + && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT + && r->headers_out.status != NGX_HTTP_NOT_MODIFIED) + { + r->headers_out.last_modified_time = -1; + r->headers_out.last_modified = NULL; + } + } + + if (r->headers_out.status == NGX_HTTP_NO_CONTENT) { + r->header_only = 1; + ngx_str_null(&r->headers_out.content_type); + r->headers_out.last_modified_time = -1; + r->headers_out.last_modified = NULL; + r->headers_out.content_length = NULL; + r->headers_out.content_length_n = -1; + } + + if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) { + r->header_only = 1; + } + + c = r->connection; + + out = NULL; + ll = &out; + + len = ngx_http_v3_encode_field_section_prefix(NULL, 0, 0, 0); + + if (r->headers_out.status == NGX_HTTP_OK) { + len += ngx_http_v3_encode_field_ri(NULL, 0, + NGX_HTTP_V3_HEADER_STATUS_200); + + } else { + len += ngx_http_v3_encode_field_lri(NULL, 0, + NGX_HTTP_V3_HEADER_STATUS_200, + NULL, 3); + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (r->headers_out.server == NULL) { + if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { + n = sizeof(NGINX_VER) - 1; + + } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { + n = sizeof(NGINX_VER_BUILD) - 1; + + } else { + n = sizeof("nginx") - 1; + } + + len += ngx_http_v3_encode_field_lri(NULL, 0, + NGX_HTTP_V3_HEADER_SERVER, + NULL, n); + } + + if (r->headers_out.date == NULL) { + len += ngx_http_v3_encode_field_lri(NULL, 0, NGX_HTTP_V3_HEADER_DATE, + NULL, ngx_cached_http_time.len); + } + + if (r->headers_out.content_type.len) { + n = r->headers_out.content_type.len; + + if (r->headers_out.content_type_len == r->headers_out.content_type.len + && r->headers_out.charset.len) + { + n += sizeof("; charset=") - 1 + r->headers_out.charset.len; + } + + len += ngx_http_v3_encode_field_lri(NULL, 0, + NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN, + NULL, n); + } + + if (r->headers_out.content_length == NULL) { + if (r->headers_out.content_length_n > 0) { + len += ngx_http_v3_encode_field_lri(NULL, 0, + NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO, + NULL, NGX_OFF_T_LEN); + + } else if (r->headers_out.content_length_n == 0) { + len += ngx_http_v3_encode_field_ri(NULL, 0, + NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO); + } + } + + if (r->headers_out.last_modified == NULL + && r->headers_out.last_modified_time != -1) + { + len += ngx_http_v3_encode_field_lri(NULL, 0, + NGX_HTTP_V3_HEADER_LAST_MODIFIED, NULL, + sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1); + } + + if (r->headers_out.location && r->headers_out.location->value.len) { + + if (r->headers_out.location->value.data[0] == '/' + && clcf->absolute_redirect) + { + if (clcf->server_name_in_redirect) { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + host = cscf->server_name; + + } else if (r->headers_in.server.len) { + host = r->headers_in.server; + + } else { + host.len = NGX_SOCKADDR_STRLEN; + host.data = addr; + + if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) { + return NGX_ERROR; + } + } + + port = ngx_inet_get_port(c->local_sockaddr); + + location.len = sizeof("https://") - 1 + host.len + + r->headers_out.location->value.len; + + if (clcf->port_in_redirect) { + port = (port == 443) ? 0 : port; + + } else { + port = 0; + } + + if (port) { + location.len += sizeof(":65535") - 1; + } + + location.data = ngx_pnalloc(r->pool, location.len); + if (location.data == NULL) { + return NGX_ERROR; + } + + p = ngx_cpymem(location.data, "https://", sizeof("https://") - 1); + p = ngx_cpymem(p, host.data, host.len); + + if (port) { + p = ngx_sprintf(p, ":%ui", port); + } + + p = ngx_cpymem(p, r->headers_out.location->value.data, + r->headers_out.location->value.len); + + /* update r->headers_out.location->value for possible logging */ + + r->headers_out.location->value.len = p - location.data; + r->headers_out.location->value.data = location.data; + ngx_str_set(&r->headers_out.location->key, "Location"); + } + + r->headers_out.location->hash = 0; + + len += ngx_http_v3_encode_field_lri(NULL, 0, + NGX_HTTP_V3_HEADER_LOCATION, NULL, + r->headers_out.location->value.len); + } + +#if (NGX_HTTP_GZIP) + if (r->gzip_vary) { + if (clcf->gzip_vary) { + len += ngx_http_v3_encode_field_ri(NULL, 0, + NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING); + + } else { + r->gzip_vary = 0; + } + } +#endif + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + len += ngx_http_v3_encode_field_l(NULL, &header[i].key, + &header[i].value); + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 header len:%uz", len); + + b = ngx_create_temp_buf(r->pool, len); + if (b == NULL) { + return NGX_ERROR; + } + + b->last = (u_char *) ngx_http_v3_encode_field_section_prefix(b->last, + 0, 0, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 output header: \":status: %03ui\"", + r->headers_out.status); + + if (r->headers_out.status == NGX_HTTP_OK) { + b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, + NGX_HTTP_V3_HEADER_STATUS_200); + + } else { + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, + NGX_HTTP_V3_HEADER_STATUS_200, + NULL, 3); + b->last = ngx_sprintf(b->last, "%03ui", r->headers_out.status); + } + + if (r->headers_out.server == NULL) { + if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) { + p = (u_char *) NGINX_VER; + n = sizeof(NGINX_VER) - 1; + + } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) { + p = (u_char *) NGINX_VER_BUILD; + n = sizeof(NGINX_VER_BUILD) - 1; + + } else { + p = (u_char *) "nginx"; + n = sizeof("nginx") - 1; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 output header: \"server: %*s\"", n, p); + + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, + NGX_HTTP_V3_HEADER_SERVER, + p, n); + } + + if (r->headers_out.date == NULL) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 output header: \"date: %V\"", + &ngx_cached_http_time); + + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, + NGX_HTTP_V3_HEADER_DATE, + ngx_cached_http_time.data, + ngx_cached_http_time.len); + } + + if (r->headers_out.content_type.len) { + if (r->headers_out.content_type_len == r->headers_out.content_type.len + && r->headers_out.charset.len) + { + n = r->headers_out.content_type.len + sizeof("; charset=") - 1 + + r->headers_out.charset.len; + + p = ngx_pnalloc(r->pool, n); + if (p == NULL) { + return NGX_ERROR; + } + + p = ngx_cpymem(p, r->headers_out.content_type.data, + r->headers_out.content_type.len); + + p = ngx_cpymem(p, "; charset=", sizeof("; charset=") - 1); + + p = ngx_cpymem(p, r->headers_out.charset.data, + r->headers_out.charset.len); + + /* updated r->headers_out.content_type is also needed for logging */ + + r->headers_out.content_type.len = n; + r->headers_out.content_type.data = p - n; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 output header: \"content-type: %V\"", + &r->headers_out.content_type); + + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, + NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN, + r->headers_out.content_type.data, + r->headers_out.content_type.len); + } + + if (r->headers_out.content_length == NULL + && r->headers_out.content_length_n >= 0) + { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 output header: \"content-length: %O\"", + r->headers_out.content_length_n); + + if (r->headers_out.content_length_n > 0) { + p = ngx_sprintf(b->last, "%O", r->headers_out.content_length_n); + n = p - b->last; + + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, + NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO, + NULL, n); + + b->last = ngx_sprintf(b->last, "%O", + r->headers_out.content_length_n); + + } else { + b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, + NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO); + } + } + + if (r->headers_out.last_modified == NULL + && r->headers_out.last_modified_time != -1) + { + n = sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1; + + p = ngx_pnalloc(r->pool, n); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_http_time(p, r->headers_out.last_modified_time); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 output header: \"last-modified: %*s\"", n, p); + + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, + NGX_HTTP_V3_HEADER_LAST_MODIFIED, + p, n); + } + + if (r->headers_out.location && r->headers_out.location->value.len) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 output header: \"location: %V\"", + &r->headers_out.location->value); + + b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0, + NGX_HTTP_V3_HEADER_LOCATION, + r->headers_out.location->value.data, + r->headers_out.location->value.len); + } + +#if (NGX_HTTP_GZIP) + if (r->gzip_vary) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 output header: \"vary: Accept-Encoding\""); + + b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, + NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING); + } +#endif + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 output header: \"%V: %V\"", + &header[i].key, &header[i].value); + + b->last = (u_char *) ngx_http_v3_encode_field_l(b->last, + &header[i].key, + &header[i].value); + } + + if (r->header_only) { + b->last_buf = 1; + } + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + cl->next = NULL; + + n = b->last - b->pos; + + h3c->payload_bytes += n; + + len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_HEADERS) + + ngx_http_v3_encode_varlen_int(NULL, n); + + b = ngx_create_temp_buf(r->pool, len); + if (b == NULL) { + return NGX_ERROR; + } + + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, + NGX_HTTP_V3_FRAME_HEADERS); + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n); + + hl = ngx_alloc_chain_link(r->pool); + if (hl == NULL) { + return NGX_ERROR; + } + + hl->buf = b; + hl->next = cl; + + *ll = hl; + ll = &cl->next; + + if (r->headers_out.content_length_n >= 0 + && !r->header_only && !r->expect_trailers) + { + len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_DATA) + + ngx_http_v3_encode_varlen_int(NULL, + r->headers_out.content_length_n); + + b = ngx_create_temp_buf(r->pool, len); + if (b == NULL) { + return NGX_ERROR; + } + + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, + NGX_HTTP_V3_FRAME_DATA); + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, + r->headers_out.content_length_n); + + h3c->payload_bytes += r->headers_out.content_length_n; + h3c->total_bytes += r->headers_out.content_length_n; + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + cl->next = NULL; + + *ll = cl; + + } else { + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_v3_filter_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_http_set_ctx(r, ctx, ngx_http_v3_filter_module); + } + + for (cl = out; cl; cl = cl->next) { + h3c->total_bytes += cl->buf->last - cl->buf->pos; + r->header_size += cl->buf->last - cl->buf->pos; + } + + return ngx_http_write_filter(r, out); +} + + +static ngx_int_t +ngx_http_v3_early_hints_filter(ngx_http_request_t *r) +{ + size_t len, n; + ngx_buf_t *b; + ngx_uint_t i; + ngx_chain_t *out, *hl, *cl; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_http_v3_session_t *h3c; + + if (r->http_version != NGX_HTTP_VERSION_30) { + return ngx_http_next_early_hints_filter(r); + } + + if (r != r->main) { + return NGX_OK; + } + + len = 0; + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + len += ngx_http_v3_encode_field_l(NULL, &header[i].key, + &header[i].value); + } + + if (len == 0) { + return NGX_OK; + } + + len += ngx_http_v3_encode_field_section_prefix(NULL, 0, 0, 0); + + len += ngx_http_v3_encode_field_ri(NULL, 0, NGX_HTTP_V3_HEADER_STATUS_103); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 header len:%uz", len); + + b = ngx_create_temp_buf(r->pool, len); + if (b == NULL) { + return NGX_ERROR; + } + + b->last = (u_char *) ngx_http_v3_encode_field_section_prefix(b->last, + 0, 0, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 output header: \":status: %03ui\"", + (ngx_uint_t) NGX_HTTP_EARLY_HINTS); + + b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0, + NGX_HTTP_V3_HEADER_STATUS_103); + + part = &r->headers_out.headers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 output header: \"%V: %V\"", + &header[i].key, &header[i].value); + + b->last = (u_char *) ngx_http_v3_encode_field_l(b->last, + &header[i].key, + &header[i].value); + } + + b->flush = 1; + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = b; + cl->next = NULL; + + n = b->last - b->pos; + + h3c = ngx_http_v3_get_session(r->connection); + h3c->payload_bytes += n; + + len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_HEADERS) + + ngx_http_v3_encode_varlen_int(NULL, n); + + b = ngx_create_temp_buf(r->pool, len); + if (b == NULL) { + return NGX_ERROR; + } + + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, + NGX_HTTP_V3_FRAME_HEADERS); + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n); + + hl = ngx_alloc_chain_link(r->pool); + if (hl == NULL) { + return NGX_ERROR; + } + + hl->buf = b; + hl->next = cl; + + out = hl; + + for (cl = out; cl; cl = cl->next) { + h3c->total_bytes += cl->buf->last - cl->buf->pos; + r->header_size += cl->buf->last - cl->buf->pos; + } + + return ngx_http_write_filter(r, out); +} + + +static ngx_int_t +ngx_http_v3_body_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + u_char *chunk; + off_t size; + ngx_int_t rc; + ngx_buf_t *b; + ngx_chain_t *out, *cl, *tl, **ll; + ngx_http_v3_session_t *h3c; + ngx_http_v3_filter_ctx_t *ctx; + + if (in == NULL) { + return ngx_http_next_body_filter(r, in); + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_v3_filter_module); + if (ctx == NULL) { + return ngx_http_next_body_filter(r, in); + } + + h3c = ngx_http_v3_get_session(r->connection); + + out = NULL; + ll = &out; + + size = 0; + cl = in; + + for ( ;; ) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 chunk: %O", ngx_buf_size(cl->buf)); + + size += ngx_buf_size(cl->buf); + + if (cl->buf->flush + || cl->buf->sync + || ngx_buf_in_memory(cl->buf) + || cl->buf->in_file) + { + tl = ngx_alloc_chain_link(r->pool); + if (tl == NULL) { + return NGX_ERROR; + } + + tl->buf = cl->buf; + *ll = tl; + ll = &tl->next; + } + + if (cl->next == NULL) { + break; + } + + cl = cl->next; + } + + if (size) { + tl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (tl == NULL) { + return NGX_ERROR; + } + + b = tl->buf; + chunk = b->start; + + if (chunk == NULL) { + chunk = ngx_palloc(r->pool, NGX_HTTP_V3_VARLEN_INT_LEN * 2); + if (chunk == NULL) { + return NGX_ERROR; + } + + b->start = chunk; + b->end = chunk + NGX_HTTP_V3_VARLEN_INT_LEN * 2; + } + + b->tag = (ngx_buf_tag_t) &ngx_http_v3_filter_module; + b->memory = 0; + b->temporary = 1; + b->pos = chunk; + + b->last = (u_char *) ngx_http_v3_encode_varlen_int(chunk, + NGX_HTTP_V3_FRAME_DATA); + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, size); + + tl->next = out; + out = tl; + + h3c->payload_bytes += size; + } + + if (cl->buf->last_buf) { + tl = ngx_http_v3_create_trailers(r, ctx); + if (tl == NULL) { + return NGX_ERROR; + } + + cl->buf->last_buf = 0; + + *ll = tl; + + } else { + *ll = NULL; + } + + for (cl = out; cl; cl = cl->next) { + h3c->total_bytes += cl->buf->last - cl->buf->pos; + } + + rc = ngx_http_next_body_filter(r, out); + + ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out, + (ngx_buf_tag_t) &ngx_http_v3_filter_module); + + return rc; +} + + +static ngx_chain_t * +ngx_http_v3_create_trailers(ngx_http_request_t *r, + ngx_http_v3_filter_ctx_t *ctx) +{ + size_t len, n; + u_char *p; + ngx_buf_t *b; + ngx_uint_t i; + ngx_chain_t *cl, *hl; + ngx_list_part_t *part; + ngx_table_elt_t *header; + ngx_http_v3_session_t *h3c; + + h3c = ngx_http_v3_get_session(r->connection); + + len = 0; + + part = &r->headers_out.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + len += ngx_http_v3_encode_field_l(NULL, &header[i].key, + &header[i].value); + } + + cl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (cl == NULL) { + return NULL; + } + + b = cl->buf; + + b->tag = (ngx_buf_tag_t) &ngx_http_v3_filter_module; + b->memory = 0; + b->last_buf = 1; + + if (len == 0) { + b->temporary = 0; + b->pos = b->last = NULL; + return cl; + } + + b->temporary = 1; + + len += ngx_http_v3_encode_field_section_prefix(NULL, 0, 0, 0); + + b->pos = ngx_palloc(r->pool, len); + if (b->pos == NULL) { + return NULL; + } + + b->last = (u_char *) ngx_http_v3_encode_field_section_prefix(b->pos, + 0, 0, 0); + + part = &r->headers_out.trailers.part; + header = part->elts; + + for (i = 0; /* void */; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + header = part->elts; + i = 0; + } + + if (header[i].hash == 0) { + continue; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 output trailer: \"%V: %V\"", + &header[i].key, &header[i].value); + + b->last = (u_char *) ngx_http_v3_encode_field_l(b->last, + &header[i].key, + &header[i].value); + } + + n = b->last - b->pos; + + h3c->payload_bytes += n; + + hl = ngx_chain_get_free_buf(r->pool, &ctx->free); + if (hl == NULL) { + return NULL; + } + + b = hl->buf; + p = b->start; + + if (p == NULL) { + p = ngx_palloc(r->pool, NGX_HTTP_V3_VARLEN_INT_LEN * 2); + if (p == NULL) { + return NULL; + } + + b->start = p; + b->end = p + NGX_HTTP_V3_VARLEN_INT_LEN * 2; + } + + b->tag = (ngx_buf_tag_t) &ngx_http_v3_filter_module; + b->memory = 0; + b->temporary = 1; + b->pos = p; + + b->last = (u_char *) ngx_http_v3_encode_varlen_int(p, + NGX_HTTP_V3_FRAME_HEADERS); + b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n); + + hl->next = cl; + + return hl; +} + + +static ngx_int_t +ngx_http_v3_filter_init(ngx_conf_t *cf) +{ + ngx_http_next_header_filter = ngx_http_top_header_filter; + ngx_http_top_header_filter = ngx_http_v3_header_filter; + + ngx_http_next_early_hints_filter = ngx_http_top_early_hints_filter; + ngx_http_top_early_hints_filter = ngx_http_v3_early_hints_filter; + + ngx_http_next_body_filter = ngx_http_top_body_filter; + ngx_http_top_body_filter = ngx_http_v3_body_filter; + + return NGX_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_module.c b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_module.c new file mode 100644 index 000000000..139bd65f3 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_module.c @@ -0,0 +1,393 @@ + +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Roman Arutyunyan + */ + + +#include +#include +#include + + +static ngx_int_t ngx_http_v3_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_v3_add_variables(ngx_conf_t *cf); +static void *ngx_http_v3_create_srv_conf(ngx_conf_t *cf); +static char *ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); +static char *ngx_http_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + + +static ngx_command_t ngx_http_v3_commands[] = { + + { ngx_string("http3"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, enable), + NULL }, + + { ngx_string("http3_hq"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, enable_hq), + NULL }, + + { ngx_string("http3_max_concurrent_streams"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, max_concurrent_streams), + NULL }, + + { ngx_string("http3_stream_buffer_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, quic.stream_buffer_size), + NULL }, + + { ngx_string("quic_retry"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, quic.retry), + NULL }, + + { ngx_string("quic_gso"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, quic.gso_enabled), + NULL }, + + { ngx_string("quic_host_key"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_http_quic_host_key, + NGX_HTTP_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("quic_active_connection_id_limit"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_v3_srv_conf_t, quic.active_connection_id_limit), + NULL }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_v3_module_ctx = { + ngx_http_v3_add_variables, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_http_v3_create_srv_conf, /* create server configuration */ + ngx_http_v3_merge_srv_conf, /* merge server configuration */ + + NULL, /* create location configuration */ + NULL /* merge location configuration */ +}; + + +ngx_module_t ngx_http_v3_module = { + NGX_MODULE_V1, + &ngx_http_v3_module_ctx, /* module context */ + ngx_http_v3_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_http_variable_t ngx_http_v3_vars[] = { + + { ngx_string("http3"), NULL, ngx_http_v3_variable, 0, 0, 0 }, + + ngx_http_null_variable +}; + +static ngx_str_t ngx_http_quic_salt = ngx_string("ngx_quic"); + + +static ngx_int_t +ngx_http_v3_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + ngx_http_v3_session_t *h3c; + + if (r->connection->quic) { + h3c = ngx_http_v3_get_session(r->connection); + + if (h3c->hq) { + v->len = sizeof("hq") - 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) "hq"; + + return NGX_OK; + } + + v->len = sizeof("h3") - 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) "h3"; + + return NGX_OK; + } + + *v = ngx_http_variable_null_value; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_add_variables(ngx_conf_t *cf) +{ + ngx_http_variable_t *var, *v; + + for (v = ngx_http_v3_vars; v->name.len; v++) { + var = ngx_http_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + +static void * +ngx_http_v3_create_srv_conf(ngx_conf_t *cf) +{ + ngx_http_v3_srv_conf_t *h3scf; + + h3scf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v3_srv_conf_t)); + if (h3scf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * h3scf->quic.host_key = { 0, NULL } + * h3scf->quic.stream_reject_code_uni = 0; + * h3scf->quic.disable_active_migration = 0; + * h3scf->quic.idle_timeout = 0; + * h3scf->max_blocked_streams = 0; + */ + + h3scf->enable = NGX_CONF_UNSET; + h3scf->enable_hq = NGX_CONF_UNSET; + h3scf->max_table_capacity = NGX_HTTP_V3_MAX_TABLE_CAPACITY; + h3scf->max_concurrent_streams = NGX_CONF_UNSET_UINT; + + h3scf->quic.stream_buffer_size = NGX_CONF_UNSET_SIZE; + h3scf->quic.max_concurrent_streams_bidi = NGX_CONF_UNSET_UINT; + h3scf->quic.max_concurrent_streams_uni = NGX_HTTP_V3_MAX_UNI_STREAMS; + h3scf->quic.retry = NGX_CONF_UNSET; + h3scf->quic.gso_enabled = NGX_CONF_UNSET; + h3scf->quic.stream_close_code = NGX_HTTP_V3_ERR_NO_ERROR; + h3scf->quic.stream_reject_code_bidi = NGX_HTTP_V3_ERR_REQUEST_REJECTED; + h3scf->quic.active_connection_id_limit = NGX_CONF_UNSET_UINT; + + h3scf->quic.init = ngx_http_v3_init; + h3scf->quic.shutdown = ngx_http_v3_shutdown; + + return h3scf; +} + + +static char * +ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_http_v3_srv_conf_t *prev = parent; + ngx_http_v3_srv_conf_t *conf = child; + + ngx_http_ssl_srv_conf_t *sscf; + ngx_http_core_srv_conf_t *cscf; + + ngx_conf_merge_value(conf->enable, prev->enable, 1); + + ngx_conf_merge_value(conf->enable_hq, prev->enable_hq, 0); + + ngx_conf_merge_uint_value(conf->max_concurrent_streams, + prev->max_concurrent_streams, 128); + + conf->max_blocked_streams = conf->max_concurrent_streams; + + ngx_conf_merge_size_value(conf->quic.stream_buffer_size, + prev->quic.stream_buffer_size, + 65536); + + conf->quic.max_concurrent_streams_bidi = conf->max_concurrent_streams; + + ngx_conf_merge_value(conf->quic.retry, prev->quic.retry, 0); + ngx_conf_merge_value(conf->quic.gso_enabled, prev->quic.gso_enabled, 0); + + ngx_conf_merge_str_value(conf->quic.host_key, prev->quic.host_key, ""); + + ngx_conf_merge_uint_value(conf->quic.active_connection_id_limit, + prev->quic.active_connection_id_limit, + 2); + + if (conf->quic.host_key.len == 0) { + + conf->quic.host_key.len = NGX_QUIC_DEFAULT_HOST_KEY_LEN; + conf->quic.host_key.data = ngx_palloc(cf->pool, + conf->quic.host_key.len); + if (conf->quic.host_key.data == NULL) { + return NGX_CONF_ERROR; + } + + if (RAND_bytes(conf->quic.host_key.data, NGX_QUIC_DEFAULT_HOST_KEY_LEN) + <= 0) + { + return NGX_CONF_ERROR; + } + } + + if (ngx_quic_derive_key(cf->log, "av_token_key", + &conf->quic.host_key, &ngx_http_quic_salt, + conf->quic.av_token_key, NGX_QUIC_AV_KEY_LEN) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + if (ngx_quic_derive_key(cf->log, "sr_token_key", + &conf->quic.host_key, &ngx_http_quic_salt, + conf->quic.sr_token_key, NGX_QUIC_SR_KEY_LEN) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module); + conf->quic.handshake_timeout = cscf->client_header_timeout; + + sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module); + conf->quic.ssl = &sscf->ssl; + + return NGX_CONF_OK; +} + + +static char * +ngx_http_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_v3_srv_conf_t *h3scf = conf; + + u_char *buf; + size_t size; + ssize_t n; + ngx_str_t *value; + ngx_file_t file; + ngx_file_info_t fi; + ngx_quic_conf_t *qcf; + + qcf = &h3scf->quic; + + if (qcf->host_key.len) { + return "is duplicate"; + } + + buf = NULL; +#if (NGX_SUPPRESS_WARN) + size = 0; +#endif + + value = cf->args->elts; + + if (ngx_conf_full_name(cf->cycle, &value[1], 1) != NGX_OK) { + return NGX_CONF_ERROR; + } + + ngx_memzero(&file, sizeof(ngx_file_t)); + file.name = value[1]; + file.log = cf->log; + + file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); + + if (file.fd == NGX_INVALID_FILE) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, + ngx_open_file_n " \"%V\" failed", &file.name); + return NGX_CONF_ERROR; + } + + if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, + ngx_fd_info_n " \"%V\" failed", &file.name); + goto failed; + } + + size = ngx_file_size(&fi); + + if (size == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%V\" zero key size", &file.name); + goto failed; + } + + buf = ngx_pnalloc(cf->pool, size); + if (buf == NULL) { + goto failed; + } + + n = ngx_read_file(&file, buf, size, 0); + + if (n == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, + ngx_read_file_n " \"%V\" failed", &file.name); + goto failed; + } + + if ((size_t) n != size) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, 0, + ngx_read_file_n " \"%V\" returned only " + "%z bytes instead of %uz", &file.name, n, size); + goto failed; + } + + qcf->host_key.data = buf; + qcf->host_key.len = n; + + if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_close_file_n " \"%V\" failed", &file.name); + } + + return NGX_CONF_OK; + +failed: + + if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_close_file_n " \"%V\" failed", &file.name); + } + + if (buf) { + ngx_explicit_memzero(buf, size); + } + + return NGX_CONF_ERROR; +} diff --git a/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_parse.c b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_parse.c new file mode 100644 index 000000000..bcbf0dbe1 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_parse.c @@ -0,0 +1,1942 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +#define ngx_http_v3_is_v2_frame(type) \ + ((type) == 0x02 || (type) == 0x06 || (type) == 0x08 || (type) == 0x09) + + +static void ngx_http_v3_parse_start_local(ngx_buf_t *b, ngx_buf_t *loc, + ngx_uint_t n); +static void ngx_http_v3_parse_end_local(ngx_buf_t *b, ngx_buf_t *loc, + ngx_uint_t *n); +static ngx_int_t ngx_http_v3_parse_skip(ngx_buf_t *b, ngx_uint_t *length); + +static ngx_int_t ngx_http_v3_parse_varlen_int(ngx_connection_t *c, + ngx_http_v3_parse_varlen_int_t *st, ngx_buf_t *b); +static ngx_int_t ngx_http_v3_parse_prefix_int(ngx_connection_t *c, + ngx_http_v3_parse_prefix_int_t *st, ngx_uint_t prefix, ngx_buf_t *b); + +static ngx_int_t ngx_http_v3_parse_field_section_prefix(ngx_connection_t *c, + ngx_http_v3_parse_field_section_prefix_t *st, ngx_buf_t *b); +static ngx_int_t ngx_http_v3_parse_field_rep(ngx_connection_t *c, + ngx_http_v3_parse_field_rep_t *st, ngx_uint_t base, ngx_buf_t *b); +static ngx_int_t ngx_http_v3_parse_literal(ngx_connection_t *c, + ngx_http_v3_parse_literal_t *st, ngx_buf_t *b); +static ngx_int_t ngx_http_v3_parse_field_ri(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, ngx_buf_t *b); +static ngx_int_t ngx_http_v3_parse_field_lri(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, ngx_buf_t *b); +static ngx_int_t ngx_http_v3_parse_field_l(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, ngx_buf_t *b); +static ngx_int_t ngx_http_v3_parse_field_pbi(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, ngx_buf_t *b); +static ngx_int_t ngx_http_v3_parse_field_lpbi(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, ngx_buf_t *b); + +static ngx_int_t ngx_http_v3_parse_control(ngx_connection_t *c, + ngx_http_v3_parse_control_t *st, ngx_buf_t *b); +static ngx_int_t ngx_http_v3_parse_settings(ngx_connection_t *c, + ngx_http_v3_parse_settings_t *st, ngx_buf_t *b); + +static ngx_int_t ngx_http_v3_parse_encoder(ngx_connection_t *c, + ngx_http_v3_parse_encoder_t *st, ngx_buf_t *b); +static ngx_int_t ngx_http_v3_parse_field_inr(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, ngx_buf_t *b); +static ngx_int_t ngx_http_v3_parse_field_iln(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, ngx_buf_t *b); + +static ngx_int_t ngx_http_v3_parse_decoder(ngx_connection_t *c, + ngx_http_v3_parse_decoder_t *st, ngx_buf_t *b); + +static ngx_int_t ngx_http_v3_parse_lookup(ngx_connection_t *c, + ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *name, ngx_str_t *value); + + +static void +ngx_http_v3_parse_start_local(ngx_buf_t *b, ngx_buf_t *loc, ngx_uint_t n) +{ + *loc = *b; + + if ((size_t) (loc->last - loc->pos) > n) { + loc->last = loc->pos + n; + } +} + + +static void +ngx_http_v3_parse_end_local(ngx_buf_t *b, ngx_buf_t *loc, ngx_uint_t *pn) +{ + *pn -= loc->pos - b->pos; + b->pos = loc->pos; +} + + +static ngx_int_t +ngx_http_v3_parse_skip(ngx_buf_t *b, ngx_uint_t *length) +{ + if ((size_t) (b->last - b->pos) < *length) { + *length -= b->last - b->pos; + b->pos = b->last; + return NGX_AGAIN; + } + + b->pos += *length; + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_v3_parse_varlen_int(ngx_connection_t *c, + ngx_http_v3_parse_varlen_int_t *st, ngx_buf_t *b) +{ + u_char ch; + enum { + sw_start = 0, + sw_length_2, + sw_length_3, + sw_length_4, + sw_length_5, + sw_length_6, + sw_length_7, + sw_length_8 + }; + + for ( ;; ) { + + if (b->pos == b->last) { + return NGX_AGAIN; + } + + ch = *b->pos++; + + switch (st->state) { + + case sw_start: + + st->value = ch; + if (st->value & 0xc0) { + st->state = sw_length_2; + break; + } + + goto done; + + case sw_length_2: + + st->value = (st->value << 8) + ch; + if ((st->value & 0xc000) == 0x4000) { + st->value &= 0x3fff; + goto done; + } + + st->state = sw_length_3; + break; + + case sw_length_4: + + st->value = (st->value << 8) + ch; + if ((st->value & 0xc0000000) == 0x80000000) { + st->value &= 0x3fffffff; + goto done; + } + + st->state = sw_length_5; + break; + + case sw_length_3: + case sw_length_5: + case sw_length_6: + case sw_length_7: + + st->value = (st->value << 8) + ch; + st->state++; + break; + + case sw_length_8: + + st->value = (st->value << 8) + ch; + st->value &= 0x3fffffffffffffff; + goto done; + } + } + +done: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse varlen int %uL", st->value); + + st->state = sw_start; + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_v3_parse_prefix_int(ngx_connection_t *c, + ngx_http_v3_parse_prefix_int_t *st, ngx_uint_t prefix, ngx_buf_t *b) +{ + u_char ch; + ngx_uint_t mask; + enum { + sw_start = 0, + sw_value + }; + + for ( ;; ) { + + if (b->pos == b->last) { + return NGX_AGAIN; + } + + ch = *b->pos++; + + switch (st->state) { + + case sw_start: + + mask = (1 << prefix) - 1; + st->value = ch & mask; + + if (st->value != mask) { + goto done; + } + + st->shift = 0; + st->state = sw_value; + break; + + case sw_value: + + st->value += (uint64_t) (ch & 0x7f) << st->shift; + + if (st->shift == 56 + && ((ch & 0x80) || (st->value & 0xc000000000000000))) + { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client exceeded integer size limit"); + return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; + } + + if (ch & 0x80) { + st->shift += 7; + break; + } + + goto done; + } + } + +done: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse prefix int %uL", st->value); + + st->state = sw_start; + return NGX_DONE; +} + + +ngx_int_t +ngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st, + ngx_buf_t *b) +{ + ngx_buf_t loc; + ngx_int_t rc; + enum { + sw_start = 0, + sw_type, + sw_length, + sw_skip, + sw_prefix, + sw_verify, + sw_field_rep, + sw_done + }; + + for ( ;; ) { + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse headers"); + + st->state = sw_type; + + /* fall through */ + + case sw_type: + + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b); + if (rc != NGX_DONE) { + return rc; + } + + st->type = st->vlint.value; + + if (ngx_http_v3_is_v2_frame(st->type) + || st->type == NGX_HTTP_V3_FRAME_DATA + || st->type == NGX_HTTP_V3_FRAME_GOAWAY + || st->type == NGX_HTTP_V3_FRAME_SETTINGS + || st->type == NGX_HTTP_V3_FRAME_MAX_PUSH_ID + || st->type == NGX_HTTP_V3_FRAME_CANCEL_PUSH + || st->type == NGX_HTTP_V3_FRAME_PUSH_PROMISE) + { + return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; + } + + st->state = sw_length; + break; + + case sw_length: + + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b); + if (rc != NGX_DONE) { + return rc; + } + + st->length = st->vlint.value; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse headers type:%ui, len:%ui", + st->type, st->length); + + if (st->type != NGX_HTTP_V3_FRAME_HEADERS) { + st->state = st->length > 0 ? sw_skip : sw_type; + break; + } + + if (st->length == 0) { + return NGX_HTTP_V3_ERR_FRAME_ERROR; + } + + st->state = sw_prefix; + break; + + case sw_skip: + + rc = ngx_http_v3_parse_skip(b, &st->length); + if (rc != NGX_DONE) { + return rc; + } + + st->state = sw_type; + break; + + case sw_prefix: + + ngx_http_v3_parse_start_local(b, &loc, st->length); + + rc = ngx_http_v3_parse_field_section_prefix(c, &st->prefix, &loc); + + ngx_http_v3_parse_end_local(b, &loc, &st->length); + + if (st->length == 0 && rc == NGX_AGAIN) { + return NGX_HTTP_V3_ERR_FRAME_ERROR; + } + + if (rc != NGX_DONE) { + return rc; + } + + st->state = sw_verify; + break; + + case sw_verify: + + rc = ngx_http_v3_check_insert_count(c, st->prefix.insert_count); + if (rc != NGX_OK) { + return rc; + } + + st->state = sw_field_rep; + + /* fall through */ + + case sw_field_rep: + + ngx_http_v3_parse_start_local(b, &loc, st->length); + + rc = ngx_http_v3_parse_field_rep(c, &st->field_rep, st->prefix.base, + &loc); + + ngx_http_v3_parse_end_local(b, &loc, &st->length); + + if (st->length == 0 && rc == NGX_AGAIN) { + return NGX_HTTP_V3_ERR_FRAME_ERROR; + } + + if (rc != NGX_DONE) { + return rc; + } + + if (st->length == 0) { + goto done; + } + + return NGX_OK; + } + } + +done: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse headers done"); + + if (st->prefix.insert_count > 0) { + if (ngx_http_v3_send_ack_section(c, c->quic->id) != NGX_OK) { + return NGX_ERROR; + } + + ngx_http_v3_ack_insert_count(c, st->prefix.insert_count); + } + + st->state = sw_start; + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_v3_parse_field_section_prefix(ngx_connection_t *c, + ngx_http_v3_parse_field_section_prefix_t *st, ngx_buf_t *b) +{ + u_char ch; + ngx_int_t rc; + enum { + sw_start = 0, + sw_req_insert_count, + sw_delta_base, + sw_read_delta_base + }; + + for ( ;; ) { + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field section prefix"); + + st->state = sw_req_insert_count; + + /* fall through */ + + case sw_req_insert_count: + + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 8, b); + if (rc != NGX_DONE) { + return rc; + } + + st->insert_count = st->pint.value; + st->state = sw_delta_base; + break; + + case sw_delta_base: + + if (b->pos == b->last) { + return NGX_AGAIN; + } + + ch = *b->pos; + + st->sign = (ch & 0x80) ? 1 : 0; + st->state = sw_read_delta_base; + + /* fall through */ + + case sw_read_delta_base: + + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b); + if (rc != NGX_DONE) { + return rc; + } + + st->delta_base = st->pint.value; + goto done; + } + } + +done: + + rc = ngx_http_v3_decode_insert_count(c, &st->insert_count); + if (rc != NGX_OK) { + return rc; + } + + if (st->sign) { + if (st->insert_count <= st->delta_base) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent negative base"); + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; + } + + st->base = st->insert_count - st->delta_base - 1; + + } else { + st->base = st->insert_count + st->delta_base; + } + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field section prefix done " + "insert_count:%ui, sign:%ui, delta_base:%ui, base:%ui", + st->insert_count, st->sign, st->delta_base, st->base); + + st->state = sw_start; + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_v3_parse_field_rep(ngx_connection_t *c, + ngx_http_v3_parse_field_rep_t *st, ngx_uint_t base, ngx_buf_t *b) +{ + u_char ch; + ngx_int_t rc; + enum { + sw_start = 0, + sw_field_ri, + sw_field_lri, + sw_field_l, + sw_field_pbi, + sw_field_lpbi + }; + + if (st->state == sw_start) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field representation"); + + if (b->pos == b->last) { + return NGX_AGAIN; + } + + ch = *b->pos; + + ngx_memzero(&st->field, sizeof(ngx_http_v3_parse_field_t)); + + st->field.base = base; + + if (ch & 0x80) { + /* Indexed Field Line */ + + st->state = sw_field_ri; + + } else if (ch & 0x40) { + /* Literal Field Line With Name Reference */ + + st->state = sw_field_lri; + + } else if (ch & 0x20) { + /* Literal Field Line With Literal Name */ + + st->state = sw_field_l; + + } else if (ch & 0x10) { + /* Indexed Field Line With Post-Base Index */ + + st->state = sw_field_pbi; + + } else { + /* Literal Field Line With Post-Base Name Reference */ + + st->state = sw_field_lpbi; + } + } + + switch (st->state) { + + case sw_field_ri: + rc = ngx_http_v3_parse_field_ri(c, &st->field, b); + break; + + case sw_field_lri: + rc = ngx_http_v3_parse_field_lri(c, &st->field, b); + break; + + case sw_field_l: + rc = ngx_http_v3_parse_field_l(c, &st->field, b); + break; + + case sw_field_pbi: + rc = ngx_http_v3_parse_field_pbi(c, &st->field, b); + break; + + case sw_field_lpbi: + rc = ngx_http_v3_parse_field_lpbi(c, &st->field, b); + break; + + default: + rc = NGX_OK; + } + + if (rc != NGX_DONE) { + return rc; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field representation done"); + + st->state = sw_start; + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st, + ngx_buf_t *b) +{ + u_char ch; + ngx_uint_t n; + ngx_http_core_srv_conf_t *cscf; + enum { + sw_start = 0, + sw_value + }; + + for ( ;; ) { + + switch (st->state) { + + case sw_start: + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse literal huff:%ui, len:%ui", + st->huffman, st->length); + + n = st->length; + + cscf = ngx_http_v3_get_module_srv_conf(c, ngx_http_core_module); + + if (n > cscf->large_client_header_buffers.size) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent too large field line"); + return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; + } + + if (st->huffman) { + if (n > NGX_MAX_INT_T_VALUE / 8) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent too large field line"); + return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD; + } + + n = n * 8 / 5; + st->huffstate = 0; + } + + st->last = ngx_pnalloc(c->pool, n + 1); + if (st->last == NULL) { + return NGX_ERROR; + } + + st->value.data = st->last; + st->state = sw_value; + + /* fall through */ + + case sw_value: + + if (b->pos == b->last) { + return NGX_AGAIN; + } + + ch = *b->pos++; + + if (st->huffman) { + if (ngx_http_huff_decode(&st->huffstate, &ch, 1, &st->last, + st->length == 1, c->log) + != NGX_OK) + { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid encoded field line"); + return NGX_ERROR; + } + + } else { + *st->last++ = ch; + } + + if (--st->length) { + break; + } + + st->value.len = st->last - st->value.data; + *st->last = '\0'; + goto done; + } + } + +done: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse literal done \"%V\"", &st->value); + + st->state = sw_start; + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_v3_parse_field_ri(ngx_connection_t *c, ngx_http_v3_parse_field_t *st, + ngx_buf_t *b) +{ + u_char ch; + ngx_int_t rc; + enum { + sw_start = 0, + sw_index + }; + + for ( ;; ) { + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field ri"); + + if (b->pos == b->last) { + return NGX_AGAIN; + } + + ch = *b->pos; + + st->dynamic = (ch & 0x40) ? 0 : 1; + st->state = sw_index; + + /* fall through */ + + case sw_index: + + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, b); + if (rc != NGX_DONE) { + return rc; + } + + st->index = st->pint.value; + goto done; + } + } + +done: + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field ri done %s%ui]", + st->dynamic ? "dynamic[-" : "static[", st->index); + + if (st->dynamic) { + st->index = st->base - st->index - 1; + } + + rc = ngx_http_v3_parse_lookup(c, st->dynamic, st->index, &st->name, + &st->value); + if (rc != NGX_OK) { + return rc; + } + + st->state = sw_start; + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_v3_parse_field_lri(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, ngx_buf_t *b) +{ + u_char ch; + ngx_int_t rc; + enum { + sw_start = 0, + sw_index, + sw_value_len, + sw_read_value_len, + sw_value + }; + + for ( ;; ) { + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field lri"); + + if (b->pos == b->last) { + return NGX_AGAIN; + } + + ch = *b->pos; + + st->dynamic = (ch & 0x10) ? 0 : 1; + st->state = sw_index; + + /* fall through */ + + case sw_index: + + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 4, b); + if (rc != NGX_DONE) { + return rc; + } + + st->index = st->pint.value; + st->state = sw_value_len; + break; + + case sw_value_len: + + if (b->pos == b->last) { + return NGX_AGAIN; + } + + ch = *b->pos; + + st->literal.huffman = (ch & 0x80) ? 1 : 0; + st->state = sw_read_value_len; + + /* fall through */ + + case sw_read_value_len: + + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b); + if (rc != NGX_DONE) { + return rc; + } + + st->literal.length = st->pint.value; + if (st->literal.length == 0) { + st->value.data = (u_char *) ""; + goto done; + } + + st->state = sw_value; + break; + + case sw_value: + + rc = ngx_http_v3_parse_literal(c, &st->literal, b); + if (rc != NGX_DONE) { + return rc; + } + + st->value = st->literal.value; + goto done; + } + } + +done: + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field lri done %s%ui] \"%V\"", + st->dynamic ? "dynamic[-" : "static[", + st->index, &st->value); + + if (st->dynamic) { + st->index = st->base - st->index - 1; + } + + rc = ngx_http_v3_parse_lookup(c, st->dynamic, st->index, &st->name, NULL); + if (rc != NGX_OK) { + return rc; + } + + st->state = sw_start; + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_v3_parse_field_l(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, ngx_buf_t *b) +{ + u_char ch; + ngx_int_t rc; + enum { + sw_start = 0, + sw_name_len, + sw_name, + sw_value_len, + sw_read_value_len, + sw_value + }; + + for ( ;; ) { + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field l"); + + if (b->pos == b->last) { + return NGX_AGAIN; + } + + ch = *b->pos; + + st->literal.huffman = (ch & 0x08) ? 1 : 0; + st->state = sw_name_len; + + /* fall through */ + + case sw_name_len: + + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 3, b); + if (rc != NGX_DONE) { + return rc; + } + + st->literal.length = st->pint.value; + if (st->literal.length == 0) { + return NGX_ERROR; + } + + st->state = sw_name; + break; + + case sw_name: + + rc = ngx_http_v3_parse_literal(c, &st->literal, b); + if (rc != NGX_DONE) { + return rc; + } + + st->name = st->literal.value; + st->state = sw_value_len; + break; + + case sw_value_len: + + if (b->pos == b->last) { + return NGX_AGAIN; + } + + ch = *b->pos; + + st->literal.huffman = (ch & 0x80) ? 1 : 0; + st->state = sw_read_value_len; + + /* fall through */ + + case sw_read_value_len: + + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b); + if (rc != NGX_DONE) { + return rc; + } + + st->literal.length = st->pint.value; + if (st->literal.length == 0) { + st->value.data = (u_char *) ""; + goto done; + } + + st->state = sw_value; + break; + + case sw_value: + + rc = ngx_http_v3_parse_literal(c, &st->literal, b); + if (rc != NGX_DONE) { + return rc; + } + + st->value = st->literal.value; + goto done; + } + } + +done: + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field l done \"%V\" \"%V\"", + &st->name, &st->value); + + st->state = sw_start; + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_v3_parse_field_pbi(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, ngx_buf_t *b) +{ + ngx_int_t rc; + enum { + sw_start = 0, + sw_index + }; + + for ( ;; ) { + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field pbi"); + + st->state = sw_index; + + /* fall through */ + + case sw_index: + + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 4, b); + if (rc != NGX_DONE) { + return rc; + } + + st->index = st->pint.value; + goto done; + } + } + +done: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field pbi done dynamic[+%ui]", st->index); + + rc = ngx_http_v3_parse_lookup(c, 1, st->base + st->index, &st->name, + &st->value); + if (rc != NGX_OK) { + return rc; + } + + st->state = sw_start; + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_v3_parse_field_lpbi(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, ngx_buf_t *b) +{ + u_char ch; + ngx_int_t rc; + enum { + sw_start = 0, + sw_index, + sw_value_len, + sw_read_value_len, + sw_value + }; + + for ( ;; ) { + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field lpbi"); + + st->state = sw_index; + + /* fall through */ + + case sw_index: + + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 3, b); + if (rc != NGX_DONE) { + return rc; + } + + st->index = st->pint.value; + st->state = sw_value_len; + break; + + case sw_value_len: + + if (b->pos == b->last) { + return NGX_AGAIN; + } + + ch = *b->pos; + + st->literal.huffman = (ch & 0x80) ? 1 : 0; + st->state = sw_read_value_len; + + /* fall through */ + + case sw_read_value_len: + + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b); + if (rc != NGX_DONE) { + return rc; + } + + st->literal.length = st->pint.value; + if (st->literal.length == 0) { + st->value.data = (u_char *) ""; + goto done; + } + + st->state = sw_value; + break; + + case sw_value: + + rc = ngx_http_v3_parse_literal(c, &st->literal, b); + if (rc != NGX_DONE) { + return rc; + } + + st->value = st->literal.value; + goto done; + } + } + +done: + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field lpbi done dynamic[+%ui] \"%V\"", + st->index, &st->value); + + rc = ngx_http_v3_parse_lookup(c, 1, st->base + st->index, &st->name, NULL); + if (rc != NGX_OK) { + return rc; + } + + st->state = sw_start; + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_v3_parse_lookup(ngx_connection_t *c, ngx_uint_t dynamic, + ngx_uint_t index, ngx_str_t *name, ngx_str_t *value) +{ + u_char *p; + + if (!dynamic) { + if (ngx_http_v3_lookup_static(c, index, name, value) != NGX_OK) { + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; + } + + return NGX_OK; + } + + if (ngx_http_v3_lookup(c, index, name, value) != NGX_OK) { + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; + } + + if (name) { + p = ngx_pnalloc(c->pool, name->len + 1); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, name->data, name->len); + p[name->len] = '\0'; + name->data = p; + } + + if (value) { + p = ngx_pnalloc(c->pool, value->len + 1); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, value->data, value->len); + p[value->len] = '\0'; + value->data = p; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_parse_control(ngx_connection_t *c, ngx_http_v3_parse_control_t *st, + ngx_buf_t *b) +{ + ngx_buf_t loc; + ngx_int_t rc; + enum { + sw_start = 0, + sw_first_type, + sw_type, + sw_length, + sw_settings, + sw_skip + }; + + for ( ;; ) { + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse control"); + + st->state = sw_first_type; + + /* fall through */ + + case sw_first_type: + case sw_type: + + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b); + if (rc != NGX_DONE) { + return rc; + } + + st->type = st->vlint.value; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse frame type:%ui", st->type); + + if (st->state == sw_first_type + && st->type != NGX_HTTP_V3_FRAME_SETTINGS) + { + return NGX_HTTP_V3_ERR_MISSING_SETTINGS; + } + + if (st->state != sw_first_type + && st->type == NGX_HTTP_V3_FRAME_SETTINGS) + { + return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; + } + + if (ngx_http_v3_is_v2_frame(st->type) + || st->type == NGX_HTTP_V3_FRAME_DATA + || st->type == NGX_HTTP_V3_FRAME_HEADERS + || st->type == NGX_HTTP_V3_FRAME_PUSH_PROMISE) + { + return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; + } + + if (st->type == NGX_HTTP_V3_FRAME_CANCEL_PUSH) { + return NGX_HTTP_V3_ERR_ID_ERROR; + } + + st->state = sw_length; + break; + + case sw_length: + + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b); + if (rc != NGX_DONE) { + return rc; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse frame len:%uL", st->vlint.value); + + st->length = st->vlint.value; + if (st->length == 0) { + st->state = sw_type; + break; + } + + switch (st->type) { + + case NGX_HTTP_V3_FRAME_SETTINGS: + st->state = sw_settings; + break; + + default: + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse skip unknown frame"); + st->state = sw_skip; + } + + break; + + case sw_settings: + + ngx_http_v3_parse_start_local(b, &loc, st->length); + + rc = ngx_http_v3_parse_settings(c, &st->settings, &loc); + + ngx_http_v3_parse_end_local(b, &loc, &st->length); + + if (st->length == 0 && rc == NGX_AGAIN) { + return NGX_HTTP_V3_ERR_SETTINGS_ERROR; + } + + if (rc != NGX_DONE) { + return rc; + } + + if (st->length == 0) { + st->state = sw_type; + } + + break; + + case sw_skip: + + rc = ngx_http_v3_parse_skip(b, &st->length); + if (rc != NGX_DONE) { + return rc; + } + + st->state = sw_type; + break; + } + } +} + + +static ngx_int_t +ngx_http_v3_parse_settings(ngx_connection_t *c, + ngx_http_v3_parse_settings_t *st, ngx_buf_t *b) +{ + ngx_int_t rc; + enum { + sw_start = 0, + sw_id, + sw_value + }; + + for ( ;; ) { + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse settings"); + + st->state = sw_id; + + /* fall through */ + + case sw_id: + + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b); + if (rc != NGX_DONE) { + return rc; + } + + st->id = st->vlint.value; + st->state = sw_value; + break; + + case sw_value: + + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b); + if (rc != NGX_DONE) { + return rc; + } + + if (ngx_http_v3_set_param(c, st->id, st->vlint.value) != NGX_OK) { + return NGX_HTTP_V3_ERR_SETTINGS_ERROR; + } + + goto done; + } + } + +done: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse settings done"); + + st->state = sw_start; + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_v3_parse_encoder(ngx_connection_t *c, ngx_http_v3_parse_encoder_t *st, + ngx_buf_t *b) +{ + u_char ch; + ngx_int_t rc; + enum { + sw_start = 0, + sw_inr, + sw_iln, + sw_capacity, + sw_duplicate + }; + + for ( ;; ) { + + if (st->state == sw_start) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse encoder instruction"); + + if (b->pos == b->last) { + return NGX_AGAIN; + } + + ch = *b->pos; + + if (ch & 0x80) { + /* Insert With Name Reference */ + + st->state = sw_inr; + + } else if (ch & 0x40) { + /* Insert With Literal Name */ + + st->state = sw_iln; + + } else if (ch & 0x20) { + /* Set Dynamic Table Capacity */ + + st->state = sw_capacity; + + } else { + /* Duplicate */ + + st->state = sw_duplicate; + } + } + + switch (st->state) { + + case sw_inr: + + rc = ngx_http_v3_parse_field_inr(c, &st->field, b); + if (rc != NGX_DONE) { + return rc; + } + + st->state = sw_start; + break; + + case sw_iln: + + rc = ngx_http_v3_parse_field_iln(c, &st->field, b); + if (rc != NGX_DONE) { + return rc; + } + + st->state = sw_start; + break; + + case sw_capacity: + + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 5, b); + if (rc != NGX_DONE) { + return rc; + } + + rc = ngx_http_v3_set_capacity(c, st->pint.value); + if (rc != NGX_OK) { + return rc; + } + + st->state = sw_start; + break; + + default: /* sw_duplicate */ + + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 5, b); + if (rc != NGX_DONE) { + return rc; + } + + rc = ngx_http_v3_duplicate(c, st->pint.value); + if (rc != NGX_OK) { + return rc; + } + + st->state = sw_start; + break; + } + } +} + + +static ngx_int_t +ngx_http_v3_parse_field_inr(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, ngx_buf_t *b) +{ + u_char ch; + ngx_int_t rc; + enum { + sw_start = 0, + sw_name_index, + sw_value_len, + sw_read_value_len, + sw_value + }; + + for ( ;; ) { + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field inr"); + + if (b->pos == b->last) { + return NGX_AGAIN; + } + + ch = *b->pos; + + st->dynamic = (ch & 0x40) ? 0 : 1; + st->state = sw_name_index; + + /* fall through */ + + case sw_name_index: + + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, b); + if (rc != NGX_DONE) { + return rc; + } + + st->index = st->pint.value; + st->state = sw_value_len; + break; + + case sw_value_len: + + if (b->pos == b->last) { + return NGX_AGAIN; + } + + ch = *b->pos; + + st->literal.huffman = (ch & 0x80) ? 1 : 0; + st->state = sw_read_value_len; + + /* fall through */ + + case sw_read_value_len: + + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b); + if (rc != NGX_DONE) { + return rc; + } + + st->literal.length = st->pint.value; + if (st->literal.length == 0) { + st->value.len = 0; + goto done; + } + + st->state = sw_value; + break; + + case sw_value: + + rc = ngx_http_v3_parse_literal(c, &st->literal, b); + if (rc != NGX_DONE) { + return rc; + } + + st->value = st->literal.value; + goto done; + } + } + +done: + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field inr done %s[%ui] \"%V\"", + st->dynamic ? "dynamic" : "static", + st->index, &st->value); + + rc = ngx_http_v3_ref_insert(c, st->dynamic, st->index, &st->value); + if (rc != NGX_OK) { + return rc; + } + + st->state = sw_start; + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_v3_parse_field_iln(ngx_connection_t *c, + ngx_http_v3_parse_field_t *st, ngx_buf_t *b) +{ + u_char ch; + ngx_int_t rc; + enum { + sw_start = 0, + sw_name_len, + sw_name, + sw_value_len, + sw_read_value_len, + sw_value + }; + + for ( ;; ) { + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field iln"); + + if (b->pos == b->last) { + return NGX_AGAIN; + } + + ch = *b->pos; + + st->literal.huffman = (ch & 0x20) ? 1 : 0; + st->state = sw_name_len; + + /* fall through */ + + case sw_name_len: + + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 5, b); + if (rc != NGX_DONE) { + return rc; + } + + st->literal.length = st->pint.value; + if (st->literal.length == 0) { + return NGX_ERROR; + } + + st->state = sw_name; + break; + + case sw_name: + + rc = ngx_http_v3_parse_literal(c, &st->literal, b); + if (rc != NGX_DONE) { + return rc; + } + + st->name = st->literal.value; + st->state = sw_value_len; + break; + + case sw_value_len: + + if (b->pos == b->last) { + return NGX_AGAIN; + } + + ch = *b->pos; + + st->literal.huffman = (ch & 0x80) ? 1 : 0; + st->state = sw_read_value_len; + + /* fall through */ + + case sw_read_value_len: + + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b); + if (rc != NGX_DONE) { + return rc; + } + + st->literal.length = st->pint.value; + if (st->literal.length == 0) { + st->value.len = 0; + goto done; + } + + st->state = sw_value; + break; + + case sw_value: + + rc = ngx_http_v3_parse_literal(c, &st->literal, b); + if (rc != NGX_DONE) { + return rc; + } + + st->value = st->literal.value; + goto done; + } + } + +done: + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse field iln done \"%V\":\"%V\"", + &st->name, &st->value); + + rc = ngx_http_v3_insert(c, &st->name, &st->value); + if (rc != NGX_OK) { + return rc; + } + + st->state = sw_start; + return NGX_DONE; +} + + +static ngx_int_t +ngx_http_v3_parse_decoder(ngx_connection_t *c, ngx_http_v3_parse_decoder_t *st, + ngx_buf_t *b) +{ + u_char ch; + ngx_int_t rc; + enum { + sw_start = 0, + sw_ack_section, + sw_cancel_stream, + sw_inc_insert_count + }; + + for ( ;; ) { + + if (st->state == sw_start) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse decoder instruction"); + + if (b->pos == b->last) { + return NGX_AGAIN; + } + + ch = *b->pos; + + if (ch & 0x80) { + /* Section Acknowledgment */ + + st->state = sw_ack_section; + + } else if (ch & 0x40) { + /* Stream Cancellation */ + + st->state = sw_cancel_stream; + + } else { + /* Insert Count Increment */ + + st->state = sw_inc_insert_count; + } + } + + switch (st->state) { + + case sw_ack_section: + + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b); + if (rc != NGX_DONE) { + return rc; + } + + rc = ngx_http_v3_ack_section(c, st->pint.value); + if (rc != NGX_OK) { + return rc; + } + + st->state = sw_start; + break; + + case sw_cancel_stream: + + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, b); + if (rc != NGX_DONE) { + return rc; + } + + rc = ngx_http_v3_cancel_stream(c, st->pint.value); + if (rc != NGX_OK) { + return rc; + } + + st->state = sw_start; + break; + + case sw_inc_insert_count: + + rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, b); + if (rc != NGX_DONE) { + return rc; + } + + rc = ngx_http_v3_inc_insert_count(c, st->pint.value); + if (rc != NGX_OK) { + return rc; + } + + st->state = sw_start; + break; + } + } +} + + +ngx_int_t +ngx_http_v3_parse_data(ngx_connection_t *c, ngx_http_v3_parse_data_t *st, + ngx_buf_t *b) +{ + ngx_int_t rc; + enum { + sw_start = 0, + sw_type, + sw_length, + sw_skip + }; + + for ( ;; ) { + + switch (st->state) { + + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse data"); + + st->state = sw_type; + + /* fall through */ + + case sw_type: + + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b); + if (rc != NGX_DONE) { + return rc; + } + + st->type = st->vlint.value; + + if (st->type == NGX_HTTP_V3_FRAME_HEADERS) { + /* trailers */ + goto done; + } + + if (ngx_http_v3_is_v2_frame(st->type) + || st->type == NGX_HTTP_V3_FRAME_GOAWAY + || st->type == NGX_HTTP_V3_FRAME_SETTINGS + || st->type == NGX_HTTP_V3_FRAME_MAX_PUSH_ID + || st->type == NGX_HTTP_V3_FRAME_CANCEL_PUSH + || st->type == NGX_HTTP_V3_FRAME_PUSH_PROMISE) + { + return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED; + } + + st->state = sw_length; + break; + + case sw_length: + + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b); + if (rc != NGX_DONE) { + return rc; + } + + st->length = st->vlint.value; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 parse data type:%ui, len:%ui", + st->type, st->length); + + if (st->type != NGX_HTTP_V3_FRAME_DATA && st->length > 0) { + st->state = sw_skip; + break; + } + + st->state = sw_type; + return NGX_OK; + + case sw_skip: + + rc = ngx_http_v3_parse_skip(b, &st->length); + if (rc != NGX_DONE) { + return rc; + } + + st->state = sw_type; + break; + } + } + +done: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse data done"); + + st->state = sw_start; + return NGX_DONE; +} + + +ngx_int_t +ngx_http_v3_parse_uni(ngx_connection_t *c, ngx_http_v3_parse_uni_t *st, + ngx_buf_t *b) +{ + ngx_int_t rc; + enum { + sw_start = 0, + sw_type, + sw_control, + sw_encoder, + sw_decoder, + sw_unknown + }; + + for ( ;; ) { + + switch (st->state) { + case sw_start: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 parse uni"); + + st->state = sw_type; + + /* fall through */ + + case sw_type: + + rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b); + if (rc != NGX_DONE) { + return rc; + } + + rc = ngx_http_v3_register_uni_stream(c, st->vlint.value); + if (rc != NGX_OK) { + return rc; + } + + switch (st->vlint.value) { + case NGX_HTTP_V3_STREAM_CONTROL: + st->state = sw_control; + break; + + case NGX_HTTP_V3_STREAM_ENCODER: + st->state = sw_encoder; + break; + + case NGX_HTTP_V3_STREAM_DECODER: + st->state = sw_decoder; + break; + + default: + st->state = sw_unknown; + } + + break; + + case sw_control: + + return ngx_http_v3_parse_control(c, &st->u.control, b); + + case sw_encoder: + + return ngx_http_v3_parse_encoder(c, &st->u.encoder, b); + + case sw_decoder: + + return ngx_http_v3_parse_decoder(c, &st->u.decoder, b); + + case sw_unknown: + + b->pos = b->last; + return NGX_AGAIN; + } + } +} diff --git a/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_parse.h b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_parse.h new file mode 100644 index 000000000..ba004db5d --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_parse.h @@ -0,0 +1,146 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_HTTP_V3_PARSE_H_INCLUDED_ +#define _NGX_HTTP_V3_PARSE_H_INCLUDED_ + + +#include +#include +#include + + +typedef struct { + ngx_uint_t state; + uint64_t value; +} ngx_http_v3_parse_varlen_int_t; + + +typedef struct { + ngx_uint_t state; + ngx_uint_t shift; + uint64_t value; +} ngx_http_v3_parse_prefix_int_t; + + +typedef struct { + ngx_uint_t state; + uint64_t id; + ngx_http_v3_parse_varlen_int_t vlint; +} ngx_http_v3_parse_settings_t; + + +typedef struct { + ngx_uint_t state; + ngx_uint_t insert_count; + ngx_uint_t delta_base; + ngx_uint_t sign; + ngx_uint_t base; + ngx_http_v3_parse_prefix_int_t pint; +} ngx_http_v3_parse_field_section_prefix_t; + + +typedef struct { + ngx_uint_t state; + ngx_uint_t length; + ngx_uint_t huffman; + ngx_str_t value; + u_char *last; + u_char huffstate; +} ngx_http_v3_parse_literal_t; + + +typedef struct { + ngx_uint_t state; + ngx_uint_t index; + ngx_uint_t base; + ngx_uint_t dynamic; + + ngx_str_t name; + ngx_str_t value; + + ngx_http_v3_parse_prefix_int_t pint; + ngx_http_v3_parse_literal_t literal; +} ngx_http_v3_parse_field_t; + + +typedef struct { + ngx_uint_t state; + ngx_http_v3_parse_field_t field; +} ngx_http_v3_parse_field_rep_t; + + +typedef struct { + ngx_uint_t state; + ngx_uint_t type; + ngx_uint_t length; + ngx_http_v3_parse_varlen_int_t vlint; + ngx_http_v3_parse_field_section_prefix_t prefix; + ngx_http_v3_parse_field_rep_t field_rep; +} ngx_http_v3_parse_headers_t; + + +typedef struct { + ngx_uint_t state; + ngx_http_v3_parse_field_t field; + ngx_http_v3_parse_prefix_int_t pint; +} ngx_http_v3_parse_encoder_t; + + +typedef struct { + ngx_uint_t state; + ngx_http_v3_parse_prefix_int_t pint; +} ngx_http_v3_parse_decoder_t; + + +typedef struct { + ngx_uint_t state; + ngx_uint_t type; + ngx_uint_t length; + ngx_http_v3_parse_varlen_int_t vlint; + ngx_http_v3_parse_settings_t settings; +} ngx_http_v3_parse_control_t; + + +typedef struct { + ngx_uint_t state; + ngx_http_v3_parse_varlen_int_t vlint; + union { + ngx_http_v3_parse_encoder_t encoder; + ngx_http_v3_parse_decoder_t decoder; + ngx_http_v3_parse_control_t control; + } u; +} ngx_http_v3_parse_uni_t; + + +typedef struct { + ngx_uint_t state; + ngx_uint_t type; + ngx_uint_t length; + ngx_http_v3_parse_varlen_int_t vlint; +} ngx_http_v3_parse_data_t; + + +/* + * Parse functions return codes: + * NGX_DONE - parsing done + * NGX_OK - sub-element done + * NGX_AGAIN - more data expected + * NGX_BUSY - waiting for external event + * NGX_ERROR - internal error + * NGX_HTTP_V3_ERROR_XXX - HTTP/3 or QPACK error + */ + +ngx_int_t ngx_http_v3_parse_headers(ngx_connection_t *c, + ngx_http_v3_parse_headers_t *st, ngx_buf_t *b); +ngx_int_t ngx_http_v3_parse_data(ngx_connection_t *c, + ngx_http_v3_parse_data_t *st, ngx_buf_t *b); +ngx_int_t ngx_http_v3_parse_uni(ngx_connection_t *c, + ngx_http_v3_parse_uni_t *st, ngx_buf_t *b); + + +#endif /* _NGX_HTTP_V3_PARSE_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_request.c b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_request.c new file mode 100644 index 000000000..7bb61311d --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_request.c @@ -0,0 +1,1741 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +static void ngx_http_v3_init_request_stream(ngx_connection_t *c); +static void ngx_http_v3_wait_request_handler(ngx_event_t *rev); +static void ngx_http_v3_cleanup_connection(void *data); +static void ngx_http_v3_cleanup_request(void *data); +static void ngx_http_v3_process_request(ngx_event_t *rev); +static ngx_int_t ngx_http_v3_process_header(ngx_http_request_t *r, + ngx_str_t *name, ngx_str_t *value); +static ngx_int_t ngx_http_v3_validate_header(ngx_http_request_t *r, + ngx_str_t *name, ngx_str_t *value); +static ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, + ngx_str_t *name, ngx_str_t *value); +static ngx_int_t ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r); +static ngx_int_t ngx_http_v3_process_request_header(ngx_http_request_t *r); +static ngx_int_t ngx_http_v3_cookie(ngx_http_request_t *r, ngx_str_t *value); +static ngx_int_t ngx_http_v3_construct_cookie_header(ngx_http_request_t *r); +static void ngx_http_v3_read_client_request_body_handler(ngx_http_request_t *r); +static ngx_int_t ngx_http_v3_do_read_client_request_body(ngx_http_request_t *r); +static ngx_int_t ngx_http_v3_request_body_filter(ngx_http_request_t *r, + ngx_chain_t *in); + + +static const struct { + ngx_str_t name; + ngx_uint_t method; +} ngx_http_v3_methods[] = { + + { ngx_string("GET"), NGX_HTTP_GET }, + { ngx_string("POST"), NGX_HTTP_POST }, + { ngx_string("HEAD"), NGX_HTTP_HEAD }, + { ngx_string("OPTIONS"), NGX_HTTP_OPTIONS }, + { ngx_string("PROPFIND"), NGX_HTTP_PROPFIND }, + { ngx_string("PUT"), NGX_HTTP_PUT }, + { ngx_string("MKCOL"), NGX_HTTP_MKCOL }, + { ngx_string("DELETE"), NGX_HTTP_DELETE }, + { ngx_string("COPY"), NGX_HTTP_COPY }, + { ngx_string("MOVE"), NGX_HTTP_MOVE }, + { ngx_string("PROPPATCH"), NGX_HTTP_PROPPATCH }, + { ngx_string("LOCK"), NGX_HTTP_LOCK }, + { ngx_string("UNLOCK"), NGX_HTTP_UNLOCK }, + { ngx_string("PATCH"), NGX_HTTP_PATCH }, + { ngx_string("TRACE"), NGX_HTTP_TRACE }, + { ngx_string("CONNECT"), NGX_HTTP_CONNECT } +}; + + +void +ngx_http_v3_init_stream(ngx_connection_t *c) +{ + ngx_http_connection_t *hc, *phc; + ngx_http_v3_srv_conf_t *h3scf; + ngx_http_core_loc_conf_t *clcf; + + hc = c->data; + + hc->ssl = 1; + + clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); + + if (c->quic == NULL) { + h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module); + h3scf->quic.idle_timeout = clcf->keepalive_timeout; + + ngx_quic_run(c, &h3scf->quic); + return; + } + + phc = ngx_http_quic_get_connection(c); + + if (phc->ssl_servername) { + hc->ssl_servername = phc->ssl_servername; +#if (NGX_PCRE) + hc->ssl_servername_regex = phc->ssl_servername_regex; +#endif + hc->conf_ctx = phc->conf_ctx; + + ngx_set_connection_log(c, clcf->error_log); + } + + if (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) { + ngx_http_v3_init_uni_stream(c); + + } else { + ngx_http_v3_init_request_stream(c); + } +} + + +ngx_int_t +ngx_http_v3_init(ngx_connection_t *c) +{ + unsigned int len; + const unsigned char *data; + ngx_http_v3_session_t *h3c; + ngx_http_v3_srv_conf_t *h3scf; + ngx_http_core_loc_conf_t *clcf; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init"); + + if (ngx_http_v3_init_session(c) != NGX_OK) { + return NGX_ERROR; + } + + h3c = ngx_http_v3_get_session(c); + clcf = ngx_http_v3_get_module_loc_conf(c, ngx_http_core_module); + ngx_add_timer(&h3c->keepalive, clcf->keepalive_timeout); + + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + + if (h3scf->enable_hq) { + if (!h3scf->enable) { + h3c->hq = 1; + return NGX_OK; + } + + SSL_get0_alpn_selected(c->ssl->connection, &data, &len); + + if (len == sizeof(NGX_HTTP_V3_HQ_PROTO) - 1 + && ngx_strncmp(data, NGX_HTTP_V3_HQ_PROTO, len) == 0) + { + h3c->hq = 1; + return NGX_OK; + } + } + + if (ngx_http_v3_send_settings(c) != NGX_OK) { + return NGX_ERROR; + } + + if (h3scf->max_table_capacity > 0) { + if (ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER) == NULL) { + return NGX_ERROR; + } + } + + return NGX_OK; +} + + +void +ngx_http_v3_shutdown(ngx_connection_t *c) +{ + ngx_http_v3_session_t *h3c; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 shutdown"); + + h3c = ngx_http_v3_get_session(c); + + if (h3c == NULL) { + ngx_quic_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, + "connection shutdown"); + return; + } + + if (!h3c->goaway) { + h3c->goaway = 1; + + if (!h3c->hq) { + (void) ngx_http_v3_send_goaway(c, h3c->next_request_id); + } + + ngx_http_v3_shutdown_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, + "connection shutdown"); + } +} + + +static void +ngx_http_v3_init_request_stream(ngx_connection_t *c) +{ + uint64_t n; + ngx_event_t *rev; + ngx_pool_cleanup_t *cln; + ngx_http_connection_t *hc; + ngx_http_v3_session_t *h3c; + ngx_http_core_loc_conf_t *clcf; + ngx_http_core_srv_conf_t *cscf; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init request stream"); + +#if (NGX_STAT_STUB) + (void) ngx_atomic_fetch_add(ngx_stat_active, 1); +#endif + + hc = c->data; + + clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); + + n = c->quic->id >> 2; + + if (n >= clcf->keepalive_requests * 2) { + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "too many requests per connection"); + ngx_http_close_connection(c); + return; + } + + h3c = ngx_http_v3_get_session(c); + + if (h3c->goaway) { + c->close = 1; + ngx_http_close_connection(c); + return; + } + + h3c->next_request_id = c->quic->id + 0x04; + + if (n + 1 == clcf->keepalive_requests + || ngx_current_msec - c->start_time > clcf->keepalive_time) + { + h3c->goaway = 1; + + if (!h3c->hq) { + if (ngx_http_v3_send_goaway(c, h3c->next_request_id) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + } + + ngx_http_v3_shutdown_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, + "reached maximum number of requests"); + } + + cln = ngx_pool_cleanup_add(c->pool, 0); + if (cln == NULL) { + ngx_http_close_connection(c); + return; + } + + cln->handler = ngx_http_v3_cleanup_connection; + cln->data = c; + + h3c->nrequests++; + + if (h3c->keepalive.timer_set) { + ngx_del_timer(&h3c->keepalive); + } + + rev = c->read; + + if (!h3c->hq) { + rev->handler = ngx_http_v3_wait_request_handler; + c->write->handler = ngx_http_empty_handler; + } + + if (rev->ready) { + rev->handler(rev); + return; + } + + cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); + + ngx_add_timer(rev, cscf->client_header_timeout); + ngx_reusable_connection(c, 1); + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_connection(c); + return; + } +} + + +static void +ngx_http_v3_wait_request_handler(ngx_event_t *rev) +{ + size_t size; + ssize_t n; + ngx_buf_t *b; + ngx_connection_t *c; + ngx_pool_cleanup_t *cln; + ngx_http_request_t *r; + ngx_http_connection_t *hc; + ngx_http_core_srv_conf_t *cscf; + + c = rev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 wait request handler"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_http_close_connection(c); + return; + } + + if (c->close) { + ngx_http_close_connection(c); + return; + } + + hc = c->data; + cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); + + size = cscf->client_header_buffer_size; + + b = c->buffer; + + if (b == NULL) { + b = ngx_create_temp_buf(c->pool, size); + if (b == NULL) { + ngx_http_close_connection(c); + return; + } + + c->buffer = b; + + } else if (b->start == NULL) { + + b->start = ngx_palloc(c->pool, size); + if (b->start == NULL) { + ngx_http_close_connection(c); + return; + } + + b->pos = b->start; + b->last = b->start; + b->end = b->last + size; + } + + n = c->recv(c, b->last, size); + + if (n == NGX_AGAIN) { + + if (!rev->timer_set) { + ngx_add_timer(rev, cscf->client_header_timeout); + ngx_reusable_connection(c, 1); + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_connection(c); + return; + } + + /* + * We are trying to not hold c->buffer's memory for an idle connection. + */ + + if (ngx_pfree(c->pool, b->start) == NGX_OK) { + b->start = NULL; + } + + return; + } + + if (n == NGX_ERROR) { + ngx_http_close_connection(c); + return; + } + + if (n == 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client closed connection"); + ngx_http_close_connection(c); + return; + } + + b->last += n; + + c->log->action = "reading client request"; + + ngx_reusable_connection(c, 0); + + r = ngx_http_create_request(c); + if (r == NULL) { + ngx_http_close_connection(c); + return; + } + + r->http_version = NGX_HTTP_VERSION_30; + + r->v3_parse = ngx_pcalloc(r->pool, sizeof(ngx_http_v3_parse_t)); + if (r->v3_parse == NULL) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + r->v3_parse->header_limit = cscf->large_client_header_buffers.size + * cscf->large_client_header_buffers.num; + + c->data = r; + c->requests = (c->quic->id >> 2) + 1; + + cln = ngx_pool_cleanup_add(r->pool, 0); + if (cln == NULL) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + + cln->handler = ngx_http_v3_cleanup_request; + cln->data = r; + + rev->handler = ngx_http_v3_process_request; + ngx_http_v3_process_request(rev); +} + + +void +ngx_http_v3_reset_stream(ngx_connection_t *c) +{ + ngx_http_v3_session_t *h3c; + + h3c = ngx_http_v3_get_session(c); + + if (!c->read->eof && !h3c->hq + && h3c->known_streams[NGX_HTTP_V3_STREAM_SERVER_DECODER] + && (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0) + { + (void) ngx_http_v3_send_cancel_stream(c, c->quic->id); + } + + if (c->timedout) { + ngx_quic_reset_stream(c, NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR); + + } else if (c->close) { + ngx_quic_reset_stream(c, NGX_HTTP_V3_ERR_REQUEST_REJECTED); + + } else if (c->requests == 0 || c->error) { + ngx_quic_reset_stream(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR); + } +} + + +static void +ngx_http_v3_cleanup_connection(void *data) +{ + ngx_connection_t *c = data; + + ngx_http_v3_session_t *h3c; + ngx_http_core_loc_conf_t *clcf; + + h3c = ngx_http_v3_get_session(c); + + if (--h3c->nrequests == 0) { + clcf = ngx_http_v3_get_module_loc_conf(c, ngx_http_core_module); + ngx_add_timer(&h3c->keepalive, clcf->keepalive_timeout); + } +} + + +static void +ngx_http_v3_cleanup_request(void *data) +{ + ngx_http_request_t *r = data; + + if (!r->response_sent) { + r->connection->error = 1; + } +} + + +static void +ngx_http_v3_process_request(ngx_event_t *rev) +{ + u_char *p; + ssize_t n; + ngx_buf_t *b; + ngx_int_t rc; + ngx_connection_t *c; + ngx_http_request_t *r; + ngx_http_v3_session_t *h3c; + ngx_http_core_srv_conf_t *cscf; + ngx_http_v3_parse_headers_t *st; + + c = rev->data; + r = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http3 process request"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT); + return; + } + + h3c = ngx_http_v3_get_session(c); + + st = &r->v3_parse->headers; + + b = r->header_in; + + for ( ;; ) { + + if (b->pos == b->last) { + + if (rev->ready) { + n = c->recv(c, b->start, b->end - b->start); + + } else { + n = NGX_AGAIN; + } + + if (n == NGX_AGAIN) { + if (!rev->timer_set) { + cscf = ngx_http_get_module_srv_conf(r, + ngx_http_core_module); + ngx_add_timer(rev, cscf->client_header_timeout); + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + break; + } + + if (n == 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client prematurely closed connection"); + } + + if (n == 0 || n == NGX_ERROR) { + c->error = 1; + c->log->action = "reading client request"; + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + break; + } + + b->pos = b->start; + b->last = b->start + n; + } + + p = b->pos; + + rc = ngx_http_v3_parse_headers(c, st, b); + + if (rc > 0) { + ngx_quic_reset_stream(c, rc); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "client sent invalid header"); + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + break; + } + + if (rc == NGX_ERROR) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + break; + } + + r->request_length += b->pos - p; + h3c->total_bytes += b->pos - p; + + if (ngx_http_v3_check_flood(c) != NGX_OK) { + ngx_http_close_request(r, NGX_HTTP_CLOSE); + break; + } + + if (rc == NGX_BUSY) { + if (rev->error) { + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + break; + } + + if (!rev->timer_set) { + cscf = ngx_http_get_module_srv_conf(r, + ngx_http_core_module); + ngx_add_timer(rev, cscf->client_header_timeout); + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + break; + } + + if (rc == NGX_AGAIN) { + continue; + } + + /* rc == NGX_OK || rc == NGX_DONE */ + + h3c->payload_bytes += ngx_http_v3_encode_field_l(NULL, + &st->field_rep.field.name, + &st->field_rep.field.value); + + if (ngx_http_v3_process_header(r, &st->field_rep.field.name, + &st->field_rep.field.value) + != NGX_OK) + { + break; + } + + if (rc == NGX_DONE) { + if (ngx_http_v3_process_request_header(r) != NGX_OK) { + break; + } + + ngx_http_process_request(r); + break; + } + } + + ngx_http_run_posted_requests(c); + + return; +} + + +static ngx_int_t +ngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name, + ngx_str_t *value) +{ + size_t len; + ngx_table_elt_t *h; + ngx_http_header_t *hh; + ngx_http_core_srv_conf_t *cscf; + ngx_http_core_main_conf_t *cmcf; + + static ngx_str_t cookie = ngx_string("cookie"); + + len = name->len + value->len; + + if (len > r->v3_parse->header_limit) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent too large header"); + ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); + return NGX_ERROR; + } + + r->v3_parse->header_limit -= len; + + if (ngx_http_v3_validate_header(r, name, value) != NGX_OK) { + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; + } + + if (r->invalid_header) { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + if (cscf->ignore_invalid_headers) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid header: \"%V\"", name); + + return NGX_OK; + } + } + + if (name->len && name->data[0] == ':') { + return ngx_http_v3_process_pseudo_header(r, name, value); + } + + if (ngx_http_v3_init_pseudo_headers(r) != NGX_OK) { + return NGX_ERROR; + } + + if (name->len == cookie.len + && ngx_memcmp(name->data, cookie.data, cookie.len) == 0) + { + if (ngx_http_v3_cookie(r, value) != NGX_OK) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + } else { + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + if (r->headers_in.count++ >= cscf->max_headers) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent too many header lines"); + ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); + return NGX_ERROR; + } + + h = ngx_list_push(&r->headers_in.headers); + if (h == NULL) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + h->key = *name; + h->value = *value; + h->lowcase_key = h->key.data; + h->hash = ngx_hash_key(h->key.data, h->key.len); + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, + h->lowcase_key, h->key.len); + + if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { + return NGX_ERROR; + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 header: \"%V: %V\"", name, value); + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_validate_header(ngx_http_request_t *r, ngx_str_t *name, + ngx_str_t *value) +{ + u_char ch; + ngx_uint_t i; + ngx_http_core_srv_conf_t *cscf; + + r->invalid_header = 0; + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + for (i = (name->data[0] == ':'); i != name->len; i++) { + ch = name->data[i]; + + if ((ch >= 'a' && ch <= 'z') + || (ch == '-') + || (ch >= '0' && ch <= '9') + || (ch == '_' && cscf->underscores_in_headers)) + { + continue; + } + + if (ch <= 0x20 || ch == 0x7f || ch == ':' + || (ch >= 'A' && ch <= 'Z')) + { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid header name: \"%V\"", name); + + return NGX_ERROR; + } + + r->invalid_header = 1; + } + + for (i = 0; i != value->len; i++) { + ch = value->data[i]; + + if (ch == '\0' || ch == LF || ch == CR) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent header \"%V\" with " + "invalid value: \"%V\"", name, value); + + return NGX_ERROR; + } + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name, + ngx_str_t *value) +{ + u_char ch, c; + ngx_uint_t i; + + if (r->request_line.len) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent out of order pseudo-headers"); + goto failed; + } + + if (name->len == 7 && ngx_strncmp(name->data, ":method", 7) == 0) { + + if (r->method_name.len) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent duplicate \":method\" header"); + goto failed; + } + + if (value->len == 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent empty \":method\" header"); + goto failed; + } + + r->method_name = *value; + + for (i = 0; i < sizeof(ngx_http_v3_methods) + / sizeof(ngx_http_v3_methods[0]); i++) + { + if (value->len == ngx_http_v3_methods[i].name.len + && ngx_strncmp(value->data, + ngx_http_v3_methods[i].name.data, value->len) + == 0) + { + r->method = ngx_http_v3_methods[i].method; + break; + } + } + + for (i = 0; i < value->len; i++) { + ch = value->data[i]; + + if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid method: \"%V\"", value); + goto failed; + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 method \"%V\" %ui", value, r->method); + return NGX_OK; + } + + if (name->len == 5 && ngx_strncmp(name->data, ":path", 5) == 0) { + + if (r->uri_start) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent duplicate \":path\" header"); + goto failed; + } + + if (value->len == 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent empty \":path\" header"); + goto failed; + } + + r->uri_start = value->data; + r->uri_end = value->data + value->len; + + if (ngx_http_parse_uri(r) != NGX_OK) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid \":path\" header: \"%V\"", + value); + goto failed; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 path \"%V\"", value); + return NGX_OK; + } + + if (name->len == 7 && ngx_strncmp(name->data, ":scheme", 7) == 0) { + + if (r->schema.len) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent duplicate \":scheme\" header"); + goto failed; + } + + if (value->len == 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent empty \":scheme\" header"); + goto failed; + } + + for (i = 0; i < value->len; i++) { + ch = value->data[i]; + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'z') { + continue; + } + + if (((ch >= '0' && ch <= '9') + || ch == '+' || ch == '-' || ch == '.') + && i > 0) + { + continue; + } + + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid \":scheme\" header: \"%V\"", + value); + goto failed; + } + + r->schema = *value; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 schema \"%V\"", value); + return NGX_OK; + } + + if (name->len == 10 && ngx_strncmp(name->data, ":authority", 10) == 0) { + + if (r->host_start) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent duplicate \":authority\" header"); + goto failed; + } + + r->host_start = value->data; + r->host_end = value->data + value->len; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 authority \"%V\"", value); + return NGX_OK; + } + + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent unknown pseudo-header \"%V\"", name); + +failed: + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; +} + + +static ngx_int_t +ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r) +{ + size_t len; + u_char *p; + ngx_int_t rc; + ngx_str_t host; + in_port_t port; + + if (r->request_line.len) { + return NGX_OK; + } + + if (r->method_name.len == 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent no \":method\" header"); + goto failed; + } + + if (r->schema.len == 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent no \":scheme\" header"); + goto failed; + } + + if (r->uri_start == NULL) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent no \":path\" header"); + goto failed; + } + + len = r->method_name.len + 1 + + (r->uri_end - r->uri_start) + 1 + + sizeof("HTTP/3.0") - 1; + + p = ngx_pnalloc(r->pool, len); + if (p == NULL) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + r->request_line.data = p; + + p = ngx_cpymem(p, r->method_name.data, r->method_name.len); + *p++ = ' '; + p = ngx_cpymem(p, r->uri_start, r->uri_end - r->uri_start); + *p++ = ' '; + p = ngx_cpymem(p, "HTTP/3.0", sizeof("HTTP/3.0") - 1); + + r->request_line.len = p - r->request_line.data; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 request line: \"%V\"", &r->request_line); + + ngx_str_set(&r->http_protocol, "HTTP/3.0"); + + if (ngx_http_process_request_uri(r) != NGX_OK) { + return NGX_ERROR; + } + + if (r->host_end) { + + host.len = r->host_end - r->host_start; + host.data = r->host_start; + + rc = ngx_http_validate_host(&host, &port, r->pool, 0); + + if (rc == NGX_DECLINED) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent invalid \":authority\" header"); + goto failed; + } + + if (rc == NGX_ERROR) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) { + return NGX_ERROR; + } + + r->headers_in.server = host; + r->port = port; + } + + if (ngx_list_init(&r->headers_in.headers, r->pool, 20, + sizeof(ngx_table_elt_t)) + != NGX_OK) + { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + return NGX_OK; + +failed: + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; +} + + +static ngx_int_t +ngx_http_v3_process_request_header(ngx_http_request_t *r) +{ + ssize_t n; + ngx_buf_t *b; + ngx_str_t host; + ngx_connection_t *c; + ngx_http_v3_session_t *h3c; + ngx_http_v3_srv_conf_t *h3scf; + + c = r->connection; + + if (ngx_http_v3_init_pseudo_headers(r) != NGX_OK) { + return NGX_ERROR; + } + + h3c = ngx_http_v3_get_session(c); + h3scf = ngx_http_get_module_srv_conf(r, ngx_http_v3_module); + + if ((h3c->hq && !h3scf->enable_hq) || (!h3c->hq && !h3scf->enable)) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client attempted to request the server name " + "for which the negotiated protocol is disabled"); + ngx_http_finalize_request(r, NGX_HTTP_MISDIRECTED_REQUEST); + return NGX_ERROR; + } + + if (ngx_http_v3_construct_cookie_header(r) != NGX_OK) { + return NGX_ERROR; + } + + if (r->headers_in.server.len == 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent neither \":authority\" nor \"Host\" header"); + goto failed; + } + + if (r->headers_in.host && r->host_end) { + + host.len = r->host_end - r->host_start; + host.data = r->host_start; + + if (r->headers_in.host->value.len != host.len + || ngx_memcmp(r->headers_in.host->value.data, host.data, host.len) + != 0) + { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent \":authority\" and \"Host\" headers " + "with different values"); + goto failed; + } + } + + if (r->headers_in.content_length) { + r->headers_in.content_length_n = + ngx_atoof(r->headers_in.content_length->value.data, + r->headers_in.content_length->value.len); + + if (r->headers_in.content_length_n == NGX_ERROR) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid \"Content-Length\" header"); + goto failed; + } + + } else { + b = r->header_in; + n = b->last - b->pos; + + if (n == 0) { + n = c->recv(c, b->start, b->end - b->start); + + if (n == NGX_ERROR) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + if (n > 0) { + b->pos = b->start; + b->last = b->start + n; + } + } + + if (n != 0) { + r->headers_in.chunked = 1; + } + } + + if (r->method == NGX_HTTP_CONNECT) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent CONNECT method"); + ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED); + return NGX_ERROR; + } + + if (r->method == NGX_HTTP_TRACE) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent TRACE method"); + ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED); + return NGX_ERROR; + } + + return NGX_OK; + +failed: + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); + return NGX_ERROR; +} + + +static ngx_int_t +ngx_http_v3_cookie(ngx_http_request_t *r, ngx_str_t *value) +{ + ngx_str_t *val; + ngx_array_t *cookies; + + cookies = r->v3_parse->cookies; + + if (cookies == NULL) { + cookies = ngx_array_create(r->pool, 2, sizeof(ngx_str_t)); + if (cookies == NULL) { + return NGX_ERROR; + } + + r->v3_parse->cookies = cookies; + } + + val = ngx_array_push(cookies); + if (val == NULL) { + return NGX_ERROR; + } + + *val = *value; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_construct_cookie_header(ngx_http_request_t *r) +{ + u_char *buf, *p, *end; + size_t len; + ngx_str_t *vals; + ngx_uint_t i; + ngx_array_t *cookies; + ngx_table_elt_t *h; + ngx_http_header_t *hh; + ngx_http_core_main_conf_t *cmcf; + + static ngx_str_t cookie = ngx_string("cookie"); + + cookies = r->v3_parse->cookies; + + if (cookies == NULL) { + return NGX_OK; + } + + vals = cookies->elts; + + i = 0; + len = 0; + + do { + len += vals[i].len + 2; + } while (++i != cookies->nelts); + + len -= 2; + + buf = ngx_pnalloc(r->pool, len + 1); + if (buf == NULL) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + p = buf; + end = buf + len; + + for (i = 0; /* void */ ; i++) { + + p = ngx_cpymem(p, vals[i].data, vals[i].len); + + if (p == end) { + *p = '\0'; + break; + } + + *p++ = ';'; *p++ = ' '; + } + + h = ngx_list_push(&r->headers_in.headers); + if (h == NULL) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash( + ngx_hash('c', 'o'), 'o'), 'k'), 'i'), 'e'); + + h->key.len = cookie.len; + h->key.data = cookie.data; + + h->value.len = len; + h->value.data = buf; + + h->lowcase_key = cookie.data; + + cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); + + hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, + h->lowcase_key, h->key.len); + + if (hh == NULL) { + ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + if (hh->handler(r, h, hh->offset) != NGX_OK) { + /* + * request has been finalized already + * in ngx_http_process_header_line() + */ + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_read_request_body(ngx_http_request_t *r) +{ + size_t preread; + ngx_int_t rc; + ngx_chain_t *cl, out; + ngx_http_request_body_t *rb; + ngx_http_core_loc_conf_t *clcf; + + rb = r->request_body; + + preread = r->header_in->last - r->header_in->pos; + + if (preread) { + + /* there is the pre-read part of the request body */ + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 client request body preread %uz", preread); + + out.buf = r->header_in; + out.next = NULL; + cl = &out; + + } else { + cl = NULL; + } + + rc = ngx_http_v3_request_body_filter(r, cl); + if (rc != NGX_OK) { + return rc; + } + + if (rb->rest == 0 && rb->last_saved) { + /* the whole request body was pre-read */ + r->request_body_no_buffering = 0; + rb->post_handler(r); + return NGX_OK; + } + + if (rb->rest < 0) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "negative request body rest"); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + rb->buf = ngx_create_temp_buf(r->pool, clcf->client_body_buffer_size); + if (rb->buf == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + r->read_event_handler = ngx_http_v3_read_client_request_body_handler; + r->write_event_handler = ngx_http_request_empty_handler; + + return ngx_http_v3_do_read_client_request_body(r); +} + + +static void +ngx_http_v3_read_client_request_body_handler(ngx_http_request_t *r) +{ + ngx_int_t rc; + + if (r->connection->read->timedout) { + r->connection->timedout = 1; + ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT); + return; + } + + rc = ngx_http_v3_do_read_client_request_body(r); + + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + ngx_http_finalize_request(r, rc); + } +} + + +ngx_int_t +ngx_http_v3_read_unbuffered_request_body(ngx_http_request_t *r) +{ + ngx_int_t rc; + + if (r->connection->read->timedout) { + r->connection->timedout = 1; + return NGX_HTTP_REQUEST_TIME_OUT; + } + + rc = ngx_http_v3_do_read_client_request_body(r); + + if (rc == NGX_OK) { + r->reading_body = 0; + } + + return rc; +} + + +static ngx_int_t +ngx_http_v3_do_read_client_request_body(ngx_http_request_t *r) +{ + off_t rest; + size_t size; + ssize_t n; + ngx_int_t rc; + ngx_uint_t flush; + ngx_chain_t out; + ngx_connection_t *c; + ngx_http_request_body_t *rb; + ngx_http_core_loc_conf_t *clcf; + + c = r->connection; + rb = r->request_body; + flush = 1; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 read client request body"); + + for ( ;; ) { + for ( ;; ) { + if (rb->rest == 0) { + break; + } + + if (rb->buf->last == rb->buf->end) { + + /* update chains */ + + rc = ngx_http_v3_request_body_filter(r, NULL); + + if (rc != NGX_OK) { + return rc; + } + + if (rb->busy != NULL) { + if (r->request_body_no_buffering) { + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + return NGX_AGAIN; + } + + if (rb->filter_need_buffering) { + clcf = ngx_http_get_module_loc_conf(r, + ngx_http_core_module); + ngx_add_timer(c->read, clcf->client_body_timeout); + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + return NGX_AGAIN; + } + + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "busy buffers after request body flush"); + + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + flush = 0; + rb->buf->pos = rb->buf->start; + rb->buf->last = rb->buf->start; + } + + size = rb->buf->end - rb->buf->last; + rest = rb->rest - (rb->buf->last - rb->buf->pos); + + if ((off_t) size > rest) { + size = (size_t) rest; + } + + if (size == 0) { + break; + } + + n = c->recv(c, rb->buf->last, size); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 client request body recv %z", n); + + if (n == NGX_AGAIN) { + break; + } + + if (n == 0) { + rb->buf->last_buf = 1; + } + + if (n == NGX_ERROR) { + c->error = 1; + return NGX_HTTP_BAD_REQUEST; + } + + rb->buf->last += n; + + /* pass buffer to request body filter chain */ + + flush = 0; + out.buf = rb->buf; + out.next = NULL; + + rc = ngx_http_v3_request_body_filter(r, &out); + + if (rc != NGX_OK) { + return rc; + } + + if (rb->rest == 0) { + break; + } + + if (rb->buf->last < rb->buf->end) { + break; + } + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 client request body rest %O", rb->rest); + + if (flush) { + rc = ngx_http_v3_request_body_filter(r, NULL); + + if (rc != NGX_OK) { + return rc; + } + } + + if (rb->rest == 0 && rb->last_saved) { + break; + } + + if (!c->read->ready || rb->rest == 0) { + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + ngx_add_timer(c->read, clcf->client_body_timeout); + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + return NGX_AGAIN; + } + } + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (!r->request_body_no_buffering) { + r->read_event_handler = ngx_http_block_reading; + rb->post_handler(r); + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in) +{ + off_t max; + size_t size; + u_char *p; + ngx_int_t rc; + ngx_buf_t *b; + ngx_uint_t last; + ngx_chain_t *cl, *out, *tl, **ll; + ngx_http_v3_session_t *h3c; + ngx_http_request_body_t *rb; + ngx_http_core_loc_conf_t *clcf; + ngx_http_core_srv_conf_t *cscf; + ngx_http_v3_parse_data_t *st; + + rb = r->request_body; + st = &r->v3_parse->body; + + h3c = ngx_http_v3_get_session(r->connection); + + if (rb->rest == -1) { + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http3 request body filter"); + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + rb->rest = cscf->large_client_header_buffers.size; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + max = r->headers_in.content_length_n; + + if (max == -1 && clcf->client_max_body_size) { + max = clcf->client_max_body_size; + } + + out = NULL; + ll = &out; + last = 0; + + for (cl = in; cl; cl = cl->next) { + + ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0, + "http3 body buf " + "t:%d f:%d %p, pos %p, size: %z file: %O, size: %O", + cl->buf->temporary, cl->buf->in_file, + cl->buf->start, cl->buf->pos, + cl->buf->last - cl->buf->pos, + cl->buf->file_pos, + cl->buf->file_last - cl->buf->file_pos); + + if (cl->buf->last_buf) { + last = 1; + } + + b = NULL; + + while (cl->buf->pos < cl->buf->last) { + + if (st->length == 0) { + p = cl->buf->pos; + + rc = ngx_http_v3_parse_data(r->connection, st, cl->buf); + + r->request_length += cl->buf->pos - p; + h3c->total_bytes += cl->buf->pos - p; + + if (ngx_http_v3_check_flood(r->connection) != NGX_OK) { + return NGX_HTTP_CLOSE; + } + + if (rc == NGX_AGAIN) { + continue; + } + + if (rc == NGX_DONE) { + last = 1; + goto done; + } + + if (rc > 0) { + ngx_quic_reset_stream(r->connection, rc); + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "client sent invalid body"); + return NGX_HTTP_BAD_REQUEST; + } + + if (rc == NGX_ERROR) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + /* rc == NGX_OK */ + + if (max != -1 && (uint64_t) (max - rb->received) < st->length) { + + if (r->headers_in.content_length_n != -1) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client intended to send body data " + "larger than declared"); + + return NGX_HTTP_BAD_REQUEST; + } + + ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, + "client intended to send too large " + "body: %O+%ui bytes", + rb->received, st->length); + + return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; + } + + continue; + } + + if (b + && st->length <= 128 + && (uint64_t) (cl->buf->last - cl->buf->pos) >= st->length) + { + rb->received += st->length; + r->request_length += st->length; + h3c->total_bytes += st->length; + h3c->payload_bytes += st->length; + + if (st->length < 8) { + + while (st->length) { + *b->last++ = *cl->buf->pos++; + st->length--; + } + + } else { + ngx_memmove(b->last, cl->buf->pos, st->length); + b->last += st->length; + cl->buf->pos += st->length; + st->length = 0; + } + + continue; + } + + tl = ngx_chain_get_free_buf(r->pool, &rb->free); + if (tl == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + b = tl->buf; + + ngx_memzero(b, sizeof(ngx_buf_t)); + + b->temporary = 1; + b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body; + b->start = cl->buf->pos; + b->pos = cl->buf->pos; + b->last = cl->buf->last; + b->end = cl->buf->end; + b->flush = r->request_body_no_buffering; + + *ll = tl; + ll = &tl->next; + + size = cl->buf->last - cl->buf->pos; + + if (size > st->length) { + cl->buf->pos += (size_t) st->length; + rb->received += st->length; + r->request_length += st->length; + h3c->total_bytes += st->length; + h3c->payload_bytes += st->length; + st->length = 0; + + } else { + st->length -= size; + rb->received += size; + r->request_length += size; + h3c->total_bytes += size; + h3c->payload_bytes += size; + cl->buf->pos = cl->buf->last; + } + + b->last = cl->buf->pos; + } + } + +done: + + if (last) { + + if (st->length > 0) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client prematurely closed stream"); + r->connection->error = 1; + return NGX_HTTP_BAD_REQUEST; + } + + if (r->headers_in.content_length_n == -1) { + r->headers_in.content_length_n = rb->received; + + } else if (r->headers_in.content_length_n != rb->received) { + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "client sent less body data than expected: " + "%O out of %O bytes of request body received", + rb->received, r->headers_in.content_length_n); + return NGX_HTTP_BAD_REQUEST; + } + + rb->rest = 0; + + tl = ngx_chain_get_free_buf(r->pool, &rb->free); + if (tl == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + b = tl->buf; + + ngx_memzero(b, sizeof(ngx_buf_t)); + + b->last_buf = 1; + + *ll = tl; + + } else { + + /* set rb->rest, amount of data we want to see next time */ + + cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); + + rb->rest = (off_t) cscf->large_client_header_buffers.size; + } + + rc = ngx_http_top_request_body_filter(r, out); + + ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out, + (ngx_buf_tag_t) &ngx_http_read_client_request_body); + + return rc; +} diff --git a/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_table.c b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_table.c new file mode 100644 index 000000000..428e7326b --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_table.c @@ -0,0 +1,715 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +#define ngx_http_v3_table_entry_size(n, v) ((n)->len + (v)->len + 32) + + +static ngx_int_t ngx_http_v3_evict(ngx_connection_t *c, size_t target); +static void ngx_http_v3_unblock(void *data); +static ngx_int_t ngx_http_v3_new_entry(ngx_connection_t *c); + + +typedef struct { + ngx_queue_t queue; + ngx_connection_t *connection; + ngx_uint_t *nblocked; +} ngx_http_v3_block_t; + + +static ngx_http_v3_field_t ngx_http_v3_static_table[] = { + + { ngx_string(":authority"), ngx_string("") }, + { ngx_string(":path"), ngx_string("/") }, + { ngx_string("age"), ngx_string("0") }, + { ngx_string("content-disposition"), ngx_string("") }, + { ngx_string("content-length"), ngx_string("0") }, + { ngx_string("cookie"), ngx_string("") }, + { ngx_string("date"), ngx_string("") }, + { ngx_string("etag"), ngx_string("") }, + { ngx_string("if-modified-since"), ngx_string("") }, + { ngx_string("if-none-match"), ngx_string("") }, + { ngx_string("last-modified"), ngx_string("") }, + { ngx_string("link"), ngx_string("") }, + { ngx_string("location"), ngx_string("") }, + { ngx_string("referer"), ngx_string("") }, + { ngx_string("set-cookie"), ngx_string("") }, + { ngx_string(":method"), ngx_string("CONNECT") }, + { ngx_string(":method"), ngx_string("DELETE") }, + { ngx_string(":method"), ngx_string("GET") }, + { ngx_string(":method"), ngx_string("HEAD") }, + { ngx_string(":method"), ngx_string("OPTIONS") }, + { ngx_string(":method"), ngx_string("POST") }, + { ngx_string(":method"), ngx_string("PUT") }, + { ngx_string(":scheme"), ngx_string("http") }, + { ngx_string(":scheme"), ngx_string("https") }, + { ngx_string(":status"), ngx_string("103") }, + { ngx_string(":status"), ngx_string("200") }, + { ngx_string(":status"), ngx_string("304") }, + { ngx_string(":status"), ngx_string("404") }, + { ngx_string(":status"), ngx_string("503") }, + { ngx_string("accept"), ngx_string("*/*") }, + { ngx_string("accept"), + ngx_string("application/dns-message") }, + { ngx_string("accept-encoding"), ngx_string("gzip, deflate, br") }, + { ngx_string("accept-ranges"), ngx_string("bytes") }, + { ngx_string("access-control-allow-headers"), + ngx_string("cache-control") }, + { ngx_string("access-control-allow-headers"), + ngx_string("content-type") }, + { ngx_string("access-control-allow-origin"), + ngx_string("*") }, + { ngx_string("cache-control"), ngx_string("max-age=0") }, + { ngx_string("cache-control"), ngx_string("max-age=2592000") }, + { ngx_string("cache-control"), ngx_string("max-age=604800") }, + { ngx_string("cache-control"), ngx_string("no-cache") }, + { ngx_string("cache-control"), ngx_string("no-store") }, + { ngx_string("cache-control"), + ngx_string("public, max-age=31536000") }, + { ngx_string("content-encoding"), ngx_string("br") }, + { ngx_string("content-encoding"), ngx_string("gzip") }, + { ngx_string("content-type"), + ngx_string("application/dns-message") }, + { ngx_string("content-type"), + ngx_string("application/javascript") }, + { ngx_string("content-type"), ngx_string("application/json") }, + { ngx_string("content-type"), + ngx_string("application/x-www-form-urlencoded") }, + { ngx_string("content-type"), ngx_string("image/gif") }, + { ngx_string("content-type"), ngx_string("image/jpeg") }, + { ngx_string("content-type"), ngx_string("image/png") }, + { ngx_string("content-type"), ngx_string("text/css") }, + { ngx_string("content-type"), + ngx_string("text/html;charset=utf-8") }, + { ngx_string("content-type"), ngx_string("text/plain") }, + { ngx_string("content-type"), + ngx_string("text/plain;charset=utf-8") }, + { ngx_string("range"), ngx_string("bytes=0-") }, + { ngx_string("strict-transport-security"), + ngx_string("max-age=31536000") }, + { ngx_string("strict-transport-security"), + ngx_string("max-age=31536000;includesubdomains") }, + { ngx_string("strict-transport-security"), + ngx_string("max-age=31536000;includesubdomains;preload") }, + { ngx_string("vary"), ngx_string("accept-encoding") }, + { ngx_string("vary"), ngx_string("origin") }, + { ngx_string("x-content-type-options"), + ngx_string("nosniff") }, + { ngx_string("x-xss-protection"), ngx_string("1;mode=block") }, + { ngx_string(":status"), ngx_string("100") }, + { ngx_string(":status"), ngx_string("204") }, + { ngx_string(":status"), ngx_string("206") }, + { ngx_string(":status"), ngx_string("302") }, + { ngx_string(":status"), ngx_string("400") }, + { ngx_string(":status"), ngx_string("403") }, + { ngx_string(":status"), ngx_string("421") }, + { ngx_string(":status"), ngx_string("425") }, + { ngx_string(":status"), ngx_string("500") }, + { ngx_string("accept-language"), ngx_string("") }, + { ngx_string("access-control-allow-credentials"), + ngx_string("FALSE") }, + { ngx_string("access-control-allow-credentials"), + ngx_string("TRUE") }, + { ngx_string("access-control-allow-headers"), + ngx_string("*") }, + { ngx_string("access-control-allow-methods"), + ngx_string("get") }, + { ngx_string("access-control-allow-methods"), + ngx_string("get, post, options") }, + { ngx_string("access-control-allow-methods"), + ngx_string("options") }, + { ngx_string("access-control-expose-headers"), + ngx_string("content-length") }, + { ngx_string("access-control-request-headers"), + ngx_string("content-type") }, + { ngx_string("access-control-request-method"), + ngx_string("get") }, + { ngx_string("access-control-request-method"), + ngx_string("post") }, + { ngx_string("alt-svc"), ngx_string("clear") }, + { ngx_string("authorization"), ngx_string("") }, + { ngx_string("content-security-policy"), + ngx_string("script-src 'none';object-src 'none';base-uri 'none'") }, + { ngx_string("early-data"), ngx_string("1") }, + { ngx_string("expect-ct"), ngx_string("") }, + { ngx_string("forwarded"), ngx_string("") }, + { ngx_string("if-range"), ngx_string("") }, + { ngx_string("origin"), ngx_string("") }, + { ngx_string("purpose"), ngx_string("prefetch") }, + { ngx_string("server"), ngx_string("") }, + { ngx_string("timing-allow-origin"), ngx_string("*") }, + { ngx_string("upgrade-insecure-requests"), + ngx_string("1") }, + { ngx_string("user-agent"), ngx_string("") }, + { ngx_string("x-forwarded-for"), ngx_string("") }, + { ngx_string("x-frame-options"), ngx_string("deny") }, + { ngx_string("x-frame-options"), ngx_string("sameorigin") } +}; + + +ngx_int_t +ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, + ngx_uint_t index, ngx_str_t *value) +{ + ngx_str_t name; + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + if (dynamic) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 ref insert dynamic[%ui] \"%V\"", index, value); + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + if (dt->base + dt->nelts <= index) { + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + + index = dt->base + dt->nelts - 1 - index; + + if (ngx_http_v3_lookup(c, index, &name, NULL) != NGX_OK) { + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + + } else { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 ref insert static[%ui] \"%V\"", index, value); + + if (ngx_http_v3_lookup_static(c, index, &name, NULL) != NGX_OK) { + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + } + + return ngx_http_v3_insert(c, &name, value); +} + + +ngx_int_t +ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) +{ + u_char *p; + size_t size; + ngx_http_v3_field_t *field; + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + size = ngx_http_v3_table_entry_size(name, value); + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + if (size > dt->capacity) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "not enough dynamic table capacity"); + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 insert [%ui] \"%V\":\"%V\", size:%uz", + dt->base + dt->nelts, name, value, size); + + p = ngx_alloc(sizeof(ngx_http_v3_field_t) + name->len + value->len, + c->log); + if (p == NULL) { + return NGX_ERROR; + } + + field = (ngx_http_v3_field_t *) p; + + field->name.data = p + sizeof(ngx_http_v3_field_t); + field->name.len = name->len; + field->value.data = ngx_cpymem(field->name.data, name->data, name->len); + field->value.len = value->len; + ngx_memcpy(field->value.data, value->data, value->len); + + dt->elts[dt->nelts++] = field; + dt->size += size; + + dt->insert_count++; + + if (ngx_http_v3_evict(c, dt->capacity) != NGX_OK) { + return NGX_ERROR; + } + + ngx_post_event(&dt->send_insert_count, &ngx_posted_events); + + if (ngx_http_v3_new_entry(c) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +void +ngx_http_v3_inc_insert_count_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + c = ev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 inc insert count handler"); + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + if (dt->insert_count > dt->ack_insert_count) { + if (ngx_http_v3_send_inc_insert_count(c, + dt->insert_count - dt->ack_insert_count) + != NGX_OK) + { + return; + } + + dt->ack_insert_count = dt->insert_count; + } +} + + +ngx_int_t +ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity) +{ + ngx_uint_t max, prev_max; + ngx_http_v3_field_t **elts; + ngx_http_v3_session_t *h3c; + ngx_http_v3_srv_conf_t *h3scf; + ngx_http_v3_dynamic_table_t *dt; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 set capacity %ui", capacity); + + h3c = ngx_http_v3_get_session(c); + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + + if (capacity > h3scf->max_table_capacity) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client exceeded http3_max_table_capacity limit"); + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + + if (ngx_http_v3_evict(c, capacity) != NGX_OK) { + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + + dt = &h3c->table; + max = capacity / 32; + prev_max = dt->capacity / 32; + + if (max > prev_max) { + elts = ngx_alloc((max + 1) * sizeof(void *), c->log); + if (elts == NULL) { + return NGX_ERROR; + } + + if (dt->elts) { + ngx_memcpy(elts, dt->elts, dt->nelts * sizeof(void *)); + ngx_free(dt->elts); + } + + dt->elts = elts; + } + + dt->capacity = capacity; + + return NGX_OK; +} + + +void +ngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c) +{ + ngx_uint_t n; + ngx_http_v3_dynamic_table_t *dt; + + dt = &h3c->table; + + if (dt->elts == NULL) { + return; + } + + for (n = 0; n < dt->nelts; n++) { + ngx_free(dt->elts[n]); + } + + ngx_free(dt->elts); +} + + +static ngx_int_t +ngx_http_v3_evict(ngx_connection_t *c, size_t target) +{ + size_t size; + ngx_uint_t n; + ngx_http_v3_field_t *field; + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + n = 0; + + while (dt->size > target) { + field = dt->elts[n++]; + size = ngx_http_v3_table_entry_size(&field->name, &field->value); + + ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 evict [%ui] \"%V\":\"%V\" size:%uz", + dt->base, &field->name, &field->value, size); + + ngx_free(field); + dt->size -= size; + } + + if (n) { + dt->nelts -= n; + dt->base += n; + ngx_memmove(dt->elts, &dt->elts[n], dt->nelts * sizeof(void *)); + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index) +{ + ngx_str_t name, value; + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 duplicate %ui", index); + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + if (dt->base + dt->nelts <= index) { + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + + index = dt->base + dt->nelts - 1 - index; + + if (ngx_http_v3_lookup(c, index, &name, &value) != NGX_OK) { + return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR; + } + + return ngx_http_v3_insert(c, &name, &value); +} + + +ngx_int_t +ngx_http_v3_ack_section(ngx_connection_t *c, ngx_uint_t stream_id) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 ack section %ui", stream_id); + + /* we do not use dynamic tables */ + + return NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR; +} + + +ngx_int_t +ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 increment insert count %ui", inc); + + /* we do not use dynamic tables */ + + return NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR; +} + + +ngx_int_t +ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index, + ngx_str_t *name, ngx_str_t *value) +{ + ngx_uint_t nelts; + ngx_http_v3_field_t *field; + + nelts = sizeof(ngx_http_v3_static_table) + / sizeof(ngx_http_v3_static_table[0]); + + if (index >= nelts) { + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 static[%ui] lookup out of bounds: %ui", + index, nelts); + return NGX_ERROR; + } + + field = &ngx_http_v3_static_table[index]; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 static[%ui] lookup \"%V\":\"%V\"", + index, &field->name, &field->value); + + if (name) { + *name = field->name; + } + + if (value) { + *value = field->value; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, ngx_str_t *name, + ngx_str_t *value) +{ + ngx_http_v3_field_t *field; + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + if (index < dt->base || index - dt->base >= dt->nelts) { + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 dynamic[%ui] lookup out of bounds: [%ui,%ui]", + index, dt->base, dt->base + dt->nelts); + return NGX_ERROR; + } + + field = dt->elts[index - dt->base]; + + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 dynamic[%ui] lookup \"%V\":\"%V\"", + index, &field->name, &field->value); + + if (name) { + *name = field->name; + } + + if (value) { + *value = field->value; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count) +{ + ngx_uint_t max_entries, full_range, max_value, + max_wrapped, req_insert_count; + ngx_http_v3_srv_conf_t *h3scf; + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + /* QPACK 4.5.1.1. Required Insert Count */ + + if (*insert_count == 0) { + return NGX_OK; + } + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + + max_entries = h3scf->max_table_capacity / 32; + full_range = 2 * max_entries; + + if (*insert_count > full_range) { + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; + } + + max_value = dt->base + dt->nelts + max_entries; + max_wrapped = (max_value / full_range) * full_range; + req_insert_count = max_wrapped + *insert_count - 1; + + if (req_insert_count > max_value) { + if (req_insert_count <= full_range) { + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; + } + + req_insert_count -= full_range; + } + + if (req_insert_count == 0) { + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 decode insert_count %ui -> %ui", + *insert_count, req_insert_count); + + *insert_count = req_insert_count; + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count) +{ + size_t n; + ngx_pool_cleanup_t *cln; + ngx_http_v3_block_t *block; + ngx_http_v3_session_t *h3c; + ngx_http_v3_srv_conf_t *h3scf; + ngx_http_v3_dynamic_table_t *dt; + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + n = dt->base + dt->nelts; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 check insert count req:%ui, have:%ui", + insert_count, n); + + if (n >= insert_count) { + return NGX_OK; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 block stream"); + + block = NULL; + + for (cln = c->pool->cleanup; cln; cln = cln->next) { + if (cln->handler == ngx_http_v3_unblock) { + block = cln->data; + break; + } + } + + if (block == NULL) { + cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_http_v3_block_t)); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->handler = ngx_http_v3_unblock; + + block = cln->data; + block->queue.prev = NULL; + block->connection = c; + block->nblocked = &h3c->nblocked; + } + + if (block->queue.prev == NULL) { + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + + if (h3c->nblocked == h3scf->max_blocked_streams) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client exceeded http3_max_blocked_streams limit"); + + ngx_http_v3_finalize_connection(c, + NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED, + "too many blocked streams"); + return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED; + } + + h3c->nblocked++; + ngx_queue_insert_tail(&h3c->blocked, &block->queue); + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 blocked:%ui", h3c->nblocked); + + return NGX_BUSY; +} + + +void +ngx_http_v3_ack_insert_count(ngx_connection_t *c, uint64_t insert_count) +{ + ngx_http_v3_session_t *h3c; + ngx_http_v3_dynamic_table_t *dt; + + h3c = ngx_http_v3_get_session(c); + dt = &h3c->table; + + if (dt->ack_insert_count < insert_count) { + dt->ack_insert_count = insert_count; + } +} + + +static void +ngx_http_v3_unblock(void *data) +{ + ngx_http_v3_block_t *block = data; + + if (block->queue.prev) { + ngx_queue_remove(&block->queue); + block->queue.prev = NULL; + (*block->nblocked)--; + } +} + + +static ngx_int_t +ngx_http_v3_new_entry(ngx_connection_t *c) +{ + ngx_queue_t *q; + ngx_connection_t *bc; + ngx_http_v3_block_t *block; + ngx_http_v3_session_t *h3c; + + h3c = ngx_http_v3_get_session(c); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 new dynamic entry, blocked:%ui", h3c->nblocked); + + while (!ngx_queue_empty(&h3c->blocked)) { + q = ngx_queue_head(&h3c->blocked); + block = (ngx_http_v3_block_t *) q; + bc = block->connection; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, bc->log, 0, "http3 unblock stream"); + + ngx_http_v3_unblock(block); + ngx_post_event(bc->read, &ngx_posted_events); + } + + return NGX_OK; +} + + +ngx_int_t +ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, uint64_t value) +{ + switch (id) { + + case NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY: + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 param QPACK_MAX_TABLE_CAPACITY:%uL", value); + break; + + case NGX_HTTP_V3_PARAM_MAX_FIELD_SECTION_SIZE: + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 param SETTINGS_MAX_FIELD_SECTION_SIZE:%uL", + value); + break; + + case NGX_HTTP_V3_PARAM_BLOCKED_STREAMS: + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 param QPACK_BLOCKED_STREAMS:%uL", value); + break; + + default: + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 param #%uL:%uL", id, value); + } + + return NGX_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_table.h b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_table.h new file mode 100644 index 000000000..1c2fb17b9 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_table.h @@ -0,0 +1,58 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_HTTP_V3_TABLE_H_INCLUDED_ +#define _NGX_HTTP_V3_TABLE_H_INCLUDED_ + + +#include +#include +#include + + +typedef struct { + ngx_str_t name; + ngx_str_t value; +} ngx_http_v3_field_t; + + +typedef struct { + ngx_http_v3_field_t **elts; + ngx_uint_t nelts; + ngx_uint_t base; + size_t size; + size_t capacity; + uint64_t insert_count; + uint64_t ack_insert_count; + ngx_event_t send_insert_count; +} ngx_http_v3_dynamic_table_t; + + +void ngx_http_v3_inc_insert_count_handler(ngx_event_t *ev); +void ngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c); +ngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic, + ngx_uint_t index, ngx_str_t *value); +ngx_int_t ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, + ngx_str_t *value); +ngx_int_t ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity); +ngx_int_t ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index); +ngx_int_t ngx_http_v3_ack_section(ngx_connection_t *c, ngx_uint_t stream_id); +ngx_int_t ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc); +ngx_int_t ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index, + ngx_str_t *name, ngx_str_t *value); +ngx_int_t ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, + ngx_str_t *name, ngx_str_t *value); +ngx_int_t ngx_http_v3_decode_insert_count(ngx_connection_t *c, + ngx_uint_t *insert_count); +ngx_int_t ngx_http_v3_check_insert_count(ngx_connection_t *c, + ngx_uint_t insert_count); +void ngx_http_v3_ack_insert_count(ngx_connection_t *c, uint64_t insert_count); +ngx_int_t ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, + uint64_t value); + + +#endif /* _NGX_HTTP_V3_TABLE_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_uni.c b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_uni.c new file mode 100644 index 000000000..302064b8b --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_uni.c @@ -0,0 +1,622 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_http_v3_parse_uni_t parse; + ngx_int_t index; +} ngx_http_v3_uni_stream_t; + + +static void ngx_http_v3_close_uni_stream(ngx_connection_t *c); +static void ngx_http_v3_uni_read_handler(ngx_event_t *rev); +static void ngx_http_v3_uni_dummy_read_handler(ngx_event_t *wev); +static void ngx_http_v3_uni_dummy_write_handler(ngx_event_t *wev); + + +void +ngx_http_v3_init_uni_stream(ngx_connection_t *c) +{ + uint64_t n; + ngx_http_v3_session_t *h3c; + ngx_http_v3_uni_stream_t *us; + + h3c = ngx_http_v3_get_session(c); + if (h3c->hq) { + ngx_http_v3_finalize_connection(c, + NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, + "uni stream in hq mode"); + c->data = NULL; + ngx_http_v3_close_uni_stream(c); + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init uni stream"); + + n = c->quic->id >> 2; + + if (n >= NGX_HTTP_V3_MAX_UNI_STREAMS) { + ngx_http_v3_finalize_connection(c, + NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, + "reached maximum number of uni streams"); + c->data = NULL; + ngx_http_v3_close_uni_stream(c); + return; + } + + ngx_quic_cancelable_stream(c); + + us = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_uni_stream_t)); + if (us == NULL) { + ngx_http_v3_finalize_connection(c, + NGX_HTTP_V3_ERR_INTERNAL_ERROR, + "memory allocation error"); + c->data = NULL; + ngx_http_v3_close_uni_stream(c); + return; + } + + us->index = -1; + + c->data = us; + + c->read->handler = ngx_http_v3_uni_read_handler; + c->write->handler = ngx_http_v3_uni_dummy_write_handler; + + ngx_http_v3_uni_read_handler(c->read); +} + + +static void +ngx_http_v3_close_uni_stream(ngx_connection_t *c) +{ + ngx_pool_t *pool; + ngx_http_v3_session_t *h3c; + ngx_http_v3_uni_stream_t *us; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 close stream"); + + us = c->data; + + if (us && us->index >= 0) { + h3c = ngx_http_v3_get_session(c); + h3c->known_streams[us->index] = NULL; + } + + c->destroyed = 1; + + pool = c->pool; + + ngx_close_connection(c); + + ngx_destroy_pool(pool); +} + + +ngx_int_t +ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type) +{ + ngx_int_t index; + ngx_http_v3_session_t *h3c; + ngx_http_v3_uni_stream_t *us; + + h3c = ngx_http_v3_get_session(c); + + switch (type) { + + case NGX_HTTP_V3_STREAM_ENCODER: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 encoder stream"); + index = NGX_HTTP_V3_STREAM_CLIENT_ENCODER; + break; + + case NGX_HTTP_V3_STREAM_DECODER: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 decoder stream"); + index = NGX_HTTP_V3_STREAM_CLIENT_DECODER; + break; + + case NGX_HTTP_V3_STREAM_CONTROL: + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 control stream"); + index = NGX_HTTP_V3_STREAM_CLIENT_CONTROL; + + break; + + default: + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 stream 0x%02xL", type); + + if (h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_ENCODER] == NULL + || h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_DECODER] == NULL + || h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_CONTROL] == NULL) + { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "missing mandatory stream"); + return NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR; + } + + index = -1; + } + + if (index >= 0) { + if (h3c->known_streams[index]) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "stream exists"); + return NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR; + } + + h3c->known_streams[index] = c; + + us = c->data; + us->index = index; + } + + return NGX_OK; +} + + +static void +ngx_http_v3_uni_read_handler(ngx_event_t *rev) +{ + u_char buf[128]; + ssize_t n; + ngx_buf_t b; + ngx_int_t rc; + ngx_connection_t *c; + ngx_http_v3_session_t *h3c; + ngx_http_v3_uni_stream_t *us; + + c = rev->data; + us = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read handler"); + + if (c->close) { + ngx_http_v3_close_uni_stream(c); + return; + } + + ngx_memzero(&b, sizeof(ngx_buf_t)); + + while (rev->ready) { + + n = c->recv(c, buf, sizeof(buf)); + + if (n == NGX_ERROR) { + rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR; + goto failed; + } + + if (n == 0) { + if (us->index >= 0) { + rc = NGX_HTTP_V3_ERR_CLOSED_CRITICAL_STREAM; + goto failed; + } + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 read eof"); + ngx_http_v3_close_uni_stream(c); + return; + } + + if (n == NGX_AGAIN) { + break; + } + + b.pos = buf; + b.last = buf + n; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (ngx_http_v3_check_flood(c) != NGX_OK) { + ngx_http_v3_close_uni_stream(c); + return; + } + + rc = ngx_http_v3_parse_uni(c, &us->parse, &b); + + if (rc == NGX_DONE) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 read done"); + ngx_http_v3_close_uni_stream(c); + return; + } + + if (rc > 0) { + goto failed; + } + + if (rc != NGX_AGAIN) { + rc = NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR; + goto failed; + } + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR; + goto failed; + } + + return; + +failed: + + ngx_http_v3_finalize_connection(c, rc, "stream error"); + ngx_http_v3_close_uni_stream(c); +} + + +static void +ngx_http_v3_uni_dummy_read_handler(ngx_event_t *rev) +{ + u_char ch; + ngx_connection_t *c; + + c = rev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 dummy read handler"); + + if (c->close) { + ngx_http_v3_close_uni_stream(c); + return; + } + + if (rev->ready) { + if (c->recv(c, &ch, 1) != 0) { + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR, NULL); + ngx_http_v3_close_uni_stream(c); + return; + } + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, + NULL); + ngx_http_v3_close_uni_stream(c); + } +} + + +static void +ngx_http_v3_uni_dummy_write_handler(ngx_event_t *wev) +{ + ngx_connection_t *c; + + c = wev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 dummy write handler"); + + if (ngx_handle_write_event(wev, 0) != NGX_OK) { + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR, + NULL); + ngx_http_v3_close_uni_stream(c); + } +} + + +ngx_connection_t * +ngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type) +{ + u_char buf[NGX_HTTP_V3_VARLEN_INT_LEN]; + size_t n; + ngx_int_t index; + ngx_connection_t *sc; + ngx_http_v3_session_t *h3c; + ngx_http_v3_uni_stream_t *us; + + switch (type) { + case NGX_HTTP_V3_STREAM_ENCODER: + index = NGX_HTTP_V3_STREAM_SERVER_ENCODER; + break; + case NGX_HTTP_V3_STREAM_DECODER: + index = NGX_HTTP_V3_STREAM_SERVER_DECODER; + break; + case NGX_HTTP_V3_STREAM_CONTROL: + index = NGX_HTTP_V3_STREAM_SERVER_CONTROL; + break; + default: + index = -1; + } + + h3c = ngx_http_v3_get_session(c); + + if (index >= 0) { + if (h3c->known_streams[index]) { + return h3c->known_streams[index]; + } + } + + sc = ngx_quic_open_stream(c, 0); + if (sc == NULL) { + goto failed; + } + + ngx_quic_cancelable_stream(sc); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 create uni stream, type:%ui", type); + + us = ngx_pcalloc(sc->pool, sizeof(ngx_http_v3_uni_stream_t)); + if (us == NULL) { + goto failed; + } + + us->index = index; + + sc->data = us; + + sc->read->handler = ngx_http_v3_uni_dummy_read_handler; + sc->write->handler = ngx_http_v3_uni_dummy_write_handler; + + if (index >= 0) { + h3c->known_streams[index] = sc; + } + + n = (u_char *) ngx_http_v3_encode_varlen_int(buf, type) - buf; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (sc->send(sc, buf, n) != (ssize_t) n) { + goto failed; + } + + ngx_post_event(sc->read, &ngx_posted_events); + + return sc; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to create server stream"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR, + "failed to create server stream"); + if (sc) { + ngx_http_v3_close_uni_stream(sc); + } + + return NULL; +} + + +ngx_int_t +ngx_http_v3_send_settings(ngx_connection_t *c) +{ + u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 6]; + size_t n; + ngx_connection_t *cc; + ngx_http_v3_session_t *h3c; + ngx_http_v3_srv_conf_t *h3scf; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send settings"); + + cc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL); + if (cc == NULL) { + return NGX_ERROR; + } + + h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module); + + n = ngx_http_v3_encode_varlen_int(NULL, + NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY); + n += ngx_http_v3_encode_varlen_int(NULL, h3scf->max_table_capacity); + n += ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_PARAM_BLOCKED_STREAMS); + n += ngx_http_v3_encode_varlen_int(NULL, h3scf->max_blocked_streams); + + p = (u_char *) ngx_http_v3_encode_varlen_int(buf, + NGX_HTTP_V3_FRAME_SETTINGS); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, n); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, + NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, h3scf->max_table_capacity); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, + NGX_HTTP_V3_PARAM_BLOCKED_STREAMS); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, h3scf->max_blocked_streams); + n = p - buf; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (cc->send(cc, buf, n) != (ssize_t) n) { + goto failed; + } + + return NGX_OK; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to send settings"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "failed to send settings"); + ngx_http_v3_close_uni_stream(cc); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id) +{ + u_char *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 3]; + size_t n; + ngx_connection_t *cc; + ngx_http_v3_session_t *h3c; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 send goaway %uL", id); + + cc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL); + if (cc == NULL) { + return NGX_ERROR; + } + + n = ngx_http_v3_encode_varlen_int(NULL, id); + p = (u_char *) ngx_http_v3_encode_varlen_int(buf, NGX_HTTP_V3_FRAME_GOAWAY); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, n); + p = (u_char *) ngx_http_v3_encode_varlen_int(p, id); + n = p - buf; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (cc->send(cc, buf, n) != (ssize_t) n) { + goto failed; + } + + return NGX_OK; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to send goaway"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "failed to send goaway"); + ngx_http_v3_close_uni_stream(cc); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_http_v3_send_ack_section(ngx_connection_t *c, ngx_uint_t stream_id) +{ + u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; + size_t n; + ngx_connection_t *dc; + ngx_http_v3_session_t *h3c; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 send section acknowledgement %ui", stream_id); + + dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); + if (dc == NULL) { + return NGX_ERROR; + } + + buf[0] = 0x80; + n = (u_char *) ngx_http_v3_encode_prefix_int(buf, stream_id, 7) - buf; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (dc->send(dc, buf, n) != (ssize_t) n) { + goto failed; + } + + return NGX_OK; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "failed to send section acknowledgement"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "failed to send section acknowledgement"); + ngx_http_v3_close_uni_stream(dc); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_http_v3_send_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) +{ + u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; + size_t n; + ngx_connection_t *dc; + ngx_http_v3_session_t *h3c; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 send stream cancellation %ui", stream_id); + + dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); + if (dc == NULL) { + return NGX_ERROR; + } + + buf[0] = 0x40; + n = (u_char *) ngx_http_v3_encode_prefix_int(buf, stream_id, 6) - buf; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (dc->send(dc, buf, n) != (ssize_t) n) { + goto failed; + } + + return NGX_OK; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, "failed to send stream cancellation"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "failed to send stream cancellation"); + ngx_http_v3_close_uni_stream(dc); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_http_v3_send_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc) +{ + u_char buf[NGX_HTTP_V3_PREFIX_INT_LEN]; + size_t n; + ngx_connection_t *dc; + ngx_http_v3_session_t *h3c; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 send insert count increment %ui", inc); + + dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER); + if (dc == NULL) { + return NGX_ERROR; + } + + buf[0] = 0; + n = (u_char *) ngx_http_v3_encode_prefix_int(buf, inc, 6) - buf; + + h3c = ngx_http_v3_get_session(c); + h3c->total_bytes += n; + + if (dc->send(dc, buf, n) != (ssize_t) n) { + goto failed; + } + + return NGX_OK; + +failed: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "failed to send insert count increment"); + + ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD, + "failed to send insert count increment"); + ngx_http_v3_close_uni_stream(dc); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http3 cancel stream %ui", stream_id); + + /* we do not use dynamic tables */ + + return NGX_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_uni.h b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_uni.h new file mode 100644 index 000000000..280589469 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/http/v3/ngx_http_v3_uni.h @@ -0,0 +1,34 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_HTTP_V3_UNI_H_INCLUDED_ +#define _NGX_HTTP_V3_UNI_H_INCLUDED_ + + +#include +#include +#include + + +void ngx_http_v3_init_uni_stream(ngx_connection_t *c); +ngx_int_t ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type); + +ngx_int_t ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id); + +ngx_connection_t *ngx_http_v3_get_uni_stream(ngx_connection_t *c, + ngx_uint_t type); +ngx_int_t ngx_http_v3_send_settings(ngx_connection_t *c); +ngx_int_t ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id); +ngx_int_t ngx_http_v3_send_ack_section(ngx_connection_t *c, + ngx_uint_t stream_id); +ngx_int_t ngx_http_v3_send_cancel_stream(ngx_connection_t *c, + ngx_uint_t stream_id); +ngx_int_t ngx_http_v3_send_inc_insert_count(ngx_connection_t *c, + ngx_uint_t inc); + + +#endif /* _NGX_HTTP_V3_UNI_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/mail/ngx_mail.c b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail.c new file mode 100644 index 000000000..cc34cb365 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail.c @@ -0,0 +1,485 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include + + +static char *ngx_mail_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static ngx_int_t ngx_mail_add_ports(ngx_conf_t *cf, ngx_array_t *ports, + ngx_mail_listen_t *listen); +static char *ngx_mail_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports); +static ngx_int_t ngx_mail_add_addrs(ngx_conf_t *cf, ngx_mail_port_t *mport, + ngx_mail_conf_addr_t *addr); +#if (NGX_HAVE_INET6) +static ngx_int_t ngx_mail_add_addrs6(ngx_conf_t *cf, ngx_mail_port_t *mport, + ngx_mail_conf_addr_t *addr); +#endif +static ngx_int_t ngx_mail_cmp_conf_addrs(const void *one, const void *two); + + +ngx_uint_t ngx_mail_max_module; + + +static ngx_command_t ngx_mail_commands[] = { + + { ngx_string("mail"), + NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_mail_block, + 0, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_core_module_t ngx_mail_module_ctx = { + ngx_string("mail"), + NULL, + NULL +}; + + +ngx_module_t ngx_mail_module = { + NGX_MODULE_V1, + &ngx_mail_module_ctx, /* module context */ + ngx_mail_commands, /* module directives */ + NGX_CORE_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static char * +ngx_mail_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *rv; + ngx_uint_t i, m, mi, s; + ngx_conf_t pcf; + ngx_array_t ports; + ngx_mail_listen_t *listen; + ngx_mail_module_t *module; + ngx_mail_conf_ctx_t *ctx; + ngx_mail_core_srv_conf_t **cscfp; + ngx_mail_core_main_conf_t *cmcf; + + if (*(ngx_mail_conf_ctx_t **) conf) { + return "is duplicate"; + } + + /* the main mail context */ + + ctx = ngx_pcalloc(cf->pool, sizeof(ngx_mail_conf_ctx_t)); + if (ctx == NULL) { + return NGX_CONF_ERROR; + } + + *(ngx_mail_conf_ctx_t **) conf = ctx; + + /* count the number of the mail modules and set up their indices */ + + ngx_mail_max_module = ngx_count_modules(cf->cycle, NGX_MAIL_MODULE); + + + /* the mail main_conf context, it is the same in the all mail contexts */ + + ctx->main_conf = ngx_pcalloc(cf->pool, + sizeof(void *) * ngx_mail_max_module); + if (ctx->main_conf == NULL) { + return NGX_CONF_ERROR; + } + + + /* + * the mail null srv_conf context, it is used to merge + * the server{}s' srv_conf's + */ + + ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_mail_max_module); + if (ctx->srv_conf == NULL) { + return NGX_CONF_ERROR; + } + + + /* + * create the main_conf's and the null srv_conf's of the all mail modules + */ + + for (m = 0; cf->cycle->modules[m]; m++) { + if (cf->cycle->modules[m]->type != NGX_MAIL_MODULE) { + continue; + } + + module = cf->cycle->modules[m]->ctx; + mi = cf->cycle->modules[m]->ctx_index; + + if (module->create_main_conf) { + ctx->main_conf[mi] = module->create_main_conf(cf); + if (ctx->main_conf[mi] == NULL) { + return NGX_CONF_ERROR; + } + } + + if (module->create_srv_conf) { + ctx->srv_conf[mi] = module->create_srv_conf(cf); + if (ctx->srv_conf[mi] == NULL) { + return NGX_CONF_ERROR; + } + } + } + + + /* parse inside the mail{} block */ + + pcf = *cf; + cf->ctx = ctx; + + cf->module_type = NGX_MAIL_MODULE; + cf->cmd_type = NGX_MAIL_MAIN_CONF; + rv = ngx_conf_parse(cf, NULL); + + if (rv != NGX_CONF_OK) { + *cf = pcf; + return rv; + } + + + /* init mail{} main_conf's, merge the server{}s' srv_conf's */ + + cmcf = ctx->main_conf[ngx_mail_core_module.ctx_index]; + cscfp = cmcf->servers.elts; + + for (m = 0; cf->cycle->modules[m]; m++) { + if (cf->cycle->modules[m]->type != NGX_MAIL_MODULE) { + continue; + } + + module = cf->cycle->modules[m]->ctx; + mi = cf->cycle->modules[m]->ctx_index; + + /* init mail{} main_conf's */ + + cf->ctx = ctx; + + if (module->init_main_conf) { + rv = module->init_main_conf(cf, ctx->main_conf[mi]); + if (rv != NGX_CONF_OK) { + *cf = pcf; + return rv; + } + } + + for (s = 0; s < cmcf->servers.nelts; s++) { + + /* merge the server{}s' srv_conf's */ + + cf->ctx = cscfp[s]->ctx; + + if (module->merge_srv_conf) { + rv = module->merge_srv_conf(cf, + ctx->srv_conf[mi], + cscfp[s]->ctx->srv_conf[mi]); + if (rv != NGX_CONF_OK) { + *cf = pcf; + return rv; + } + } + } + } + + *cf = pcf; + + + if (ngx_array_init(&ports, cf->temp_pool, 4, sizeof(ngx_mail_conf_port_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + listen = cmcf->listen.elts; + + for (i = 0; i < cmcf->listen.nelts; i++) { + if (ngx_mail_add_ports(cf, &ports, &listen[i]) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + return ngx_mail_optimize_servers(cf, &ports); +} + + +static ngx_int_t +ngx_mail_add_ports(ngx_conf_t *cf, ngx_array_t *ports, + ngx_mail_listen_t *listen) +{ + in_port_t p; + ngx_uint_t i; + struct sockaddr *sa; + ngx_mail_conf_port_t *port; + ngx_mail_conf_addr_t *addr; + + sa = listen->sockaddr; + p = ngx_inet_get_port(sa); + + port = ports->elts; + for (i = 0; i < ports->nelts; i++) { + if (p == port[i].port && sa->sa_family == port[i].family) { + + /* a port is already in the port list */ + + port = &port[i]; + goto found; + } + } + + /* add a port to the port list */ + + port = ngx_array_push(ports); + if (port == NULL) { + return NGX_ERROR; + } + + port->family = sa->sa_family; + port->port = p; + + if (ngx_array_init(&port->addrs, cf->temp_pool, 2, + sizeof(ngx_mail_conf_addr_t)) + != NGX_OK) + { + return NGX_ERROR; + } + +found: + + addr = ngx_array_push(&port->addrs); + if (addr == NULL) { + return NGX_ERROR; + } + + addr->opt = *listen; + + return NGX_OK; +} + + +static char * +ngx_mail_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports) +{ + ngx_uint_t i, p, last, bind_wildcard; + ngx_listening_t *ls; + ngx_mail_port_t *mport; + ngx_mail_conf_port_t *port; + ngx_mail_conf_addr_t *addr; + ngx_mail_core_srv_conf_t *cscf; + + port = ports->elts; + for (p = 0; p < ports->nelts; p++) { + + ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts, + sizeof(ngx_mail_conf_addr_t), ngx_mail_cmp_conf_addrs); + + addr = port[p].addrs.elts; + last = port[p].addrs.nelts; + + /* + * if there is the binding to the "*:port" then we need to bind() + * to the "*:port" only and ignore the other bindings + */ + + if (addr[last - 1].opt.wildcard) { + addr[last - 1].opt.bind = 1; + bind_wildcard = 1; + + } else { + bind_wildcard = 0; + } + + i = 0; + + while (i < last) { + + if (bind_wildcard && !addr[i].opt.bind) { + i++; + continue; + } + + ls = ngx_create_listening(cf, addr[i].opt.sockaddr, + addr[i].opt.socklen); + if (ls == NULL) { + return NGX_CONF_ERROR; + } + + ls->addr_ntop = 1; + ls->handler = ngx_mail_init_connection; + ls->pool_size = 256; + + cscf = addr->opt.ctx->srv_conf[ngx_mail_core_module.ctx_index]; + + ls->logp = cscf->error_log; + ls->log.data = &ls->addr_text; + ls->log.handler = ngx_accept_log_error; + + ls->protocol = addr[i].opt.protocol; + ls->backlog = addr[i].opt.backlog; + ls->rcvbuf = addr[i].opt.rcvbuf; + ls->sndbuf = addr[i].opt.sndbuf; + + ls->keepalive = addr[i].opt.so_keepalive; +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + ls->keepidle = addr[i].opt.tcp_keepidle; + ls->keepintvl = addr[i].opt.tcp_keepintvl; + ls->keepcnt = addr[i].opt.tcp_keepcnt; +#endif + +#if (NGX_HAVE_INET6) + ls->ipv6only = addr[i].opt.ipv6only; +#endif + + mport = ngx_palloc(cf->pool, sizeof(ngx_mail_port_t)); + if (mport == NULL) { + return NGX_CONF_ERROR; + } + + ls->servers = mport; + + mport->naddrs = i + 1; + + switch (ls->sockaddr->sa_family) { +#if (NGX_HAVE_INET6) + case AF_INET6: + if (ngx_mail_add_addrs6(cf, mport, addr) != NGX_OK) { + return NGX_CONF_ERROR; + } + break; +#endif + default: /* AF_INET */ + if (ngx_mail_add_addrs(cf, mport, addr) != NGX_OK) { + return NGX_CONF_ERROR; + } + break; + } + + addr++; + last--; + } + } + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_mail_add_addrs(ngx_conf_t *cf, ngx_mail_port_t *mport, + ngx_mail_conf_addr_t *addr) +{ + ngx_uint_t i; + ngx_mail_in_addr_t *addrs; + struct sockaddr_in *sin; + + mport->addrs = ngx_pcalloc(cf->pool, + mport->naddrs * sizeof(ngx_mail_in_addr_t)); + if (mport->addrs == NULL) { + return NGX_ERROR; + } + + addrs = mport->addrs; + + for (i = 0; i < mport->naddrs; i++) { + + sin = (struct sockaddr_in *) addr[i].opt.sockaddr; + addrs[i].addr = sin->sin_addr.s_addr; + + addrs[i].conf.ctx = addr[i].opt.ctx; +#if (NGX_MAIL_SSL) + addrs[i].conf.ssl = addr[i].opt.ssl; +#endif + addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; + addrs[i].conf.addr_text = addr[i].opt.addr_text; + } + + return NGX_OK; +} + + +#if (NGX_HAVE_INET6) + +static ngx_int_t +ngx_mail_add_addrs6(ngx_conf_t *cf, ngx_mail_port_t *mport, + ngx_mail_conf_addr_t *addr) +{ + ngx_uint_t i; + ngx_mail_in6_addr_t *addrs6; + struct sockaddr_in6 *sin6; + + mport->addrs = ngx_pcalloc(cf->pool, + mport->naddrs * sizeof(ngx_mail_in6_addr_t)); + if (mport->addrs == NULL) { + return NGX_ERROR; + } + + addrs6 = mport->addrs; + + for (i = 0; i < mport->naddrs; i++) { + + sin6 = (struct sockaddr_in6 *) addr[i].opt.sockaddr; + addrs6[i].addr6 = sin6->sin6_addr; + + addrs6[i].conf.ctx = addr[i].opt.ctx; +#if (NGX_MAIL_SSL) + addrs6[i].conf.ssl = addr[i].opt.ssl; +#endif + addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; + addrs6[i].conf.addr_text = addr[i].opt.addr_text; + } + + return NGX_OK; +} + +#endif + + +static ngx_int_t +ngx_mail_cmp_conf_addrs(const void *one, const void *two) +{ + ngx_mail_conf_addr_t *first, *second; + + first = (ngx_mail_conf_addr_t *) one; + second = (ngx_mail_conf_addr_t *) two; + + if (first->opt.wildcard) { + /* a wildcard must be the last resort, shift it to the end */ + return 1; + } + + if (second->opt.wildcard) { + /* a wildcard must be the last resort, shift it to the end */ + return -1; + } + + if (first->opt.bind && !second->opt.bind) { + /* shift explicit bind()ed addresses to the start */ + return -1; + } + + if (!first->opt.bind && second->opt.bind) { + /* shift explicit bind()ed addresses to the start */ + return 1; + } + + /* do not sort by default */ + + return 0; +} diff --git a/src/deps/src/nginx-1.30.1/src/mail/ngx_mail.h b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail.h new file mode 100644 index 000000000..221faf67d --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail.h @@ -0,0 +1,425 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_MAIL_H_INCLUDED_ +#define _NGX_MAIL_H_INCLUDED_ + + +#include +#include +#include +#include + +#if (NGX_MAIL_SSL) +#include +#endif + + + +typedef struct { + void **main_conf; + void **srv_conf; +} ngx_mail_conf_ctx_t; + + +typedef struct { + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_str_t addr_text; + + /* server ctx */ + ngx_mail_conf_ctx_t *ctx; + + unsigned bind:1; + unsigned wildcard:1; + unsigned ssl:1; +#if (NGX_HAVE_INET6) + unsigned ipv6only:1; +#endif + unsigned so_keepalive:2; + unsigned proxy_protocol:1; +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + int tcp_keepidle; + int tcp_keepintvl; + int tcp_keepcnt; +#endif + int protocol; + int backlog; + int rcvbuf; + int sndbuf; +} ngx_mail_listen_t; + + +typedef struct { + ngx_mail_conf_ctx_t *ctx; + ngx_str_t addr_text; + unsigned ssl:1; + unsigned proxy_protocol:1; +} ngx_mail_addr_conf_t; + +typedef struct { + in_addr_t addr; + ngx_mail_addr_conf_t conf; +} ngx_mail_in_addr_t; + + +#if (NGX_HAVE_INET6) + +typedef struct { + struct in6_addr addr6; + ngx_mail_addr_conf_t conf; +} ngx_mail_in6_addr_t; + +#endif + + +typedef struct { + /* ngx_mail_in_addr_t or ngx_mail_in6_addr_t */ + void *addrs; + ngx_uint_t naddrs; +} ngx_mail_port_t; + + +typedef struct { + int family; + in_port_t port; + ngx_array_t addrs; /* array of ngx_mail_conf_addr_t */ +} ngx_mail_conf_port_t; + + +typedef struct { + ngx_mail_listen_t opt; +} ngx_mail_conf_addr_t; + + +typedef struct { + ngx_array_t servers; /* ngx_mail_core_srv_conf_t */ + ngx_array_t listen; /* ngx_mail_listen_t */ +} ngx_mail_core_main_conf_t; + + +#define NGX_MAIL_POP3_PROTOCOL 0 +#define NGX_MAIL_IMAP_PROTOCOL 1 +#define NGX_MAIL_SMTP_PROTOCOL 2 + + +typedef struct ngx_mail_protocol_s ngx_mail_protocol_t; + + +typedef struct { + ngx_mail_protocol_t *protocol; + + ngx_msec_t timeout; + ngx_msec_t resolver_timeout; + + ngx_uint_t max_errors; + + ngx_str_t server_name; + + u_char *file_name; + ngx_uint_t line; + + ngx_resolver_t *resolver; + ngx_log_t *error_log; + + /* server ctx */ + ngx_mail_conf_ctx_t *ctx; + + ngx_uint_t listen; /* unsigned listen:1; */ +} ngx_mail_core_srv_conf_t; + + +typedef enum { + ngx_pop3_start = 0, + ngx_pop3_user, + ngx_pop3_passwd, + ngx_pop3_auth_login_username, + ngx_pop3_auth_login_password, + ngx_pop3_auth_plain, + ngx_pop3_auth_cram_md5, + ngx_pop3_auth_external +} ngx_pop3_state_e; + + +typedef enum { + ngx_imap_start = 0, + ngx_imap_auth_login_username, + ngx_imap_auth_login_password, + ngx_imap_auth_plain, + ngx_imap_auth_cram_md5, + ngx_imap_auth_external, + ngx_imap_login, + ngx_imap_user, + ngx_imap_passwd +} ngx_imap_state_e; + + +typedef enum { + ngx_smtp_start = 0, + ngx_smtp_auth_login_username, + ngx_smtp_auth_login_password, + ngx_smtp_auth_plain, + ngx_smtp_auth_cram_md5, + ngx_smtp_auth_external, + ngx_smtp_helo, + ngx_smtp_helo_xclient, + ngx_smtp_helo_auth, + ngx_smtp_helo_from, + ngx_smtp_xclient, + ngx_smtp_xclient_from, + ngx_smtp_xclient_helo, + ngx_smtp_xclient_auth, + ngx_smtp_from, + ngx_smtp_to +} ngx_smtp_state_e; + + +typedef struct { + ngx_peer_connection_t upstream; + ngx_buf_t *buffer; + ngx_uint_t proxy_protocol; /* unsigned proxy_protocol:1; */ +} ngx_mail_proxy_ctx_t; + + +typedef struct { + uint32_t signature; /* "MAIL" */ + + ngx_connection_t *connection; + + ngx_str_t out; + ngx_buf_t *buffer; + + void **ctx; + void **main_conf; + void **srv_conf; + + ngx_resolver_ctx_t *resolver_ctx; + + ngx_mail_proxy_ctx_t *proxy; + + ngx_uint_t mail_state; + + unsigned ssl:1; + unsigned protocol:3; + unsigned blocked:1; + unsigned quit:1; + unsigned quoted:1; + unsigned backslash:1; + unsigned no_sync_literal:1; + unsigned starttls:1; + unsigned esmtp:1; + unsigned auth_method:3; + unsigned auth_wait:1; + + ngx_str_t login; + ngx_str_t passwd; + + ngx_str_t salt; + ngx_str_t tag; + ngx_str_t tagged_line; + ngx_str_t text; + + ngx_str_t *addr_text; + ngx_str_t host; + ngx_str_t smtp_helo; + ngx_str_t smtp_from; + ngx_str_t smtp_to; + + ngx_str_t cmd; + + ngx_uint_t command; + ngx_array_t args; + + ngx_uint_t errors; + ngx_uint_t login_attempt; + + /* used to parse POP3/IMAP/SMTP command */ + + ngx_uint_t state; + u_char *tag_start; + u_char *cmd_start; + u_char *arg_start; + ngx_uint_t literal_len; +} ngx_mail_session_t; + + +typedef struct { + ngx_str_t *client; + ngx_mail_session_t *session; +} ngx_mail_log_ctx_t; + + +#define NGX_POP3_USER 1 +#define NGX_POP3_PASS 2 +#define NGX_POP3_CAPA 3 +#define NGX_POP3_QUIT 4 +#define NGX_POP3_NOOP 5 +#define NGX_POP3_STLS 6 +#define NGX_POP3_APOP 7 +#define NGX_POP3_AUTH 8 +#define NGX_POP3_STAT 9 +#define NGX_POP3_LIST 10 +#define NGX_POP3_RETR 11 +#define NGX_POP3_DELE 12 +#define NGX_POP3_RSET 13 +#define NGX_POP3_TOP 14 +#define NGX_POP3_UIDL 15 + + +#define NGX_IMAP_LOGIN 1 +#define NGX_IMAP_LOGOUT 2 +#define NGX_IMAP_CAPABILITY 3 +#define NGX_IMAP_NOOP 4 +#define NGX_IMAP_STARTTLS 5 + +#define NGX_IMAP_NEXT 6 + +#define NGX_IMAP_AUTHENTICATE 7 + + +#define NGX_SMTP_HELO 1 +#define NGX_SMTP_EHLO 2 +#define NGX_SMTP_AUTH 3 +#define NGX_SMTP_QUIT 4 +#define NGX_SMTP_NOOP 5 +#define NGX_SMTP_MAIL 6 +#define NGX_SMTP_RSET 7 +#define NGX_SMTP_RCPT 8 +#define NGX_SMTP_DATA 9 +#define NGX_SMTP_VRFY 10 +#define NGX_SMTP_EXPN 11 +#define NGX_SMTP_HELP 12 +#define NGX_SMTP_STARTTLS 13 + + +#define NGX_MAIL_AUTH_PLAIN 0 +#define NGX_MAIL_AUTH_LOGIN 1 +#define NGX_MAIL_AUTH_LOGIN_USERNAME 2 +#define NGX_MAIL_AUTH_APOP 3 +#define NGX_MAIL_AUTH_CRAM_MD5 4 +#define NGX_MAIL_AUTH_EXTERNAL 5 +#define NGX_MAIL_AUTH_NONE 6 + + +#define NGX_MAIL_AUTH_PLAIN_ENABLED 0x0002 +#define NGX_MAIL_AUTH_LOGIN_ENABLED 0x0004 +#define NGX_MAIL_AUTH_APOP_ENABLED 0x0008 +#define NGX_MAIL_AUTH_CRAM_MD5_ENABLED 0x0010 +#define NGX_MAIL_AUTH_EXTERNAL_ENABLED 0x0020 +#define NGX_MAIL_AUTH_NONE_ENABLED 0x0040 + + +#define NGX_MAIL_PARSE_INVALID_COMMAND 20 + + +typedef void (*ngx_mail_init_session_pt)(ngx_mail_session_t *s, + ngx_connection_t *c); +typedef void (*ngx_mail_init_protocol_pt)(ngx_event_t *rev); +typedef void (*ngx_mail_auth_state_pt)(ngx_event_t *rev); +typedef ngx_int_t (*ngx_mail_parse_command_pt)(ngx_mail_session_t *s); + + +struct ngx_mail_protocol_s { + ngx_str_t name; + ngx_str_t alpn; + in_port_t port[4]; + ngx_uint_t type; + + ngx_mail_init_session_pt init_session; + ngx_mail_init_protocol_pt init_protocol; + ngx_mail_parse_command_pt parse_command; + ngx_mail_auth_state_pt auth_state; + + ngx_str_t internal_server_error; + ngx_str_t cert_error; + ngx_str_t no_cert; +}; + + +typedef struct { + ngx_mail_protocol_t *protocol; + + void *(*create_main_conf)(ngx_conf_t *cf); + char *(*init_main_conf)(ngx_conf_t *cf, void *conf); + + void *(*create_srv_conf)(ngx_conf_t *cf); + char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, + void *conf); +} ngx_mail_module_t; + + +#define NGX_MAIL_MODULE 0x4C49414D /* "MAIL" */ + +#define NGX_MAIL_MAIN_CONF 0x02000000 +#define NGX_MAIL_SRV_CONF 0x04000000 + + +#define NGX_MAIL_MAIN_CONF_OFFSET offsetof(ngx_mail_conf_ctx_t, main_conf) +#define NGX_MAIL_SRV_CONF_OFFSET offsetof(ngx_mail_conf_ctx_t, srv_conf) + + +#define ngx_mail_get_module_ctx(s, module) (s)->ctx[module.ctx_index] +#define ngx_mail_set_ctx(s, c, module) s->ctx[module.ctx_index] = c; +#define ngx_mail_delete_ctx(s, module) s->ctx[module.ctx_index] = NULL; + + +#define ngx_mail_get_module_main_conf(s, module) \ + (s)->main_conf[module.ctx_index] +#define ngx_mail_get_module_srv_conf(s, module) (s)->srv_conf[module.ctx_index] + +#define ngx_mail_conf_get_module_main_conf(cf, module) \ + ((ngx_mail_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index] +#define ngx_mail_conf_get_module_srv_conf(cf, module) \ + ((ngx_mail_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index] + + +#if (NGX_MAIL_SSL) +void ngx_mail_starttls_handler(ngx_event_t *rev); +ngx_int_t ngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c); +#endif + + +void ngx_mail_init_connection(ngx_connection_t *c); + +ngx_int_t ngx_mail_salt(ngx_mail_session_t *s, ngx_connection_t *c, + ngx_mail_core_srv_conf_t *cscf); +ngx_int_t ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, + ngx_uint_t n); +ngx_int_t ngx_mail_auth_login_username(ngx_mail_session_t *s, + ngx_connection_t *c, ngx_uint_t n); +ngx_int_t ngx_mail_auth_login_password(ngx_mail_session_t *s, + ngx_connection_t *c); +ngx_int_t ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s, + ngx_connection_t *c, char *prefix, size_t len); +ngx_int_t ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c); +ngx_int_t ngx_mail_auth_external(ngx_mail_session_t *s, ngx_connection_t *c, + ngx_uint_t n); +ngx_int_t ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c); + +void ngx_mail_send(ngx_event_t *wev); +ngx_int_t ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c); +void ngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c); +void ngx_mail_close_connection(ngx_connection_t *c); +void ngx_mail_session_internal_server_error(ngx_mail_session_t *s); +u_char *ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len); + + +char *ngx_mail_capabilities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + + +/* STUB */ +void ngx_mail_proxy_init(ngx_mail_session_t *s, ngx_addr_t *peer); +void ngx_mail_auth_http_init(ngx_mail_session_t *s); +ngx_int_t ngx_mail_realip_handler(ngx_mail_session_t *s); +/**/ + + +extern ngx_uint_t ngx_mail_max_module; +extern ngx_module_t ngx_mail_core_module; + + +#endif /* _NGX_MAIL_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_auth_http_module.c b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_auth_http_module.c new file mode 100644 index 000000000..3e5095a2d --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_auth_http_module.c @@ -0,0 +1,1662 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include +#include + + +typedef struct { + ngx_addr_t *peer; + + ngx_msec_t timeout; + ngx_flag_t pass_client_cert; + + ngx_str_t host_header; + ngx_str_t uri; + ngx_str_t header; + + ngx_array_t *headers; + + u_char *file; + ngx_uint_t line; +} ngx_mail_auth_http_conf_t; + + +typedef struct ngx_mail_auth_http_ctx_s ngx_mail_auth_http_ctx_t; + +typedef void (*ngx_mail_auth_http_handler_pt)(ngx_mail_session_t *s, + ngx_mail_auth_http_ctx_t *ctx); + +struct ngx_mail_auth_http_ctx_s { + ngx_buf_t *request; + ngx_buf_t *response; + ngx_peer_connection_t peer; + + ngx_mail_auth_http_handler_pt handler; + + ngx_uint_t state; + + u_char *header_name_start; + u_char *header_name_end; + u_char *header_start; + u_char *header_end; + + ngx_str_t addr; + ngx_str_t port; + ngx_str_t err; + ngx_str_t errmsg; + ngx_str_t errcode; + + time_t sleep; + + ngx_pool_t *pool; +}; + + +static void ngx_mail_auth_http_write_handler(ngx_event_t *wev); +static void ngx_mail_auth_http_read_handler(ngx_event_t *rev); +static void ngx_mail_auth_http_ignore_status_line(ngx_mail_session_t *s, + ngx_mail_auth_http_ctx_t *ctx); +static void ngx_mail_auth_http_process_headers(ngx_mail_session_t *s, + ngx_mail_auth_http_ctx_t *ctx); +static void ngx_mail_auth_sleep_handler(ngx_event_t *rev); +static ngx_int_t ngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s, + ngx_mail_auth_http_ctx_t *ctx); +static void ngx_mail_auth_http_block_read(ngx_event_t *rev); +static void ngx_mail_auth_http_dummy_handler(ngx_event_t *ev); +static ngx_buf_t *ngx_mail_auth_http_create_request(ngx_mail_session_t *s, + ngx_pool_t *pool, ngx_mail_auth_http_conf_t *ahcf); +static ngx_int_t ngx_mail_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text, + ngx_str_t *escaped); + +static void *ngx_mail_auth_http_create_conf(ngx_conf_t *cf); +static char *ngx_mail_auth_http_merge_conf(ngx_conf_t *cf, void *parent, + void *child); +static char *ngx_mail_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_mail_auth_http_header(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + + +static ngx_command_t ngx_mail_auth_http_commands[] = { + + { ngx_string("auth_http"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_mail_auth_http, + NGX_MAIL_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("auth_http_timeout"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_auth_http_conf_t, timeout), + NULL }, + + { ngx_string("auth_http_header"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE2, + ngx_mail_auth_http_header, + NGX_MAIL_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("auth_http_pass_client_cert"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_auth_http_conf_t, pass_client_cert), + NULL }, + + ngx_null_command +}; + + +static ngx_mail_module_t ngx_mail_auth_http_module_ctx = { + NULL, /* protocol */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_mail_auth_http_create_conf, /* create server configuration */ + ngx_mail_auth_http_merge_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_mail_auth_http_module = { + NGX_MODULE_V1, + &ngx_mail_auth_http_module_ctx, /* module context */ + ngx_mail_auth_http_commands, /* module directives */ + NGX_MAIL_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_str_t ngx_mail_auth_http_method[] = { + ngx_string("plain"), + ngx_string("plain"), + ngx_string("plain"), + ngx_string("apop"), + ngx_string("cram-md5"), + ngx_string("external"), + ngx_string("none") +}; + +static ngx_str_t ngx_mail_smtp_errcode = ngx_string("535 5.7.0"); + + +void +ngx_mail_auth_http_init(ngx_mail_session_t *s) +{ + ngx_int_t rc; + ngx_pool_t *pool; + ngx_mail_auth_http_ctx_t *ctx; + ngx_mail_auth_http_conf_t *ahcf; + + s->connection->log->action = "in http auth state"; + + pool = ngx_create_pool(2048, s->connection->log); + if (pool == NULL) { + ngx_mail_session_internal_server_error(s); + return; + } + + ctx = ngx_pcalloc(pool, sizeof(ngx_mail_auth_http_ctx_t)); + if (ctx == NULL) { + ngx_destroy_pool(pool); + ngx_mail_session_internal_server_error(s); + return; + } + + ctx->pool = pool; + + ahcf = ngx_mail_get_module_srv_conf(s, ngx_mail_auth_http_module); + + ctx->request = ngx_mail_auth_http_create_request(s, pool, ahcf); + if (ctx->request == NULL) { + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); + return; + } + + ngx_mail_set_ctx(s, ctx, ngx_mail_auth_http_module); + + ctx->peer.sockaddr = ahcf->peer->sockaddr; + ctx->peer.socklen = ahcf->peer->socklen; + ctx->peer.name = &ahcf->peer->name; + ctx->peer.get = ngx_event_get_peer; + ctx->peer.log = s->connection->log; + ctx->peer.log_error = NGX_ERROR_ERR; + + rc = ngx_event_connect_peer(&ctx->peer); + + if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) { + if (ctx->peer.connection) { + ngx_close_connection(ctx->peer.connection); + } + + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); + return; + } + + ctx->peer.connection->data = s; + ctx->peer.connection->pool = s->connection->pool; + + s->connection->read->handler = ngx_mail_auth_http_block_read; + ctx->peer.connection->read->handler = ngx_mail_auth_http_read_handler; + ctx->peer.connection->write->handler = ngx_mail_auth_http_write_handler; + + ctx->handler = ngx_mail_auth_http_ignore_status_line; + + ngx_add_timer(ctx->peer.connection->read, ahcf->timeout); + ngx_add_timer(ctx->peer.connection->write, ahcf->timeout); + + if (rc == NGX_OK) { + ngx_mail_auth_http_write_handler(ctx->peer.connection->write); + return; + } +} + + +static void +ngx_mail_auth_http_write_handler(ngx_event_t *wev) +{ + ssize_t n, size; + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_auth_http_ctx_t *ctx; + ngx_mail_auth_http_conf_t *ahcf; + + c = wev->data; + s = c->data; + + ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module); + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, + "mail auth http write handler"); + + if (wev->timedout) { + ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT, + "auth http server %V timed out", ctx->peer.name); + ngx_close_connection(c); + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); + return; + } + + size = ctx->request->last - ctx->request->pos; + + n = ngx_send(c, ctx->request->pos, size); + + if (n == NGX_ERROR) { + ngx_close_connection(c); + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); + return; + } + + if (n > 0) { + ctx->request->pos += n; + + if (n == size) { + wev->handler = ngx_mail_auth_http_dummy_handler; + + if (wev->timer_set) { + ngx_del_timer(wev); + } + + if (ngx_handle_write_event(wev, 0) != NGX_OK) { + ngx_close_connection(c); + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); + } + + return; + } + } + + if (!wev->timer_set) { + ahcf = ngx_mail_get_module_srv_conf(s, ngx_mail_auth_http_module); + ngx_add_timer(wev, ahcf->timeout); + } +} + + +static void +ngx_mail_auth_http_read_handler(ngx_event_t *rev) +{ + ssize_t n, size; + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_auth_http_ctx_t *ctx; + + c = rev->data; + s = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, + "mail auth http read handler"); + + ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT, + "auth http server %V timed out", ctx->peer.name); + ngx_close_connection(c); + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); + return; + } + + if (ctx->response == NULL) { + ctx->response = ngx_create_temp_buf(ctx->pool, 1024); + if (ctx->response == NULL) { + ngx_close_connection(c); + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); + return; + } + } + + size = ctx->response->end - ctx->response->last; + + n = ngx_recv(c, ctx->response->pos, size); + + if (n > 0) { + ctx->response->last += n; + + ctx->handler(s, ctx); + return; + } + + if (n == NGX_AGAIN) { + return; + } + + ngx_close_connection(c); + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); +} + + +static void +ngx_mail_auth_http_ignore_status_line(ngx_mail_session_t *s, + ngx_mail_auth_http_ctx_t *ctx) +{ + u_char *p, ch; + enum { + sw_start = 0, + sw_H, + sw_HT, + sw_HTT, + sw_HTTP, + sw_skip, + sw_almost_done + } state; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0, + "mail auth http process status line"); + + state = ctx->state; + + for (p = ctx->response->pos; p < ctx->response->last; p++) { + ch = *p; + + switch (state) { + + /* "HTTP/" */ + case sw_start: + if (ch == 'H') { + state = sw_H; + break; + } + goto next; + + case sw_H: + if (ch == 'T') { + state = sw_HT; + break; + } + goto next; + + case sw_HT: + if (ch == 'T') { + state = sw_HTT; + break; + } + goto next; + + case sw_HTT: + if (ch == 'P') { + state = sw_HTTP; + break; + } + goto next; + + case sw_HTTP: + if (ch == '/') { + state = sw_skip; + break; + } + goto next; + + /* any text until end of line */ + case sw_skip: + switch (ch) { + case CR: + state = sw_almost_done; + + break; + case LF: + goto done; + } + break; + + /* end of status line */ + case sw_almost_done: + if (ch == LF) { + goto done; + } + + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "auth http server %V sent invalid response", + ctx->peer.name); + ngx_close_connection(ctx->peer.connection); + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); + return; + } + } + + ctx->response->pos = p; + ctx->state = state; + + return; + +next: + + p = ctx->response->start - 1; + +done: + + ctx->response->pos = p + 1; + ctx->state = 0; + ctx->handler = ngx_mail_auth_http_process_headers; + ctx->handler(s, ctx); +} + + +static void +ngx_mail_auth_http_process_headers(ngx_mail_session_t *s, + ngx_mail_auth_http_ctx_t *ctx) +{ + u_char *p; + time_t timer; + size_t len, size; + ngx_int_t rc, port, n; + ngx_addr_t *peer; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0, + "mail auth http process headers"); + + for ( ;; ) { + rc = ngx_mail_auth_http_parse_header_line(s, ctx); + + if (rc == NGX_OK) { + +#if (NGX_DEBUG) + { + ngx_str_t key, value; + + key.len = ctx->header_name_end - ctx->header_name_start; + key.data = ctx->header_name_start; + value.len = ctx->header_end - ctx->header_start; + value.data = ctx->header_start; + + ngx_log_debug2(NGX_LOG_DEBUG_MAIL, s->connection->log, 0, + "mail auth http header: \"%V: %V\"", + &key, &value); + } +#endif + + len = ctx->header_name_end - ctx->header_name_start; + + if (len == sizeof("Auth-Status") - 1 + && ngx_strncasecmp(ctx->header_name_start, + (u_char *) "Auth-Status", + sizeof("Auth-Status") - 1) + == 0) + { + len = ctx->header_end - ctx->header_start; + + if (len == 2 + && ctx->header_start[0] == 'O' + && ctx->header_start[1] == 'K') + { + continue; + } + + if (len == 4 + && ctx->header_start[0] == 'W' + && ctx->header_start[1] == 'A' + && ctx->header_start[2] == 'I' + && ctx->header_start[3] == 'T') + { + s->auth_wait = 1; + continue; + } + + ctx->errmsg.len = len; + ctx->errmsg.data = ctx->header_start; + + switch (s->protocol) { + + case NGX_MAIL_POP3_PROTOCOL: + size = sizeof("-ERR ") - 1 + len + sizeof(CRLF) - 1; + break; + + case NGX_MAIL_IMAP_PROTOCOL: + size = s->tag.len + sizeof("NO ") - 1 + len + + sizeof(CRLF) - 1; + break; + + default: /* NGX_MAIL_SMTP_PROTOCOL */ + ctx->err = ctx->errmsg; + continue; + } + + p = ngx_pnalloc(s->connection->pool, size); + if (p == NULL) { + ngx_close_connection(ctx->peer.connection); + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); + return; + } + + ctx->err.data = p; + + switch (s->protocol) { + + case NGX_MAIL_POP3_PROTOCOL: + *p++ = '-'; *p++ = 'E'; *p++ = 'R'; *p++ = 'R'; *p++ = ' '; + break; + + case NGX_MAIL_IMAP_PROTOCOL: + p = ngx_cpymem(p, s->tag.data, s->tag.len); + *p++ = 'N'; *p++ = 'O'; *p++ = ' '; + break; + + default: /* NGX_MAIL_SMTP_PROTOCOL */ + break; + } + + p = ngx_cpymem(p, ctx->header_start, len); + *p++ = CR; *p++ = LF; + + ctx->err.len = p - ctx->err.data; + + continue; + } + + if (len == sizeof("Auth-Server") - 1 + && ngx_strncasecmp(ctx->header_name_start, + (u_char *) "Auth-Server", + sizeof("Auth-Server") - 1) + == 0) + { + ctx->addr.len = ctx->header_end - ctx->header_start; + ctx->addr.data = ctx->header_start; + + continue; + } + + if (len == sizeof("Auth-Port") - 1 + && ngx_strncasecmp(ctx->header_name_start, + (u_char *) "Auth-Port", + sizeof("Auth-Port") - 1) + == 0) + { + ctx->port.len = ctx->header_end - ctx->header_start; + ctx->port.data = ctx->header_start; + + continue; + } + + if (len == sizeof("Auth-User") - 1 + && ngx_strncasecmp(ctx->header_name_start, + (u_char *) "Auth-User", + sizeof("Auth-User") - 1) + == 0) + { + s->login.len = ctx->header_end - ctx->header_start; + + s->login.data = ngx_pnalloc(s->connection->pool, s->login.len); + if (s->login.data == NULL) { + ngx_close_connection(ctx->peer.connection); + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); + return; + } + + ngx_memcpy(s->login.data, ctx->header_start, s->login.len); + + continue; + } + + if (len == sizeof("Auth-Pass") - 1 + && ngx_strncasecmp(ctx->header_name_start, + (u_char *) "Auth-Pass", + sizeof("Auth-Pass") - 1) + == 0) + { + s->passwd.len = ctx->header_end - ctx->header_start; + + s->passwd.data = ngx_pnalloc(s->connection->pool, + s->passwd.len); + if (s->passwd.data == NULL) { + ngx_close_connection(ctx->peer.connection); + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); + return; + } + + ngx_memcpy(s->passwd.data, ctx->header_start, s->passwd.len); + + continue; + } + + if (len == sizeof("Auth-Wait") - 1 + && ngx_strncasecmp(ctx->header_name_start, + (u_char *) "Auth-Wait", + sizeof("Auth-Wait") - 1) + == 0) + { + n = ngx_atoi(ctx->header_start, + ctx->header_end - ctx->header_start); + + if (n != NGX_ERROR) { + ctx->sleep = n; + } + + continue; + } + + if (len == sizeof("Auth-Error-Code") - 1 + && ngx_strncasecmp(ctx->header_name_start, + (u_char *) "Auth-Error-Code", + sizeof("Auth-Error-Code") - 1) + == 0) + { + ctx->errcode.len = ctx->header_end - ctx->header_start; + + ctx->errcode.data = ngx_pnalloc(s->connection->pool, + ctx->errcode.len); + if (ctx->errcode.data == NULL) { + ngx_close_connection(ctx->peer.connection); + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); + return; + } + + ngx_memcpy(ctx->errcode.data, ctx->header_start, + ctx->errcode.len); + + continue; + } + + /* ignore other headers */ + + continue; + } + + if (rc == NGX_DONE) { + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0, + "mail auth http header done"); + + ngx_close_connection(ctx->peer.connection); + + if (ctx->err.len) { + + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "client login failed: \"%V\"", &ctx->errmsg); + + if (s->protocol == NGX_MAIL_SMTP_PROTOCOL) { + + if (ctx->errcode.len == 0) { + ctx->errcode = ngx_mail_smtp_errcode; + } + + ctx->err.len = ctx->errcode.len + ctx->errmsg.len + + sizeof(" " CRLF) - 1; + + p = ngx_pnalloc(s->connection->pool, ctx->err.len); + if (p == NULL) { + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); + return; + } + + ctx->err.data = p; + + p = ngx_cpymem(p, ctx->errcode.data, ctx->errcode.len); + *p++ = ' '; + p = ngx_cpymem(p, ctx->errmsg.data, ctx->errmsg.len); + *p++ = CR; *p = LF; + } + + s->out = ctx->err; + timer = ctx->sleep; + + ngx_destroy_pool(ctx->pool); + + if (timer == 0) { + s->quit = 1; + ngx_mail_send(s->connection->write); + return; + } + + ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000)); + + s->connection->read->handler = ngx_mail_auth_sleep_handler; + + return; + } + + if (s->auth_wait) { + timer = ctx->sleep; + + ngx_destroy_pool(ctx->pool); + + if (timer == 0) { + ngx_mail_auth_http_init(s); + return; + } + + ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000)); + + s->connection->read->handler = ngx_mail_auth_sleep_handler; + + return; + } + + if (ctx->addr.len == 0 || ctx->port.len == 0) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "auth http server %V did not send server or port", + ctx->peer.name); + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); + return; + } + + if (s->passwd.data == NULL + && s->protocol != NGX_MAIL_SMTP_PROTOCOL) + { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "auth http server %V did not send password", + ctx->peer.name); + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); + return; + } + + peer = ngx_pcalloc(s->connection->pool, sizeof(ngx_addr_t)); + if (peer == NULL) { + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); + return; + } + + rc = ngx_parse_addr(s->connection->pool, peer, + ctx->addr.data, ctx->addr.len); + + switch (rc) { + case NGX_OK: + break; + + case NGX_DECLINED: + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "auth http server %V sent invalid server " + "address:\"%V\"", + ctx->peer.name, &ctx->addr); + /* fall through */ + + default: + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); + return; + } + + port = ngx_atoi(ctx->port.data, ctx->port.len); + if (port == NGX_ERROR || port < 1 || port > 65535) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "auth http server %V sent invalid server " + "port:\"%V\"", + ctx->peer.name, &ctx->port); + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); + return; + } + + ngx_inet_set_port(peer->sockaddr, (in_port_t) port); + + len = ctx->addr.len + 1 + ctx->port.len; + + peer->name.len = len; + + peer->name.data = ngx_pnalloc(s->connection->pool, len); + if (peer->name.data == NULL) { + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); + return; + } + + len = ctx->addr.len; + + ngx_memcpy(peer->name.data, ctx->addr.data, len); + + peer->name.data[len++] = ':'; + + ngx_memcpy(peer->name.data + len, ctx->port.data, ctx->port.len); + + ngx_destroy_pool(ctx->pool); + ngx_mail_proxy_init(s, peer); + + return; + } + + if (rc == NGX_AGAIN ) { + return; + } + + /* rc == NGX_ERROR */ + + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "auth http server %V sent invalid header in response", + ctx->peer.name); + ngx_close_connection(ctx->peer.connection); + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); + + return; + } +} + + +static void +ngx_mail_auth_sleep_handler(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_core_srv_conf_t *cscf; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail auth sleep handler"); + + c = rev->data; + s = c->data; + + if (rev->timedout) { + + rev->timedout = 0; + + if (s->auth_wait) { + s->auth_wait = 0; + ngx_mail_auth_http_init(s); + return; + } + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + rev->handler = cscf->protocol->auth_state; + + s->mail_state = 0; + s->auth_method = NGX_MAIL_AUTH_PLAIN; + + c->log->action = "in auth state"; + + ngx_mail_send(c->write); + + if (c->destroyed) { + return; + } + + ngx_add_timer(rev, cscf->timeout); + + if (rev->ready) { + rev->handler(rev); + return; + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_mail_close_connection(c); + } + + return; + } + + if (rev->active) { + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_mail_close_connection(c); + } + } +} + + +static ngx_int_t +ngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s, + ngx_mail_auth_http_ctx_t *ctx) +{ + u_char c, ch, *p; + enum { + sw_start = 0, + sw_name, + sw_space_before_value, + sw_value, + sw_space_after_value, + sw_almost_done, + sw_header_almost_done + } state; + + state = ctx->state; + + for (p = ctx->response->pos; p < ctx->response->last; p++) { + ch = *p; + + switch (state) { + + /* first char */ + case sw_start: + + switch (ch) { + case CR: + ctx->header_end = p; + state = sw_header_almost_done; + break; + case LF: + ctx->header_end = p; + goto header_done; + default: + state = sw_name; + ctx->header_name_start = p; + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'z') { + break; + } + + if (ch >= '0' && ch <= '9') { + break; + } + + return NGX_ERROR; + } + break; + + /* header name */ + case sw_name: + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'z') { + break; + } + + if (ch == ':') { + ctx->header_name_end = p; + state = sw_space_before_value; + break; + } + + if (ch == '-') { + break; + } + + if (ch >= '0' && ch <= '9') { + break; + } + + if (ch == CR) { + ctx->header_name_end = p; + ctx->header_start = p; + ctx->header_end = p; + state = sw_almost_done; + break; + } + + if (ch == LF) { + ctx->header_name_end = p; + ctx->header_start = p; + ctx->header_end = p; + goto done; + } + + return NGX_ERROR; + + /* space* before header value */ + case sw_space_before_value: + switch (ch) { + case ' ': + break; + case CR: + ctx->header_start = p; + ctx->header_end = p; + state = sw_almost_done; + break; + case LF: + ctx->header_start = p; + ctx->header_end = p; + goto done; + default: + ctx->header_start = p; + state = sw_value; + break; + } + break; + + /* header value */ + case sw_value: + switch (ch) { + case ' ': + ctx->header_end = p; + state = sw_space_after_value; + break; + case CR: + ctx->header_end = p; + state = sw_almost_done; + break; + case LF: + ctx->header_end = p; + goto done; + } + break; + + /* space* before end of header line */ + case sw_space_after_value: + switch (ch) { + case ' ': + break; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + default: + state = sw_value; + break; + } + break; + + /* end of header line */ + case sw_almost_done: + switch (ch) { + case LF: + goto done; + default: + return NGX_ERROR; + } + + /* end of header */ + case sw_header_almost_done: + switch (ch) { + case LF: + goto header_done; + default: + return NGX_ERROR; + } + } + } + + ctx->response->pos = p; + ctx->state = state; + + return NGX_AGAIN; + +done: + + ctx->response->pos = p + 1; + ctx->state = sw_start; + + return NGX_OK; + +header_done: + + ctx->response->pos = p + 1; + ctx->state = sw_start; + + return NGX_DONE; +} + + +static void +ngx_mail_auth_http_block_read(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_auth_http_ctx_t *ctx; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, + "mail auth http block read"); + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + c = rev->data; + s = c->data; + + ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module); + + ngx_close_connection(ctx->peer.connection); + ngx_destroy_pool(ctx->pool); + ngx_mail_session_internal_server_error(s); + } +} + + +static void +ngx_mail_auth_http_dummy_handler(ngx_event_t *ev) +{ + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, ev->log, 0, + "mail auth http dummy handler"); +} + + +static ngx_buf_t * +ngx_mail_auth_http_create_request(ngx_mail_session_t *s, ngx_pool_t *pool, + ngx_mail_auth_http_conf_t *ahcf) +{ + size_t len; + ngx_buf_t *b; + ngx_str_t login, passwd; + ngx_connection_t *c; +#if (NGX_MAIL_SSL) + ngx_str_t protocol, cipher, verify, subject, issuer, + serial, fingerprint, raw_cert, cert; + ngx_mail_ssl_conf_t *sslcf; +#endif + ngx_mail_core_srv_conf_t *cscf; + + if (ngx_mail_auth_http_escape(pool, &s->login, &login) != NGX_OK) { + return NULL; + } + + if (ngx_mail_auth_http_escape(pool, &s->passwd, &passwd) != NGX_OK) { + return NULL; + } + + c = s->connection; + +#if (NGX_MAIL_SSL) + + if (c->ssl) { + + if (ngx_ssl_get_protocol(c, pool, &protocol) != NGX_OK) { + return NULL; + } + + protocol.len = ngx_strlen(protocol.data); + + if (ngx_ssl_get_cipher_name(c, pool, &cipher) != NGX_OK) { + return NULL; + } + + cipher.len = ngx_strlen(cipher.data); + + } else { + ngx_str_null(&protocol); + ngx_str_null(&cipher); + } + + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); + + if (c->ssl && sslcf->verify) { + + /* certificate details */ + + if (ngx_ssl_get_client_verify(c, pool, &verify) != NGX_OK) { + return NULL; + } + + if (ngx_ssl_get_subject_dn(c, pool, &subject) != NGX_OK) { + return NULL; + } + + if (ngx_ssl_get_issuer_dn(c, pool, &issuer) != NGX_OK) { + return NULL; + } + + if (ngx_ssl_get_serial_number(c, pool, &serial) != NGX_OK) { + return NULL; + } + + if (ngx_ssl_get_fingerprint(c, pool, &fingerprint) != NGX_OK) { + return NULL; + } + + if (ahcf->pass_client_cert) { + + /* certificate itself, if configured */ + + if (ngx_ssl_get_raw_certificate(c, pool, &raw_cert) != NGX_OK) { + return NULL; + } + + if (ngx_mail_auth_http_escape(pool, &raw_cert, &cert) != NGX_OK) { + return NULL; + } + + } else { + ngx_str_null(&cert); + } + + } else { + ngx_str_null(&verify); + ngx_str_null(&subject); + ngx_str_null(&issuer); + ngx_str_null(&serial); + ngx_str_null(&fingerprint); + ngx_str_null(&cert); + } + +#endif + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + len = sizeof("GET ") - 1 + ahcf->uri.len + sizeof(" HTTP/1.0" CRLF) - 1 + + sizeof("Host: ") - 1 + ahcf->host_header.len + sizeof(CRLF) - 1 + + sizeof("Auth-Method: ") - 1 + + ngx_mail_auth_http_method[s->auth_method].len + + sizeof(CRLF) - 1 + + sizeof("Auth-User: ") - 1 + login.len + sizeof(CRLF) - 1 + + sizeof("Auth-Pass: ") - 1 + passwd.len + sizeof(CRLF) - 1 + + sizeof("Auth-Salt: ") - 1 + s->salt.len + + sizeof("Auth-Protocol: ") - 1 + cscf->protocol->name.len + + sizeof(CRLF) - 1 + + sizeof("Auth-Login-Attempt: ") - 1 + NGX_INT_T_LEN + + sizeof(CRLF) - 1 + + sizeof("Client-IP: ") - 1 + s->connection->addr_text.len + + sizeof(CRLF) - 1 + + sizeof("Client-Host: ") - 1 + s->host.len + sizeof(CRLF) - 1 + + ahcf->header.len + + sizeof(CRLF) - 1; + + if (c->proxy_protocol) { + len += sizeof("Proxy-Protocol-Addr: ") - 1 + + c->proxy_protocol->src_addr.len + sizeof(CRLF) - 1 + + sizeof("Proxy-Protocol-Port: ") - 1 + + sizeof("65535") - 1 + sizeof(CRLF) - 1 + + sizeof("Proxy-Protocol-Server-Addr: ") - 1 + + c->proxy_protocol->dst_addr.len + sizeof(CRLF) - 1 + + sizeof("Proxy-Protocol-Server-Port: ") - 1 + + sizeof("65535") - 1 + sizeof(CRLF) - 1; + } + + if (s->auth_method == NGX_MAIL_AUTH_NONE) { + len += sizeof("Auth-SMTP-Helo: ") - 1 + s->smtp_helo.len + + sizeof(CRLF) - 1 + + sizeof("Auth-SMTP-From: ") - 1 + s->smtp_from.len + + sizeof(CRLF) - 1 + + sizeof("Auth-SMTP-To: ") - 1 + s->smtp_to.len + + sizeof(CRLF) - 1; + } + +#if (NGX_MAIL_SSL) + + if (c->ssl) { + len += sizeof("Auth-SSL: on" CRLF) - 1 + + sizeof("Auth-SSL-Protocol: ") - 1 + protocol.len + + sizeof(CRLF) - 1 + + sizeof("Auth-SSL-Cipher: ") - 1 + cipher.len + + sizeof(CRLF) - 1 + + sizeof("Auth-SSL-Verify: ") - 1 + verify.len + + sizeof(CRLF) - 1 + + sizeof("Auth-SSL-Subject: ") - 1 + subject.len + + sizeof(CRLF) - 1 + + sizeof("Auth-SSL-Issuer: ") - 1 + issuer.len + + sizeof(CRLF) - 1 + + sizeof("Auth-SSL-Serial: ") - 1 + serial.len + + sizeof(CRLF) - 1 + + sizeof("Auth-SSL-Fingerprint: ") - 1 + fingerprint.len + + sizeof(CRLF) - 1 + + sizeof("Auth-SSL-Cert: ") - 1 + cert.len + + sizeof(CRLF) - 1; + } + +#endif + + b = ngx_create_temp_buf(pool, len); + if (b == NULL) { + return NULL; + } + + b->last = ngx_cpymem(b->last, "GET ", sizeof("GET ") - 1); + b->last = ngx_copy(b->last, ahcf->uri.data, ahcf->uri.len); + b->last = ngx_cpymem(b->last, " HTTP/1.0" CRLF, + sizeof(" HTTP/1.0" CRLF) - 1); + + b->last = ngx_cpymem(b->last, "Host: ", sizeof("Host: ") - 1); + b->last = ngx_copy(b->last, ahcf->host_header.data, + ahcf->host_header.len); + *b->last++ = CR; *b->last++ = LF; + + b->last = ngx_cpymem(b->last, "Auth-Method: ", + sizeof("Auth-Method: ") - 1); + b->last = ngx_cpymem(b->last, + ngx_mail_auth_http_method[s->auth_method].data, + ngx_mail_auth_http_method[s->auth_method].len); + *b->last++ = CR; *b->last++ = LF; + + b->last = ngx_cpymem(b->last, "Auth-User: ", sizeof("Auth-User: ") - 1); + b->last = ngx_copy(b->last, login.data, login.len); + *b->last++ = CR; *b->last++ = LF; + + b->last = ngx_cpymem(b->last, "Auth-Pass: ", sizeof("Auth-Pass: ") - 1); + b->last = ngx_copy(b->last, passwd.data, passwd.len); + *b->last++ = CR; *b->last++ = LF; + + if ((s->auth_method == NGX_MAIL_AUTH_APOP + || s->auth_method == NGX_MAIL_AUTH_CRAM_MD5) + && s->salt.len) + { + b->last = ngx_cpymem(b->last, "Auth-Salt: ", sizeof("Auth-Salt: ") - 1); + b->last = ngx_copy(b->last, s->salt.data, s->salt.len); + + ngx_str_null(&s->passwd); + } + + b->last = ngx_cpymem(b->last, "Auth-Protocol: ", + sizeof("Auth-Protocol: ") - 1); + b->last = ngx_cpymem(b->last, cscf->protocol->name.data, + cscf->protocol->name.len); + *b->last++ = CR; *b->last++ = LF; + + b->last = ngx_sprintf(b->last, "Auth-Login-Attempt: %ui" CRLF, + s->login_attempt); + + b->last = ngx_cpymem(b->last, "Client-IP: ", sizeof("Client-IP: ") - 1); + b->last = ngx_copy(b->last, s->connection->addr_text.data, + s->connection->addr_text.len); + *b->last++ = CR; *b->last++ = LF; + + if (s->host.len) { + b->last = ngx_cpymem(b->last, "Client-Host: ", + sizeof("Client-Host: ") - 1); + b->last = ngx_copy(b->last, s->host.data, s->host.len); + *b->last++ = CR; *b->last++ = LF; + } + + if (c->proxy_protocol) { + b->last = ngx_cpymem(b->last, "Proxy-Protocol-Addr: ", + sizeof("Proxy-Protocol-Addr: ") - 1); + b->last = ngx_copy(b->last, c->proxy_protocol->src_addr.data, + c->proxy_protocol->src_addr.len); + *b->last++ = CR; *b->last++ = LF; + + b->last = ngx_sprintf(b->last, "Proxy-Protocol-Port: %d" CRLF, + c->proxy_protocol->src_port); + + b->last = ngx_cpymem(b->last, "Proxy-Protocol-Server-Addr: ", + sizeof("Proxy-Protocol-Server-Addr: ") - 1); + b->last = ngx_copy(b->last, c->proxy_protocol->dst_addr.data, + c->proxy_protocol->dst_addr.len); + *b->last++ = CR; *b->last++ = LF; + + b->last = ngx_sprintf(b->last, "Proxy-Protocol-Server-Port: %d" CRLF, + c->proxy_protocol->dst_port); + } + + if (s->auth_method == NGX_MAIL_AUTH_NONE) { + + /* HELO, MAIL FROM, and RCPT TO can't contain CRLF, no need to escape */ + + b->last = ngx_cpymem(b->last, "Auth-SMTP-Helo: ", + sizeof("Auth-SMTP-Helo: ") - 1); + b->last = ngx_copy(b->last, s->smtp_helo.data, s->smtp_helo.len); + *b->last++ = CR; *b->last++ = LF; + + b->last = ngx_cpymem(b->last, "Auth-SMTP-From: ", + sizeof("Auth-SMTP-From: ") - 1); + b->last = ngx_copy(b->last, s->smtp_from.data, s->smtp_from.len); + *b->last++ = CR; *b->last++ = LF; + + b->last = ngx_cpymem(b->last, "Auth-SMTP-To: ", + sizeof("Auth-SMTP-To: ") - 1); + b->last = ngx_copy(b->last, s->smtp_to.data, s->smtp_to.len); + *b->last++ = CR; *b->last++ = LF; + + } + +#if (NGX_MAIL_SSL) + + if (c->ssl) { + b->last = ngx_cpymem(b->last, "Auth-SSL: on" CRLF, + sizeof("Auth-SSL: on" CRLF) - 1); + + if (protocol.len) { + b->last = ngx_cpymem(b->last, "Auth-SSL-Protocol: ", + sizeof("Auth-SSL-Protocol: ") - 1); + b->last = ngx_copy(b->last, protocol.data, protocol.len); + *b->last++ = CR; *b->last++ = LF; + } + + if (cipher.len) { + b->last = ngx_cpymem(b->last, "Auth-SSL-Cipher: ", + sizeof("Auth-SSL-Cipher: ") - 1); + b->last = ngx_copy(b->last, cipher.data, cipher.len); + *b->last++ = CR; *b->last++ = LF; + } + + if (verify.len) { + b->last = ngx_cpymem(b->last, "Auth-SSL-Verify: ", + sizeof("Auth-SSL-Verify: ") - 1); + b->last = ngx_copy(b->last, verify.data, verify.len); + *b->last++ = CR; *b->last++ = LF; + } + + if (subject.len) { + b->last = ngx_cpymem(b->last, "Auth-SSL-Subject: ", + sizeof("Auth-SSL-Subject: ") - 1); + b->last = ngx_copy(b->last, subject.data, subject.len); + *b->last++ = CR; *b->last++ = LF; + } + + if (issuer.len) { + b->last = ngx_cpymem(b->last, "Auth-SSL-Issuer: ", + sizeof("Auth-SSL-Issuer: ") - 1); + b->last = ngx_copy(b->last, issuer.data, issuer.len); + *b->last++ = CR; *b->last++ = LF; + } + + if (serial.len) { + b->last = ngx_cpymem(b->last, "Auth-SSL-Serial: ", + sizeof("Auth-SSL-Serial: ") - 1); + b->last = ngx_copy(b->last, serial.data, serial.len); + *b->last++ = CR; *b->last++ = LF; + } + + if (fingerprint.len) { + b->last = ngx_cpymem(b->last, "Auth-SSL-Fingerprint: ", + sizeof("Auth-SSL-Fingerprint: ") - 1); + b->last = ngx_copy(b->last, fingerprint.data, fingerprint.len); + *b->last++ = CR; *b->last++ = LF; + } + + if (cert.len) { + b->last = ngx_cpymem(b->last, "Auth-SSL-Cert: ", + sizeof("Auth-SSL-Cert: ") - 1); + b->last = ngx_copy(b->last, cert.data, cert.len); + *b->last++ = CR; *b->last++ = LF; + } + } + +#endif + + if (ahcf->header.len) { + b->last = ngx_copy(b->last, ahcf->header.data, ahcf->header.len); + } + + /* add "\r\n" at the header end */ + *b->last++ = CR; *b->last++ = LF; + +#if (NGX_DEBUG_MAIL_PASSWD) + ngx_log_debug2(NGX_LOG_DEBUG_MAIL, s->connection->log, 0, + "mail auth http header:%N\"%*s\"", + (size_t) (b->last - b->pos), b->pos); +#endif + + return b; +} + + +static ngx_int_t +ngx_mail_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text, ngx_str_t *escaped) +{ + u_char *p; + uintptr_t n; + + n = ngx_escape_uri(NULL, text->data, text->len, NGX_ESCAPE_MAIL_AUTH); + + if (n == 0) { + *escaped = *text; + return NGX_OK; + } + + escaped->len = text->len + n * 2; + + p = ngx_pnalloc(pool, escaped->len); + if (p == NULL) { + return NGX_ERROR; + } + + (void) ngx_escape_uri(p, text->data, text->len, NGX_ESCAPE_MAIL_AUTH); + + escaped->data = p; + + return NGX_OK; +} + + +static void * +ngx_mail_auth_http_create_conf(ngx_conf_t *cf) +{ + ngx_mail_auth_http_conf_t *ahcf; + + ahcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_auth_http_conf_t)); + if (ahcf == NULL) { + return NULL; + } + + ahcf->timeout = NGX_CONF_UNSET_MSEC; + ahcf->pass_client_cert = NGX_CONF_UNSET; + + ahcf->file = cf->conf_file->file.name.data; + ahcf->line = cf->conf_file->line; + + return ahcf; +} + + +static char * +ngx_mail_auth_http_merge_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_mail_auth_http_conf_t *prev = parent; + ngx_mail_auth_http_conf_t *conf = child; + + u_char *p; + size_t len; + ngx_uint_t i; + ngx_table_elt_t *header; + + if (conf->peer == NULL) { + conf->peer = prev->peer; + conf->host_header = prev->host_header; + conf->uri = prev->uri; + + if (conf->peer == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"auth_http\" is defined for server in %s:%ui", + conf->file, conf->line); + + return NGX_CONF_ERROR; + } + } + + ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000); + + ngx_conf_merge_value(conf->pass_client_cert, prev->pass_client_cert, 0); + + if (conf->headers == NULL) { + conf->headers = prev->headers; + conf->header = prev->header; + } + + if (conf->headers && conf->header.len == 0) { + len = 0; + header = conf->headers->elts; + for (i = 0; i < conf->headers->nelts; i++) { + len += header[i].key.len + 2 + header[i].value.len + 2; + } + + p = ngx_pnalloc(cf->pool, len); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->header.len = len; + conf->header.data = p; + + for (i = 0; i < conf->headers->nelts; i++) { + p = ngx_cpymem(p, header[i].key.data, header[i].key.len); + *p++ = ':'; *p++ = ' '; + p = ngx_cpymem(p, header[i].value.data, header[i].value.len); + *p++ = CR; *p++ = LF; + } + } + + return NGX_CONF_OK; +} + + +static char * +ngx_mail_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_mail_auth_http_conf_t *ahcf = conf; + + ngx_str_t *value; + ngx_url_t u; + + value = cf->args->elts; + + ngx_memzero(&u, sizeof(ngx_url_t)); + + u.url = value[1]; + u.default_port = 80; + u.uri_part = 1; + + if (ngx_strncmp(u.url.data, "http://", 7) == 0) { + u.url.len -= 7; + u.url.data += 7; + } + + if (ngx_parse_url(cf->pool, &u) != NGX_OK) { + if (u.err) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "%s in auth_http \"%V\"", u.err, &u.url); + } + + return NGX_CONF_ERROR; + } + + ahcf->peer = u.addrs; + + if (u.family != AF_UNIX) { + ahcf->host_header = u.host; + + } else { + ngx_str_set(&ahcf->host_header, "localhost"); + } + + ahcf->uri = u.uri; + + if (ahcf->uri.len == 0) { + ngx_str_set(&ahcf->uri, "/"); + } + + return NGX_CONF_OK; +} + + +static char * +ngx_mail_auth_http_header(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_mail_auth_http_conf_t *ahcf = conf; + + ngx_str_t *value; + ngx_table_elt_t *header; + + if (ahcf->headers == NULL) { + ahcf->headers = ngx_array_create(cf->pool, 1, sizeof(ngx_table_elt_t)); + if (ahcf->headers == NULL) { + return NGX_CONF_ERROR; + } + } + + header = ngx_array_push(ahcf->headers); + if (header == NULL) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + header->key = value[1]; + header->value = value[2]; + + return NGX_CONF_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_core_module.c b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_core_module.c new file mode 100644 index 000000000..5532ceeb1 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_core_module.c @@ -0,0 +1,728 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include + + +static void *ngx_mail_core_create_main_conf(ngx_conf_t *cf); +static void *ngx_mail_core_create_srv_conf(ngx_conf_t *cf); +static char *ngx_mail_core_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); +static char *ngx_mail_core_server(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_mail_core_protocol(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_mail_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + + +static ngx_command_t ngx_mail_core_commands[] = { + + { ngx_string("server"), + NGX_MAIL_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_mail_core_server, + 0, + 0, + NULL }, + + { ngx_string("listen"), + NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, + ngx_mail_core_listen, + NGX_MAIL_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("protocol"), + NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_mail_core_protocol, + NGX_MAIL_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("timeout"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_core_srv_conf_t, timeout), + NULL }, + + { ngx_string("server_name"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_core_srv_conf_t, server_name), + NULL }, + + { ngx_string("error_log"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, + ngx_mail_core_error_log, + NGX_MAIL_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("resolver"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, + ngx_mail_core_resolver, + NGX_MAIL_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("resolver_timeout"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_core_srv_conf_t, resolver_timeout), + NULL }, + + { ngx_string("max_errors"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_core_srv_conf_t, max_errors), + NULL }, + + ngx_null_command +}; + + +static ngx_mail_module_t ngx_mail_core_module_ctx = { + NULL, /* protocol */ + + ngx_mail_core_create_main_conf, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_mail_core_create_srv_conf, /* create server configuration */ + ngx_mail_core_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_mail_core_module = { + NGX_MODULE_V1, + &ngx_mail_core_module_ctx, /* module context */ + ngx_mail_core_commands, /* module directives */ + NGX_MAIL_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static void * +ngx_mail_core_create_main_conf(ngx_conf_t *cf) +{ + ngx_mail_core_main_conf_t *cmcf; + + cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_core_main_conf_t)); + if (cmcf == NULL) { + return NULL; + } + + if (ngx_array_init(&cmcf->servers, cf->pool, 4, + sizeof(ngx_mail_core_srv_conf_t *)) + != NGX_OK) + { + return NULL; + } + + if (ngx_array_init(&cmcf->listen, cf->pool, 4, sizeof(ngx_mail_listen_t)) + != NGX_OK) + { + return NULL; + } + + return cmcf; +} + + +static void * +ngx_mail_core_create_srv_conf(ngx_conf_t *cf) +{ + ngx_mail_core_srv_conf_t *cscf; + + cscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_core_srv_conf_t)); + if (cscf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * cscf->protocol = NULL; + * cscf->error_log = NULL; + */ + + cscf->timeout = NGX_CONF_UNSET_MSEC; + cscf->resolver_timeout = NGX_CONF_UNSET_MSEC; + + cscf->max_errors = NGX_CONF_UNSET_UINT; + + cscf->resolver = NGX_CONF_UNSET_PTR; + + cscf->file_name = cf->conf_file->file.name.data; + cscf->line = cf->conf_file->line; + + return cscf; +} + + +static char * +ngx_mail_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_mail_core_srv_conf_t *prev = parent; + ngx_mail_core_srv_conf_t *conf = child; + + ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000); + ngx_conf_merge_msec_value(conf->resolver_timeout, prev->resolver_timeout, + 30000); + + ngx_conf_merge_uint_value(conf->max_errors, prev->max_errors, 5); + + ngx_conf_merge_str_value(conf->server_name, prev->server_name, ""); + + if (conf->server_name.len == 0) { + conf->server_name = cf->cycle->hostname; + } + + if (conf->protocol == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "unknown mail protocol for server in %s:%ui", + conf->file_name, conf->line); + return NGX_CONF_ERROR; + } + + if (conf->error_log == NULL) { + if (prev->error_log) { + conf->error_log = prev->error_log; + } else { + conf->error_log = &cf->cycle->new_log; + } + } + + ngx_conf_merge_ptr_value(conf->resolver, prev->resolver, NULL); + + return NGX_CONF_OK; +} + + +static char * +ngx_mail_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *rv; + void *mconf; + ngx_uint_t m; + ngx_conf_t pcf; + ngx_mail_module_t *module; + ngx_mail_conf_ctx_t *ctx, *mail_ctx; + ngx_mail_core_srv_conf_t *cscf, **cscfp; + ngx_mail_core_main_conf_t *cmcf; + + ctx = ngx_pcalloc(cf->pool, sizeof(ngx_mail_conf_ctx_t)); + if (ctx == NULL) { + return NGX_CONF_ERROR; + } + + mail_ctx = cf->ctx; + ctx->main_conf = mail_ctx->main_conf; + + /* the server{}'s srv_conf */ + + ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_mail_max_module); + if (ctx->srv_conf == NULL) { + return NGX_CONF_ERROR; + } + + for (m = 0; cf->cycle->modules[m]; m++) { + if (cf->cycle->modules[m]->type != NGX_MAIL_MODULE) { + continue; + } + + module = cf->cycle->modules[m]->ctx; + + if (module->create_srv_conf) { + mconf = module->create_srv_conf(cf); + if (mconf == NULL) { + return NGX_CONF_ERROR; + } + + ctx->srv_conf[cf->cycle->modules[m]->ctx_index] = mconf; + } + } + + /* the server configuration context */ + + cscf = ctx->srv_conf[ngx_mail_core_module.ctx_index]; + cscf->ctx = ctx; + + cmcf = ctx->main_conf[ngx_mail_core_module.ctx_index]; + + cscfp = ngx_array_push(&cmcf->servers); + if (cscfp == NULL) { + return NGX_CONF_ERROR; + } + + *cscfp = cscf; + + + /* parse inside server{} */ + + pcf = *cf; + cf->ctx = ctx; + cf->cmd_type = NGX_MAIL_SRV_CONF; + + rv = ngx_conf_parse(cf, NULL); + + *cf = pcf; + + if (rv == NGX_CONF_OK && !cscf->listen) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"listen\" is defined for server in %s:%ui", + cscf->file_name, cscf->line); + return NGX_CONF_ERROR; + } + + return rv; +} + + +static char * +ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_mail_core_srv_conf_t *cscf = conf; + + ngx_str_t *value, size; + ngx_url_t u; + ngx_uint_t i, n, m; + ngx_mail_listen_t *ls, *als, *nls; + ngx_mail_module_t *module; + ngx_mail_core_main_conf_t *cmcf; + + cscf->listen = 1; + + value = cf->args->elts; + + ngx_memzero(&u, sizeof(ngx_url_t)); + + u.url = value[1]; + u.listen = 1; + + if (ngx_parse_url(cf->pool, &u) != NGX_OK) { + if (u.err) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "%s in \"%V\" of the \"listen\" directive", + u.err, &u.url); + } + + return NGX_CONF_ERROR; + } + + cmcf = ngx_mail_conf_get_module_main_conf(cf, ngx_mail_core_module); + + ls = ngx_array_push(&cmcf->listen); + if (ls == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(ls, sizeof(ngx_mail_listen_t)); + + ls->backlog = NGX_LISTEN_BACKLOG; + ls->rcvbuf = -1; + ls->sndbuf = -1; + ls->ctx = cf->ctx; + +#if (NGX_HAVE_INET6) + ls->ipv6only = 1; +#endif + + if (cscf->protocol == NULL) { + for (m = 0; cf->cycle->modules[m]; m++) { + if (cf->cycle->modules[m]->type != NGX_MAIL_MODULE) { + continue; + } + + module = cf->cycle->modules[m]->ctx; + + if (module->protocol == NULL) { + continue; + } + + for (i = 0; module->protocol->port[i]; i++) { + if (module->protocol->port[i] == u.port) { + cscf->protocol = module->protocol; + break; + } + } + } + } + + for (i = 2; i < cf->args->nelts; i++) { + + if (ngx_strcmp(value[i].data, "bind") == 0) { + ls->bind = 1; + continue; + } + + if (ngx_strncmp(value[i].data, "backlog=", 8) == 0) { + ls->backlog = ngx_atoi(value[i].data + 8, value[i].len - 8); + ls->bind = 1; + + if (ls->backlog == NGX_ERROR || ls->backlog == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid backlog \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "rcvbuf=", 7) == 0) { + size.len = value[i].len - 7; + size.data = value[i].data + 7; + + ls->rcvbuf = ngx_parse_size(&size); + ls->bind = 1; + + if (ls->rcvbuf == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid rcvbuf \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "sndbuf=", 7) == 0) { + size.len = value[i].len - 7; + size.data = value[i].data + 7; + + ls->sndbuf = ngx_parse_size(&size); + ls->bind = 1; + + if (ls->sndbuf == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid sndbuf \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "ipv6only=o", 10) == 0) { +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + if (ngx_strcmp(&value[i].data[10], "n") == 0) { + ls->ipv6only = 1; + + } else if (ngx_strcmp(&value[i].data[10], "ff") == 0) { + ls->ipv6only = 0; + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid ipv6only flags \"%s\"", + &value[i].data[9]); + return NGX_CONF_ERROR; + } + + ls->bind = 1; + continue; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "ipv6only is not supported " + "on this platform"); + return NGX_CONF_ERROR; +#endif + } + + if (ngx_strcmp(value[i].data, "multipath") == 0) { +#ifdef IPPROTO_MPTCP + ls->protocol = IPPROTO_MPTCP; + ls->bind = 1; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "multipath is not supported " + "on this platform, ignored"); +#endif + continue; + } + + if (ngx_strcmp(value[i].data, "ssl") == 0) { +#if (NGX_MAIL_SSL) + ngx_mail_ssl_conf_t *sslcf; + + sslcf = ngx_mail_conf_get_module_srv_conf(cf, ngx_mail_ssl_module); + + sslcf->listen = 1; + sslcf->file = cf->conf_file->file.name.data; + sslcf->line = cf->conf_file->line; + + ls->ssl = 1; + + continue; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"ssl\" parameter requires " + "ngx_mail_ssl_module"); + return NGX_CONF_ERROR; +#endif + } + + if (ngx_strncmp(value[i].data, "so_keepalive=", 13) == 0) { + + if (ngx_strcmp(&value[i].data[13], "on") == 0) { + ls->so_keepalive = 1; + + } else if (ngx_strcmp(&value[i].data[13], "off") == 0) { + ls->so_keepalive = 2; + + } else { + +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + u_char *p, *end; + ngx_str_t s; + + end = value[i].data + value[i].len; + s.data = value[i].data + 13; + + p = ngx_strlchr(s.data, end, ':'); + if (p == NULL) { + p = end; + } + + if (p > s.data) { + s.len = p - s.data; + + ls->tcp_keepidle = ngx_parse_time(&s, 1); + if (ls->tcp_keepidle == (time_t) NGX_ERROR) { + goto invalid_so_keepalive; + } + } + + s.data = (p < end) ? (p + 1) : end; + + p = ngx_strlchr(s.data, end, ':'); + if (p == NULL) { + p = end; + } + + if (p > s.data) { + s.len = p - s.data; + + ls->tcp_keepintvl = ngx_parse_time(&s, 1); + if (ls->tcp_keepintvl == (time_t) NGX_ERROR) { + goto invalid_so_keepalive; + } + } + + s.data = (p < end) ? (p + 1) : end; + + if (s.data < end) { + s.len = end - s.data; + + ls->tcp_keepcnt = ngx_atoi(s.data, s.len); + if (ls->tcp_keepcnt == NGX_ERROR) { + goto invalid_so_keepalive; + } + } + + if (ls->tcp_keepidle == 0 && ls->tcp_keepintvl == 0 + && ls->tcp_keepcnt == 0) + { + goto invalid_so_keepalive; + } + + ls->so_keepalive = 1; + +#else + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"so_keepalive\" parameter accepts " + "only \"on\" or \"off\" on this platform"); + return NGX_CONF_ERROR; + +#endif + } + + ls->bind = 1; + + continue; + +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + invalid_so_keepalive: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid so_keepalive value: \"%s\"", + &value[i].data[13]); + return NGX_CONF_ERROR; +#endif + } + + if (ngx_strcmp(value[i].data, "proxy_protocol") == 0) { + ls->proxy_protocol = 1; + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + for (n = 0; n < u.naddrs; n++) { + + for (i = 0; i < n; i++) { + if (ngx_cmp_sockaddr(u.addrs[n].sockaddr, u.addrs[n].socklen, + u.addrs[i].sockaddr, u.addrs[i].socklen, 1) + == NGX_OK) + { + goto next; + } + } + + if (n != 0) { + nls = ngx_array_push(&cmcf->listen); + if (nls == NULL) { + return NGX_CONF_ERROR; + } + + *nls = *ls; + + } else { + nls = ls; + } + + nls->sockaddr = u.addrs[n].sockaddr; + nls->socklen = u.addrs[n].socklen; + nls->addr_text = u.addrs[n].name; + nls->wildcard = ngx_inet_wildcard(nls->sockaddr); + + als = cmcf->listen.elts; + + for (i = 0; i < cmcf->listen.nelts - 1; i++) { + + if (ngx_cmp_sockaddr(als[i].sockaddr, als[i].socklen, + nls->sockaddr, nls->socklen, 1) + != NGX_OK) + { + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate \"%V\" address and port pair", + &nls->addr_text); + return NGX_CONF_ERROR; + } + + next: + continue; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_mail_core_protocol(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_mail_core_srv_conf_t *cscf = conf; + + ngx_str_t *value; + ngx_uint_t m; + ngx_mail_module_t *module; + + value = cf->args->elts; + + for (m = 0; cf->cycle->modules[m]; m++) { + if (cf->cycle->modules[m]->type != NGX_MAIL_MODULE) { + continue; + } + + module = cf->cycle->modules[m]->ctx; + + if (module->protocol + && ngx_strcmp(module->protocol->name.data, value[1].data) == 0) + { + cscf->protocol = module->protocol; + + return NGX_CONF_OK; + } + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unknown protocol \"%V\"", &value[1]); + return NGX_CONF_ERROR; +} + + +static char * +ngx_mail_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_mail_core_srv_conf_t *cscf = conf; + + return ngx_log_set_log(cf, &cscf->error_log); +} + + +static char * +ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_mail_core_srv_conf_t *cscf = conf; + + ngx_str_t *value; + + value = cf->args->elts; + + if (cscf->resolver != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + if (ngx_strcmp(value[1].data, "off") == 0) { + cscf->resolver = NULL; + return NGX_CONF_OK; + } + + cscf->resolver = ngx_resolver_create(cf, &value[1], cf->args->nelts - 1); + if (cscf->resolver == NULL) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +char * +ngx_mail_capabilities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_str_t *c, *value; + ngx_uint_t i; + ngx_array_t *a; + + a = (ngx_array_t *) (p + cmd->offset); + + value = cf->args->elts; + + for (i = 1; i < cf->args->nelts; i++) { + c = ngx_array_push(a); + if (c == NULL) { + return NGX_CONF_ERROR; + } + + *c = value[i]; + } + + return NGX_CONF_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_handler.c b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_handler.c new file mode 100644 index 000000000..a88e6c287 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_handler.c @@ -0,0 +1,1022 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include + + +static void ngx_mail_proxy_protocol_handler(ngx_event_t *rev); +static void ngx_mail_init_session_handler(ngx_event_t *rev); +static void ngx_mail_init_session(ngx_connection_t *c); + +#if (NGX_MAIL_SSL) +static void ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c); +static void ngx_mail_ssl_handshake_handler(ngx_connection_t *c); +static ngx_int_t ngx_mail_verify_cert(ngx_mail_session_t *s, + ngx_connection_t *c); +#endif + + +void +ngx_mail_init_connection(ngx_connection_t *c) +{ + size_t len; + ngx_uint_t i; + ngx_event_t *rev; + ngx_mail_port_t *port; + struct sockaddr *sa; + struct sockaddr_in *sin; + ngx_mail_log_ctx_t *ctx; + ngx_mail_in_addr_t *addr; + ngx_mail_session_t *s; + ngx_mail_addr_conf_t *addr_conf; + ngx_mail_core_srv_conf_t *cscf; + u_char text[NGX_SOCKADDR_STRLEN]; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; + ngx_mail_in6_addr_t *addr6; +#endif + + + /* find the server configuration for the address:port */ + + port = c->listening->servers; + + if (port->naddrs > 1) { + + /* + * There are several addresses on this port and one of them + * is the "*:port" wildcard so getsockname() is needed to determine + * the server address. + * + * AcceptEx() already gave this address. + */ + + if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { + ngx_mail_close_connection(c); + return; + } + + sa = c->local_sockaddr; + + switch (sa->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) sa; + + addr6 = port->addrs; + + /* the last address is "*" */ + + for (i = 0; i < port->naddrs - 1; i++) { + if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) { + break; + } + } + + addr_conf = &addr6[i].conf; + + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) sa; + + addr = port->addrs; + + /* the last address is "*" */ + + for (i = 0; i < port->naddrs - 1; i++) { + if (addr[i].addr == sin->sin_addr.s_addr) { + break; + } + } + + addr_conf = &addr[i].conf; + + break; + } + + } else { + switch (c->local_sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + addr6 = port->addrs; + addr_conf = &addr6[0].conf; + break; +#endif + + default: /* AF_INET */ + addr = port->addrs; + addr_conf = &addr[0].conf; + break; + } + } + + s = ngx_pcalloc(c->pool, sizeof(ngx_mail_session_t)); + if (s == NULL) { + ngx_mail_close_connection(c); + return; + } + + s->signature = NGX_MAIL_MODULE; + + s->main_conf = addr_conf->ctx->main_conf; + s->srv_conf = addr_conf->ctx->srv_conf; + +#if (NGX_MAIL_SSL) + s->ssl = addr_conf->ssl; +#endif + + s->addr_text = &addr_conf->addr_text; + + c->data = s; + s->connection = c; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + ngx_set_connection_log(c, cscf->error_log); + + len = ngx_sock_ntop(c->sockaddr, c->socklen, text, NGX_SOCKADDR_STRLEN, 1); + + ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%uA client %*s connected to %V", + c->number, len, text, s->addr_text); + + ctx = ngx_palloc(c->pool, sizeof(ngx_mail_log_ctx_t)); + if (ctx == NULL) { + ngx_mail_close_connection(c); + return; + } + + ctx->client = &c->addr_text; + ctx->session = s; + + c->log->connection = c->number; + c->log->handler = ngx_mail_log_error; + c->log->data = ctx; + c->log->action = "sending client greeting line"; + + c->log_error = NGX_ERROR_INFO; + + rev = c->read; + rev->handler = ngx_mail_init_session_handler; + + if (addr_conf->proxy_protocol) { + c->log->action = "reading PROXY protocol"; + + rev->handler = ngx_mail_proxy_protocol_handler; + + if (!rev->ready) { + ngx_add_timer(rev, cscf->timeout); + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_mail_close_connection(c); + } + + return; + } + } + + if (ngx_use_accept_mutex) { + ngx_post_event(rev, &ngx_posted_events); + return; + } + + rev->handler(rev); +} + + +static void +ngx_mail_proxy_protocol_handler(ngx_event_t *rev) +{ + u_char *p, buf[NGX_PROXY_PROTOCOL_MAX_HEADER]; + size_t size; + ssize_t n; + ngx_err_t err; + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_core_srv_conf_t *cscf; + + c = rev->data; + s = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail PROXY protocol handler"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + n = recv(c->fd, (char *) buf, sizeof(buf), MSG_PEEK); + + err = ngx_socket_errno; + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "recv(): %z", n); + + if (n == -1) { + if (err == NGX_EAGAIN) { + rev->ready = 0; + + if (!rev->timer_set) { + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + ngx_add_timer(rev, cscf->timeout); + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_mail_close_connection(c); + } + + return; + } + + ngx_connection_error(c, err, "recv() failed"); + + ngx_mail_close_connection(c); + return; + } + + p = ngx_proxy_protocol_read(c, buf, buf + n); + + if (p == NULL) { + ngx_mail_close_connection(c); + return; + } + + size = p - buf; + + if (c->recv(c, buf, size) != (ssize_t) size) { + ngx_mail_close_connection(c); + return; + } + + if (ngx_mail_realip_handler(s) != NGX_OK) { + ngx_mail_close_connection(c); + return; + } + + ngx_mail_init_session_handler(rev); +} + + +static void +ngx_mail_init_session_handler(ngx_event_t *rev) +{ + ngx_connection_t *c; + + c = rev->data; + +#if (NGX_MAIL_SSL) + { + ngx_mail_session_t *s; + ngx_mail_ssl_conf_t *sslcf; + + s = c->data; + + if (s->ssl) { + c->log->action = "SSL handshaking"; + + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); + + ngx_mail_ssl_init_connection(&sslcf->ssl, c); + return; + } + + } +#endif + + ngx_mail_init_session(c); +} + + +#if (NGX_MAIL_SSL) + +void +ngx_mail_starttls_handler(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_ssl_conf_t *sslcf; + + c = rev->data; + s = c->data; + s->starttls = 1; + + c->log->action = "in starttls state"; + + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); + + ngx_mail_ssl_init_connection(&sslcf->ssl, c); +} + + +static void +ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c) +{ + ngx_mail_session_t *s; + ngx_mail_core_srv_conf_t *cscf; + + if (ngx_ssl_create_connection(ssl, c, 0) != NGX_OK) { + ngx_mail_close_connection(c); + return; + } + + if (ngx_ssl_handshake(c) == NGX_AGAIN) { + + s = c->data; + + if (!c->read->timer_set) { + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + ngx_add_timer(c->read, cscf->timeout); + } + + c->ssl->handler = ngx_mail_ssl_handshake_handler; + + return; + } + + ngx_mail_ssl_handshake_handler(c); +} + + +static void +ngx_mail_ssl_handshake_handler(ngx_connection_t *c) +{ + ngx_mail_session_t *s; + ngx_mail_core_srv_conf_t *cscf; + + if (c->ssl->handshaked) { + + s = c->data; + + if (ngx_mail_verify_cert(s, c) != NGX_OK) { + return; + } + + if (s->starttls) { + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + c->read->handler = cscf->protocol->init_protocol; + c->write->handler = ngx_mail_send; + + cscf->protocol->init_protocol(c->read); + + return; + } + + c->read->ready = 0; + + ngx_mail_init_session(c); + return; + } + + ngx_mail_close_connection(c); +} + + +static ngx_int_t +ngx_mail_verify_cert(ngx_mail_session_t *s, ngx_connection_t *c) +{ + long rc; + X509 *cert; + ngx_mail_ssl_conf_t *sslcf; + ngx_mail_core_srv_conf_t *cscf; + + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); + + if (!sslcf->verify) { + return NGX_OK; + } + + rc = SSL_get_verify_result(c->ssl->connection); + + if (rc != X509_V_OK + && (sslcf->verify != 3 || !ngx_ssl_verify_error_optional(rc))) + { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client SSL certificate verify error: (%l:%s)", + rc, X509_verify_cert_error_string(rc)); + + ngx_ssl_remove_cached_session(c->ssl->session_ctx, + (SSL_get0_session(c->ssl->connection))); + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + s->out = cscf->protocol->cert_error; + s->quit = 1; + + c->write->handler = ngx_mail_send; + + ngx_mail_send(s->connection->write); + return NGX_ERROR; + } + + if (sslcf->verify == 1) { + cert = SSL_get_peer_certificate(c->ssl->connection); + + if (cert == NULL) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent no required SSL certificate"); + + ngx_ssl_remove_cached_session(c->ssl->session_ctx, + (SSL_get0_session(c->ssl->connection))); + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + s->out = cscf->protocol->no_cert; + s->quit = 1; + + c->write->handler = ngx_mail_send; + + ngx_mail_send(s->connection->write); + return NGX_ERROR; + } + + X509_free(cert); + } + + return NGX_OK; +} + +#endif + + +static void +ngx_mail_init_session(ngx_connection_t *c) +{ + ngx_mail_session_t *s; + ngx_mail_core_srv_conf_t *cscf; + + s = c->data; + + c->log->action = "sending client greeting line"; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + s->protocol = cscf->protocol->type; + + s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_mail_max_module); + if (s->ctx == NULL) { + ngx_mail_session_internal_server_error(s); + return; + } + + c->write->handler = ngx_mail_send; + + cscf->protocol->init_session(s, c); +} + + +ngx_int_t +ngx_mail_salt(ngx_mail_session_t *s, ngx_connection_t *c, + ngx_mail_core_srv_conf_t *cscf) +{ + s->salt.data = ngx_pnalloc(c->pool, + sizeof(" <18446744073709551616.@>" CRLF) - 1 + + NGX_TIME_T_LEN + + cscf->server_name.len); + if (s->salt.data == NULL) { + return NGX_ERROR; + } + + s->salt.len = ngx_sprintf(s->salt.data, "<%ul.%T@%V>" CRLF, + ngx_random(), ngx_time(), &cscf->server_name) + - s->salt.data; + + return NGX_OK; +} + + +#if (NGX_MAIL_SSL) + +ngx_int_t +ngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_mail_ssl_conf_t *sslcf; + + if (c->ssl) { + return 0; + } + + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); + + if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { + return 1; + } + + return 0; +} + +#endif + + +ngx_int_t +ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n) +{ + u_char *p, *pos, *last; + ngx_str_t *arg, plain; + + arg = s->args.elts; + +#if (NGX_DEBUG_MAIL_PASSWD) + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth plain: \"%V\"", &arg[n]); +#endif + + plain.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len)); + if (plain.data == NULL) { + return NGX_ERROR; + } + + if (ngx_decode_base64(&plain, &arg[n]) != NGX_OK) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid base64 encoding in AUTH PLAIN command"); + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + p = plain.data; + last = p + plain.len; + + while (p < last && *p++) { /* void */ } + + if (p == last) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid login in AUTH PLAIN command"); + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + pos = p; + + while (p < last && *p) { p++; } + + if (p == last) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid password in AUTH PLAIN command"); + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + s->login.len = p++ - pos; + s->login.data = pos; + + s->passwd.len = last - p; + s->passwd.data = p; + +#if (NGX_DEBUG_MAIL_PASSWD) + ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth plain: \"%V\" \"%V\"", &s->login, &s->passwd); +#endif + + return NGX_DONE; +} + + +ngx_int_t +ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c, + ngx_uint_t n) +{ + ngx_str_t *arg, login; + + arg = s->args.elts; + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth login username: \"%V\"", &arg[n]); + + login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len)); + if (login.data == NULL) { + return NGX_ERROR; + } + + if (ngx_decode_base64(&login, &arg[n]) != NGX_OK) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid base64 encoding in AUTH LOGIN command"); + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + s->login = login; + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth login username: \"%V\"", &s->login); + + return NGX_OK; +} + + +ngx_int_t +ngx_mail_auth_login_password(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg, passwd; + + arg = s->args.elts; + +#if (NGX_DEBUG_MAIL_PASSWD) + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth login password: \"%V\"", &arg[0]); +#endif + + passwd.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len)); + if (passwd.data == NULL) { + return NGX_ERROR; + } + + if (ngx_decode_base64(&passwd, &arg[0]) != NGX_OK) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid base64 encoding in AUTH LOGIN command"); + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + s->passwd = passwd; + +#if (NGX_DEBUG_MAIL_PASSWD) + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth login password: \"%V\"", &s->passwd); +#endif + + return NGX_DONE; +} + + +ngx_int_t +ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s, ngx_connection_t *c, + char *prefix, size_t len) +{ + u_char *p; + ngx_str_t salt; + ngx_uint_t n; + + p = ngx_pnalloc(c->pool, len + ngx_base64_encoded_length(s->salt.len) + 2); + if (p == NULL) { + return NGX_ERROR; + } + + salt.data = ngx_cpymem(p, prefix, len); + s->salt.len -= 2; + + ngx_encode_base64(&salt, &s->salt); + + s->salt.len += 2; + n = len + salt.len; + p[n++] = CR; p[n++] = LF; + + s->out.len = n; + s->out.data = p; + + return NGX_OK; +} + + +ngx_int_t +ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c) +{ + u_char *p, *last; + ngx_str_t *arg, login; + + arg = s->args.elts; + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth cram-md5: \"%V\"", &arg[0]); + + login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len)); + if (login.data == NULL) { + return NGX_ERROR; + } + + if (ngx_decode_base64(&login, &arg[0]) != NGX_OK) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid base64 encoding in AUTH CRAM-MD5 command"); + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + s->login = login; + + p = s->login.data; + last = p + s->login.len; + + while (p < last) { + if (*p++ == ' ') { + s->login.len = p - s->login.data - 1; + s->passwd.len = last - p; + s->passwd.data = p; + break; + } + } + + if (s->passwd.len != 32) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid CRAM-MD5 hash in AUTH CRAM-MD5 command"); + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth cram-md5: \"%V\" \"%V\"", &s->login, &s->passwd); + + s->auth_method = NGX_MAIL_AUTH_CRAM_MD5; + + return NGX_DONE; +} + + +ngx_int_t +ngx_mail_auth_external(ngx_mail_session_t *s, ngx_connection_t *c, + ngx_uint_t n) +{ + ngx_str_t *arg, external; + + arg = s->args.elts; + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth external: \"%V\"", &arg[n]); + + external.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len)); + if (external.data == NULL) { + return NGX_ERROR; + } + + if (ngx_decode_base64(&external, &arg[n]) != NGX_OK) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent invalid base64 encoding in AUTH EXTERNAL command"); + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + s->login.len = external.len; + s->login.data = external.data; + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "mail auth external: \"%V\"", &s->login); + + s->auth_method = NGX_MAIL_AUTH_EXTERNAL; + + return NGX_DONE; +} + + +void +ngx_mail_send(ngx_event_t *wev) +{ + ngx_int_t n; + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_core_srv_conf_t *cscf; + + c = wev->data; + s = c->data; + + if (wev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + if (s->out.len == 0) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { + ngx_mail_close_connection(c); + } + + return; + } + + n = c->send(c, s->out.data, s->out.len); + + if (n > 0) { + s->out.data += n; + s->out.len -= n; + + if (s->out.len != 0) { + goto again; + } + + if (wev->timer_set) { + ngx_del_timer(wev); + } + + if (s->quit) { + ngx_mail_close_connection(c); + return; + } + + if (s->blocked) { + c->read->handler(c->read); + } + + return; + } + + if (n == NGX_ERROR) { + ngx_mail_close_connection(c); + return; + } + + /* n == NGX_AGAIN */ + +again: + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + ngx_add_timer(c->write, cscf->timeout); + + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { + ngx_mail_close_connection(c); + return; + } +} + + +ngx_int_t +ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ssize_t n; + ngx_int_t rc; + ngx_str_t l; + ngx_mail_core_srv_conf_t *cscf; + + if (s->buffer->last < s->buffer->end) { + + n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last); + + if (n == NGX_ERROR || n == 0) { + ngx_mail_close_connection(c); + return NGX_ERROR; + } + + if (n > 0) { + s->buffer->last += n; + } + + if (n == NGX_AGAIN) { + if (s->buffer->pos == s->buffer->last) { + return NGX_AGAIN; + } + } + } + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + rc = cscf->protocol->parse_command(s); + + if (rc == NGX_AGAIN) { + + if (s->buffer->last < s->buffer->end) { + return rc; + } + + l.len = s->buffer->last - s->buffer->start; + l.data = s->buffer->start; + + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent too long command \"%V\"", &l); + + s->quit = 1; + + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + if (rc == NGX_MAIL_PARSE_INVALID_COMMAND) { + + s->errors++; + + if (s->errors >= cscf->max_errors) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent too many invalid commands"); + s->quit = 1; + } + + return rc; + } + + if (rc == NGX_IMAP_NEXT) { + return rc; + } + + if (rc == NGX_ERROR) { + ngx_mail_close_connection(c); + return NGX_ERROR; + } + + return NGX_OK; +} + + +void +ngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c) +{ + s->args.nelts = 0; + + if (s->buffer->pos == s->buffer->last) { + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + } + + s->state = 0; + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + s->login_attempt++; + + ngx_mail_auth_http_init(s); +} + + +void +ngx_mail_session_internal_server_error(ngx_mail_session_t *s) +{ + ngx_mail_core_srv_conf_t *cscf; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + s->out = cscf->protocol->internal_server_error; + s->quit = 1; + + ngx_mail_send(s->connection->write); +} + + +void +ngx_mail_close_connection(ngx_connection_t *c) +{ + ngx_pool_t *pool; + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "close mail connection: %d", c->fd); + +#if (NGX_MAIL_SSL) + + if (c->ssl) { + if (ngx_ssl_shutdown(c) == NGX_AGAIN) { + c->ssl->handler = ngx_mail_close_connection; + return; + } + } + +#endif + +#if (NGX_STAT_STUB) + (void) ngx_atomic_fetch_add(ngx_stat_active, -1); +#endif + + c->destroyed = 1; + + pool = c->pool; + + ngx_close_connection(c); + + ngx_destroy_pool(pool); +} + + +u_char * +ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len) +{ + u_char *p; + ngx_mail_session_t *s; + ngx_mail_log_ctx_t *ctx; + + if (log->action) { + p = ngx_snprintf(buf, len, " while %s", log->action); + len -= p - buf; + buf = p; + } + + ctx = log->data; + + p = ngx_snprintf(buf, len, ", client: %V", ctx->client); + len -= p - buf; + buf = p; + + s = ctx->session; + + if (s == NULL) { + return p; + } + + p = ngx_snprintf(buf, len, "%s, server: %V", + s->starttls ? " using starttls" : "", + s->addr_text); + len -= p - buf; + buf = p; + + if (s->login.len) { + p = ngx_snprintf(buf, len, ", login: \"%V\"", &s->login); + len -= p - buf; + buf = p; + } + + if (s->proxy == NULL) { + return p; + } + + p = ngx_snprintf(buf, len, ", upstream: %V", s->proxy->upstream.name); + + return p; +} diff --git a/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_imap_handler.c b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_imap_handler.c new file mode 100644 index 000000000..291e87a4d --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_imap_handler.c @@ -0,0 +1,477 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include +#include + + +static ngx_int_t ngx_mail_imap_login(ngx_mail_session_t *s, + ngx_connection_t *c); +static ngx_int_t ngx_mail_imap_authenticate(ngx_mail_session_t *s, + ngx_connection_t *c); +static ngx_int_t ngx_mail_imap_capability(ngx_mail_session_t *s, + ngx_connection_t *c); +static ngx_int_t ngx_mail_imap_starttls(ngx_mail_session_t *s, + ngx_connection_t *c); + + +static u_char imap_greeting[] = "* OK IMAP4 ready" CRLF; +static u_char imap_star[] = "* "; +static u_char imap_ok[] = "OK completed" CRLF; +static u_char imap_next[] = "+ OK" CRLF; +static u_char imap_plain_next[] = "+ " CRLF; +static u_char imap_username[] = "+ VXNlcm5hbWU6" CRLF; +static u_char imap_password[] = "+ UGFzc3dvcmQ6" CRLF; +static u_char imap_bye[] = "* BYE" CRLF; +static u_char imap_invalid_command[] = "BAD invalid command" CRLF; + + +void +ngx_mail_imap_init_session(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_mail_core_srv_conf_t *cscf; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + ngx_str_set(&s->out, imap_greeting); + + c->read->handler = ngx_mail_imap_init_protocol; + + ngx_add_timer(c->read, cscf->timeout); + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_close_connection(c); + } + + ngx_mail_send(c->write); +} + + +void +ngx_mail_imap_init_protocol(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_imap_srv_conf_t *iscf; + + c = rev->data; + + c->log->action = "in auth state"; + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + s = c->data; + + if (s->buffer == NULL) { + if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) + == NGX_ERROR) + { + ngx_mail_session_internal_server_error(s); + return; + } + + iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module); + + s->buffer = ngx_create_temp_buf(c->pool, iscf->client_buffer_size); + if (s->buffer == NULL) { + ngx_mail_session_internal_server_error(s); + return; + } + } + + s->mail_state = ngx_imap_start; + c->read->handler = ngx_mail_imap_auth_state; + + ngx_mail_imap_auth_state(rev); +} + + +void +ngx_mail_imap_auth_state(ngx_event_t *rev) +{ + u_char *p; + ngx_int_t rc; + ngx_uint_t tag; + ngx_connection_t *c; + ngx_mail_session_t *s; + + c = rev->data; + s = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap auth state"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + if (s->out.len) { + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap send handler busy"); + s->blocked = 1; + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_close_connection(c); + return; + } + + return; + } + + s->blocked = 0; + + rc = ngx_mail_read_command(s, c); + + if (rc == NGX_AGAIN) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_session_internal_server_error(s); + return; + } + + return; + } + + if (rc == NGX_ERROR) { + return; + } + + tag = 1; + s->text.len = 0; + ngx_str_set(&s->out, imap_ok); + + if (rc == NGX_OK) { + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap auth command: %i", + s->command); + + switch (s->mail_state) { + + case ngx_imap_start: + + switch (s->command) { + + case NGX_IMAP_LOGIN: + rc = ngx_mail_imap_login(s, c); + break; + + case NGX_IMAP_AUTHENTICATE: + rc = ngx_mail_imap_authenticate(s, c); + tag = (rc != NGX_OK); + break; + + case NGX_IMAP_CAPABILITY: + rc = ngx_mail_imap_capability(s, c); + break; + + case NGX_IMAP_LOGOUT: + s->quit = 1; + ngx_str_set(&s->text, imap_bye); + break; + + case NGX_IMAP_NOOP: + break; + + case NGX_IMAP_STARTTLS: + rc = ngx_mail_imap_starttls(s, c); + break; + + default: + rc = NGX_MAIL_PARSE_INVALID_COMMAND; + break; + } + + break; + + case ngx_imap_auth_login_username: + rc = ngx_mail_auth_login_username(s, c, 0); + + tag = 0; + ngx_str_set(&s->out, imap_password); + s->mail_state = ngx_imap_auth_login_password; + + break; + + case ngx_imap_auth_login_password: + rc = ngx_mail_auth_login_password(s, c); + break; + + case ngx_imap_auth_plain: + rc = ngx_mail_auth_plain(s, c, 0); + break; + + case ngx_imap_auth_cram_md5: + rc = ngx_mail_auth_cram_md5(s, c); + break; + + case ngx_imap_auth_external: + rc = ngx_mail_auth_external(s, c, 0); + break; + } + + } else if (rc == NGX_IMAP_NEXT) { + tag = 0; + ngx_str_set(&s->out, imap_next); + } + + if (s->buffer->pos < s->buffer->last) { + s->blocked = 1; + } + + switch (rc) { + + case NGX_DONE: + ngx_mail_auth(s, c); + return; + + case NGX_ERROR: + ngx_mail_session_internal_server_error(s); + return; + + case NGX_MAIL_PARSE_INVALID_COMMAND: + s->state = 0; + ngx_str_set(&s->out, imap_invalid_command); + s->mail_state = ngx_imap_start; + break; + } + + if (tag) { + if (s->tag.len == 0) { + ngx_str_set(&s->tag, imap_star); + } + + if (s->tagged_line.len < s->tag.len + s->text.len + s->out.len) { + s->tagged_line.len = s->tag.len + s->text.len + s->out.len; + s->tagged_line.data = ngx_pnalloc(c->pool, s->tagged_line.len); + if (s->tagged_line.data == NULL) { + ngx_mail_close_connection(c); + return; + } + } + + p = s->tagged_line.data; + + if (s->text.len) { + p = ngx_cpymem(p, s->text.data, s->text.len); + } + + p = ngx_cpymem(p, s->tag.data, s->tag.len); + ngx_memcpy(p, s->out.data, s->out.len); + + s->out.len = s->text.len + s->tag.len + s->out.len; + s->out.data = s->tagged_line.data; + } + + if (rc != NGX_IMAP_NEXT) { + s->args.nelts = 0; + + if (s->state) { + /* preserve tag */ + s->arg_start = s->buffer->pos; + + } else { + if (s->buffer->pos == s->buffer->last) { + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + } + + s->tag.len = 0; + } + } + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_session_internal_server_error(s); + return; + } + + ngx_mail_send(c->write); +} + + +static ngx_int_t +ngx_mail_imap_login(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + +#if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +#endif + + arg = s->args.elts; + + if (s->args.nelts != 2 || arg[0].len == 0) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + s->login.len = arg[0].len; + s->login.data = ngx_pnalloc(c->pool, s->login.len); + if (s->login.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->login.data, arg[0].data, s->login.len); + + s->passwd.len = arg[1].len; + s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len); + if (s->passwd.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len); + +#if (NGX_DEBUG_MAIL_PASSWD) + ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, + "imap login:\"%V\" passwd:\"%V\"", + &s->login, &s->passwd); +#else + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "imap login:\"%V\"", &s->login); +#endif + + return NGX_DONE; +} + + +static ngx_int_t +ngx_mail_imap_authenticate(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_int_t rc; + ngx_mail_core_srv_conf_t *cscf; + ngx_mail_imap_srv_conf_t *iscf; + +#if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +#endif + + iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module); + + rc = ngx_mail_auth_parse(s, c); + + switch (rc) { + + case NGX_MAIL_AUTH_LOGIN: + + ngx_str_set(&s->out, imap_username); + s->mail_state = ngx_imap_auth_login_username; + + return NGX_OK; + + case NGX_MAIL_AUTH_LOGIN_USERNAME: + + ngx_str_set(&s->out, imap_password); + s->mail_state = ngx_imap_auth_login_password; + + return ngx_mail_auth_login_username(s, c, 1); + + case NGX_MAIL_AUTH_PLAIN: + + ngx_str_set(&s->out, imap_plain_next); + s->mail_state = ngx_imap_auth_plain; + + return NGX_OK; + + case NGX_MAIL_AUTH_CRAM_MD5: + + if (!(iscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + if (s->salt.data == NULL) { + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + if (ngx_mail_salt(s, c, cscf) != NGX_OK) { + return NGX_ERROR; + } + } + + if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) { + s->mail_state = ngx_imap_auth_cram_md5; + return NGX_OK; + } + + return NGX_ERROR; + + case NGX_MAIL_AUTH_EXTERNAL: + + if (!(iscf->auth_methods & NGX_MAIL_AUTH_EXTERNAL_ENABLED)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + ngx_str_set(&s->out, imap_username); + s->mail_state = ngx_imap_auth_external; + + return NGX_OK; + } + + return rc; +} + + +static ngx_int_t +ngx_mail_imap_capability(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_mail_imap_srv_conf_t *iscf; + + iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module); + +#if (NGX_MAIL_SSL) + + if (c->ssl == NULL) { + ngx_mail_ssl_conf_t *sslcf; + + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); + + if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) { + s->text = iscf->starttls_capability; + return NGX_OK; + } + + if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { + s->text = iscf->starttls_only_capability; + return NGX_OK; + } + } +#endif + + s->text = iscf->capability; + + return NGX_OK; +} + + +static ngx_int_t +ngx_mail_imap_starttls(ngx_mail_session_t *s, ngx_connection_t *c) +{ +#if (NGX_MAIL_SSL) + ngx_mail_ssl_conf_t *sslcf; + + if (c->ssl == NULL) { + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); + if (sslcf->starttls) { + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + c->read->handler = ngx_mail_starttls_handler; + return NGX_OK; + } + } + +#endif + + return NGX_MAIL_PARSE_INVALID_COMMAND; +} diff --git a/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_imap_module.c b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_imap_module.c new file mode 100644 index 000000000..02c684cd4 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_imap_module.c @@ -0,0 +1,258 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include +#include + + +static void *ngx_mail_imap_create_srv_conf(ngx_conf_t *cf); +static char *ngx_mail_imap_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); + + +static ngx_str_t ngx_mail_imap_default_capabilities[] = { + ngx_string("IMAP4"), + ngx_string("IMAP4rev1"), + ngx_string("UIDPLUS"), + ngx_null_string +}; + + +static ngx_conf_bitmask_t ngx_mail_imap_auth_methods[] = { + { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED }, + { ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED }, + { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED }, + { ngx_string("external"), NGX_MAIL_AUTH_EXTERNAL_ENABLED }, + { ngx_null_string, 0 } +}; + + +static ngx_str_t ngx_mail_imap_auth_methods_names[] = { + ngx_string("AUTH=PLAIN"), + ngx_string("AUTH=LOGIN"), + ngx_null_string, /* APOP */ + ngx_string("AUTH=CRAM-MD5"), + ngx_string("AUTH=EXTERNAL"), + ngx_null_string /* NONE */ +}; + + +static ngx_mail_protocol_t ngx_mail_imap_protocol = { + ngx_string("imap"), + ngx_string("\x04imap"), + { 143, 993, 0, 0 }, + NGX_MAIL_IMAP_PROTOCOL, + + ngx_mail_imap_init_session, + ngx_mail_imap_init_protocol, + ngx_mail_imap_parse_command, + ngx_mail_imap_auth_state, + + ngx_string("* BAD internal server error" CRLF), + ngx_string("* BYE SSL certificate error" CRLF), + ngx_string("* BYE No required SSL certificate" CRLF) +}; + + +static ngx_command_t ngx_mail_imap_commands[] = { + + { ngx_string("imap_client_buffer"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_imap_srv_conf_t, client_buffer_size), + NULL }, + + { ngx_string("imap_capabilities"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, + ngx_mail_capabilities, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_imap_srv_conf_t, capabilities), + NULL }, + + { ngx_string("imap_auth"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_imap_srv_conf_t, auth_methods), + &ngx_mail_imap_auth_methods }, + + ngx_null_command +}; + + +static ngx_mail_module_t ngx_mail_imap_module_ctx = { + &ngx_mail_imap_protocol, /* protocol */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_mail_imap_create_srv_conf, /* create server configuration */ + ngx_mail_imap_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_mail_imap_module = { + NGX_MODULE_V1, + &ngx_mail_imap_module_ctx, /* module context */ + ngx_mail_imap_commands, /* module directives */ + NGX_MAIL_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static void * +ngx_mail_imap_create_srv_conf(ngx_conf_t *cf) +{ + ngx_mail_imap_srv_conf_t *iscf; + + iscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_imap_srv_conf_t)); + if (iscf == NULL) { + return NULL; + } + + iscf->client_buffer_size = NGX_CONF_UNSET_SIZE; + + if (ngx_array_init(&iscf->capabilities, cf->pool, 4, sizeof(ngx_str_t)) + != NGX_OK) + { + return NULL; + } + + return iscf; +} + + +static char * +ngx_mail_imap_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_mail_imap_srv_conf_t *prev = parent; + ngx_mail_imap_srv_conf_t *conf = child; + + u_char *p, *auth; + size_t size; + ngx_str_t *c, *d; + ngx_uint_t i, m; + + ngx_conf_merge_size_value(conf->client_buffer_size, + prev->client_buffer_size, + (size_t) ngx_pagesize); + + ngx_conf_merge_bitmask_value(conf->auth_methods, + prev->auth_methods, + (NGX_CONF_BITMASK_SET + |NGX_MAIL_AUTH_PLAIN_ENABLED)); + + + if (conf->capabilities.nelts == 0) { + conf->capabilities = prev->capabilities; + } + + if (conf->capabilities.nelts == 0) { + + for (d = ngx_mail_imap_default_capabilities; d->len; d++) { + c = ngx_array_push(&conf->capabilities); + if (c == NULL) { + return NGX_CONF_ERROR; + } + + *c = *d; + } + } + + size = sizeof("* CAPABILITY" CRLF) - 1; + + c = conf->capabilities.elts; + for (i = 0; i < conf->capabilities.nelts; i++) { + size += 1 + c[i].len; + } + + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; + m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; + m <<= 1, i++) + { + if (m & conf->auth_methods) { + size += 1 + ngx_mail_imap_auth_methods_names[i].len; + } + } + + p = ngx_pnalloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->capability.len = size; + conf->capability.data = p; + + p = ngx_cpymem(p, "* CAPABILITY", sizeof("* CAPABILITY") - 1); + + for (i = 0; i < conf->capabilities.nelts; i++) { + *p++ = ' '; + p = ngx_cpymem(p, c[i].data, c[i].len); + } + + auth = p; + + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; + m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; + m <<= 1, i++) + { + if (m & conf->auth_methods) { + *p++ = ' '; + p = ngx_cpymem(p, ngx_mail_imap_auth_methods_names[i].data, + ngx_mail_imap_auth_methods_names[i].len); + } + } + + *p++ = CR; *p = LF; + + + size += sizeof(" STARTTLS") - 1; + + p = ngx_pnalloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->starttls_capability.len = size; + conf->starttls_capability.data = p; + + p = ngx_cpymem(p, conf->capability.data, + conf->capability.len - (sizeof(CRLF) - 1)); + p = ngx_cpymem(p, " STARTTLS", sizeof(" STARTTLS") - 1); + *p++ = CR; *p = LF; + + + size = (auth - conf->capability.data) + sizeof(CRLF) - 1 + + sizeof(" STARTTLS LOGINDISABLED") - 1; + + p = ngx_pnalloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->starttls_only_capability.len = size; + conf->starttls_only_capability.data = p; + + p = ngx_cpymem(p, conf->capability.data, + auth - conf->capability.data); + p = ngx_cpymem(p, " STARTTLS LOGINDISABLED", + sizeof(" STARTTLS LOGINDISABLED") - 1); + *p++ = CR; *p = LF; + + return NGX_CONF_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_imap_module.h b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_imap_module.h new file mode 100644 index 000000000..131b44597 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_imap_module.h @@ -0,0 +1,39 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_MAIL_IMAP_MODULE_H_INCLUDED_ +#define _NGX_MAIL_IMAP_MODULE_H_INCLUDED_ + + +#include +#include +#include + + +typedef struct { + size_t client_buffer_size; + + ngx_str_t capability; + ngx_str_t starttls_capability; + ngx_str_t starttls_only_capability; + + ngx_uint_t auth_methods; + + ngx_array_t capabilities; +} ngx_mail_imap_srv_conf_t; + + +void ngx_mail_imap_init_session(ngx_mail_session_t *s, ngx_connection_t *c); +void ngx_mail_imap_init_protocol(ngx_event_t *rev); +void ngx_mail_imap_auth_state(ngx_event_t *rev); +ngx_int_t ngx_mail_imap_parse_command(ngx_mail_session_t *s); + + +extern ngx_module_t ngx_mail_imap_module; + + +#endif /* _NGX_MAIL_IMAP_MODULE_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_parse.c b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_parse.c new file mode 100644 index 000000000..227b63abb --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_parse.c @@ -0,0 +1,974 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include +#include +#include +#include + + +ngx_int_t +ngx_mail_pop3_parse_command(ngx_mail_session_t *s) +{ + u_char ch, *p, *c, c0, c1, c2, c3; + ngx_str_t *arg; + enum { + sw_start = 0, + sw_command, + sw_invalid, + sw_spaces_before_argument, + sw_argument, + sw_almost_done + } state; + + state = s->state; + + for (p = s->buffer->pos; p < s->buffer->last; p++) { + ch = *p; + + switch (state) { + + /* POP3 command */ + case sw_start: + s->cmd_start = p; + state = sw_command; + + /* fall through */ + + case sw_command: + if (ch == ' ' || ch == CR || ch == LF) { + c = s->cmd_start; + + if (p - c == 4) { + + c0 = ngx_toupper(c[0]); + c1 = ngx_toupper(c[1]); + c2 = ngx_toupper(c[2]); + c3 = ngx_toupper(c[3]); + + if (c0 == 'U' && c1 == 'S' && c2 == 'E' && c3 == 'R') + { + s->command = NGX_POP3_USER; + + } else if (c0 == 'P' && c1 == 'A' && c2 == 'S' && c3 == 'S') + { + s->command = NGX_POP3_PASS; + + } else if (c0 == 'A' && c1 == 'P' && c2 == 'O' && c3 == 'P') + { + s->command = NGX_POP3_APOP; + + } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T') + { + s->command = NGX_POP3_QUIT; + + } else if (c0 == 'C' && c1 == 'A' && c2 == 'P' && c3 == 'A') + { + s->command = NGX_POP3_CAPA; + + } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H') + { + s->command = NGX_POP3_AUTH; + + } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P') + { + s->command = NGX_POP3_NOOP; +#if (NGX_MAIL_SSL) + } else if (c0 == 'S' && c1 == 'T' && c2 == 'L' && c3 == 'S') + { + s->command = NGX_POP3_STLS; +#endif + } else { + goto invalid; + } + + } else { + goto invalid; + } + + s->cmd.data = s->cmd_start; + s->cmd.len = p - s->cmd_start; + + switch (ch) { + case ' ': + state = sw_spaces_before_argument; + break; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + } + break; + } + + if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) { + goto invalid; + } + + break; + + case sw_invalid: + goto invalid; + + case sw_spaces_before_argument: + switch (ch) { + case ' ': + break; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + default: + if (s->args.nelts <= 2) { + state = sw_argument; + s->arg_start = p; + break; + } + goto invalid; + } + break; + + case sw_argument: + switch (ch) { + + case ' ': + + /* + * the space should be considered as part of the at username + * or password, but not of argument in other commands + */ + + if (s->command == NGX_POP3_USER + || s->command == NGX_POP3_PASS) + { + break; + } + + /* fall through */ + + case CR: + case LF: + arg = ngx_array_push(&s->args); + if (arg == NULL) { + return NGX_ERROR; + } + arg->len = p - s->arg_start; + arg->data = s->arg_start; + s->arg_start = NULL; + + switch (ch) { + case ' ': + state = sw_spaces_before_argument; + break; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + } + break; + + default: + break; + } + break; + + case sw_almost_done: + switch (ch) { + case LF: + goto done; + default: + goto invalid; + } + } + } + + s->buffer->pos = p; + s->state = state; + + return NGX_AGAIN; + +done: + + s->buffer->pos = p + 1; + s->state = (s->command != NGX_POP3_AUTH) ? sw_start : sw_argument; + + return NGX_OK; + +invalid: + + s->state = sw_invalid; + + /* skip invalid command till LF */ + + for ( /* void */ ; p < s->buffer->last; p++) { + if (*p == LF) { + s->state = sw_start; + s->buffer->pos = p + 1; + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + } + + s->buffer->pos = p; + + return NGX_AGAIN; +} + + +ngx_int_t +ngx_mail_imap_parse_command(ngx_mail_session_t *s) +{ + u_char ch, *p, *c, *dst, *src, *end; + ngx_str_t *arg; + enum { + sw_start = 0, + sw_tag, + sw_invalid, + sw_spaces_before_command, + sw_command, + sw_spaces_before_argument, + sw_argument, + sw_backslash, + sw_literal, + sw_no_sync_literal_argument, + sw_start_literal_argument, + sw_literal_argument, + sw_end_literal_argument, + sw_almost_done + } state; + + state = s->state; + + for (p = s->buffer->pos; p < s->buffer->last; p++) { + ch = *p; + + switch (state) { + + /* IMAP tag */ + case sw_start: + s->tag_start = p; + state = sw_tag; + + /* fall through */ + + case sw_tag: + switch (ch) { + case ' ': + s->tag.len = p - s->tag_start + 1; + s->tag.data = s->tag_start; + state = sw_spaces_before_command; + break; + case CR: + case LF: + goto invalid; + default: + if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z') + && (ch < '0' || ch > '9') && ch != '-' && ch != '.' + && ch != '_') + { + goto invalid; + } + if (p - s->tag_start > 31) { + goto invalid; + } + break; + } + break; + + case sw_invalid: + goto invalid; + + case sw_spaces_before_command: + switch (ch) { + case ' ': + break; + case CR: + case LF: + goto invalid; + default: + s->cmd_start = p; + state = sw_command; + break; + } + break; + + case sw_command: + if (ch == ' ' || ch == CR || ch == LF) { + + c = s->cmd_start; + + switch (p - c) { + + case 4: + if ((c[0] == 'N' || c[0] == 'n') + && (c[1] == 'O'|| c[1] == 'o') + && (c[2] == 'O'|| c[2] == 'o') + && (c[3] == 'P'|| c[3] == 'p')) + { + s->command = NGX_IMAP_NOOP; + + } else { + goto invalid; + } + break; + + case 5: + if ((c[0] == 'L'|| c[0] == 'l') + && (c[1] == 'O'|| c[1] == 'o') + && (c[2] == 'G'|| c[2] == 'g') + && (c[3] == 'I'|| c[3] == 'i') + && (c[4] == 'N'|| c[4] == 'n')) + { + s->command = NGX_IMAP_LOGIN; + + } else { + goto invalid; + } + break; + + case 6: + if ((c[0] == 'L'|| c[0] == 'l') + && (c[1] == 'O'|| c[1] == 'o') + && (c[2] == 'G'|| c[2] == 'g') + && (c[3] == 'O'|| c[3] == 'o') + && (c[4] == 'U'|| c[4] == 'u') + && (c[5] == 'T'|| c[5] == 't')) + { + s->command = NGX_IMAP_LOGOUT; + + } else { + goto invalid; + } + break; + +#if (NGX_MAIL_SSL) + case 8: + if ((c[0] == 'S'|| c[0] == 's') + && (c[1] == 'T'|| c[1] == 't') + && (c[2] == 'A'|| c[2] == 'a') + && (c[3] == 'R'|| c[3] == 'r') + && (c[4] == 'T'|| c[4] == 't') + && (c[5] == 'T'|| c[5] == 't') + && (c[6] == 'L'|| c[6] == 'l') + && (c[7] == 'S'|| c[7] == 's')) + { + s->command = NGX_IMAP_STARTTLS; + + } else { + goto invalid; + } + break; +#endif + + case 10: + if ((c[0] == 'C'|| c[0] == 'c') + && (c[1] == 'A'|| c[1] == 'a') + && (c[2] == 'P'|| c[2] == 'p') + && (c[3] == 'A'|| c[3] == 'a') + && (c[4] == 'B'|| c[4] == 'b') + && (c[5] == 'I'|| c[5] == 'i') + && (c[6] == 'L'|| c[6] == 'l') + && (c[7] == 'I'|| c[7] == 'i') + && (c[8] == 'T'|| c[8] == 't') + && (c[9] == 'Y'|| c[9] == 'y')) + { + s->command = NGX_IMAP_CAPABILITY; + + } else { + goto invalid; + } + break; + + case 12: + if ((c[0] == 'A'|| c[0] == 'a') + && (c[1] == 'U'|| c[1] == 'u') + && (c[2] == 'T'|| c[2] == 't') + && (c[3] == 'H'|| c[3] == 'h') + && (c[4] == 'E'|| c[4] == 'e') + && (c[5] == 'N'|| c[5] == 'n') + && (c[6] == 'T'|| c[6] == 't') + && (c[7] == 'I'|| c[7] == 'i') + && (c[8] == 'C'|| c[8] == 'c') + && (c[9] == 'A'|| c[9] == 'a') + && (c[10] == 'T'|| c[10] == 't') + && (c[11] == 'E'|| c[11] == 'e')) + { + s->command = NGX_IMAP_AUTHENTICATE; + + } else { + goto invalid; + } + break; + + default: + goto invalid; + } + + s->cmd.data = s->cmd_start; + s->cmd.len = p - s->cmd_start; + + switch (ch) { + case ' ': + state = sw_spaces_before_argument; + break; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + } + break; + } + + if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) { + goto invalid; + } + + break; + + case sw_spaces_before_argument: + switch (ch) { + case ' ': + break; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + case '"': + if (s->args.nelts <= 2) { + s->quoted = 1; + s->arg_start = p + 1; + state = sw_argument; + break; + } + goto invalid; + case '{': + if (s->args.nelts <= 2) { + state = sw_literal; + break; + } + goto invalid; + default: + if (s->args.nelts <= 2) { + s->arg_start = p; + state = sw_argument; + break; + } + goto invalid; + } + break; + + case sw_argument: + if (ch == ' ' && s->quoted) { + break; + } + + switch (ch) { + case '"': + if (!s->quoted) { + break; + } + s->quoted = 0; + /* fall through */ + case ' ': + case CR: + case LF: + arg = ngx_array_push(&s->args); + if (arg == NULL) { + return NGX_ERROR; + } + arg->len = p - s->arg_start; + arg->data = s->arg_start; + + if (s->backslash) { + dst = s->arg_start; + end = p; + + for (src = dst; src < end; dst++) { + *dst = *src; + if (*src++ == '\\') { + *dst = *src++; + } + } + + arg->len = dst - s->arg_start; + s->backslash = 0; + } + + s->arg_start = NULL; + + switch (ch) { + case '"': + case ' ': + state = sw_spaces_before_argument; + break; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + } + break; + case '\\': + if (s->quoted) { + s->backslash = 1; + state = sw_backslash; + } + break; + } + break; + + case sw_backslash: + switch (ch) { + case CR: + case LF: + goto invalid; + default: + state = sw_argument; + } + break; + + case sw_literal: + if (s->literal_len > NGX_MAX_SIZE_T_VALUE / 10) { + goto invalid; + } + if (ch >= '0' && ch <= '9') { + s->literal_len = s->literal_len * 10 + (ch - '0'); + break; + } + if (ch == '}') { + state = sw_start_literal_argument; + break; + } + if (ch == '+') { + state = sw_no_sync_literal_argument; + break; + } + goto invalid; + + case sw_no_sync_literal_argument: + if (ch == '}') { + s->no_sync_literal = 1; + state = sw_start_literal_argument; + break; + } + goto invalid; + + case sw_start_literal_argument: + switch (ch) { + case CR: + break; + case LF: + s->buffer->pos = p + 1; + s->arg_start = p + 1; + if (s->no_sync_literal == 0) { + s->state = sw_literal_argument; + return NGX_IMAP_NEXT; + } + state = sw_literal_argument; + s->no_sync_literal = 0; + break; + default: + goto invalid; + } + break; + + case sw_literal_argument: + if (s->literal_len && --s->literal_len) { + break; + } + + arg = ngx_array_push(&s->args); + if (arg == NULL) { + return NGX_ERROR; + } + arg->len = p + 1 - s->arg_start; + arg->data = s->arg_start; + s->arg_start = NULL; + state = sw_end_literal_argument; + + break; + + case sw_end_literal_argument: + switch (ch) { + case ' ': + state = sw_spaces_before_argument; + break; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + default: + goto invalid; + } + break; + + case sw_almost_done: + switch (ch) { + case LF: + goto done; + default: + goto invalid; + } + } + } + + s->buffer->pos = p; + s->state = state; + + return NGX_AGAIN; + +done: + + s->buffer->pos = p + 1; + s->state = (s->command != NGX_IMAP_AUTHENTICATE) ? sw_start : sw_argument; + + return NGX_OK; + +invalid: + + s->state = sw_invalid; + s->quoted = 0; + s->backslash = 0; + s->no_sync_literal = 0; + s->literal_len = 0; + + /* skip invalid command till LF */ + + for ( /* void */ ; p < s->buffer->last; p++) { + if (*p == LF) { + s->state = sw_start; + s->buffer->pos = p + 1; + + /* detect non-synchronizing literals */ + + if ((size_t) (p - s->buffer->start) > sizeof("{1+}") - 1) { + p--; + + if (*p == CR) { + p--; + } + + if (*p == '}' && *(p - 1) == '+') { + s->quit = 1; + } + } + + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + } + + s->buffer->pos = p; + + return NGX_AGAIN; +} + + +ngx_int_t +ngx_mail_smtp_parse_command(ngx_mail_session_t *s) +{ + u_char ch, *p, *c, c0, c1, c2, c3; + ngx_str_t *arg; + enum { + sw_start = 0, + sw_command, + sw_invalid, + sw_spaces_before_argument, + sw_argument, + sw_almost_done + } state; + + state = s->state; + + for (p = s->buffer->pos; p < s->buffer->last; p++) { + ch = *p; + + switch (state) { + + /* SMTP command */ + case sw_start: + s->cmd_start = p; + state = sw_command; + + /* fall through */ + + case sw_command: + if (ch == ' ' || ch == CR || ch == LF) { + c = s->cmd_start; + + if (p - c == 4) { + + c0 = ngx_toupper(c[0]); + c1 = ngx_toupper(c[1]); + c2 = ngx_toupper(c[2]); + c3 = ngx_toupper(c[3]); + + if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'O') + { + s->command = NGX_SMTP_HELO; + + } else if (c0 == 'E' && c1 == 'H' && c2 == 'L' && c3 == 'O') + { + s->command = NGX_SMTP_EHLO; + + } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T') + { + s->command = NGX_SMTP_QUIT; + + } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H') + { + s->command = NGX_SMTP_AUTH; + + } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P') + { + s->command = NGX_SMTP_NOOP; + + } else if (c0 == 'M' && c1 == 'A' && c2 == 'I' && c3 == 'L') + { + s->command = NGX_SMTP_MAIL; + + } else if (c0 == 'R' && c1 == 'S' && c2 == 'E' && c3 == 'T') + { + s->command = NGX_SMTP_RSET; + + } else if (c0 == 'R' && c1 == 'C' && c2 == 'P' && c3 == 'T') + { + s->command = NGX_SMTP_RCPT; + + } else if (c0 == 'V' && c1 == 'R' && c2 == 'F' && c3 == 'Y') + { + s->command = NGX_SMTP_VRFY; + + } else if (c0 == 'E' && c1 == 'X' && c2 == 'P' && c3 == 'N') + { + s->command = NGX_SMTP_EXPN; + + } else if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'P') + { + s->command = NGX_SMTP_HELP; + + } else { + goto invalid; + } +#if (NGX_MAIL_SSL) + } else if (p - c == 8) { + + if ((c[0] == 'S'|| c[0] == 's') + && (c[1] == 'T'|| c[1] == 't') + && (c[2] == 'A'|| c[2] == 'a') + && (c[3] == 'R'|| c[3] == 'r') + && (c[4] == 'T'|| c[4] == 't') + && (c[5] == 'T'|| c[5] == 't') + && (c[6] == 'L'|| c[6] == 'l') + && (c[7] == 'S'|| c[7] == 's')) + { + s->command = NGX_SMTP_STARTTLS; + + } else { + goto invalid; + } +#endif + } else { + goto invalid; + } + + s->cmd.data = s->cmd_start; + s->cmd.len = p - s->cmd_start; + + switch (ch) { + case ' ': + state = sw_spaces_before_argument; + break; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + } + break; + } + + if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) { + goto invalid; + } + + break; + + case sw_invalid: + goto invalid; + + case sw_spaces_before_argument: + switch (ch) { + case ' ': + break; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + default: + if (s->args.nelts <= 10) { + state = sw_argument; + s->arg_start = p; + break; + } + goto invalid; + } + break; + + case sw_argument: + switch (ch) { + case ' ': + case CR: + case LF: + arg = ngx_array_push(&s->args); + if (arg == NULL) { + return NGX_ERROR; + } + arg->len = p - s->arg_start; + arg->data = s->arg_start; + s->arg_start = NULL; + + switch (ch) { + case ' ': + state = sw_spaces_before_argument; + break; + case CR: + state = sw_almost_done; + break; + case LF: + goto done; + } + break; + + default: + break; + } + break; + + case sw_almost_done: + switch (ch) { + case LF: + goto done; + default: + goto invalid; + } + } + } + + s->buffer->pos = p; + s->state = state; + + return NGX_AGAIN; + +done: + + s->buffer->pos = p + 1; + s->state = (s->command != NGX_SMTP_AUTH) ? sw_start : sw_argument; + + return NGX_OK; + +invalid: + + s->state = sw_invalid; + + /* skip invalid command till LF */ + + for ( /* void */ ; p < s->buffer->last; p++) { + if (*p == LF) { + s->state = sw_start; + s->buffer->pos = p + 1; + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + } + + s->buffer->pos = p; + + return NGX_AGAIN; +} + + +ngx_int_t +ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + +#if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +#endif + + if (s->args.nelts == 0) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + arg = s->args.elts; + + if (arg[0].len == 5) { + + if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5) == 0) { + + if (s->args.nelts == 1) { + return NGX_MAIL_AUTH_LOGIN; + } + + if (s->args.nelts == 2) { + return NGX_MAIL_AUTH_LOGIN_USERNAME; + } + + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN", 5) == 0) { + + if (s->args.nelts == 1) { + return NGX_MAIL_AUTH_PLAIN; + } + + if (s->args.nelts == 2) { + return ngx_mail_auth_plain(s, c, 1); + } + } + + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + if (arg[0].len == 8) { + + if (ngx_strncasecmp(arg[0].data, (u_char *) "CRAM-MD5", 8) == 0) { + + if (s->args.nelts != 1) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + return NGX_MAIL_AUTH_CRAM_MD5; + } + + if (ngx_strncasecmp(arg[0].data, (u_char *) "EXTERNAL", 8) == 0) { + + if (s->args.nelts == 1) { + return NGX_MAIL_AUTH_EXTERNAL; + } + + if (s->args.nelts == 2) { + return ngx_mail_auth_external(s, c, 1); + } + } + + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + return NGX_MAIL_PARSE_INVALID_COMMAND; +} diff --git a/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_pop3_handler.c b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_pop3_handler.c new file mode 100644 index 000000000..226e7419b --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_pop3_handler.c @@ -0,0 +1,544 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include +#include + + +static ngx_int_t ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c, + ngx_int_t stls); +static ngx_int_t ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c); + + +static u_char pop3_greeting[] = "+OK POP3 ready" CRLF; +static u_char pop3_ok[] = "+OK" CRLF; +static u_char pop3_next[] = "+ " CRLF; +static u_char pop3_username[] = "+ VXNlcm5hbWU6" CRLF; +static u_char pop3_password[] = "+ UGFzc3dvcmQ6" CRLF; +static u_char pop3_invalid_command[] = "-ERR invalid command" CRLF; + + +void +ngx_mail_pop3_init_session(ngx_mail_session_t *s, ngx_connection_t *c) +{ + u_char *p; + ngx_mail_core_srv_conf_t *cscf; + ngx_mail_pop3_srv_conf_t *pscf; + + pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module); + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + if (pscf->auth_methods + & (NGX_MAIL_AUTH_APOP_ENABLED|NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) + { + if (ngx_mail_salt(s, c, cscf) != NGX_OK) { + ngx_mail_session_internal_server_error(s); + return; + } + + s->out.data = ngx_pnalloc(c->pool, sizeof(pop3_greeting) + s->salt.len); + if (s->out.data == NULL) { + ngx_mail_session_internal_server_error(s); + return; + } + + p = ngx_cpymem(s->out.data, pop3_greeting, sizeof(pop3_greeting) - 3); + *p++ = ' '; + p = ngx_cpymem(p, s->salt.data, s->salt.len); + + s->out.len = p - s->out.data; + + } else { + ngx_str_set(&s->out, pop3_greeting); + } + + c->read->handler = ngx_mail_pop3_init_protocol; + + ngx_add_timer(c->read, cscf->timeout); + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_close_connection(c); + } + + ngx_mail_send(c->write); +} + + +void +ngx_mail_pop3_init_protocol(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + + c = rev->data; + + c->log->action = "in auth state"; + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + s = c->data; + + if (s->buffer == NULL) { + if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) + == NGX_ERROR) + { + ngx_mail_session_internal_server_error(s); + return; + } + + s->buffer = ngx_create_temp_buf(c->pool, 128); + if (s->buffer == NULL) { + ngx_mail_session_internal_server_error(s); + return; + } + } + + s->mail_state = ngx_pop3_start; + c->read->handler = ngx_mail_pop3_auth_state; + + ngx_mail_pop3_auth_state(rev); +} + + +void +ngx_mail_pop3_auth_state(ngx_event_t *rev) +{ + ngx_int_t rc; + ngx_connection_t *c; + ngx_mail_session_t *s; + + c = rev->data; + s = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 auth state"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + if (s->out.len) { + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 send handler busy"); + s->blocked = 1; + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_close_connection(c); + return; + } + + return; + } + + s->blocked = 0; + + rc = ngx_mail_read_command(s, c); + + if (rc == NGX_AGAIN) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_session_internal_server_error(s); + return; + } + + return; + } + + if (rc == NGX_ERROR) { + return; + } + + ngx_str_set(&s->out, pop3_ok); + + if (rc == NGX_OK) { + switch (s->mail_state) { + + case ngx_pop3_start: + + switch (s->command) { + + case NGX_POP3_USER: + rc = ngx_mail_pop3_user(s, c); + break; + + case NGX_POP3_CAPA: + rc = ngx_mail_pop3_capa(s, c, 1); + break; + + case NGX_POP3_APOP: + rc = ngx_mail_pop3_apop(s, c); + break; + + case NGX_POP3_AUTH: + rc = ngx_mail_pop3_auth(s, c); + break; + + case NGX_POP3_QUIT: + s->quit = 1; + break; + + case NGX_POP3_NOOP: + break; + + case NGX_POP3_STLS: + rc = ngx_mail_pop3_stls(s, c); + break; + + default: + rc = NGX_MAIL_PARSE_INVALID_COMMAND; + break; + } + + break; + + case ngx_pop3_user: + + switch (s->command) { + + case NGX_POP3_PASS: + rc = ngx_mail_pop3_pass(s, c); + break; + + case NGX_POP3_CAPA: + rc = ngx_mail_pop3_capa(s, c, 0); + break; + + case NGX_POP3_QUIT: + s->quit = 1; + break; + + case NGX_POP3_NOOP: + break; + + default: + rc = NGX_MAIL_PARSE_INVALID_COMMAND; + break; + } + + break; + + /* suppress warnings */ + case ngx_pop3_passwd: + break; + + case ngx_pop3_auth_login_username: + rc = ngx_mail_auth_login_username(s, c, 0); + + ngx_str_set(&s->out, pop3_password); + s->mail_state = ngx_pop3_auth_login_password; + break; + + case ngx_pop3_auth_login_password: + rc = ngx_mail_auth_login_password(s, c); + break; + + case ngx_pop3_auth_plain: + rc = ngx_mail_auth_plain(s, c, 0); + break; + + case ngx_pop3_auth_cram_md5: + rc = ngx_mail_auth_cram_md5(s, c); + break; + + case ngx_pop3_auth_external: + rc = ngx_mail_auth_external(s, c, 0); + break; + } + } + + if (s->buffer->pos < s->buffer->last) { + s->blocked = 1; + } + + switch (rc) { + + case NGX_DONE: + ngx_mail_auth(s, c); + return; + + case NGX_ERROR: + ngx_mail_session_internal_server_error(s); + return; + + case NGX_MAIL_PARSE_INVALID_COMMAND: + s->mail_state = ngx_pop3_start; + s->state = 0; + + ngx_str_set(&s->out, pop3_invalid_command); + + /* fall through */ + + case NGX_OK: + + s->args.nelts = 0; + + if (s->buffer->pos == s->buffer->last) { + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + } + + if (s->state) { + s->arg_start = s->buffer->pos; + } + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_session_internal_server_error(s); + return; + } + + ngx_mail_send(c->write); + } +} + +static ngx_int_t +ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + +#if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +#endif + + if (s->args.nelts != 1) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + arg = s->args.elts; + s->login.len = arg[0].len; + s->login.data = ngx_pnalloc(c->pool, s->login.len); + if (s->login.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->login.data, arg[0].data, s->login.len); + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "pop3 login: \"%V\"", &s->login); + + s->mail_state = ngx_pop3_user; + + return NGX_OK; +} + + +static ngx_int_t +ngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + + if (s->args.nelts != 1) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + arg = s->args.elts; + s->passwd.len = arg[0].len; + s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len); + if (s->passwd.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->passwd.data, arg[0].data, s->passwd.len); + +#if (NGX_DEBUG_MAIL_PASSWD) + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "pop3 passwd: \"%V\"", &s->passwd); +#endif + + return NGX_DONE; +} + + +static ngx_int_t +ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c, ngx_int_t stls) +{ + ngx_mail_pop3_srv_conf_t *pscf; + + pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module); + +#if (NGX_MAIL_SSL) + + if (stls && c->ssl == NULL) { + ngx_mail_ssl_conf_t *sslcf; + + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); + + if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) { + s->out = pscf->starttls_capability; + return NGX_OK; + } + + if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { + s->out = pscf->starttls_only_capability; + return NGX_OK; + } + } + +#endif + + s->out = pscf->capability; + return NGX_OK; +} + + +static ngx_int_t +ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c) +{ +#if (NGX_MAIL_SSL) + ngx_mail_ssl_conf_t *sslcf; + + if (c->ssl == NULL) { + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); + if (sslcf->starttls) { + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + c->read->handler = ngx_mail_starttls_handler; + return NGX_OK; + } + } + +#endif + + return NGX_MAIL_PARSE_INVALID_COMMAND; +} + + +static ngx_int_t +ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + ngx_mail_pop3_srv_conf_t *pscf; + +#if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +#endif + + if (s->args.nelts != 2) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module); + + if (!(pscf->auth_methods & NGX_MAIL_AUTH_APOP_ENABLED)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + arg = s->args.elts; + + s->login.len = arg[0].len; + s->login.data = ngx_pnalloc(c->pool, s->login.len); + if (s->login.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->login.data, arg[0].data, s->login.len); + + s->passwd.len = arg[1].len; + s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len); + if (s->passwd.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len); + + ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, + "pop3 apop: \"%V\" \"%V\"", &s->login, &s->passwd); + + s->auth_method = NGX_MAIL_AUTH_APOP; + + return NGX_DONE; +} + + +static ngx_int_t +ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_int_t rc; + ngx_mail_pop3_srv_conf_t *pscf; + +#if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +#endif + + pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module); + + if (s->args.nelts == 0) { + s->out = pscf->auth_capability; + s->state = 0; + + return NGX_OK; + } + + rc = ngx_mail_auth_parse(s, c); + + switch (rc) { + + case NGX_MAIL_AUTH_LOGIN: + + ngx_str_set(&s->out, pop3_username); + s->mail_state = ngx_pop3_auth_login_username; + + return NGX_OK; + + case NGX_MAIL_AUTH_LOGIN_USERNAME: + + ngx_str_set(&s->out, pop3_password); + s->mail_state = ngx_pop3_auth_login_password; + + return ngx_mail_auth_login_username(s, c, 1); + + case NGX_MAIL_AUTH_PLAIN: + + ngx_str_set(&s->out, pop3_next); + s->mail_state = ngx_pop3_auth_plain; + + return NGX_OK; + + case NGX_MAIL_AUTH_CRAM_MD5: + + if (!(pscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) { + s->mail_state = ngx_pop3_auth_cram_md5; + return NGX_OK; + } + + return NGX_ERROR; + + case NGX_MAIL_AUTH_EXTERNAL: + + if (!(pscf->auth_methods & NGX_MAIL_AUTH_EXTERNAL_ENABLED)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + ngx_str_set(&s->out, pop3_username); + s->mail_state = ngx_pop3_auth_external; + + return NGX_OK; + } + + return rc; +} diff --git a/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_pop3_module.c b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_pop3_module.c new file mode 100644 index 000000000..a257b5a70 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_pop3_module.c @@ -0,0 +1,323 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include +#include + + +static void *ngx_mail_pop3_create_srv_conf(ngx_conf_t *cf); +static char *ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); + + +static ngx_str_t ngx_mail_pop3_default_capabilities[] = { + ngx_string("TOP"), + ngx_string("USER"), + ngx_string("UIDL"), + ngx_null_string +}; + + +static ngx_conf_bitmask_t ngx_mail_pop3_auth_methods[] = { + { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED }, + { ngx_string("apop"), NGX_MAIL_AUTH_APOP_ENABLED }, + { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED }, + { ngx_string("external"), NGX_MAIL_AUTH_EXTERNAL_ENABLED }, + { ngx_null_string, 0 } +}; + + +static ngx_str_t ngx_mail_pop3_auth_methods_names[] = { + ngx_string("PLAIN"), + ngx_string("LOGIN"), + ngx_null_string, /* APOP */ + ngx_string("CRAM-MD5"), + ngx_string("EXTERNAL"), + ngx_null_string /* NONE */ +}; + + +static ngx_mail_protocol_t ngx_mail_pop3_protocol = { + ngx_string("pop3"), + ngx_string("\x04pop3"), + { 110, 995, 0, 0 }, + NGX_MAIL_POP3_PROTOCOL, + + ngx_mail_pop3_init_session, + ngx_mail_pop3_init_protocol, + ngx_mail_pop3_parse_command, + ngx_mail_pop3_auth_state, + + ngx_string("-ERR internal server error" CRLF), + ngx_string("-ERR SSL certificate error" CRLF), + ngx_string("-ERR No required SSL certificate" CRLF) +}; + + +static ngx_command_t ngx_mail_pop3_commands[] = { + + { ngx_string("pop3_capabilities"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, + ngx_mail_capabilities, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_pop3_srv_conf_t, capabilities), + NULL }, + + { ngx_string("pop3_auth"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_pop3_srv_conf_t, auth_methods), + &ngx_mail_pop3_auth_methods }, + + ngx_null_command +}; + + +static ngx_mail_module_t ngx_mail_pop3_module_ctx = { + &ngx_mail_pop3_protocol, /* protocol */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_mail_pop3_create_srv_conf, /* create server configuration */ + ngx_mail_pop3_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_mail_pop3_module = { + NGX_MODULE_V1, + &ngx_mail_pop3_module_ctx, /* module context */ + ngx_mail_pop3_commands, /* module directives */ + NGX_MAIL_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static void * +ngx_mail_pop3_create_srv_conf(ngx_conf_t *cf) +{ + ngx_mail_pop3_srv_conf_t *pscf; + + pscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_pop3_srv_conf_t)); + if (pscf == NULL) { + return NULL; + } + + if (ngx_array_init(&pscf->capabilities, cf->pool, 4, sizeof(ngx_str_t)) + != NGX_OK) + { + return NULL; + } + + return pscf; +} + + +static char * +ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_mail_pop3_srv_conf_t *prev = parent; + ngx_mail_pop3_srv_conf_t *conf = child; + + u_char *p; + size_t size, stls_only_size; + ngx_str_t *c, *d; + ngx_uint_t i, m; + + ngx_conf_merge_bitmask_value(conf->auth_methods, + prev->auth_methods, + (NGX_CONF_BITMASK_SET + |NGX_MAIL_AUTH_PLAIN_ENABLED)); + + if (conf->auth_methods & NGX_MAIL_AUTH_PLAIN_ENABLED) { + conf->auth_methods |= NGX_MAIL_AUTH_LOGIN_ENABLED; + } + + if (conf->capabilities.nelts == 0) { + conf->capabilities = prev->capabilities; + } + + if (conf->capabilities.nelts == 0) { + + for (d = ngx_mail_pop3_default_capabilities; d->len; d++) { + c = ngx_array_push(&conf->capabilities); + if (c == NULL) { + return NGX_CONF_ERROR; + } + + *c = *d; + } + } + + size = sizeof("+OK Capability list follows" CRLF) - 1 + + sizeof("." CRLF) - 1; + + stls_only_size = size + sizeof("STLS" CRLF) - 1; + + c = conf->capabilities.elts; + for (i = 0; i < conf->capabilities.nelts; i++) { + size += c[i].len + sizeof(CRLF) - 1; + + if (ngx_strcasecmp(c[i].data, (u_char *) "USER") == 0) { + continue; + } + + stls_only_size += c[i].len + sizeof(CRLF) - 1; + } + + size += sizeof("SASL") - 1 + sizeof(CRLF) - 1; + + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; + m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; + m <<= 1, i++) + { + if (ngx_mail_pop3_auth_methods_names[i].len == 0) { + continue; + } + + if (m & conf->auth_methods) { + size += 1 + ngx_mail_pop3_auth_methods_names[i].len; + } + } + + p = ngx_pnalloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->capability.len = size; + conf->capability.data = p; + + p = ngx_cpymem(p, "+OK Capability list follows" CRLF, + sizeof("+OK Capability list follows" CRLF) - 1); + + for (i = 0; i < conf->capabilities.nelts; i++) { + p = ngx_cpymem(p, c[i].data, c[i].len); + *p++ = CR; *p++ = LF; + } + + p = ngx_cpymem(p, "SASL", sizeof("SASL") - 1); + + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; + m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; + m <<= 1, i++) + { + if (ngx_mail_pop3_auth_methods_names[i].len == 0) { + continue; + } + + if (m & conf->auth_methods) { + *p++ = ' '; + p = ngx_cpymem(p, ngx_mail_pop3_auth_methods_names[i].data, + ngx_mail_pop3_auth_methods_names[i].len); + } + } + + *p++ = CR; *p++ = LF; + + *p++ = '.'; *p++ = CR; *p = LF; + + + size += sizeof("STLS" CRLF) - 1; + + p = ngx_pnalloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->starttls_capability.len = size; + conf->starttls_capability.data = p; + + p = ngx_cpymem(p, conf->capability.data, + conf->capability.len - (sizeof("." CRLF) - 1)); + + p = ngx_cpymem(p, "STLS" CRLF, sizeof("STLS" CRLF) - 1); + *p++ = '.'; *p++ = CR; *p = LF; + + + size = sizeof("+OK methods supported:" CRLF) - 1 + + sizeof("." CRLF) - 1; + + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; + m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; + m <<= 1, i++) + { + if (ngx_mail_pop3_auth_methods_names[i].len == 0) { + continue; + } + + if (m & conf->auth_methods) { + size += ngx_mail_pop3_auth_methods_names[i].len + + sizeof(CRLF) - 1; + } + } + + p = ngx_pnalloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->auth_capability.data = p; + conf->auth_capability.len = size; + + p = ngx_cpymem(p, "+OK methods supported:" CRLF, + sizeof("+OK methods supported:" CRLF) - 1); + + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; + m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; + m <<= 1, i++) + { + if (ngx_mail_pop3_auth_methods_names[i].len == 0) { + continue; + } + + if (m & conf->auth_methods) { + p = ngx_cpymem(p, ngx_mail_pop3_auth_methods_names[i].data, + ngx_mail_pop3_auth_methods_names[i].len); + *p++ = CR; *p++ = LF; + } + } + + *p++ = '.'; *p++ = CR; *p = LF; + + + p = ngx_pnalloc(cf->pool, stls_only_size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->starttls_only_capability.len = stls_only_size; + conf->starttls_only_capability.data = p; + + p = ngx_cpymem(p, "+OK Capability list follows" CRLF, + sizeof("+OK Capability list follows" CRLF) - 1); + + for (i = 0; i < conf->capabilities.nelts; i++) { + if (ngx_strcasecmp(c[i].data, (u_char *) "USER") == 0) { + continue; + } + + p = ngx_cpymem(p, c[i].data, c[i].len); + *p++ = CR; *p++ = LF; + } + + p = ngx_cpymem(p, "STLS" CRLF, sizeof("STLS" CRLF) - 1); + *p++ = '.'; *p++ = CR; *p = LF; + + return NGX_CONF_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_pop3_module.h b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_pop3_module.h new file mode 100644 index 000000000..86947a772 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_pop3_module.h @@ -0,0 +1,38 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_MAIL_POP3_MODULE_H_INCLUDED_ +#define _NGX_MAIL_POP3_MODULE_H_INCLUDED_ + + +#include +#include +#include + + +typedef struct { + ngx_str_t capability; + ngx_str_t starttls_capability; + ngx_str_t starttls_only_capability; + ngx_str_t auth_capability; + + ngx_uint_t auth_methods; + + ngx_array_t capabilities; +} ngx_mail_pop3_srv_conf_t; + + +void ngx_mail_pop3_init_session(ngx_mail_session_t *s, ngx_connection_t *c); +void ngx_mail_pop3_init_protocol(ngx_event_t *rev); +void ngx_mail_pop3_auth_state(ngx_event_t *rev); +ngx_int_t ngx_mail_pop3_parse_command(ngx_mail_session_t *s); + + +extern ngx_module_t ngx_mail_pop3_module; + + +#endif /* _NGX_MAIL_POP3_MODULE_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_proxy_module.c b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_proxy_module.c new file mode 100644 index 000000000..84a7f61a4 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_proxy_module.c @@ -0,0 +1,1411 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include +#include + + +typedef struct { + ngx_flag_t enable; + ngx_flag_t pass_error_message; + ngx_flag_t xclient; + ngx_flag_t smtp_auth; + ngx_flag_t proxy_protocol; + size_t buffer_size; + ngx_msec_t timeout; +} ngx_mail_proxy_conf_t; + + +static void ngx_mail_proxy_block_read(ngx_event_t *rev); +static void ngx_mail_proxy_pop3_handler(ngx_event_t *rev); +static void ngx_mail_proxy_imap_handler(ngx_event_t *rev); +static void ngx_mail_proxy_smtp_handler(ngx_event_t *rev); +static void ngx_mail_proxy_write_handler(ngx_event_t *wev); +static ngx_int_t ngx_mail_proxy_send_proxy_protocol(ngx_mail_session_t *s); +static ngx_int_t ngx_mail_proxy_read_response(ngx_mail_session_t *s, + ngx_uint_t state); +static void ngx_mail_proxy_handler(ngx_event_t *ev); +static void ngx_mail_proxy_upstream_error(ngx_mail_session_t *s); +static void ngx_mail_proxy_internal_server_error(ngx_mail_session_t *s); +static void ngx_mail_proxy_close_session(ngx_mail_session_t *s); +static void *ngx_mail_proxy_create_conf(ngx_conf_t *cf); +static char *ngx_mail_proxy_merge_conf(ngx_conf_t *cf, void *parent, + void *child); + + +static ngx_command_t ngx_mail_proxy_commands[] = { + + { ngx_string("proxy"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_proxy_conf_t, enable), + NULL }, + + { ngx_string("proxy_buffer"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_proxy_conf_t, buffer_size), + NULL }, + + { ngx_string("proxy_timeout"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_proxy_conf_t, timeout), + NULL }, + + { ngx_string("proxy_pass_error_message"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_proxy_conf_t, pass_error_message), + NULL }, + + { ngx_string("xclient"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_proxy_conf_t, xclient), + NULL }, + + { ngx_string("proxy_smtp_auth"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_proxy_conf_t, smtp_auth), + NULL }, + + { ngx_string("proxy_protocol"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_proxy_conf_t, proxy_protocol), + NULL }, + + ngx_null_command +}; + + +static ngx_mail_module_t ngx_mail_proxy_module_ctx = { + NULL, /* protocol */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_mail_proxy_create_conf, /* create server configuration */ + ngx_mail_proxy_merge_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_mail_proxy_module = { + NGX_MODULE_V1, + &ngx_mail_proxy_module_ctx, /* module context */ + ngx_mail_proxy_commands, /* module directives */ + NGX_MAIL_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static u_char smtp_auth_ok[] = "235 2.0.0 OK" CRLF; + + +void +ngx_mail_proxy_init(ngx_mail_session_t *s, ngx_addr_t *peer) +{ + ngx_int_t rc; + ngx_mail_proxy_ctx_t *p; + ngx_mail_proxy_conf_t *pcf; + ngx_mail_core_srv_conf_t *cscf; + + s->connection->log->action = "connecting to upstream"; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + p = ngx_pcalloc(s->connection->pool, sizeof(ngx_mail_proxy_ctx_t)); + if (p == NULL) { + ngx_mail_session_internal_server_error(s); + return; + } + + s->proxy = p; + + p->upstream.sockaddr = peer->sockaddr; + p->upstream.socklen = peer->socklen; + p->upstream.name = &peer->name; + p->upstream.get = ngx_event_get_peer; + p->upstream.log = s->connection->log; + p->upstream.log_error = NGX_ERROR_ERR; + + rc = ngx_event_connect_peer(&p->upstream); + + if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + ngx_add_timer(p->upstream.connection->read, cscf->timeout); + + p->upstream.connection->data = s; + p->upstream.connection->pool = s->connection->pool; + + s->connection->read->handler = ngx_mail_proxy_block_read; + p->upstream.connection->write->handler = ngx_mail_proxy_write_handler; + + pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); + + s->proxy->buffer = ngx_create_temp_buf(s->connection->pool, + pcf->buffer_size); + if (s->proxy->buffer == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + s->proxy->proxy_protocol = pcf->proxy_protocol; + + s->out.len = 0; + + switch (s->protocol) { + + case NGX_MAIL_POP3_PROTOCOL: + p->upstream.connection->read->handler = ngx_mail_proxy_pop3_handler; + s->mail_state = ngx_pop3_start; + break; + + case NGX_MAIL_IMAP_PROTOCOL: + p->upstream.connection->read->handler = ngx_mail_proxy_imap_handler; + s->mail_state = ngx_imap_start; + break; + + default: /* NGX_MAIL_SMTP_PROTOCOL */ + p->upstream.connection->read->handler = ngx_mail_proxy_smtp_handler; + s->mail_state = ngx_smtp_start; + break; + } + + if (rc == NGX_AGAIN) { + return; + } + + ngx_mail_proxy_write_handler(p->upstream.connection->write); +} + + +static void +ngx_mail_proxy_block_read(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy block read"); + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + c = rev->data; + s = c->data; + + ngx_mail_proxy_close_session(s); + } +} + + +static void +ngx_mail_proxy_pop3_handler(ngx_event_t *rev) +{ + u_char *p; + ngx_int_t rc; + ngx_str_t line; + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_proxy_conf_t *pcf; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, + "mail proxy pop3 auth handler"); + + c = rev->data; + s = c->data; + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, + "upstream timed out"); + c->timedout = 1; + ngx_mail_proxy_internal_server_error(s); + return; + } + + if (s->proxy->proxy_protocol) { + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "mail proxy pop3 busy"); + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + return; + } + + rc = ngx_mail_proxy_read_response(s, 0); + + if (rc == NGX_AGAIN) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + return; + } + + if (rc == NGX_ERROR) { + ngx_mail_proxy_upstream_error(s); + return; + } + + switch (s->mail_state) { + + case ngx_pop3_start: + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send user"); + + s->connection->log->action = "sending user name to upstream"; + + line.len = sizeof("USER ") - 1 + s->login.len + 2; + line.data = ngx_pnalloc(c->pool, line.len); + if (line.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + p = ngx_cpymem(line.data, "USER ", sizeof("USER ") - 1); + p = ngx_cpymem(p, s->login.data, s->login.len); + *p++ = CR; *p = LF; + + s->mail_state = ngx_pop3_user; + break; + + case ngx_pop3_user: + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send pass"); + + s->connection->log->action = "sending password to upstream"; + + line.len = sizeof("PASS ") - 1 + s->passwd.len + 2; + line.data = ngx_pnalloc(c->pool, line.len); + if (line.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + p = ngx_cpymem(line.data, "PASS ", sizeof("PASS ") - 1); + p = ngx_cpymem(p, s->passwd.data, s->passwd.len); + *p++ = CR; *p = LF; + + s->mail_state = ngx_pop3_passwd; + break; + + case ngx_pop3_passwd: + s->connection->read->handler = ngx_mail_proxy_handler; + s->connection->write->handler = ngx_mail_proxy_handler; + rev->handler = ngx_mail_proxy_handler; + c->write->handler = ngx_mail_proxy_handler; + + pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); + ngx_add_timer(s->connection->read, pcf->timeout); + ngx_del_timer(c->read); + + c->log->action = NULL; + ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in"); + + if (s->buffer->pos < s->buffer->last + || s->connection->read->ready) + { + ngx_post_event(c->write, &ngx_posted_events); + } + + ngx_mail_proxy_handler(s->connection->write); + + return; + + default: +#if (NGX_SUPPRESS_WARN) + ngx_str_null(&line); +#endif + break; + } + + if (c->send(c, line.data, line.len) < (ssize_t) line.len) { + /* + * we treat the incomplete sending as NGX_ERROR + * because it is very strange here + */ + ngx_mail_proxy_internal_server_error(s); + return; + } + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + s->proxy->buffer->pos = s->proxy->buffer->start; + s->proxy->buffer->last = s->proxy->buffer->start; +} + + +static void +ngx_mail_proxy_imap_handler(ngx_event_t *rev) +{ + u_char *p; + ngx_int_t rc; + ngx_str_t line; + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_proxy_conf_t *pcf; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, + "mail proxy imap auth handler"); + + c = rev->data; + s = c->data; + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, + "upstream timed out"); + c->timedout = 1; + ngx_mail_proxy_internal_server_error(s); + return; + } + + if (s->proxy->proxy_protocol) { + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "mail proxy imap busy"); + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + return; + } + + rc = ngx_mail_proxy_read_response(s, s->mail_state); + + if (rc == NGX_AGAIN) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + return; + } + + if (rc == NGX_ERROR) { + ngx_mail_proxy_upstream_error(s); + return; + } + + switch (s->mail_state) { + + case ngx_imap_start: + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, + "mail proxy send login"); + + s->connection->log->action = "sending LOGIN command to upstream"; + + line.len = s->tag.len + sizeof("LOGIN ") - 1 + + 1 + NGX_SIZE_T_LEN + 1 + 2; + line.data = ngx_pnalloc(c->pool, line.len); + if (line.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + line.len = ngx_sprintf(line.data, "%VLOGIN {%uz}" CRLF, + &s->tag, s->login.len) + - line.data; + + s->mail_state = ngx_imap_login; + break; + + case ngx_imap_login: + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send user"); + + s->connection->log->action = "sending user name to upstream"; + + line.len = s->login.len + 1 + 1 + NGX_SIZE_T_LEN + 1 + 2; + line.data = ngx_pnalloc(c->pool, line.len); + if (line.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + line.len = ngx_sprintf(line.data, "%V {%uz}" CRLF, + &s->login, s->passwd.len) + - line.data; + + s->mail_state = ngx_imap_user; + break; + + case ngx_imap_user: + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, + "mail proxy send passwd"); + + s->connection->log->action = "sending password to upstream"; + + line.len = s->passwd.len + 2; + line.data = ngx_pnalloc(c->pool, line.len); + if (line.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + p = ngx_cpymem(line.data, s->passwd.data, s->passwd.len); + *p++ = CR; *p = LF; + + s->mail_state = ngx_imap_passwd; + break; + + case ngx_imap_passwd: + s->connection->read->handler = ngx_mail_proxy_handler; + s->connection->write->handler = ngx_mail_proxy_handler; + rev->handler = ngx_mail_proxy_handler; + c->write->handler = ngx_mail_proxy_handler; + + pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); + ngx_add_timer(s->connection->read, pcf->timeout); + ngx_del_timer(c->read); + + c->log->action = NULL; + ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in"); + + if (s->buffer->pos < s->buffer->last + || s->connection->read->ready) + { + ngx_post_event(c->write, &ngx_posted_events); + } + + ngx_mail_proxy_handler(s->connection->write); + + return; + + default: +#if (NGX_SUPPRESS_WARN) + ngx_str_null(&line); +#endif + break; + } + + if (c->send(c, line.data, line.len) < (ssize_t) line.len) { + /* + * we treat the incomplete sending as NGX_ERROR + * because it is very strange here + */ + ngx_mail_proxy_internal_server_error(s); + return; + } + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + s->proxy->buffer->pos = s->proxy->buffer->start; + s->proxy->buffer->last = s->proxy->buffer->start; +} + + +static void +ngx_mail_proxy_smtp_handler(ngx_event_t *rev) +{ + u_char *p; + ngx_int_t rc; + ngx_str_t line, auth, encoded; + ngx_buf_t *b; + uintptr_t n; + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_proxy_conf_t *pcf; + ngx_mail_core_srv_conf_t *cscf; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, + "mail proxy smtp auth handler"); + + c = rev->data; + s = c->data; + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, + "upstream timed out"); + c->timedout = 1; + ngx_mail_proxy_internal_server_error(s); + return; + } + + if (s->proxy->proxy_protocol) { + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "mail proxy smtp busy"); + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + return; + } + + rc = ngx_mail_proxy_read_response(s, s->mail_state); + + if (rc == NGX_AGAIN) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + return; + } + + if (rc == NGX_ERROR) { + ngx_mail_proxy_upstream_error(s); + return; + } + + switch (s->mail_state) { + + case ngx_smtp_start: + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send ehlo"); + + s->connection->log->action = "sending HELO/EHLO to upstream"; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + line.len = sizeof("HELO ") - 1 + cscf->server_name.len + 2; + line.data = ngx_pnalloc(c->pool, line.len); + if (line.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); + + p = ngx_cpymem(line.data, + ((s->esmtp || pcf->xclient) ? "EHLO " : "HELO "), + sizeof("HELO ") - 1); + + p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len); + *p++ = CR; *p = LF; + + if (pcf->xclient) { + s->mail_state = ngx_smtp_helo_xclient; + + } else if (s->auth_method == NGX_MAIL_AUTH_NONE) { + s->mail_state = ngx_smtp_helo_from; + + } else if (pcf->smtp_auth) { + s->mail_state = ngx_smtp_helo_auth; + + } else { + s->mail_state = ngx_smtp_helo; + } + + break; + + case ngx_smtp_helo_xclient: + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, + "mail proxy send xclient"); + + s->connection->log->action = "sending XCLIENT to upstream"; + + line.len = sizeof("XCLIENT ADDR= LOGIN= NAME=" + CRLF) - 1 + + s->connection->addr_text.len + s->login.len + s->host.len; + + n = ngx_escape_uri(NULL, s->login.data, s->login.len, + NGX_ESCAPE_MAIL_XTEXT); + line.len += n * 2; + +#if (NGX_HAVE_INET6) + if (s->connection->sockaddr->sa_family == AF_INET6) { + line.len += sizeof("IPV6:") - 1; + } +#endif + + line.data = ngx_pnalloc(c->pool, line.len); + if (line.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + p = ngx_cpymem(line.data, "XCLIENT ADDR=", sizeof("XCLIENT ADDR=") - 1); + +#if (NGX_HAVE_INET6) + if (s->connection->sockaddr->sa_family == AF_INET6) { + p = ngx_cpymem(p, "IPV6:", sizeof("IPV6:") - 1); + } +#endif + + p = ngx_copy(p, s->connection->addr_text.data, + s->connection->addr_text.len); + + pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); + + if (s->login.len && !pcf->smtp_auth) { + p = ngx_cpymem(p, " LOGIN=", sizeof(" LOGIN=") - 1); + + if (n == 0) { + p = ngx_copy(p, s->login.data, s->login.len); + + } else { + p = (u_char *) ngx_escape_uri(p, s->login.data, s->login.len, + NGX_ESCAPE_MAIL_XTEXT); + } + } + + p = ngx_cpymem(p, " NAME=", sizeof(" NAME=") - 1); + p = ngx_copy(p, s->host.data, s->host.len); + + *p++ = CR; *p++ = LF; + + line.len = p - line.data; + + if (s->smtp_helo.len) { + s->mail_state = ngx_smtp_xclient_helo; + + } else if (s->auth_method == NGX_MAIL_AUTH_NONE) { + s->mail_state = ngx_smtp_xclient_from; + + } else if (pcf->smtp_auth) { + s->mail_state = ngx_smtp_xclient_auth; + + } else { + s->mail_state = ngx_smtp_xclient; + } + + break; + + case ngx_smtp_xclient_helo: + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, + "mail proxy send client ehlo"); + + s->connection->log->action = "sending client HELO/EHLO to upstream"; + + line.len = sizeof("HELO " CRLF) - 1 + s->smtp_helo.len; + + line.data = ngx_pnalloc(c->pool, line.len); + if (line.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + line.len = ngx_sprintf(line.data, + ((s->esmtp) ? "EHLO %V" CRLF : "HELO %V" CRLF), + &s->smtp_helo) + - line.data; + + pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); + + if (s->auth_method == NGX_MAIL_AUTH_NONE) { + s->mail_state = ngx_smtp_helo_from; + + } else if (pcf->smtp_auth) { + s->mail_state = ngx_smtp_helo_auth; + + } else { + s->mail_state = ngx_smtp_helo; + } + + break; + + case ngx_smtp_helo_auth: + case ngx_smtp_xclient_auth: + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, + "mail proxy send auth"); + + s->connection->log->action = "sending AUTH to upstream"; + + if (s->passwd.data == NULL) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "no password available"); + ngx_mail_proxy_internal_server_error(s); + return; + } + + auth.len = 1 + s->login.len + 1 + s->passwd.len; + auth.data = ngx_pnalloc(c->pool, auth.len); + if (auth.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + auth.len = ngx_sprintf(auth.data, "%Z%V%Z%V", &s->login, &s->passwd) + - auth.data; + + line.len = sizeof("AUTH PLAIN " CRLF) - 1 + + ngx_base64_encoded_length(auth.len); + + line.data = ngx_pnalloc(c->pool, line.len); + if (line.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + encoded.data = ngx_cpymem(line.data, "AUTH PLAIN ", + sizeof("AUTH PLAIN ") - 1); + + ngx_encode_base64(&encoded, &auth); + + p = encoded.data + encoded.len; + *p++ = CR; *p = LF; + + s->mail_state = ngx_smtp_auth_plain; + + break; + + case ngx_smtp_helo_from: + case ngx_smtp_xclient_from: + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, + "mail proxy send mail from"); + + s->connection->log->action = "sending MAIL FROM to upstream"; + + line.len = s->smtp_from.len + sizeof(CRLF) - 1; + line.data = ngx_pnalloc(c->pool, line.len); + if (line.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + p = ngx_cpymem(line.data, s->smtp_from.data, s->smtp_from.len); + *p++ = CR; *p = LF; + + s->mail_state = ngx_smtp_from; + + break; + + case ngx_smtp_from: + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, + "mail proxy send rcpt to"); + + s->connection->log->action = "sending RCPT TO to upstream"; + + line.len = s->smtp_to.len + sizeof(CRLF) - 1; + line.data = ngx_pnalloc(c->pool, line.len); + if (line.data == NULL) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + p = ngx_cpymem(line.data, s->smtp_to.data, s->smtp_to.len); + *p++ = CR; *p = LF; + + s->mail_state = ngx_smtp_to; + + break; + + case ngx_smtp_helo: + case ngx_smtp_xclient: + case ngx_smtp_auth_plain: + case ngx_smtp_to: + + b = s->proxy->buffer; + + if (s->auth_method == NGX_MAIL_AUTH_NONE) { + b->pos = b->start; + + } else { + ngx_memcpy(b->start, smtp_auth_ok, sizeof(smtp_auth_ok) - 1); + b->last = b->start + sizeof(smtp_auth_ok) - 1; + } + + s->connection->read->handler = ngx_mail_proxy_handler; + s->connection->write->handler = ngx_mail_proxy_handler; + rev->handler = ngx_mail_proxy_handler; + c->write->handler = ngx_mail_proxy_handler; + + pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); + ngx_add_timer(s->connection->read, pcf->timeout); + ngx_del_timer(c->read); + + c->log->action = NULL; + ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in"); + + if (s->buffer->pos < s->buffer->last + || s->connection->read->ready) + { + ngx_post_event(c->write, &ngx_posted_events); + } + + ngx_mail_proxy_handler(s->connection->write); + + return; + + default: +#if (NGX_SUPPRESS_WARN) + ngx_str_null(&line); +#endif + break; + } + + if (c->send(c, line.data, line.len) < (ssize_t) line.len) { + /* + * we treat the incomplete sending as NGX_ERROR + * because it is very strange here + */ + ngx_mail_proxy_internal_server_error(s); + return; + } + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_proxy_internal_server_error(s); + return; + } + + s->proxy->buffer->pos = s->proxy->buffer->start; + s->proxy->buffer->last = s->proxy->buffer->start; +} + + +static void +ngx_mail_proxy_write_handler(ngx_event_t *wev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, "mail proxy write handler"); + + c = wev->data; + s = c->data; + + if (s->proxy->proxy_protocol) { + if (ngx_mail_proxy_send_proxy_protocol(s) != NGX_OK) { + return; + } + + s->proxy->proxy_protocol = 0; + } + + if (ngx_handle_write_event(wev, 0) != NGX_OK) { + ngx_mail_proxy_internal_server_error(s); + } + + if (c->read->ready) { + ngx_post_event(c->read, &ngx_posted_events); + } +} + + +static ngx_int_t +ngx_mail_proxy_send_proxy_protocol(ngx_mail_session_t *s) +{ + u_char *p; + ssize_t n, size; + ngx_connection_t *c; + u_char buf[NGX_PROXY_PROTOCOL_V1_MAX_HEADER]; + + s->connection->log->action = "sending PROXY protocol header to upstream"; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0, + "mail proxy send PROXY protocol header"); + + p = ngx_proxy_protocol_write(s->connection, buf, + buf + NGX_PROXY_PROTOCOL_V1_MAX_HEADER); + if (p == NULL) { + ngx_mail_proxy_internal_server_error(s); + return NGX_ERROR; + } + + c = s->proxy->upstream.connection; + + size = p - buf; + + n = c->send(c, buf, size); + + if (n == NGX_AGAIN) { + if (ngx_handle_write_event(c->write, 0) != NGX_OK) { + ngx_mail_proxy_internal_server_error(s); + return NGX_ERROR; + } + + return NGX_AGAIN; + } + + if (n == NGX_ERROR) { + ngx_mail_proxy_internal_server_error(s); + return NGX_ERROR; + } + + if (n != size) { + + /* + * PROXY protocol specification: + * The sender must always ensure that the header + * is sent at once, so that the transport layer + * maintains atomicity along the path to the receiver. + */ + + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "could not send PROXY protocol header at once"); + + ngx_mail_proxy_internal_server_error(s); + + return NGX_ERROR; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_mail_proxy_read_response(ngx_mail_session_t *s, ngx_uint_t state) +{ + u_char *p, *m; + ssize_t n; + ngx_buf_t *b; + ngx_mail_proxy_conf_t *pcf; + + s->connection->log->action = "reading response from upstream"; + + b = s->proxy->buffer; + + n = s->proxy->upstream.connection->recv(s->proxy->upstream.connection, + b->last, b->end - b->last); + + if (n == NGX_ERROR || n == 0) { + return NGX_ERROR; + } + + if (n == NGX_AGAIN) { + return NGX_AGAIN; + } + + b->last += n; + + if (b->last - b->pos < 4) { + return NGX_AGAIN; + } + + if (*(b->last - 2) != CR || *(b->last - 1) != LF) { + if (b->last == b->end) { + *(b->last - 1) = '\0'; + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "upstream sent too long response line: \"%s\"", + b->pos); + return NGX_ERROR; + } + + return NGX_AGAIN; + } + + p = b->pos; + + switch (s->protocol) { + + case NGX_MAIL_POP3_PROTOCOL: + if (p[0] == '+' && p[1] == 'O' && p[2] == 'K') { + return NGX_OK; + } + break; + + case NGX_MAIL_IMAP_PROTOCOL: + switch (state) { + + case ngx_imap_start: + if (p[0] == '*' && p[1] == ' ' && p[2] == 'O' && p[3] == 'K') { + return NGX_OK; + } + break; + + case ngx_imap_login: + case ngx_imap_user: + if (p[0] == '+') { + return NGX_OK; + } + break; + + case ngx_imap_passwd: + + /* + * untagged CAPABILITY response (draft-crispin-imapv-16), + * known to be sent by SmarterMail and Gmail + */ + + if (p[0] == '*' && p[1] == ' ') { + p += 2; + + while (p < b->last - 1) { + if (p[0] == CR && p[1] == LF) { + p += 2; + break; + } + + p++; + } + + if (b->last - p < 4) { + return NGX_AGAIN; + } + } + + if (ngx_strncmp(p, s->tag.data, s->tag.len) == 0) { + p += s->tag.len; + if (p[0] == 'O' && p[1] == 'K') { + return NGX_OK; + } + } + + break; + } + + break; + + default: /* NGX_MAIL_SMTP_PROTOCOL */ + + if (p[3] == '-') { + /* multiline reply, check if we got last line */ + + m = b->last - (sizeof(CRLF "200" CRLF) - 1); + + while (m > p) { + if (m[0] == CR && m[1] == LF) { + break; + } + + m--; + } + + if (m <= p || m[5] == '-') { + return NGX_AGAIN; + } + } + + switch (state) { + + case ngx_smtp_start: + if (p[0] == '2' && p[1] == '2' && p[2] == '0') { + return NGX_OK; + } + break; + + case ngx_smtp_helo: + case ngx_smtp_helo_xclient: + case ngx_smtp_helo_from: + case ngx_smtp_helo_auth: + case ngx_smtp_from: + if (p[0] == '2' && p[1] == '5' && p[2] == '0') { + return NGX_OK; + } + break; + + case ngx_smtp_xclient: + case ngx_smtp_xclient_from: + case ngx_smtp_xclient_helo: + case ngx_smtp_xclient_auth: + if (p[0] == '2' && (p[1] == '2' || p[1] == '5') && p[2] == '0') { + return NGX_OK; + } + break; + + case ngx_smtp_auth_plain: + if (p[0] == '2' && p[1] == '3' && p[2] == '5') { + return NGX_OK; + } + break; + + case ngx_smtp_to: + return NGX_OK; + } + + break; + } + + pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); + + if (pcf->pass_error_message == 0) { + *(b->last - 2) = '\0'; + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "upstream sent invalid response: \"%s\"", p); + return NGX_ERROR; + } + + s->out.len = b->last - p - 2; + s->out.data = p; + + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "upstream sent invalid response: \"%V\"", &s->out); + + s->out.len = b->last - b->pos; + s->out.data = b->pos; + + return NGX_ERROR; +} + + +static void +ngx_mail_proxy_handler(ngx_event_t *ev) +{ + char *action, *recv_action, *send_action; + size_t size; + ssize_t n; + ngx_buf_t *b; + ngx_uint_t do_write; + ngx_connection_t *c, *src, *dst; + ngx_mail_session_t *s; + ngx_mail_proxy_conf_t *pcf; + + c = ev->data; + s = c->data; + + if (ev->timedout || c->close) { + c->log->action = "proxying"; + + if (c->close) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "shutdown timeout"); + + } else if (c == s->connection) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, + "client timed out"); + c->timedout = 1; + + } else { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, + "upstream timed out"); + } + + ngx_mail_proxy_close_session(s); + return; + } + + if (c == s->connection) { + if (ev->write) { + recv_action = "proxying and reading from upstream"; + send_action = "proxying and sending to client"; + src = s->proxy->upstream.connection; + dst = c; + b = s->proxy->buffer; + + } else { + recv_action = "proxying and reading from client"; + send_action = "proxying and sending to upstream"; + src = c; + dst = s->proxy->upstream.connection; + b = s->buffer; + } + + } else { + if (ev->write) { + recv_action = "proxying and reading from client"; + send_action = "proxying and sending to upstream"; + src = s->connection; + dst = c; + b = s->buffer; + + } else { + recv_action = "proxying and reading from upstream"; + send_action = "proxying and sending to client"; + src = c; + dst = s->connection; + b = s->proxy->buffer; + } + } + + do_write = ev->write ? 1 : 0; + + ngx_log_debug3(NGX_LOG_DEBUG_MAIL, ev->log, 0, + "mail proxy handler: %ui, #%d > #%d", + do_write, src->fd, dst->fd); + + for ( ;; ) { + + if (do_write) { + + size = b->last - b->pos; + + if (size && dst->write->ready) { + c->log->action = send_action; + + n = dst->send(dst, b->pos, size); + + if (n == NGX_ERROR) { + ngx_mail_proxy_close_session(s); + return; + } + + if (n > 0) { + b->pos += n; + + if (b->pos == b->last) { + b->pos = b->start; + b->last = b->start; + } + } + } + } + + size = b->end - b->last; + + if (size && src->read->ready) { + c->log->action = recv_action; + + n = src->recv(src, b->last, size); + + if (n == NGX_AGAIN || n == 0) { + break; + } + + if (n > 0) { + do_write = 1; + b->last += n; + + continue; + } + + if (n == NGX_ERROR) { + src->read->eof = 1; + } + } + + break; + } + + c->log->action = "proxying"; + + if ((s->connection->read->eof && s->buffer->pos == s->buffer->last) + || (s->proxy->upstream.connection->read->eof + && s->proxy->buffer->pos == s->proxy->buffer->last) + || (s->connection->read->eof + && s->proxy->upstream.connection->read->eof)) + { + action = c->log->action; + c->log->action = NULL; + ngx_log_error(NGX_LOG_INFO, c->log, 0, "proxied session done"); + c->log->action = action; + + ngx_mail_proxy_close_session(s); + return; + } + + if (ngx_handle_write_event(dst->write, 0) != NGX_OK) { + ngx_mail_proxy_close_session(s); + return; + } + + if (ngx_handle_read_event(dst->read, 0) != NGX_OK) { + ngx_mail_proxy_close_session(s); + return; + } + + if (ngx_handle_write_event(src->write, 0) != NGX_OK) { + ngx_mail_proxy_close_session(s); + return; + } + + if (ngx_handle_read_event(src->read, 0) != NGX_OK) { + ngx_mail_proxy_close_session(s); + return; + } + + if (c == s->connection) { + pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); + ngx_add_timer(c->read, pcf->timeout); + } +} + + +static void +ngx_mail_proxy_upstream_error(ngx_mail_session_t *s) +{ + if (s->proxy->upstream.connection) { + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0, + "close mail proxy connection: %d", + s->proxy->upstream.connection->fd); + + ngx_close_connection(s->proxy->upstream.connection); + } + + if (s->out.len == 0) { + ngx_mail_session_internal_server_error(s); + return; + } + + s->quit = 1; + ngx_mail_send(s->connection->write); +} + + +static void +ngx_mail_proxy_internal_server_error(ngx_mail_session_t *s) +{ + if (s->proxy->upstream.connection) { + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0, + "close mail proxy connection: %d", + s->proxy->upstream.connection->fd); + + ngx_close_connection(s->proxy->upstream.connection); + } + + ngx_mail_session_internal_server_error(s); +} + + +static void +ngx_mail_proxy_close_session(ngx_mail_session_t *s) +{ + if (s->proxy->upstream.connection) { + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0, + "close mail proxy connection: %d", + s->proxy->upstream.connection->fd); + + ngx_close_connection(s->proxy->upstream.connection); + } + + ngx_mail_close_connection(s->connection); +} + + +static void * +ngx_mail_proxy_create_conf(ngx_conf_t *cf) +{ + ngx_mail_proxy_conf_t *pcf; + + pcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_proxy_conf_t)); + if (pcf == NULL) { + return NULL; + } + + pcf->enable = NGX_CONF_UNSET; + pcf->pass_error_message = NGX_CONF_UNSET; + pcf->xclient = NGX_CONF_UNSET; + pcf->smtp_auth = NGX_CONF_UNSET; + pcf->proxy_protocol = NGX_CONF_UNSET; + pcf->buffer_size = NGX_CONF_UNSET_SIZE; + pcf->timeout = NGX_CONF_UNSET_MSEC; + + return pcf; +} + + +static char * +ngx_mail_proxy_merge_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_mail_proxy_conf_t *prev = parent; + ngx_mail_proxy_conf_t *conf = child; + + ngx_conf_merge_value(conf->enable, prev->enable, 0); + ngx_conf_merge_value(conf->pass_error_message, prev->pass_error_message, 0); + ngx_conf_merge_value(conf->xclient, prev->xclient, 1); + ngx_conf_merge_value(conf->smtp_auth, prev->smtp_auth, 0); + ngx_conf_merge_value(conf->proxy_protocol, prev->proxy_protocol, 0); + ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, + (size_t) ngx_pagesize); + ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 24 * 60 * 60000); + + return NGX_CONF_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_realip_module.c b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_realip_module.c new file mode 100644 index 000000000..c93d7d33c --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_realip_module.c @@ -0,0 +1,269 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_array_t *from; /* array of ngx_cidr_t */ +} ngx_mail_realip_srv_conf_t; + + +static ngx_int_t ngx_mail_realip_set_addr(ngx_mail_session_t *s, + ngx_addr_t *addr); +static char *ngx_mail_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static void *ngx_mail_realip_create_srv_conf(ngx_conf_t *cf); +static char *ngx_mail_realip_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); + + +static ngx_command_t ngx_mail_realip_commands[] = { + + { ngx_string("set_real_ip_from"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_mail_realip_from, + NGX_MAIL_SRV_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_mail_module_t ngx_mail_realip_module_ctx = { + NULL, /* protocol */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_mail_realip_create_srv_conf, /* create server configuration */ + ngx_mail_realip_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_mail_realip_module = { + NGX_MODULE_V1, + &ngx_mail_realip_module_ctx, /* module context */ + ngx_mail_realip_commands, /* module directives */ + NGX_MAIL_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +ngx_int_t +ngx_mail_realip_handler(ngx_mail_session_t *s) +{ + ngx_addr_t addr; + ngx_connection_t *c; + ngx_mail_realip_srv_conf_t *rscf; + + rscf = ngx_mail_get_module_srv_conf(s, ngx_mail_realip_module); + + if (rscf->from == NULL) { + return NGX_OK; + } + + c = s->connection; + + if (c->proxy_protocol == NULL) { + return NGX_OK; + } + + if (ngx_cidr_match(c->sockaddr, rscf->from) != NGX_OK) { + return NGX_OK; + } + + if (ngx_parse_addr(c->pool, &addr, c->proxy_protocol->src_addr.data, + c->proxy_protocol->src_addr.len) + != NGX_OK) + { + return NGX_OK; + } + + ngx_inet_set_port(addr.sockaddr, c->proxy_protocol->src_port); + + return ngx_mail_realip_set_addr(s, &addr); +} + + +static ngx_int_t +ngx_mail_realip_set_addr(ngx_mail_session_t *s, ngx_addr_t *addr) +{ + size_t len; + u_char *p; + u_char text[NGX_SOCKADDR_STRLEN]; + ngx_connection_t *c; + + c = s->connection; + + len = ngx_sock_ntop(addr->sockaddr, addr->socklen, text, + NGX_SOCKADDR_STRLEN, 0); + if (len == 0) { + return NGX_ERROR; + } + + p = ngx_pnalloc(c->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, text, len); + + c->sockaddr = addr->sockaddr; + c->socklen = addr->socklen; + c->addr_text.len = len; + c->addr_text.data = p; + + return NGX_OK; +} + + +static char * +ngx_mail_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_mail_realip_srv_conf_t *rscf = conf; + + ngx_int_t rc; + ngx_str_t *value; + ngx_url_t u; + ngx_cidr_t c, *cidr; + ngx_uint_t i; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + value = cf->args->elts; + + if (rscf->from == NULL) { + rscf->from = ngx_array_create(cf->pool, 2, + sizeof(ngx_cidr_t)); + if (rscf->from == NULL) { + return NGX_CONF_ERROR; + } + } + +#if (NGX_HAVE_UNIX_DOMAIN) + + if (ngx_strcmp(value[1].data, "unix:") == 0) { + cidr = ngx_array_push(rscf->from); + if (cidr == NULL) { + return NGX_CONF_ERROR; + } + + cidr->family = AF_UNIX; + return NGX_CONF_OK; + } + +#endif + + rc = ngx_ptocidr(&value[1], &c); + + if (rc != NGX_ERROR) { + if (rc == NGX_DONE) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "low address bits of %V are meaningless", + &value[1]); + } + + cidr = ngx_array_push(rscf->from); + if (cidr == NULL) { + return NGX_CONF_ERROR; + } + + *cidr = c; + + return NGX_CONF_OK; + } + + ngx_memzero(&u, sizeof(ngx_url_t)); + u.host = value[1]; + + if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) { + if (u.err) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "%s in set_real_ip_from \"%V\"", + u.err, &u.host); + } + + return NGX_CONF_ERROR; + } + + cidr = ngx_array_push_n(rscf->from, u.naddrs); + if (cidr == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(cidr, u.naddrs * sizeof(ngx_cidr_t)); + + for (i = 0; i < u.naddrs; i++) { + cidr[i].family = u.addrs[i].sockaddr->sa_family; + + switch (cidr[i].family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) u.addrs[i].sockaddr; + cidr[i].u.in6.addr = sin6->sin6_addr; + ngx_memset(cidr[i].u.in6.mask.s6_addr, 0xff, 16); + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) u.addrs[i].sockaddr; + cidr[i].u.in.addr = sin->sin_addr.s_addr; + cidr[i].u.in.mask = 0xffffffff; + break; + } + } + + return NGX_CONF_OK; +} + + +static void * +ngx_mail_realip_create_srv_conf(ngx_conf_t *cf) +{ + ngx_mail_realip_srv_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_realip_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * conf->from = NULL; + */ + + return conf; +} + + +static char * +ngx_mail_realip_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_mail_realip_srv_conf_t *prev = parent; + ngx_mail_realip_srv_conf_t *conf = child; + + if (conf->from == NULL) { + conf->from = prev->from; + } + + return NGX_CONF_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_smtp_handler.c b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_smtp_handler.c new file mode 100644 index 000000000..97bbd7063 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_smtp_handler.c @@ -0,0 +1,983 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include +#include + + +static void ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx); +static ngx_int_t ngx_mail_smtp_validate_host(ngx_str_t *name); +static void ngx_mail_smtp_resolve_name(ngx_event_t *rev); +static void ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx); +static void ngx_mail_smtp_block_reading(ngx_event_t *rev); +static void ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c); +static void ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev); +static ngx_int_t ngx_mail_smtp_create_buffer(ngx_mail_session_t *s, + ngx_connection_t *c); + +static ngx_int_t ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_smtp_starttls(ngx_mail_session_t *s, + ngx_connection_t *c); +static ngx_int_t ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c); +static ngx_int_t ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c); + +static ngx_int_t ngx_mail_smtp_discard_command(ngx_mail_session_t *s, + ngx_connection_t *c, char *err); +static void ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s, + ngx_connection_t *c, char *err); + + +static u_char smtp_ok[] = "250 2.0.0 OK" CRLF; +static u_char smtp_bye[] = "221 2.0.0 Bye" CRLF; +static u_char smtp_starttls[] = "220 2.0.0 Start TLS" CRLF; +static u_char smtp_next[] = "334 " CRLF; +static u_char smtp_username[] = "334 VXNlcm5hbWU6" CRLF; +static u_char smtp_password[] = "334 UGFzc3dvcmQ6" CRLF; +static u_char smtp_invalid_command[] = "500 5.5.1 Invalid command" CRLF; +static u_char smtp_invalid_pipelining[] = + "503 5.5.0 Improper use of SMTP command pipelining" CRLF; +static u_char smtp_invalid_argument[] = "501 5.5.4 Invalid argument" CRLF; +static u_char smtp_auth_required[] = "530 5.7.1 Authentication required" CRLF; +static u_char smtp_bad_sequence[] = "503 5.5.1 Bad sequence of commands" CRLF; + + +static ngx_str_t smtp_unavailable = ngx_string("[UNAVAILABLE]"); +static ngx_str_t smtp_tempunavail = ngx_string("[TEMPUNAVAIL]"); + + +void +ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_resolver_ctx_t *ctx; + ngx_mail_core_srv_conf_t *cscf; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + if (cscf->resolver == NULL) { + s->host = smtp_unavailable; + ngx_mail_smtp_greeting(s, c); + return; + } + +#if (NGX_HAVE_UNIX_DOMAIN) + if (c->sockaddr->sa_family == AF_UNIX) { + s->host = smtp_tempunavail; + ngx_mail_smtp_greeting(s, c); + return; + } +#endif + + c->log->action = "in resolving client address"; + + ctx = ngx_resolve_start(cscf->resolver, NULL); + if (ctx == NULL) { + ngx_mail_close_connection(c); + return; + } + + ctx->addr.sockaddr = c->sockaddr; + ctx->addr.socklen = c->socklen; + ctx->handler = ngx_mail_smtp_resolve_addr_handler; + ctx->data = s; + ctx->timeout = cscf->resolver_timeout; + + s->resolver_ctx = ctx; + c->read->handler = ngx_mail_smtp_block_reading; + + if (ngx_resolve_addr(ctx) != NGX_OK) { + ngx_mail_close_connection(c); + } +} + + +static void +ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + + s = ctx->data; + c = s->connection; + + if (ctx->state) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "%V could not be resolved (%i: %s)", + &c->addr_text, ctx->state, + ngx_resolver_strerror(ctx->state)); + + if (ctx->state == NGX_RESOLVE_NXDOMAIN) { + s->host = smtp_unavailable; + + } else { + s->host = smtp_tempunavail; + } + + ngx_resolve_addr_done(ctx); + + ngx_mail_smtp_greeting(s, s->connection); + + return; + } + + if (ngx_mail_smtp_validate_host(&ctx->name) != NGX_OK) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "%V resolved to invalid host name \"%V\"", + &c->addr_text, &ctx->name); + + s->host = smtp_tempunavail; + + ngx_resolve_addr_done(ctx); + + ngx_mail_smtp_greeting(s, s->connection); + + return; + } + + c->log->action = "in resolving client hostname"; + + s->host.data = ngx_pstrdup(c->pool, &ctx->name); + if (s->host.data == NULL) { + ngx_resolve_addr_done(ctx); + ngx_mail_close_connection(c); + return; + } + + s->host.len = ctx->name.len; + + ngx_resolve_addr_done(ctx); + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "address resolved: %V", &s->host); + + c->read->handler = ngx_mail_smtp_resolve_name; + + ngx_post_event(c->read, &ngx_posted_events); +} + + +static ngx_int_t +ngx_mail_smtp_validate_host(ngx_str_t *name) +{ + u_char ch; + ngx_uint_t i; + + if (name->len == 0) { + return NGX_DECLINED; + } + + for (i = 0; i < name->len; i++) { + ch = name->data[i]; + + /* allow only characters from RFC 1034, Section 3.5 */ + + if ((ch >= 'a' && ch <= 'z') + || (ch >= 'A' && ch <= 'Z') + || (ch >= '0' && ch <= '9') + || ch == '-' || ch == '.') + { + continue; + } + + return NGX_DECLINED; + } + + return NGX_OK; +} + + +static void +ngx_mail_smtp_resolve_name(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_resolver_ctx_t *ctx; + ngx_mail_core_srv_conf_t *cscf; + + c = rev->data; + s = c->data; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + ctx = ngx_resolve_start(cscf->resolver, NULL); + if (ctx == NULL) { + ngx_mail_close_connection(c); + return; + } + + ctx->name = s->host; + ctx->handler = ngx_mail_smtp_resolve_name_handler; + ctx->data = s; + ctx->timeout = cscf->resolver_timeout; + + s->resolver_ctx = ctx; + c->read->handler = ngx_mail_smtp_block_reading; + + if (ngx_resolve_name(ctx) != NGX_OK) { + ngx_mail_close_connection(c); + } +} + + +static void +ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx) +{ + ngx_uint_t i; + ngx_connection_t *c; + ngx_mail_session_t *s; + + s = ctx->data; + c = s->connection; + + if (ctx->state) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "\"%V\" could not be resolved (%i: %s)", + &ctx->name, ctx->state, + ngx_resolver_strerror(ctx->state)); + + if (ctx->state == NGX_RESOLVE_NXDOMAIN) { + s->host = smtp_unavailable; + + } else { + s->host = smtp_tempunavail; + } + + } else { + +#if (NGX_DEBUG) + { + u_char text[NGX_SOCKADDR_STRLEN]; + ngx_str_t addr; + + addr.data = text; + + for (i = 0; i < ctx->naddrs; i++) { + addr.len = ngx_sock_ntop(ctx->addrs[i].sockaddr, + ctx->addrs[i].socklen, + text, NGX_SOCKADDR_STRLEN, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "name was resolved to %V", &addr); + } + } +#endif + + for (i = 0; i < ctx->naddrs; i++) { + if (ngx_cmp_sockaddr(ctx->addrs[i].sockaddr, ctx->addrs[i].socklen, + c->sockaddr, c->socklen, 0) + == NGX_OK) + { + goto found; + } + } + + s->host = smtp_unavailable; + } + +found: + + ngx_resolve_name_done(ctx); + + ngx_mail_smtp_greeting(s, c); +} + + +static void +ngx_mail_smtp_block_reading(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_resolver_ctx_t *ctx; + + c = rev->data; + s = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp reading blocked"); + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + + if (s->resolver_ctx) { + ctx = s->resolver_ctx; + + if (ctx->handler == ngx_mail_smtp_resolve_addr_handler) { + ngx_resolve_addr_done(ctx); + + } else if (ctx->handler == ngx_mail_smtp_resolve_name_handler) { + ngx_resolve_name_done(ctx); + } + + s->resolver_ctx = NULL; + } + + ngx_mail_close_connection(c); + } +} + + +static void +ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_msec_t timeout; + ngx_mail_core_srv_conf_t *cscf; + ngx_mail_smtp_srv_conf_t *sscf; + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "smtp greeting for \"%V\"", &s->host); + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); + + timeout = sscf->greeting_delay ? sscf->greeting_delay : cscf->timeout; + ngx_add_timer(c->read, timeout); + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_close_connection(c); + } + + if (c->read->ready) { + ngx_post_event(c->read, &ngx_posted_events); + } + + if (sscf->greeting_delay) { + c->read->handler = ngx_mail_smtp_invalid_pipelining; + return; + } + + c->read->handler = ngx_mail_smtp_init_protocol; + + s->out = sscf->greeting; + + ngx_mail_send(c->write); +} + + +static void +ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_core_srv_conf_t *cscf; + ngx_mail_smtp_srv_conf_t *sscf; + + c = rev->data; + s = c->data; + + c->log->action = "in delay pipelining state"; + + if (rev->timedout) { + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "delay greeting"); + + rev->timedout = 0; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + c->read->handler = ngx_mail_smtp_init_protocol; + + ngx_add_timer(c->read, cscf->timeout); + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_close_connection(c); + return; + } + + sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); + + s->out = sscf->greeting; + + } else { + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "invalid pipelining"); + + if (s->buffer == NULL) { + if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) { + return; + } + } + + if (ngx_mail_smtp_discard_command(s, c, + "client was rejected before greeting: \"%V\"") + != NGX_OK) + { + return; + } + + ngx_str_set(&s->out, smtp_invalid_pipelining); + s->quit = 1; + } + + ngx_mail_send(c->write); +} + + +void +ngx_mail_smtp_init_protocol(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + + c = rev->data; + + c->log->action = "in auth state"; + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + s = c->data; + + if (s->buffer == NULL) { + if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) { + return; + } + } + + s->mail_state = ngx_smtp_start; + c->read->handler = ngx_mail_smtp_auth_state; + + ngx_mail_smtp_auth_state(rev); +} + + +static ngx_int_t +ngx_mail_smtp_create_buffer(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_mail_smtp_srv_conf_t *sscf; + + if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) == NGX_ERROR) { + ngx_mail_session_internal_server_error(s); + return NGX_ERROR; + } + + sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); + + s->buffer = ngx_create_temp_buf(c->pool, sscf->client_buffer_size); + if (s->buffer == NULL) { + ngx_mail_session_internal_server_error(s); + return NGX_ERROR; + } + + return NGX_OK; +} + + +void +ngx_mail_smtp_auth_state(ngx_event_t *rev) +{ + ngx_int_t rc; + ngx_connection_t *c; + ngx_mail_session_t *s; + + c = rev->data; + s = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp auth state"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + c->timedout = 1; + ngx_mail_close_connection(c); + return; + } + + if (s->out.len) { + ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp send handler busy"); + s->blocked = 1; + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_close_connection(c); + return; + } + + return; + } + + s->blocked = 0; + + rc = ngx_mail_read_command(s, c); + + if (rc == NGX_AGAIN) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_session_internal_server_error(s); + return; + } + + return; + } + + if (rc == NGX_ERROR) { + return; + } + + ngx_str_set(&s->out, smtp_ok); + + if (rc == NGX_OK) { + switch (s->mail_state) { + + case ngx_smtp_start: + + switch (s->command) { + + case NGX_SMTP_HELO: + case NGX_SMTP_EHLO: + rc = ngx_mail_smtp_helo(s, c); + break; + + case NGX_SMTP_AUTH: + rc = ngx_mail_smtp_auth(s, c); + break; + + case NGX_SMTP_QUIT: + s->quit = 1; + ngx_str_set(&s->out, smtp_bye); + break; + + case NGX_SMTP_MAIL: + rc = ngx_mail_smtp_mail(s, c); + break; + + case NGX_SMTP_RCPT: + rc = ngx_mail_smtp_rcpt(s, c); + break; + + case NGX_SMTP_RSET: + rc = ngx_mail_smtp_rset(s, c); + break; + + case NGX_SMTP_NOOP: + break; + + case NGX_SMTP_STARTTLS: + rc = ngx_mail_smtp_starttls(s, c); + ngx_str_set(&s->out, smtp_starttls); + break; + + default: + rc = NGX_MAIL_PARSE_INVALID_COMMAND; + break; + } + + break; + + case ngx_smtp_auth_login_username: + rc = ngx_mail_auth_login_username(s, c, 0); + + ngx_str_set(&s->out, smtp_password); + s->mail_state = ngx_smtp_auth_login_password; + break; + + case ngx_smtp_auth_login_password: + rc = ngx_mail_auth_login_password(s, c); + break; + + case ngx_smtp_auth_plain: + rc = ngx_mail_auth_plain(s, c, 0); + break; + + case ngx_smtp_auth_cram_md5: + rc = ngx_mail_auth_cram_md5(s, c); + break; + + case ngx_smtp_auth_external: + rc = ngx_mail_auth_external(s, c, 0); + break; + } + } + + if (s->buffer->pos < s->buffer->last) { + s->blocked = 1; + } + + switch (rc) { + + case NGX_DONE: + ngx_mail_auth(s, c); + return; + + case NGX_ERROR: + ngx_mail_session_internal_server_error(s); + return; + + case NGX_MAIL_PARSE_INVALID_COMMAND: + s->mail_state = ngx_smtp_start; + s->state = 0; + ngx_str_set(&s->out, smtp_invalid_command); + + /* fall through */ + + case NGX_OK: + s->args.nelts = 0; + + if (s->buffer->pos == s->buffer->last) { + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + } + + if (s->state) { + s->arg_start = s->buffer->pos; + } + + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_session_internal_server_error(s); + return; + } + + ngx_mail_send(c->write); + } +} + + +static ngx_int_t +ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg; + ngx_mail_smtp_srv_conf_t *sscf; + + if (s->args.nelts != 1) { + ngx_str_set(&s->out, smtp_invalid_argument); + s->state = 0; + return NGX_OK; + } + + arg = s->args.elts; + + s->smtp_helo.len = arg[0].len; + + s->smtp_helo.data = ngx_pnalloc(c->pool, arg[0].len); + if (s->smtp_helo.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->smtp_helo.data, arg[0].data, arg[0].len); + + ngx_str_null(&s->smtp_from); + ngx_str_null(&s->smtp_to); + + sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); + + if (s->command == NGX_SMTP_HELO) { + s->out = sscf->server_name; + + } else { + s->esmtp = 1; + +#if (NGX_MAIL_SSL) + + if (c->ssl == NULL) { + ngx_mail_ssl_conf_t *sslcf; + + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); + + if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) { + s->out = sscf->starttls_capability; + return NGX_OK; + } + + if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) { + s->out = sscf->starttls_only_capability; + return NGX_OK; + } + } +#endif + + s->out = sscf->capability; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_int_t rc; + ngx_mail_core_srv_conf_t *cscf; + ngx_mail_smtp_srv_conf_t *sscf; + +#if (NGX_MAIL_SSL) + if (ngx_mail_starttls_only(s, c)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } +#endif + + if (s->args.nelts == 0) { + ngx_str_set(&s->out, smtp_invalid_argument); + s->state = 0; + return NGX_OK; + } + + sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); + + rc = ngx_mail_auth_parse(s, c); + + switch (rc) { + + case NGX_MAIL_AUTH_LOGIN: + + ngx_str_set(&s->out, smtp_username); + s->mail_state = ngx_smtp_auth_login_username; + + return NGX_OK; + + case NGX_MAIL_AUTH_LOGIN_USERNAME: + + ngx_str_set(&s->out, smtp_password); + s->mail_state = ngx_smtp_auth_login_password; + + return ngx_mail_auth_login_username(s, c, 1); + + case NGX_MAIL_AUTH_PLAIN: + + ngx_str_set(&s->out, smtp_next); + s->mail_state = ngx_smtp_auth_plain; + + return NGX_OK; + + case NGX_MAIL_AUTH_CRAM_MD5: + + if (!(sscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + if (s->salt.data == NULL) { + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + if (ngx_mail_salt(s, c, cscf) != NGX_OK) { + return NGX_ERROR; + } + } + + if (ngx_mail_auth_cram_md5_salt(s, c, "334 ", 4) == NGX_OK) { + s->mail_state = ngx_smtp_auth_cram_md5; + return NGX_OK; + } + + return NGX_ERROR; + + case NGX_MAIL_AUTH_EXTERNAL: + + if (!(sscf->auth_methods & NGX_MAIL_AUTH_EXTERNAL_ENABLED)) { + return NGX_MAIL_PARSE_INVALID_COMMAND; + } + + ngx_str_set(&s->out, smtp_username); + s->mail_state = ngx_smtp_auth_external; + + return NGX_OK; + } + + return rc; +} + + +static ngx_int_t +ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg, cmd; + ngx_mail_smtp_srv_conf_t *sscf; + + sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module); + + if (!(sscf->auth_methods & NGX_MAIL_AUTH_NONE_ENABLED)) { + ngx_mail_smtp_log_rejected_command(s, c, "client was rejected: \"%V\""); + ngx_str_set(&s->out, smtp_auth_required); + return NGX_OK; + } + + /* auth none */ + + if (s->smtp_from.len) { + ngx_str_set(&s->out, smtp_bad_sequence); + return NGX_OK; + } + + if (s->args.nelts == 0) { + ngx_str_set(&s->out, smtp_invalid_argument); + return NGX_OK; + } + + arg = s->args.elts; + arg += s->args.nelts - 1; + + cmd.len = arg->data + arg->len - s->cmd.data; + cmd.data = s->cmd.data; + + s->smtp_from.len = cmd.len; + + s->smtp_from.data = ngx_pnalloc(c->pool, cmd.len); + if (s->smtp_from.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->smtp_from.data, cmd.data, cmd.len); + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "smtp mail from:\"%V\"", &s->smtp_from); + + ngx_str_set(&s->out, smtp_ok); + + ngx_str_null(&s->login); + ngx_str_null(&s->passwd); + + return NGX_OK; +} + + +static ngx_int_t +ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_t *arg, cmd; + + if (s->smtp_from.len == 0) { + ngx_str_set(&s->out, smtp_bad_sequence); + return NGX_OK; + } + + if (s->args.nelts == 0) { + ngx_str_set(&s->out, smtp_invalid_argument); + return NGX_OK; + } + + arg = s->args.elts; + arg += s->args.nelts - 1; + + cmd.len = arg->data + arg->len - s->cmd.data; + cmd.data = s->cmd.data; + + s->smtp_to.len = cmd.len; + + s->smtp_to.data = ngx_pnalloc(c->pool, cmd.len); + if (s->smtp_to.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(s->smtp_to.data, cmd.data, cmd.len); + + ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, + "smtp rcpt to:\"%V\"", &s->smtp_to); + + s->auth_method = NGX_MAIL_AUTH_NONE; + + return NGX_DONE; +} + + +static ngx_int_t +ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c) +{ + ngx_str_null(&s->smtp_from); + ngx_str_null(&s->smtp_to); + ngx_str_set(&s->out, smtp_ok); + + return NGX_OK; +} + + +static ngx_int_t +ngx_mail_smtp_starttls(ngx_mail_session_t *s, ngx_connection_t *c) +{ +#if (NGX_MAIL_SSL) + ngx_mail_ssl_conf_t *sslcf; + + if (c->ssl == NULL) { + sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); + if (sslcf->starttls) { + + /* + * RFC3207 requires us to discard any knowledge + * obtained from client before STARTTLS. + */ + + ngx_str_null(&s->smtp_helo); + ngx_str_null(&s->smtp_from); + ngx_str_null(&s->smtp_to); + + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + + c->read->handler = ngx_mail_starttls_handler; + return NGX_OK; + } + } + +#endif + + return NGX_MAIL_PARSE_INVALID_COMMAND; +} + + +static ngx_int_t +ngx_mail_smtp_discard_command(ngx_mail_session_t *s, ngx_connection_t *c, + char *err) +{ + ssize_t n; + + n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last); + + if (n == NGX_ERROR || n == 0) { + ngx_mail_close_connection(c); + return NGX_ERROR; + } + + if (n > 0) { + s->buffer->last += n; + } + + if (n == NGX_AGAIN) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_mail_session_internal_server_error(s); + return NGX_ERROR; + } + + return NGX_AGAIN; + } + + ngx_mail_smtp_log_rejected_command(s, c, err); + + s->buffer->pos = s->buffer->start; + s->buffer->last = s->buffer->start; + + return NGX_OK; +} + + +static void +ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s, ngx_connection_t *c, + char *err) +{ + u_char ch; + ngx_str_t cmd; + ngx_uint_t i; + + if (c->log->log_level < NGX_LOG_INFO) { + return; + } + + cmd.len = s->buffer->last - s->buffer->start; + cmd.data = s->buffer->start; + + for (i = 0; i < cmd.len; i++) { + ch = cmd.data[i]; + + if (ch != CR && ch != LF) { + continue; + } + + cmd.data[i] = '_'; + } + + cmd.len = i; + + ngx_log_error(NGX_LOG_INFO, c->log, 0, err, &cmd); +} diff --git a/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_smtp_module.c b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_smtp_module.c new file mode 100644 index 000000000..0e05fdc03 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_smtp_module.c @@ -0,0 +1,312 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include +#include + + +static void *ngx_mail_smtp_create_srv_conf(ngx_conf_t *cf); +static char *ngx_mail_smtp_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); + + +static ngx_conf_bitmask_t ngx_mail_smtp_auth_methods[] = { + { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED }, + { ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED }, + { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED }, + { ngx_string("external"), NGX_MAIL_AUTH_EXTERNAL_ENABLED }, + { ngx_string("none"), NGX_MAIL_AUTH_NONE_ENABLED }, + { ngx_null_string, 0 } +}; + + +static ngx_str_t ngx_mail_smtp_auth_methods_names[] = { + ngx_string("PLAIN"), + ngx_string("LOGIN"), + ngx_null_string, /* APOP */ + ngx_string("CRAM-MD5"), + ngx_string("EXTERNAL"), + ngx_null_string /* NONE */ +}; + + +static ngx_mail_protocol_t ngx_mail_smtp_protocol = { + ngx_string("smtp"), + ngx_string("\x04smtp"), + { 25, 465, 587, 0 }, + NGX_MAIL_SMTP_PROTOCOL, + + ngx_mail_smtp_init_session, + ngx_mail_smtp_init_protocol, + ngx_mail_smtp_parse_command, + ngx_mail_smtp_auth_state, + + ngx_string("451 4.3.2 Internal server error" CRLF), + ngx_string("421 4.7.1 SSL certificate error" CRLF), + ngx_string("421 4.7.1 No required SSL certificate" CRLF) +}; + + +static ngx_command_t ngx_mail_smtp_commands[] = { + + { ngx_string("smtp_client_buffer"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_smtp_srv_conf_t, client_buffer_size), + NULL }, + + { ngx_string("smtp_greeting_delay"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_smtp_srv_conf_t, greeting_delay), + NULL }, + + { ngx_string("smtp_capabilities"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, + ngx_mail_capabilities, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_smtp_srv_conf_t, capabilities), + NULL }, + + { ngx_string("smtp_auth"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_smtp_srv_conf_t, auth_methods), + &ngx_mail_smtp_auth_methods }, + + ngx_null_command +}; + + +static ngx_mail_module_t ngx_mail_smtp_module_ctx = { + &ngx_mail_smtp_protocol, /* protocol */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_mail_smtp_create_srv_conf, /* create server configuration */ + ngx_mail_smtp_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_mail_smtp_module = { + NGX_MODULE_V1, + &ngx_mail_smtp_module_ctx, /* module context */ + ngx_mail_smtp_commands, /* module directives */ + NGX_MAIL_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static void * +ngx_mail_smtp_create_srv_conf(ngx_conf_t *cf) +{ + ngx_mail_smtp_srv_conf_t *sscf; + + sscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_smtp_srv_conf_t)); + if (sscf == NULL) { + return NULL; + } + + sscf->client_buffer_size = NGX_CONF_UNSET_SIZE; + sscf->greeting_delay = NGX_CONF_UNSET_MSEC; + + if (ngx_array_init(&sscf->capabilities, cf->pool, 4, sizeof(ngx_str_t)) + != NGX_OK) + { + return NULL; + } + + return sscf; +} + + +static char * +ngx_mail_smtp_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_mail_smtp_srv_conf_t *prev = parent; + ngx_mail_smtp_srv_conf_t *conf = child; + + u_char *p, *auth, *last; + size_t size; + ngx_str_t *c; + ngx_uint_t i, m, auth_enabled; + ngx_mail_core_srv_conf_t *cscf; + + ngx_conf_merge_size_value(conf->client_buffer_size, + prev->client_buffer_size, + (size_t) ngx_pagesize); + + ngx_conf_merge_msec_value(conf->greeting_delay, + prev->greeting_delay, 0); + + ngx_conf_merge_bitmask_value(conf->auth_methods, + prev->auth_methods, + (NGX_CONF_BITMASK_SET + |NGX_MAIL_AUTH_PLAIN_ENABLED + |NGX_MAIL_AUTH_LOGIN_ENABLED)); + + + cscf = ngx_mail_conf_get_module_srv_conf(cf, ngx_mail_core_module); + + size = sizeof("220 ESMTP ready" CRLF) - 1 + cscf->server_name.len; + + p = ngx_pnalloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->greeting.len = size; + conf->greeting.data = p; + + *p++ = '2'; *p++ = '2'; *p++ = '0'; *p++ = ' '; + p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len); + ngx_memcpy(p, " ESMTP ready" CRLF, sizeof(" ESMTP ready" CRLF) - 1); + + + size = sizeof("250 " CRLF) - 1 + cscf->server_name.len; + + p = ngx_pnalloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->server_name.len = size; + conf->server_name.data = p; + + *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' '; + p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len); + *p++ = CR; *p = LF; + + + if (conf->capabilities.nelts == 0) { + conf->capabilities = prev->capabilities; + } + + size = sizeof("250-") - 1 + cscf->server_name.len + sizeof(CRLF) - 1; + + c = conf->capabilities.elts; + for (i = 0; i < conf->capabilities.nelts; i++) { + size += sizeof("250 ") - 1 + c[i].len + sizeof(CRLF) - 1; + } + + auth_enabled = 0; + + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; + m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; + m <<= 1, i++) + { + if (m & conf->auth_methods) { + size += 1 + ngx_mail_smtp_auth_methods_names[i].len; + auth_enabled = 1; + } + } + + if (auth_enabled) { + size += sizeof("250 AUTH") - 1 + sizeof(CRLF) - 1; + } + + p = ngx_pnalloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->capability.len = size; + conf->capability.data = p; + + last = p; + + *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-'; + p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len); + *p++ = CR; *p++ = LF; + + for (i = 0; i < conf->capabilities.nelts; i++) { + last = p; + *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-'; + p = ngx_cpymem(p, c[i].data, c[i].len); + *p++ = CR; *p++ = LF; + } + + auth = p; + + if (auth_enabled) { + last = p; + + *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' '; + *p++ = 'A'; *p++ = 'U'; *p++ = 'T'; *p++ = 'H'; + + for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0; + m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED; + m <<= 1, i++) + { + if (m & conf->auth_methods) { + *p++ = ' '; + p = ngx_cpymem(p, ngx_mail_smtp_auth_methods_names[i].data, + ngx_mail_smtp_auth_methods_names[i].len); + } + } + + *p++ = CR; *p = LF; + + } else { + last[3] = ' '; + } + + size += sizeof("250 STARTTLS" CRLF) - 1; + + p = ngx_pnalloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->starttls_capability.len = size; + conf->starttls_capability.data = p; + + p = ngx_cpymem(p, conf->capability.data, conf->capability.len); + + ngx_memcpy(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1); + + p = conf->starttls_capability.data + + (last - conf->capability.data) + 3; + *p = '-'; + + size = (auth - conf->capability.data) + + sizeof("250 STARTTLS" CRLF) - 1; + + p = ngx_pnalloc(cf->pool, size); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + conf->starttls_only_capability.len = size; + conf->starttls_only_capability.data = p; + + p = ngx_cpymem(p, conf->capability.data, auth - conf->capability.data); + + ngx_memcpy(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1); + + if (last < auth) { + p = conf->starttls_only_capability.data + + (last - conf->capability.data) + 3; + *p = '-'; + } + + return NGX_CONF_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_smtp_module.h b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_smtp_module.h new file mode 100644 index 000000000..04ffab60a --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_smtp_module.h @@ -0,0 +1,45 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_MAIL_SMTP_MODULE_H_INCLUDED_ +#define _NGX_MAIL_SMTP_MODULE_H_INCLUDED_ + + +#include +#include +#include +#include + + +typedef struct { + ngx_msec_t greeting_delay; + + size_t client_buffer_size; + + ngx_str_t capability; + ngx_str_t starttls_capability; + ngx_str_t starttls_only_capability; + + ngx_str_t server_name; + ngx_str_t greeting; + + ngx_uint_t auth_methods; + + ngx_array_t capabilities; +} ngx_mail_smtp_srv_conf_t; + + +void ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c); +void ngx_mail_smtp_init_protocol(ngx_event_t *rev); +void ngx_mail_smtp_auth_state(ngx_event_t *rev); +ngx_int_t ngx_mail_smtp_parse_command(ngx_mail_session_t *s); + + +extern ngx_module_t ngx_mail_smtp_module; + + +#endif /* _NGX_MAIL_SMTP_MODULE_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_ssl_module.c b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_ssl_module.c new file mode 100644 index 000000000..079d0e773 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_ssl_module.c @@ -0,0 +1,714 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +#define NGX_DEFAULT_CIPHERS "HIGH:!aNULL:!MD5" +#define NGX_DEFAULT_ECDH_CURVE "auto" + + +#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation +static int ngx_mail_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, + const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, void *arg); +#endif + +static void *ngx_mail_ssl_create_conf(ngx_conf_t *cf); +static char *ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child); + +static char *ngx_mail_ssl_starttls(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_mail_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +static char *ngx_mail_ssl_conf_command_check(ngx_conf_t *cf, void *post, + void *data); + + +static ngx_conf_enum_t ngx_mail_starttls_state[] = { + { ngx_string("off"), NGX_MAIL_STARTTLS_OFF }, + { ngx_string("on"), NGX_MAIL_STARTTLS_ON }, + { ngx_string("only"), NGX_MAIL_STARTTLS_ONLY }, + { ngx_null_string, 0 } +}; + + + +static ngx_conf_bitmask_t ngx_mail_ssl_protocols[] = { + { ngx_string("SSLv2"), NGX_SSL_SSLv2 }, + { ngx_string("SSLv3"), NGX_SSL_SSLv3 }, + { ngx_string("TLSv1"), NGX_SSL_TLSv1 }, + { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 }, + { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 }, + { ngx_string("TLSv1.3"), NGX_SSL_TLSv1_3 }, + { ngx_null_string, 0 } +}; + + +static ngx_conf_enum_t ngx_mail_ssl_verify[] = { + { ngx_string("off"), 0 }, + { ngx_string("on"), 1 }, + { ngx_string("optional"), 2 }, + { ngx_string("optional_no_ca"), 3 }, + { ngx_null_string, 0 } +}; + + +static ngx_conf_post_t ngx_mail_ssl_conf_command_post = + { ngx_mail_ssl_conf_command_check }; + + +static ngx_command_t ngx_mail_ssl_commands[] = { + + { ngx_string("starttls"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_mail_ssl_starttls, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, starttls), + ngx_mail_starttls_state }, + + { ngx_string("ssl_certificate"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_array_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, certificates), + NULL }, + + { ngx_string("ssl_certificate_key"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_array_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, certificate_keys), + NULL }, + + { ngx_string("ssl_password_file"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_mail_ssl_password_file, + NGX_MAIL_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("ssl_certificate_compression"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, certificate_compression), + NULL }, + + { ngx_string("ssl_dhparam"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, dhparam), + NULL }, + + { ngx_string("ssl_ecdh_curve"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, ecdh_curve), + NULL }, + + { ngx_string("ssl_protocols"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, protocols), + &ngx_mail_ssl_protocols }, + + { ngx_string("ssl_ciphers"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, ciphers), + NULL }, + + { ngx_string("ssl_prefer_server_ciphers"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, prefer_server_ciphers), + NULL }, + + { ngx_string("ssl_session_cache"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE12, + ngx_mail_ssl_session_cache, + NGX_MAIL_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("ssl_session_tickets"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, session_tickets), + NULL }, + + { ngx_string("ssl_session_ticket_key"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_array_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, session_ticket_keys), + NULL }, + + { ngx_string("ssl_session_timeout"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_sec_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, session_timeout), + NULL }, + + { ngx_string("ssl_verify_client"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_enum_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, verify), + &ngx_mail_ssl_verify }, + + { ngx_string("ssl_verify_depth"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, verify_depth), + NULL }, + + { ngx_string("ssl_client_certificate"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, client_certificate), + NULL }, + + { ngx_string("ssl_trusted_certificate"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, trusted_certificate), + NULL }, + + { ngx_string("ssl_crl"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, crl), + NULL }, + + { ngx_string("ssl_conf_command"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE2, + ngx_conf_set_keyval_slot, + NGX_MAIL_SRV_CONF_OFFSET, + offsetof(ngx_mail_ssl_conf_t, conf_commands), + &ngx_mail_ssl_conf_command_post }, + + ngx_null_command +}; + + +static ngx_mail_module_t ngx_mail_ssl_module_ctx = { + NULL, /* protocol */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_mail_ssl_create_conf, /* create server configuration */ + ngx_mail_ssl_merge_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_mail_ssl_module = { + NGX_MODULE_V1, + &ngx_mail_ssl_module_ctx, /* module context */ + ngx_mail_ssl_commands, /* module directives */ + NGX_MAIL_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_str_t ngx_mail_ssl_sess_id_ctx = ngx_string("MAIL"); + + +#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation + +static int +ngx_mail_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, + unsigned char *outlen, const unsigned char *in, unsigned int inlen, + void *arg) +{ + unsigned int srvlen; + unsigned char *srv; + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_core_srv_conf_t *cscf; +#if (NGX_DEBUG) + unsigned int i; +#endif + + c = ngx_ssl_get_connection(ssl_conn); + s = c->data; + +#if (NGX_DEBUG) + for (i = 0; i < inlen; i += in[i] + 1) { + ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, + "SSL ALPN supported by client: %*s", + (size_t) in[i], &in[i + 1]); + } +#endif + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + srv = cscf->protocol->alpn.data; + srvlen = cscf->protocol->alpn.len; + + if (SSL_select_next_proto((unsigned char **) out, outlen, srv, srvlen, + in, inlen) + != OPENSSL_NPN_NEGOTIATED) + { + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0, + "SSL ALPN selected: %*s", (size_t) *outlen, *out); + + return SSL_TLSEXT_ERR_OK; +} + +#endif + + +static void * +ngx_mail_ssl_create_conf(ngx_conf_t *cf) +{ + ngx_mail_ssl_conf_t *scf; + + scf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_ssl_conf_t)); + if (scf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * scf->listen = 0; + * scf->protocols = 0; + * scf->dhparam = { 0, NULL }; + * scf->ecdh_curve = { 0, NULL }; + * scf->client_certificate = { 0, NULL }; + * scf->trusted_certificate = { 0, NULL }; + * scf->crl = { 0, NULL }; + * scf->ciphers = { 0, NULL }; + * scf->shm_zone = NULL; + */ + + scf->starttls = NGX_CONF_UNSET_UINT; + scf->certificates = NGX_CONF_UNSET_PTR; + scf->certificate_keys = NGX_CONF_UNSET_PTR; + scf->passwords = NGX_CONF_UNSET_PTR; + scf->conf_commands = NGX_CONF_UNSET_PTR; + scf->prefer_server_ciphers = NGX_CONF_UNSET; + scf->certificate_compression = NGX_CONF_UNSET; + scf->verify = NGX_CONF_UNSET_UINT; + scf->verify_depth = NGX_CONF_UNSET_UINT; + scf->builtin_session_cache = NGX_CONF_UNSET; + scf->session_timeout = NGX_CONF_UNSET; + scf->session_tickets = NGX_CONF_UNSET; + scf->session_ticket_keys = NGX_CONF_UNSET_PTR; + + return scf; +} + + +static char * +ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_mail_ssl_conf_t *prev = parent; + ngx_mail_ssl_conf_t *conf = child; + + char *mode; + ngx_pool_cleanup_t *cln; + + ngx_conf_merge_uint_value(conf->starttls, prev->starttls, + NGX_MAIL_STARTTLS_OFF); + + ngx_conf_merge_value(conf->session_timeout, + prev->session_timeout, 300); + + ngx_conf_merge_value(conf->prefer_server_ciphers, + prev->prefer_server_ciphers, 0); + + ngx_conf_merge_value(conf->certificate_compression, + prev->certificate_compression, 0); + + ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, + (NGX_CONF_BITMASK_SET|NGX_SSL_DEFAULT_PROTOCOLS)); + + ngx_conf_merge_uint_value(conf->verify, prev->verify, 0); + ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1); + + ngx_conf_merge_ptr_value(conf->certificates, prev->certificates, NULL); + ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys, + NULL); + + ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL); + + ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, ""); + + ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve, + NGX_DEFAULT_ECDH_CURVE); + + ngx_conf_merge_str_value(conf->client_certificate, + prev->client_certificate, ""); + ngx_conf_merge_str_value(conf->trusted_certificate, + prev->trusted_certificate, ""); + ngx_conf_merge_str_value(conf->crl, prev->crl, ""); + + ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS); + + ngx_conf_merge_ptr_value(conf->conf_commands, prev->conf_commands, NULL); + + + conf->ssl.log = cf->log; + + if (conf->listen) { + mode = "listen ... ssl"; + + } else if (conf->starttls != NGX_MAIL_STARTTLS_OFF) { + mode = "starttls"; + + } else { + return NGX_CONF_OK; + } + + if (conf->file == NULL) { + conf->file = prev->file; + conf->line = prev->line; + } + + if (conf->certificates == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"ssl_certificate\" is defined for " + "the \"%s\" directive in %s:%ui", + mode, conf->file, conf->line); + return NGX_CONF_ERROR; + } + + if (conf->certificate_keys == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"ssl_certificate_key\" is defined for " + "the \"%s\" directive in %s:%ui", + mode, conf->file, conf->line); + return NGX_CONF_ERROR; + } + + if (conf->certificate_keys->nelts < conf->certificates->nelts) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"ssl_certificate_key\" is defined " + "for certificate \"%V\" and " + "the \"%s\" directive in %s:%ui", + ((ngx_str_t *) conf->certificates->elts) + + conf->certificates->nelts - 1, + mode, conf->file, conf->line); + return NGX_CONF_ERROR; + } + + if (ngx_ssl_create(&conf->ssl, conf->protocols, NULL) != NGX_OK) { + return NGX_CONF_ERROR; + } + + cln = ngx_pool_cleanup_add(cf->pool, 0); + if (cln == NULL) { + ngx_ssl_cleanup_ctx(&conf->ssl); + return NGX_CONF_ERROR; + } + + cln->handler = ngx_ssl_cleanup_ctx; + cln->data = &conf->ssl; + +#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation + SSL_CTX_set_alpn_select_cb(conf->ssl.ctx, ngx_mail_ssl_alpn_select, NULL); +#endif + + if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers, + conf->prefer_server_ciphers) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates, + conf->certificate_keys, conf->passwords) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + if (ngx_ssl_certificate_compression(cf, &conf->ssl, + conf->certificate_compression) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + if (conf->verify) { + + if (conf->verify != 3 + && conf->client_certificate.len == 0 + && conf->trusted_certificate.len == 0) + { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no ssl_client_certificate or " + "ssl_trusted_certificate for ssl_verify_client"); + return NGX_CONF_ERROR; + } + + if (ngx_ssl_client_certificate(cf, &conf->ssl, + &conf->client_certificate, + conf->verify_depth) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + if (ngx_ssl_trusted_certificate(cf, &conf->ssl, + &conf->trusted_certificate, + conf->verify_depth) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) { + return NGX_CONF_ERROR; + } + + ngx_conf_merge_value(conf->builtin_session_cache, + prev->builtin_session_cache, NGX_SSL_NONE_SCACHE); + + if (conf->shm_zone == NULL) { + conf->shm_zone = prev->shm_zone; + } + + if (ngx_ssl_session_cache(&conf->ssl, &ngx_mail_ssl_sess_id_ctx, + conf->certificates, conf->builtin_session_cache, + conf->shm_zone, conf->session_timeout) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + ngx_conf_merge_value(conf->session_tickets, + prev->session_tickets, 1); + +#ifdef SSL_OP_NO_TICKET + if (!conf->session_tickets) { + SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_NO_TICKET); + } +#endif + + ngx_conf_merge_ptr_value(conf->session_ticket_keys, + prev->session_ticket_keys, NULL); + + if (ngx_ssl_session_ticket_keys(cf, &conf->ssl, conf->session_ticket_keys) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + if (ngx_ssl_conf_commands(cf, &conf->ssl, conf->conf_commands) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_mail_ssl_starttls(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_mail_ssl_conf_t *scf = conf; + + char *rv; + + rv = ngx_conf_set_enum_slot(cf, cmd, conf); + + if (rv != NGX_CONF_OK) { + return rv; + } + + if (!scf->listen) { + scf->file = cf->conf_file->file.name.data; + scf->line = cf->conf_file->line; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_mail_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_mail_ssl_conf_t *scf = conf; + + ngx_str_t *value; + + if (scf->passwords != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + value = cf->args->elts; + + scf->passwords = ngx_ssl_read_password_file(cf, &value[1]); + + if (scf->passwords == NULL) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_mail_ssl_conf_t *scf = conf; + + size_t len; + ngx_str_t *value, name, size; + ngx_int_t n; + ngx_uint_t i, j; + + value = cf->args->elts; + + for (i = 1; i < cf->args->nelts; i++) { + + if (ngx_strcmp(value[i].data, "off") == 0) { + scf->builtin_session_cache = NGX_SSL_NO_SCACHE; + continue; + } + + if (ngx_strcmp(value[i].data, "none") == 0) { + scf->builtin_session_cache = NGX_SSL_NONE_SCACHE; + continue; + } + + if (ngx_strcmp(value[i].data, "builtin") == 0) { + scf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE; + continue; + } + + if (value[i].len > sizeof("builtin:") - 1 + && ngx_strncmp(value[i].data, "builtin:", sizeof("builtin:") - 1) + == 0) + { + n = ngx_atoi(value[i].data + sizeof("builtin:") - 1, + value[i].len - (sizeof("builtin:") - 1)); + + if (n == NGX_ERROR) { + goto invalid; + } + + scf->builtin_session_cache = n; + + continue; + } + + if (value[i].len > sizeof("shared:") - 1 + && ngx_strncmp(value[i].data, "shared:", sizeof("shared:") - 1) + == 0) + { + len = 0; + + for (j = sizeof("shared:") - 1; j < value[i].len; j++) { + if (value[i].data[j] == ':') { + break; + } + + len++; + } + + if (len == 0 || j == value[i].len) { + goto invalid; + } + + name.len = len; + name.data = value[i].data + sizeof("shared:") - 1; + + size.len = value[i].len - j - 1; + size.data = name.data + len + 1; + + n = ngx_parse_size(&size); + + if (n == NGX_ERROR) { + goto invalid; + } + + if (n < (ngx_int_t) (8 * ngx_pagesize)) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "session cache \"%V\" is too small", + &value[i]); + + return NGX_CONF_ERROR; + } + + scf->shm_zone = ngx_shared_memory_add(cf, &name, n, + &ngx_mail_ssl_module); + if (scf->shm_zone == NULL) { + return NGX_CONF_ERROR; + } + + scf->shm_zone->init = ngx_ssl_session_cache_init; + + continue; + } + + goto invalid; + } + + if (scf->shm_zone && scf->builtin_session_cache == NGX_CONF_UNSET) { + scf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE; + } + + return NGX_CONF_OK; + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid session cache \"%V\"", &value[i]); + + return NGX_CONF_ERROR; +} + + +static char * +ngx_mail_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data) +{ +#ifndef SSL_CONF_FLAG_FILE + return "is not supported on this platform"; +#else + return NGX_CONF_OK; +#endif +} diff --git a/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_ssl_module.h b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_ssl_module.h new file mode 100644 index 000000000..a0e9a173a --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/mail/ngx_mail_ssl_module.h @@ -0,0 +1,66 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_MAIL_SSL_H_INCLUDED_ +#define _NGX_MAIL_SSL_H_INCLUDED_ + + +#include +#include +#include + + +#define NGX_MAIL_STARTTLS_OFF 0 +#define NGX_MAIL_STARTTLS_ON 1 +#define NGX_MAIL_STARTTLS_ONLY 2 + + +typedef struct { + ngx_flag_t prefer_server_ciphers; + ngx_flag_t certificate_compression; + + ngx_ssl_t ssl; + + ngx_uint_t starttls; + ngx_uint_t listen; + ngx_uint_t protocols; + + ngx_uint_t verify; + ngx_uint_t verify_depth; + + ssize_t builtin_session_cache; + + time_t session_timeout; + + ngx_array_t *certificates; + ngx_array_t *certificate_keys; + + ngx_str_t dhparam; + ngx_str_t ecdh_curve; + ngx_str_t client_certificate; + ngx_str_t trusted_certificate; + ngx_str_t crl; + + ngx_str_t ciphers; + + ngx_array_t *passwords; + ngx_array_t *conf_commands; + + ngx_shm_zone_t *shm_zone; + + ngx_flag_t session_tickets; + ngx_array_t *session_ticket_keys; + + u_char *file; + ngx_uint_t line; +} ngx_mail_ssl_conf_t; + + +extern ngx_module_t ngx_mail_ssl_module; + + +#endif /* _NGX_MAIL_SSL_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/misc/ngx_cpp_test_module.cpp b/src/deps/src/nginx-1.30.1/src/misc/ngx_cpp_test_module.cpp new file mode 100644 index 000000000..002640990 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/misc/ngx_cpp_test_module.cpp @@ -0,0 +1,31 @@ + +// stub module to test header files' C++ compatibility + +extern "C" { + #include + #include + #include + #include + #include + + #include + + #include + #include + #include + #include + + #include +} + +// nginx header files should go before other, because they define 64-bit off_t +// #include + + +void ngx_cpp_test_handler(void *data); + +void +ngx_cpp_test_handler(void *data) +{ + return; +} diff --git a/src/deps/src/nginx-1.30.1/src/misc/ngx_google_perftools_module.c b/src/deps/src/nginx-1.30.1/src/misc/ngx_google_perftools_module.c new file mode 100644 index 000000000..381f3d105 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/misc/ngx_google_perftools_module.c @@ -0,0 +1,126 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + +/* + * declare Profiler interface here because + * is C++ header file + */ + +int ProfilerStart(u_char* fname); +void ProfilerStop(void); +void ProfilerRegisterThread(void); + + +static void *ngx_google_perftools_create_conf(ngx_cycle_t *cycle); +static ngx_int_t ngx_google_perftools_worker(ngx_cycle_t *cycle); + + +typedef struct { + ngx_str_t profiles; +} ngx_google_perftools_conf_t; + + +static ngx_command_t ngx_google_perftools_commands[] = { + + { ngx_string("google_perftools_profiles"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + 0, + offsetof(ngx_google_perftools_conf_t, profiles), + NULL }, + + ngx_null_command +}; + + +static ngx_core_module_t ngx_google_perftools_module_ctx = { + ngx_string("google_perftools"), + ngx_google_perftools_create_conf, + NULL +}; + + +ngx_module_t ngx_google_perftools_module = { + NGX_MODULE_V1, + &ngx_google_perftools_module_ctx, /* module context */ + ngx_google_perftools_commands, /* module directives */ + NGX_CORE_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + ngx_google_perftools_worker, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static void * +ngx_google_perftools_create_conf(ngx_cycle_t *cycle) +{ + ngx_google_perftools_conf_t *gptcf; + + gptcf = ngx_pcalloc(cycle->pool, sizeof(ngx_google_perftools_conf_t)); + if (gptcf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc() + * + * gptcf->profiles = { 0, NULL }; + */ + + return gptcf; +} + + +static ngx_int_t +ngx_google_perftools_worker(ngx_cycle_t *cycle) +{ + u_char *profile; + ngx_google_perftools_conf_t *gptcf; + + gptcf = (ngx_google_perftools_conf_t *) + ngx_get_conf(cycle->conf_ctx, ngx_google_perftools_module); + + if (gptcf->profiles.len == 0) { + return NGX_OK; + } + + profile = ngx_alloc(gptcf->profiles.len + NGX_INT_T_LEN + 2, cycle->log); + if (profile == NULL) { + return NGX_OK; + } + + if (getenv("CPUPROFILE")) { + /* disable inherited Profiler enabled in master process */ + ProfilerStop(); + } + + ngx_sprintf(profile, "%V.%d%Z", &gptcf->profiles, ngx_pid); + + if (ProfilerStart(profile)) { + /* start ITIMER_PROF timer */ + ProfilerRegisterThread(); + + } else { + ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_errno, + "ProfilerStart(%s) failed", profile); + } + + ngx_free(profile); + + return NGX_OK; +} + + +/* ProfilerStop() is called on Profiler destruction */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_alloc.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_alloc.c new file mode 100644 index 000000000..5c2f78702 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_alloc.c @@ -0,0 +1,90 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +ngx_uint_t ngx_pagesize; +ngx_uint_t ngx_pagesize_shift; +ngx_uint_t ngx_cacheline_size; + + +void * +ngx_alloc(size_t size, ngx_log_t *log) +{ + void *p; + + p = malloc(size); + if (p == NULL) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "malloc(%uz) failed", size); + } + + ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, "malloc: %p:%uz", p, size); + + return p; +} + + +void * +ngx_calloc(size_t size, ngx_log_t *log) +{ + void *p; + + p = ngx_alloc(size, log); + + if (p) { + ngx_memzero(p, size); + } + + return p; +} + + +#if (NGX_HAVE_POSIX_MEMALIGN) + +void * +ngx_memalign(size_t alignment, size_t size, ngx_log_t *log) +{ + void *p; + int err; + + err = posix_memalign(&p, alignment, size); + + if (err) { + ngx_log_error(NGX_LOG_EMERG, log, err, + "posix_memalign(%uz, %uz) failed", alignment, size); + p = NULL; + } + + ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, log, 0, + "posix_memalign: %p:%uz @%uz", p, size, alignment); + + return p; +} + +#elif (NGX_HAVE_MEMALIGN) + +void * +ngx_memalign(size_t alignment, size_t size, ngx_log_t *log) +{ + void *p; + + p = memalign(alignment, size); + if (p == NULL) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "memalign(%uz, %uz) failed", alignment, size); + } + + ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, log, 0, + "memalign: %p:%uz @%uz", p, size, alignment); + + return p; +} + +#endif diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_alloc.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_alloc.h new file mode 100644 index 000000000..655db257f --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_alloc.h @@ -0,0 +1,45 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_ALLOC_H_INCLUDED_ +#define _NGX_ALLOC_H_INCLUDED_ + + +#include +#include + + +void *ngx_alloc(size_t size, ngx_log_t *log); +void *ngx_calloc(size_t size, ngx_log_t *log); + +#define ngx_free free + + +/* + * Linux has memalign() or posix_memalign() + * Solaris has memalign() + * FreeBSD 7.0 has posix_memalign(), besides, early version's malloc() + * aligns allocations bigger than page size at the page boundary + */ + +#if (NGX_HAVE_POSIX_MEMALIGN || NGX_HAVE_MEMALIGN) + +void *ngx_memalign(size_t alignment, size_t size, ngx_log_t *log); + +#else + +#define ngx_memalign(alignment, size, log) ngx_alloc(size, log) + +#endif + + +extern ngx_uint_t ngx_pagesize; +extern ngx_uint_t ngx_pagesize_shift; +extern ngx_uint_t ngx_cacheline_size; + + +#endif /* _NGX_ALLOC_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_atomic.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_atomic.h new file mode 100644 index 000000000..fcab2d6a0 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_atomic.h @@ -0,0 +1,313 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_ATOMIC_H_INCLUDED_ +#define _NGX_ATOMIC_H_INCLUDED_ + + +#include +#include + + +#if (NGX_HAVE_LIBATOMIC) + +#define AO_REQUIRE_CAS +#include + +#define NGX_HAVE_ATOMIC_OPS 1 + +typedef long ngx_atomic_int_t; +typedef AO_t ngx_atomic_uint_t; +typedef volatile ngx_atomic_uint_t ngx_atomic_t; + +#if (NGX_PTR_SIZE == 8) +#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1) +#else +#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1) +#endif + +#define ngx_atomic_cmp_set(lock, old, new) \ + AO_compare_and_swap(lock, old, new) +#define ngx_atomic_fetch_add(value, add) \ + AO_fetch_and_add(value, add) +#define ngx_memory_barrier() AO_nop() +#define ngx_cpu_pause() + + +#elif (NGX_HAVE_GCC_ATOMIC) + +/* GCC 4.1 builtin atomic operations */ + +#define NGX_HAVE_ATOMIC_OPS 1 + +typedef long ngx_atomic_int_t; +typedef unsigned long ngx_atomic_uint_t; + +#if (NGX_PTR_SIZE == 8) +#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1) +#else +#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1) +#endif + +typedef volatile ngx_atomic_uint_t ngx_atomic_t; + + +#define ngx_atomic_cmp_set(lock, old, set) \ + __sync_bool_compare_and_swap(lock, old, set) + +#define ngx_atomic_fetch_add(value, add) \ + __sync_fetch_and_add(value, add) + +#define ngx_memory_barrier() __sync_synchronize() + +#if ( __i386__ || __i386 || __amd64__ || __amd64 ) +#define ngx_cpu_pause() __asm__ ("pause") +#else +#define ngx_cpu_pause() +#endif + + +#elif (NGX_DARWIN_ATOMIC) + +/* + * use Darwin 8 atomic(3) and barrier(3) operations + * optimized at run-time for UP and SMP + */ + +#include + +/* "bool" conflicts with perl's CORE/handy.h */ +#if 0 +#undef bool +#endif + + +#define NGX_HAVE_ATOMIC_OPS 1 + +#if (NGX_PTR_SIZE == 8) + +typedef int64_t ngx_atomic_int_t; +typedef uint64_t ngx_atomic_uint_t; +#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1) + +#define ngx_atomic_cmp_set(lock, old, new) \ + OSAtomicCompareAndSwap64Barrier(old, new, (int64_t *) lock) + +#define ngx_atomic_fetch_add(value, add) \ + (OSAtomicAdd64(add, (int64_t *) value) - add) + +#else + +typedef int32_t ngx_atomic_int_t; +typedef uint32_t ngx_atomic_uint_t; +#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1) + +#define ngx_atomic_cmp_set(lock, old, new) \ + OSAtomicCompareAndSwap32Barrier(old, new, (int32_t *) lock) + +#define ngx_atomic_fetch_add(value, add) \ + (OSAtomicAdd32(add, (int32_t *) value) - add) + +#endif + +#define ngx_memory_barrier() OSMemoryBarrier() + +#define ngx_cpu_pause() + +typedef volatile ngx_atomic_uint_t ngx_atomic_t; + + +#elif ( __i386__ || __i386 ) + +typedef int32_t ngx_atomic_int_t; +typedef uint32_t ngx_atomic_uint_t; +typedef volatile ngx_atomic_uint_t ngx_atomic_t; +#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1) + + +#if ( __SUNPRO_C ) + +#define NGX_HAVE_ATOMIC_OPS 1 + +ngx_atomic_uint_t +ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old, + ngx_atomic_uint_t set); + +ngx_atomic_int_t +ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add); + +/* + * Sun Studio 12 exits with segmentation fault on '__asm ("pause")', + * so ngx_cpu_pause is declared in src/os/unix/ngx_sunpro_x86.il + */ + +void +ngx_cpu_pause(void); + +/* the code in src/os/unix/ngx_sunpro_x86.il */ + +#define ngx_memory_barrier() __asm (".volatile"); __asm (".nonvolatile") + + +#else /* ( __GNUC__ || __INTEL_COMPILER ) */ + +#define NGX_HAVE_ATOMIC_OPS 1 + +#include "ngx_gcc_atomic_x86.h" + +#endif + + +#elif ( __amd64__ || __amd64 ) + +typedef int64_t ngx_atomic_int_t; +typedef uint64_t ngx_atomic_uint_t; +typedef volatile ngx_atomic_uint_t ngx_atomic_t; +#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1) + + +#if ( __SUNPRO_C ) + +#define NGX_HAVE_ATOMIC_OPS 1 + +ngx_atomic_uint_t +ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old, + ngx_atomic_uint_t set); + +ngx_atomic_int_t +ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add); + +/* + * Sun Studio 12 exits with segmentation fault on '__asm ("pause")', + * so ngx_cpu_pause is declared in src/os/unix/ngx_sunpro_amd64.il + */ + +void +ngx_cpu_pause(void); + +/* the code in src/os/unix/ngx_sunpro_amd64.il */ + +#define ngx_memory_barrier() __asm (".volatile"); __asm (".nonvolatile") + + +#else /* ( __GNUC__ || __INTEL_COMPILER ) */ + +#define NGX_HAVE_ATOMIC_OPS 1 + +#include "ngx_gcc_atomic_amd64.h" + +#endif + + +#elif ( __sparc__ || __sparc || __sparcv9 ) + +#if (NGX_PTR_SIZE == 8) + +typedef int64_t ngx_atomic_int_t; +typedef uint64_t ngx_atomic_uint_t; +#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1) + +#else + +typedef int32_t ngx_atomic_int_t; +typedef uint32_t ngx_atomic_uint_t; +#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1) + +#endif + +typedef volatile ngx_atomic_uint_t ngx_atomic_t; + + +#if ( __SUNPRO_C ) + +#define NGX_HAVE_ATOMIC_OPS 1 + +#include "ngx_sunpro_atomic_sparc64.h" + + +#else /* ( __GNUC__ || __INTEL_COMPILER ) */ + +#define NGX_HAVE_ATOMIC_OPS 1 + +#include "ngx_gcc_atomic_sparc64.h" + +#endif + + +#elif ( __powerpc__ || __POWERPC__ ) + +#define NGX_HAVE_ATOMIC_OPS 1 + +#if (NGX_PTR_SIZE == 8) + +typedef int64_t ngx_atomic_int_t; +typedef uint64_t ngx_atomic_uint_t; +#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1) + +#else + +typedef int32_t ngx_atomic_int_t; +typedef uint32_t ngx_atomic_uint_t; +#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1) + +#endif + +typedef volatile ngx_atomic_uint_t ngx_atomic_t; + + +#include "ngx_gcc_atomic_ppc.h" + +#endif + + +#if !(NGX_HAVE_ATOMIC_OPS) + +#define NGX_HAVE_ATOMIC_OPS 0 + +typedef int32_t ngx_atomic_int_t; +typedef uint32_t ngx_atomic_uint_t; +typedef volatile ngx_atomic_uint_t ngx_atomic_t; +#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1) + + +static ngx_inline ngx_atomic_uint_t +ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old, + ngx_atomic_uint_t set) +{ + if (*lock == old) { + *lock = set; + return 1; + } + + return 0; +} + + +static ngx_inline ngx_atomic_int_t +ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add) +{ + ngx_atomic_int_t old; + + old = *value; + *value += add; + + return old; +} + +#define ngx_memory_barrier() +#define ngx_cpu_pause() + +#endif + + +void ngx_spinlock(ngx_atomic_t *lock, ngx_atomic_int_t value, ngx_uint_t spin); + +#define ngx_trylock(lock) (*(lock) == 0 && ngx_atomic_cmp_set(lock, 0, 1)) +#define ngx_unlock(lock) *(lock) = 0 + + +#endif /* _NGX_ATOMIC_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_channel.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_channel.c new file mode 100644 index 000000000..1efa0667c --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_channel.c @@ -0,0 +1,253 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +ngx_int_t +ngx_write_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size, + ngx_log_t *log) +{ + ssize_t n; + ngx_err_t err; + struct iovec iov[1]; + struct msghdr msg; + +#if (NGX_HAVE_MSGHDR_MSG_CONTROL) + + union { + struct cmsghdr cm; + char space[CMSG_SPACE(sizeof(int))]; + } cmsg; + + if (ch->fd == -1) { + msg.msg_control = NULL; + msg.msg_controllen = 0; + + } else { + msg.msg_control = (caddr_t) &cmsg; + msg.msg_controllen = sizeof(cmsg); + + ngx_memzero(&cmsg, sizeof(cmsg)); + + cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int)); + cmsg.cm.cmsg_level = SOL_SOCKET; + cmsg.cm.cmsg_type = SCM_RIGHTS; + + /* + * We have to use ngx_memcpy() instead of simple + * *(int *) CMSG_DATA(&cmsg.cm) = ch->fd; + * because some gcc 4.4 with -O2/3/s optimization issues the warning: + * dereferencing type-punned pointer will break strict-aliasing rules + * + * Fortunately, gcc with -O1 compiles this ngx_memcpy() + * in the same simple assignment as in the code above + */ + + ngx_memcpy(CMSG_DATA(&cmsg.cm), &ch->fd, sizeof(int)); + } + + msg.msg_flags = 0; + +#else + + if (ch->fd == -1) { + msg.msg_accrights = NULL; + msg.msg_accrightslen = 0; + + } else { + msg.msg_accrights = (caddr_t) &ch->fd; + msg.msg_accrightslen = sizeof(int); + } + +#endif + + iov[0].iov_base = (char *) ch; + iov[0].iov_len = size; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + n = sendmsg(s, &msg, 0); + + if (n == -1) { + err = ngx_errno; + if (err == NGX_EAGAIN) { + return NGX_AGAIN; + } + + ngx_log_error(NGX_LOG_ALERT, log, err, "sendmsg() failed"); + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_read_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size, ngx_log_t *log) +{ + ssize_t n; + ngx_err_t err; + struct iovec iov[1]; + struct msghdr msg; + +#if (NGX_HAVE_MSGHDR_MSG_CONTROL) + union { + struct cmsghdr cm; + char space[CMSG_SPACE(sizeof(int))]; + } cmsg; +#else + int fd; +#endif + + iov[0].iov_base = (char *) ch; + iov[0].iov_len = size; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + +#if (NGX_HAVE_MSGHDR_MSG_CONTROL) + msg.msg_control = (caddr_t) &cmsg; + msg.msg_controllen = sizeof(cmsg); +#else + msg.msg_accrights = (caddr_t) &fd; + msg.msg_accrightslen = sizeof(int); +#endif + + n = recvmsg(s, &msg, 0); + + if (n == -1) { + err = ngx_errno; + if (err == NGX_EAGAIN) { + return NGX_AGAIN; + } + + ngx_log_error(NGX_LOG_ALERT, log, err, "recvmsg() failed"); + return NGX_ERROR; + } + + if (n == 0) { + ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, "recvmsg() returned zero"); + return NGX_ERROR; + } + + if ((size_t) n < sizeof(ngx_channel_t)) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "recvmsg() returned not enough data: %z", n); + return NGX_ERROR; + } + +#if (NGX_HAVE_MSGHDR_MSG_CONTROL) + + if (ch->command == NGX_CMD_OPEN_CHANNEL) { + + if (cmsg.cm.cmsg_len < (socklen_t) CMSG_LEN(sizeof(int))) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "recvmsg() returned too small ancillary data"); + return NGX_ERROR; + } + + if (cmsg.cm.cmsg_level != SOL_SOCKET || cmsg.cm.cmsg_type != SCM_RIGHTS) + { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "recvmsg() returned invalid ancillary data " + "level %d or type %d", + cmsg.cm.cmsg_level, cmsg.cm.cmsg_type); + return NGX_ERROR; + } + + /* ch->fd = *(int *) CMSG_DATA(&cmsg.cm); */ + + ngx_memcpy(&ch->fd, CMSG_DATA(&cmsg.cm), sizeof(int)); + } + + if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "recvmsg() truncated data"); + } + +#else + + if (ch->command == NGX_CMD_OPEN_CHANNEL) { + if (msg.msg_accrightslen != sizeof(int)) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "recvmsg() returned no ancillary data"); + return NGX_ERROR; + } + + ch->fd = fd; + } + +#endif + + return n; +} + + +ngx_int_t +ngx_add_channel_event(ngx_cycle_t *cycle, ngx_fd_t fd, ngx_int_t event, + ngx_event_handler_pt handler) +{ + ngx_event_t *ev, *rev, *wev; + ngx_connection_t *c; + + c = ngx_get_connection(fd, cycle->log); + + if (c == NULL) { + return NGX_ERROR; + } + + c->pool = cycle->pool; + + rev = c->read; + wev = c->write; + + rev->log = cycle->log; + wev->log = cycle->log; + + rev->channel = 1; + wev->channel = 1; + + ev = (event == NGX_READ_EVENT) ? rev : wev; + + ev->handler = handler; + + if (ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT) == 0) { + if (ngx_add_conn(c) == NGX_ERROR) { + ngx_free_connection(c); + return NGX_ERROR; + } + + } else { + if (ngx_add_event(ev, event, 0) == NGX_ERROR) { + ngx_free_connection(c); + return NGX_ERROR; + } + } + + return NGX_OK; +} + + +void +ngx_close_channel(ngx_fd_t *fd, ngx_log_t *log) +{ + if (close(fd[0]) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "close() channel failed"); + } + + if (close(fd[1]) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "close() channel failed"); + } +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_channel.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_channel.h new file mode 100644 index 000000000..362cc649a --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_channel.h @@ -0,0 +1,34 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_CHANNEL_H_INCLUDED_ +#define _NGX_CHANNEL_H_INCLUDED_ + + +#include +#include +#include + + +typedef struct { + ngx_uint_t command; + ngx_pid_t pid; + ngx_int_t slot; + ngx_fd_t fd; +} ngx_channel_t; + + +ngx_int_t ngx_write_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size, + ngx_log_t *log); +ngx_int_t ngx_read_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size, + ngx_log_t *log); +ngx_int_t ngx_add_channel_event(ngx_cycle_t *cycle, ngx_fd_t fd, + ngx_int_t event, ngx_event_handler_pt handler); +void ngx_close_channel(ngx_fd_t *fd, ngx_log_t *log); + + +#endif /* _NGX_CHANNEL_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_daemon.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_daemon.c new file mode 100644 index 000000000..385c49b6c --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_daemon.c @@ -0,0 +1,71 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +ngx_int_t +ngx_daemon(ngx_log_t *log) +{ + int fd; + + switch (fork()) { + case -1: + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "fork() failed"); + return NGX_ERROR; + + case 0: + break; + + default: + exit(0); + } + + ngx_parent = ngx_pid; + ngx_pid = ngx_getpid(); + + if (setsid() == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "setsid() failed"); + return NGX_ERROR; + } + + umask(0); + + fd = open("/dev/null", O_RDWR); + if (fd == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "open(\"/dev/null\") failed"); + return NGX_ERROR; + } + + if (dup2(fd, STDIN_FILENO) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDIN) failed"); + return NGX_ERROR; + } + + if (dup2(fd, STDOUT_FILENO) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDOUT) failed"); + return NGX_ERROR; + } + +#if 0 + if (dup2(fd, STDERR_FILENO) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDERR) failed"); + return NGX_ERROR; + } +#endif + + if (fd > STDERR_FILENO) { + if (close(fd) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "close() failed"); + return NGX_ERROR; + } + } + + return NGX_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_darwin.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_darwin.h new file mode 100644 index 000000000..4d01b26ea --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_darwin.h @@ -0,0 +1,23 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_DARWIN_H_INCLUDED_ +#define _NGX_DARWIN_H_INCLUDED_ + + +void ngx_debug_init(void); +ngx_chain_t *ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); + +extern int ngx_darwin_kern_osreldate; +extern int ngx_darwin_hw_ncpu; +extern u_long ngx_darwin_net_inet_tcp_sendspace; + +extern ngx_uint_t ngx_debug_malloc; + + +#endif /* _NGX_DARWIN_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_darwin_config.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_darwin_config.h new file mode 100644 index 000000000..0dfe63374 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_darwin_config.h @@ -0,0 +1,100 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_DARWIN_CONFIG_H_INCLUDED_ +#define _NGX_DARWIN_CONFIG_H_INCLUDED_ + + +#define __APPLE_USE_RFC_3542 /* IPV6_PKTINFO */ + + +#include +#include +#include +#include +#include +#include /* offsetof() */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* statfs() */ + +#include /* FIONBIO */ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include /* TCP_NODELAY */ +#include +#include +#include + +#include +#include + +#include + + +#ifndef IOV_MAX +#define IOV_MAX 64 +#endif + + +#include + + +#if (NGX_HAVE_POSIX_SEM) +#include +#endif + + +#if (NGX_HAVE_POLL) +#include +#endif + + +#if (NGX_HAVE_KQUEUE) +#include +#endif + + +#define NGX_LISTEN_BACKLOG -1 + + +#ifndef NGX_HAVE_INHERITED_NONBLOCK +#define NGX_HAVE_INHERITED_NONBLOCK 1 +#endif + + +#ifndef NGX_HAVE_CASELESS_FILESYSTEM +#define NGX_HAVE_CASELESS_FILESYSTEM 1 +#endif + + +#define NGX_HAVE_OS_SPECIFIC_INIT 1 +#define NGX_HAVE_DEBUG_MALLOC 1 + + +extern char **environ; + + +#endif /* _NGX_DARWIN_CONFIG_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_darwin_init.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_darwin_init.c new file mode 100644 index 000000000..70748ee57 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_darwin_init.c @@ -0,0 +1,204 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +char ngx_darwin_kern_ostype[16]; +char ngx_darwin_kern_osrelease[128]; +int ngx_darwin_hw_ncpu; +int ngx_darwin_kern_ipc_somaxconn; +u_long ngx_darwin_net_inet_tcp_sendspace; +int64_t ngx_darwin_hw_cachelinesize; + +ngx_uint_t ngx_debug_malloc; + + +static ngx_os_io_t ngx_darwin_io = { + ngx_unix_recv, + ngx_readv_chain, + ngx_udp_unix_recv, + ngx_unix_send, + ngx_udp_unix_send, + ngx_udp_unix_sendmsg_chain, +#if (NGX_HAVE_SENDFILE) + ngx_darwin_sendfile_chain, + NGX_IO_SENDFILE +#else + ngx_writev_chain, + 0 +#endif +}; + + +typedef struct { + char *name; + void *value; + size_t size; + ngx_uint_t exists; +} sysctl_t; + + +sysctl_t sysctls[] = { + { "hw.ncpu", + &ngx_darwin_hw_ncpu, + sizeof(ngx_darwin_hw_ncpu), 0 }, + + { "net.inet.tcp.sendspace", + &ngx_darwin_net_inet_tcp_sendspace, + sizeof(ngx_darwin_net_inet_tcp_sendspace), 0 }, + + { "kern.ipc.somaxconn", + &ngx_darwin_kern_ipc_somaxconn, + sizeof(ngx_darwin_kern_ipc_somaxconn), 0 }, + + { "hw.cachelinesize", + &ngx_darwin_hw_cachelinesize, + sizeof(ngx_darwin_hw_cachelinesize), 0 }, + + { NULL, NULL, 0, 0 } +}; + + +void +ngx_debug_init(void) +{ +#if (NGX_DEBUG_MALLOC) + + /* + * MacOSX 10.6, 10.7: MallocScribble fills freed memory with 0x55 + * and fills allocated memory with 0xAA. + * MacOSX 10.4, 10.5: MallocScribble fills freed memory with 0x55, + * MallocPreScribble fills allocated memory with 0xAA. + * MacOSX 10.3: MallocScribble fills freed memory with 0x55, + * and no way to fill allocated memory. + */ + + setenv("MallocScribble", "1", 0); + + ngx_debug_malloc = 1; + +#else + + if (getenv("MallocScribble")) { + ngx_debug_malloc = 1; + } + +#endif +} + + +ngx_int_t +ngx_os_specific_init(ngx_log_t *log) +{ + size_t size; + ngx_err_t err; + ngx_uint_t i; + + size = sizeof(ngx_darwin_kern_ostype); + if (sysctlbyname("kern.ostype", ngx_darwin_kern_ostype, &size, NULL, 0) + == -1) + { + err = ngx_errno; + + if (err != NGX_ENOENT) { + + ngx_log_error(NGX_LOG_ALERT, log, err, + "sysctlbyname(kern.ostype) failed"); + + if (err != NGX_ENOMEM) { + return NGX_ERROR; + } + + ngx_darwin_kern_ostype[size - 1] = '\0'; + } + } + + size = sizeof(ngx_darwin_kern_osrelease); + if (sysctlbyname("kern.osrelease", ngx_darwin_kern_osrelease, &size, + NULL, 0) + == -1) + { + err = ngx_errno; + + if (err != NGX_ENOENT) { + + ngx_log_error(NGX_LOG_ALERT, log, err, + "sysctlbyname(kern.osrelease) failed"); + + if (err != NGX_ENOMEM) { + return NGX_ERROR; + } + + ngx_darwin_kern_osrelease[size - 1] = '\0'; + } + } + + for (i = 0; sysctls[i].name; i++) { + size = sysctls[i].size; + + if (sysctlbyname(sysctls[i].name, sysctls[i].value, &size, NULL, 0) + == 0) + { + sysctls[i].exists = 1; + continue; + } + + err = ngx_errno; + + if (err == NGX_ENOENT) { + continue; + } + + ngx_log_error(NGX_LOG_ALERT, log, err, + "sysctlbyname(%s) failed", sysctls[i].name); + return NGX_ERROR; + } + + ngx_cacheline_size = ngx_darwin_hw_cachelinesize; + ngx_ncpu = ngx_darwin_hw_ncpu; + + if (ngx_darwin_kern_ipc_somaxconn > 32767) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "sysctl kern.ipc.somaxconn must be less than 32768"); + return NGX_ERROR; + } + + ngx_tcp_nodelay_and_tcp_nopush = 1; + + ngx_os_io = ngx_darwin_io; + + return NGX_OK; +} + + +void +ngx_os_specific_status(ngx_log_t *log) +{ + u_long value; + ngx_uint_t i; + + if (ngx_darwin_kern_ostype[0]) { + ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s", + ngx_darwin_kern_ostype, ngx_darwin_kern_osrelease); + } + + for (i = 0; sysctls[i].name; i++) { + if (sysctls[i].exists) { + if (sysctls[i].size == sizeof(long)) { + value = *(long *) sysctls[i].value; + + } else { + value = *(int *) sysctls[i].value; + } + + ngx_log_error(NGX_LOG_NOTICE, log, 0, "%s: %l", + sysctls[i].name, value); + } + } +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_darwin_sendfile_chain.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_darwin_sendfile_chain.c new file mode 100644 index 000000000..2a76c154d --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_darwin_sendfile_chain.c @@ -0,0 +1,206 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +/* + * It seems that Darwin 9.4 (Mac OS X 1.5) sendfile() has the same + * old bug as early FreeBSD sendfile() syscall: + * http://bugs.freebsd.org/33771 + * + * Besides sendfile() has another bug: if one calls sendfile() + * with both a header and a trailer, then sendfile() ignores a file part + * at all and sends only the header and the trailer together. + * For this reason we send a trailer only if there is no a header. + * + * Although sendfile() allows to pass a header or a trailer, + * it may send the header or the trailer and a part of the file + * in different packets. And FreeBSD workaround (TCP_NOPUSH option) + * does not help. + */ + + +ngx_chain_t * +ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) +{ + int rc; + off_t send, prev_send, sent; + off_t file_size; + ssize_t n; + ngx_uint_t eintr; + ngx_err_t err; + ngx_buf_t *file; + ngx_event_t *wev; + ngx_chain_t *cl; + ngx_iovec_t header, trailer; + struct sf_hdtr hdtr; + struct iovec headers[NGX_IOVS_PREALLOCATE]; + struct iovec trailers[NGX_IOVS_PREALLOCATE]; + + wev = c->write; + + if (!wev->ready) { + return in; + } + +#if (NGX_HAVE_KQUEUE) + + if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) { + (void) ngx_connection_error(c, wev->kq_errno, + "kevent() reported about an closed connection"); + wev->error = 1; + return NGX_CHAIN_ERROR; + } + +#endif + + /* the maximum limit size is the maximum size_t value - the page size */ + + if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) { + limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize; + } + + send = 0; + + header.iovs = headers; + header.nalloc = NGX_IOVS_PREALLOCATE; + + trailer.iovs = trailers; + trailer.nalloc = NGX_IOVS_PREALLOCATE; + + for ( ;; ) { + eintr = 0; + prev_send = send; + + /* create the header iovec and coalesce the neighbouring bufs */ + + cl = ngx_output_chain_to_iovec(&header, in, limit - send, c->log); + + if (cl == NGX_CHAIN_ERROR) { + return NGX_CHAIN_ERROR; + } + + send += header.size; + + if (cl && cl->buf->in_file && send < limit) { + file = cl->buf; + + /* coalesce the neighbouring file bufs */ + + file_size = ngx_chain_coalesce_file(&cl, limit - send); + + send += file_size; + + if (header.count == 0 && send < limit) { + + /* + * create the trailer iovec and coalesce the neighbouring bufs + */ + + cl = ngx_output_chain_to_iovec(&trailer, cl, limit - send, + c->log); + if (cl == NGX_CHAIN_ERROR) { + return NGX_CHAIN_ERROR; + } + + send += trailer.size; + + } else { + trailer.count = 0; + } + + /* + * sendfile() returns EINVAL if sf_hdtr's count is 0, + * but corresponding pointer is not NULL + */ + + hdtr.headers = header.count ? header.iovs : NULL; + hdtr.hdr_cnt = header.count; + hdtr.trailers = trailer.count ? trailer.iovs : NULL; + hdtr.trl_cnt = trailer.count; + + sent = header.size + file_size; + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendfile: @%O %O h:%uz", + file->file_pos, sent, header.size); + + rc = sendfile(file->file->fd, c->fd, file->file_pos, + &sent, &hdtr, 0); + + if (rc == -1) { + err = ngx_errno; + + switch (err) { + case NGX_EAGAIN: + break; + + case NGX_EINTR: + eintr = 1; + break; + + default: + wev->error = 1; + (void) ngx_connection_error(c, err, "sendfile() failed"); + return NGX_CHAIN_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendfile() sent only %O bytes", sent); + } + + if (rc == 0 && sent == 0) { + + /* + * if rc and sent equal to zero, then someone + * has truncated the file, so the offset became beyond + * the end of the file + */ + + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "sendfile() reported that \"%s\" was truncated", + file->file->name.data); + + return NGX_CHAIN_ERROR; + } + + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendfile: %d, @%O %O:%O", + rc, file->file_pos, sent, file_size + header.size); + + } else { + n = ngx_writev(c, &header); + + if (n == NGX_ERROR) { + return NGX_CHAIN_ERROR; + } + + sent = (n == NGX_AGAIN) ? 0 : n; + } + + c->sent += sent; + + in = ngx_chain_update_sent(in, sent); + + if (eintr) { + send = prev_send + sent; + continue; + } + + if (send - prev_send != sent) { + wev->ready = 0; + return in; + } + + if (send >= limit || in == NULL) { + return in; + } + } +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_dlopen.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_dlopen.c new file mode 100644 index 000000000..a0efc6925 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_dlopen.c @@ -0,0 +1,28 @@ + +/* + * Copyright (C) Maxim Dounin + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +#if (NGX_HAVE_DLOPEN) + +char * +ngx_dlerror(void) +{ + char *err; + + err = (char *) dlerror(); + + if (err == NULL) { + return ""; + } + + return err; +} + +#endif diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_dlopen.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_dlopen.h new file mode 100644 index 000000000..7a3159f43 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_dlopen.h @@ -0,0 +1,31 @@ + +/* + * Copyright (C) Maxim Dounin + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_DLOPEN_H_INCLUDED_ +#define _NGX_DLOPEN_H_INCLUDED_ + + +#include +#include + + +#define ngx_dlopen(path) dlopen((char *) path, RTLD_NOW | RTLD_GLOBAL) +#define ngx_dlopen_n "dlopen()" + +#define ngx_dlsym(handle, symbol) dlsym(handle, symbol) +#define ngx_dlsym_n "dlsym()" + +#define ngx_dlclose(handle) dlclose(handle) +#define ngx_dlclose_n "dlclose()" + + +#if (NGX_HAVE_DLOPEN) +char *ngx_dlerror(void); +#endif + + +#endif /* _NGX_DLOPEN_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_errno.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_errno.c new file mode 100644 index 000000000..ca23b2d3f --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_errno.c @@ -0,0 +1,210 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +static ngx_str_t ngx_unknown_error = ngx_string("Unknown error"); + + +#if (NGX_HAVE_STRERRORDESC_NP) + +/* + * The strerrordesc_np() function, introduced in glibc 2.32, is + * async-signal-safe. This makes it possible to use it directly, + * without copying error messages. + */ + + +u_char * +ngx_strerror(ngx_err_t err, u_char *errstr, size_t size) +{ + size_t len; + const char *msg; + + msg = strerrordesc_np(err); + + if (msg == NULL) { + msg = (char *) ngx_unknown_error.data; + len = ngx_unknown_error.len; + + } else { + len = ngx_strlen(msg); + } + + size = ngx_min(size, len); + + return ngx_cpymem(errstr, msg, size); +} + + +ngx_int_t +ngx_strerror_init(void) +{ + return NGX_OK; +} + + +#else + +/* + * The strerror() messages are copied because: + * + * 1) strerror() and strerror_r() functions are not Async-Signal-Safe, + * therefore, they cannot be used in signal handlers; + * + * 2) a direct sys_errlist[] array may be used instead of these functions, + * but Linux linker warns about its usage: + * + * warning: `sys_errlist' is deprecated; use `strerror' or `strerror_r' instead + * warning: `sys_nerr' is deprecated; use `strerror' or `strerror_r' instead + * + * causing false bug reports. + */ + + +static ngx_str_t *ngx_sys_errlist; +static ngx_err_t ngx_first_error; +static ngx_err_t ngx_last_error; + + +u_char * +ngx_strerror(ngx_err_t err, u_char *errstr, size_t size) +{ + ngx_str_t *msg; + + if (err >= ngx_first_error && err < ngx_last_error) { + msg = &ngx_sys_errlist[err - ngx_first_error]; + + } else { + msg = &ngx_unknown_error; + } + + size = ngx_min(size, msg->len); + + return ngx_cpymem(errstr, msg->data, size); +} + + +ngx_int_t +ngx_strerror_init(void) +{ + char *msg; + u_char *p; + size_t len; + ngx_err_t err; + +#if (NGX_SYS_NERR) + ngx_first_error = 0; + ngx_last_error = NGX_SYS_NERR; + +#elif (EPERM > 1000 && EPERM < 0x7fffffff - 1000) + + /* + * If number of errors is not known, and EPERM error code has large + * but reasonable value, guess possible error codes based on the error + * messages returned by strerror(), starting from EPERM. Notably, + * this covers GNU/Hurd, where errors start at 0x40000001. + */ + + for (err = EPERM; err > EPERM - 1000; err--) { + ngx_set_errno(0); + msg = strerror(err); + + if (errno == EINVAL + || msg == NULL + || strncmp(msg, "Unknown error", 13) == 0) + { + continue; + } + + ngx_first_error = err; + } + + for (err = EPERM; err < EPERM + 1000; err++) { + ngx_set_errno(0); + msg = strerror(err); + + if (errno == EINVAL + || msg == NULL + || strncmp(msg, "Unknown error", 13) == 0) + { + continue; + } + + ngx_last_error = err + 1; + } + +#else + + /* + * If number of errors is not known, guess it based on the error + * messages returned by strerror(). + */ + + ngx_first_error = 0; + + for (err = 0; err < 1000; err++) { + ngx_set_errno(0); + msg = strerror(err); + + if (errno == EINVAL + || msg == NULL + || strncmp(msg, "Unknown error", 13) == 0) + { + continue; + } + + ngx_last_error = err + 1; + } + +#endif + + /* + * ngx_strerror() is not ready to work at this stage, therefore, + * malloc() is used and possible errors are logged using strerror(). + */ + + len = (ngx_last_error - ngx_first_error) * sizeof(ngx_str_t); + + ngx_sys_errlist = malloc(len); + if (ngx_sys_errlist == NULL) { + goto failed; + } + + for (err = ngx_first_error; err < ngx_last_error; err++) { + msg = strerror(err); + + if (msg == NULL) { + ngx_sys_errlist[err - ngx_first_error] = ngx_unknown_error; + continue; + } + + len = ngx_strlen(msg); + + p = malloc(len); + if (p == NULL) { + goto failed; + } + + ngx_memcpy(p, msg, len); + ngx_sys_errlist[err - ngx_first_error].len = len; + ngx_sys_errlist[err - ngx_first_error].data = p; + } + + return NGX_OK; + +failed: + + err = errno; + ngx_log_stderr(0, "malloc(%uz) failed (%d: %s)", len, err, strerror(err)); + + return NGX_ERROR; +} + +#endif diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_errno.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_errno.h new file mode 100644 index 000000000..07fa8ced1 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_errno.h @@ -0,0 +1,80 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_ERRNO_H_INCLUDED_ +#define _NGX_ERRNO_H_INCLUDED_ + + +#include +#include + + +typedef int ngx_err_t; + +#define NGX_EPERM EPERM +#define NGX_ENOENT ENOENT +#define NGX_ENOPATH ENOENT +#define NGX_ESRCH ESRCH +#define NGX_EINTR EINTR +#define NGX_ECHILD ECHILD +#define NGX_ENOMEM ENOMEM +#define NGX_EACCES EACCES +#define NGX_EBUSY EBUSY +#define NGX_EEXIST EEXIST +#define NGX_EEXIST_FILE EEXIST +#define NGX_EXDEV EXDEV +#define NGX_ENOTDIR ENOTDIR +#define NGX_EISDIR EISDIR +#define NGX_EINVAL EINVAL +#define NGX_ENFILE ENFILE +#define NGX_EMFILE EMFILE +#define NGX_ENOSPC ENOSPC +#define NGX_EPIPE EPIPE +#define NGX_EINPROGRESS EINPROGRESS +#define NGX_ENOPROTOOPT ENOPROTOOPT +#define NGX_EOPNOTSUPP EOPNOTSUPP +#define NGX_EADDRINUSE EADDRINUSE +#define NGX_ECONNABORTED ECONNABORTED +#define NGX_ECONNRESET ECONNRESET +#define NGX_ENOTCONN ENOTCONN +#define NGX_ETIMEDOUT ETIMEDOUT +#define NGX_ECONNREFUSED ECONNREFUSED +#define NGX_ENAMETOOLONG ENAMETOOLONG +#define NGX_ENETDOWN ENETDOWN +#define NGX_ENETUNREACH ENETUNREACH +#define NGX_EHOSTDOWN EHOSTDOWN +#define NGX_EHOSTUNREACH EHOSTUNREACH +#define NGX_ENOSYS ENOSYS +#define NGX_ECANCELED ECANCELED +#define NGX_EILSEQ EILSEQ +#define NGX_ENOMOREFILES 0 +#define NGX_ELOOP ELOOP +#define NGX_EBADF EBADF +#define NGX_EMSGSIZE EMSGSIZE + +#if (NGX_HAVE_OPENAT) +#define NGX_EMLINK EMLINK +#endif + +#if (__hpux__) +#define NGX_EAGAIN EWOULDBLOCK +#else +#define NGX_EAGAIN EAGAIN +#endif + + +#define ngx_errno errno +#define ngx_socket_errno errno +#define ngx_set_errno(err) errno = err +#define ngx_set_socket_errno(err) errno = err + + +u_char *ngx_strerror(ngx_err_t err, u_char *errstr, size_t size); +ngx_int_t ngx_strerror_init(void); + + +#endif /* _NGX_ERRNO_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_file_aio_read.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_file_aio_read.c new file mode 100644 index 000000000..bb60ee824 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_file_aio_read.c @@ -0,0 +1,216 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +/* + * FreeBSD file AIO features and quirks: + * + * if an asked data are already in VM cache, then aio_error() returns 0, + * and the data are already copied in buffer; + * + * aio_read() preread in VM cache as minimum 16K (probably BKVASIZE); + * the first AIO preload may be up to 128K; + * + * aio_read/aio_error() may return EINPROGRESS for just written data; + * + * kqueue EVFILT_AIO filter is level triggered only: an event repeats + * until aio_return() will be called; + * + * aio_cancel() cannot cancel file AIO: it returns AIO_NOTCANCELED always. + */ + + +extern int ngx_kqueue; + + +static ssize_t ngx_file_aio_result(ngx_file_t *file, ngx_event_aio_t *aio, + ngx_event_t *ev); +static void ngx_file_aio_event_handler(ngx_event_t *ev); + + +ngx_int_t +ngx_file_aio_init(ngx_file_t *file, ngx_pool_t *pool) +{ + ngx_event_aio_t *aio; + + aio = ngx_pcalloc(pool, sizeof(ngx_event_aio_t)); + if (aio == NULL) { + return NGX_ERROR; + } + + aio->file = file; + aio->fd = file->fd; + aio->event.data = aio; + aio->event.ready = 1; + aio->event.log = file->log; + + file->aio = aio; + + return NGX_OK; +} + + +ssize_t +ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset, + ngx_pool_t *pool) +{ + int n; + ngx_event_t *ev; + ngx_event_aio_t *aio; + + if (!ngx_file_aio) { + return ngx_read_file(file, buf, size, offset); + } + + if (file->aio == NULL && ngx_file_aio_init(file, pool) != NGX_OK) { + return NGX_ERROR; + } + + aio = file->aio; + ev = &aio->event; + + if (!ev->ready) { + ngx_log_error(NGX_LOG_ALERT, file->log, 0, + "second aio post for \"%V\"", &file->name); + return NGX_AGAIN; + } + + ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0, + "aio complete:%d @%O:%uz %V", + ev->complete, offset, size, &file->name); + + if (ev->complete) { + ev->complete = 0; + ngx_set_errno(aio->err); + + if (aio->err == 0) { + return aio->nbytes; + } + + ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, + "aio read \"%s\" failed", file->name.data); + + return NGX_ERROR; + } + + ngx_memzero(&aio->aiocb, sizeof(struct aiocb)); + + aio->aiocb.aio_fildes = file->fd; + aio->aiocb.aio_offset = offset; + aio->aiocb.aio_buf = buf; + aio->aiocb.aio_nbytes = size; +#if (NGX_HAVE_KQUEUE) + aio->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue; + aio->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT; + aio->aiocb.aio_sigevent.sigev_value.sival_ptr = ev; +#endif + ev->handler = ngx_file_aio_event_handler; + + n = aio_read(&aio->aiocb); + + if (n == -1) { + n = ngx_errno; + + if (n == NGX_EAGAIN) { + return ngx_read_file(file, buf, size, offset); + } + + ngx_log_error(NGX_LOG_CRIT, file->log, n, + "aio_read(\"%V\") failed", &file->name); + + if (n == NGX_ENOSYS) { + ngx_file_aio = 0; + return ngx_read_file(file, buf, size, offset); + } + + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0, + "aio_read: fd:%d %d", file->fd, n); + + ev->active = 1; + ev->ready = 0; + ev->complete = 0; + + return ngx_file_aio_result(aio->file, aio, ev); +} + + +static ssize_t +ngx_file_aio_result(ngx_file_t *file, ngx_event_aio_t *aio, ngx_event_t *ev) +{ + int n; + ngx_err_t err; + + n = aio_error(&aio->aiocb); + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0, + "aio_error: fd:%d %d", file->fd, n); + + if (n == -1) { + err = ngx_errno; + aio->err = err; + + ngx_log_error(NGX_LOG_ALERT, file->log, err, + "aio_error(\"%V\") failed", &file->name); + return NGX_ERROR; + } + + if (n == NGX_EINPROGRESS) { + if (ev->ready) { + ev->ready = 0; + ngx_log_error(NGX_LOG_ALERT, file->log, n, + "aio_read(\"%V\") still in progress", + &file->name); + } + + return NGX_AGAIN; + } + + n = aio_return(&aio->aiocb); + + if (n == -1) { + err = ngx_errno; + aio->err = err; + ev->ready = 1; + + ngx_log_error(NGX_LOG_CRIT, file->log, err, + "aio_return(\"%V\") failed", &file->name); + return NGX_ERROR; + } + + aio->err = 0; + aio->nbytes = n; + ev->ready = 1; + ev->active = 0; + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0, + "aio_return: fd:%d %d", file->fd, n); + + return n; +} + + +static void +ngx_file_aio_event_handler(ngx_event_t *ev) +{ + ngx_event_aio_t *aio; + + aio = ev->data; + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, ev->log, 0, + "aio event handler fd:%d %V", aio->fd, &aio->file->name); + + if (ngx_file_aio_result(aio->file, aio, ev) != NGX_AGAIN) { + aio->handler(ev); + } +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_files.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_files.c new file mode 100644 index 000000000..2fec1ece1 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_files.c @@ -0,0 +1,956 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +#if (NGX_THREADS) +#include +static void ngx_thread_read_handler(void *data, ngx_log_t *log); +static void ngx_thread_write_chain_to_file_handler(void *data, ngx_log_t *log); +#endif + +static ngx_chain_t *ngx_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *cl); +static ssize_t ngx_writev_file(ngx_file_t *file, ngx_iovec_t *vec, + off_t offset); + + +#if (NGX_HAVE_FILE_AIO) + +ngx_uint_t ngx_file_aio = 1; + +#endif + + +ssize_t +ngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset) +{ + ssize_t n; + + ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0, + "read: %d, %p, %uz, %O", file->fd, buf, size, offset); + +#if (NGX_HAVE_PREAD) + + n = pread(file->fd, buf, size, offset); + + if (n == -1) { + ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, + "pread() \"%s\" failed", file->name.data); + return NGX_ERROR; + } + +#else + + if (file->sys_offset != offset) { + if (lseek(file->fd, offset, SEEK_SET) == -1) { + ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, + "lseek() \"%s\" failed", file->name.data); + return NGX_ERROR; + } + + file->sys_offset = offset; + } + + n = read(file->fd, buf, size); + + if (n == -1) { + ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, + "read() \"%s\" failed", file->name.data); + return NGX_ERROR; + } + + file->sys_offset += n; + +#endif + + file->offset += n; + + return n; +} + + +#if (NGX_THREADS) + +typedef struct { + ngx_fd_t fd; + ngx_uint_t write; /* unsigned write:1; */ + + u_char *buf; + size_t size; + ngx_chain_t *chain; + off_t offset; + + size_t nbytes; + ngx_err_t err; +} ngx_thread_file_ctx_t; + + +ssize_t +ngx_thread_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset, + ngx_pool_t *pool) +{ + ngx_thread_task_t *task; + ngx_thread_file_ctx_t *ctx; + + ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0, + "thread read: %d, %p, %uz, %O", + file->fd, buf, size, offset); + + task = file->thread_task; + + if (task == NULL) { + task = ngx_thread_task_alloc(pool, sizeof(ngx_thread_file_ctx_t)); + if (task == NULL) { + return NGX_ERROR; + } + + task->event.log = file->log; + + file->thread_task = task; + } + + ctx = task->ctx; + + if (task->event.complete) { + task->event.complete = 0; + + if (ctx->write) { + ngx_log_error(NGX_LOG_ALERT, file->log, 0, + "invalid thread call, read instead of write"); + return NGX_ERROR; + } + + if (ctx->err) { + ngx_log_error(NGX_LOG_CRIT, file->log, ctx->err, + "pread() \"%s\" failed", file->name.data); + return NGX_ERROR; + } + + return ctx->nbytes; + } + + task->handler = ngx_thread_read_handler; + + ctx->write = 0; + + ctx->fd = file->fd; + ctx->buf = buf; + ctx->size = size; + ctx->offset = offset; + + if (file->thread_handler(task, file) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_AGAIN; +} + + +#if (NGX_HAVE_PREAD) + +static void +ngx_thread_read_handler(void *data, ngx_log_t *log) +{ + ngx_thread_file_ctx_t *ctx = data; + + ssize_t n; + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, "thread read handler"); + + n = pread(ctx->fd, ctx->buf, ctx->size, ctx->offset); + + if (n == -1) { + ctx->err = ngx_errno; + + } else { + ctx->nbytes = n; + ctx->err = 0; + } + +#if 0 + ngx_time_update(); +#endif + + ngx_log_debug4(NGX_LOG_DEBUG_CORE, log, 0, + "pread: %z (err: %d) of %uz @%O", + n, ctx->err, ctx->size, ctx->offset); +} + +#else + +#error pread() is required! + +#endif + +#endif /* NGX_THREADS */ + + +ssize_t +ngx_write_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset) +{ + ssize_t n, written; + ngx_err_t err; + + ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0, + "write: %d, %p, %uz, %O", file->fd, buf, size, offset); + + written = 0; + +#if (NGX_HAVE_PWRITE) + + for ( ;; ) { + n = pwrite(file->fd, buf + written, size, offset); + + if (n == -1) { + err = ngx_errno; + + if (err == NGX_EINTR) { + ngx_log_debug0(NGX_LOG_DEBUG_CORE, file->log, err, + "pwrite() was interrupted"); + continue; + } + + ngx_log_error(NGX_LOG_CRIT, file->log, err, + "pwrite() \"%s\" failed", file->name.data); + return NGX_ERROR; + } + + file->offset += n; + written += n; + + if ((size_t) n == size) { + return written; + } + + offset += n; + size -= n; + } + +#else + + if (file->sys_offset != offset) { + if (lseek(file->fd, offset, SEEK_SET) == -1) { + ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, + "lseek() \"%s\" failed", file->name.data); + return NGX_ERROR; + } + + file->sys_offset = offset; + } + + for ( ;; ) { + n = write(file->fd, buf + written, size); + + if (n == -1) { + err = ngx_errno; + + if (err == NGX_EINTR) { + ngx_log_debug0(NGX_LOG_DEBUG_CORE, file->log, err, + "write() was interrupted"); + continue; + } + + ngx_log_error(NGX_LOG_CRIT, file->log, err, + "write() \"%s\" failed", file->name.data); + return NGX_ERROR; + } + + file->sys_offset += n; + file->offset += n; + written += n; + + if ((size_t) n == size) { + return written; + } + + size -= n; + } +#endif +} + + +ngx_fd_t +ngx_open_tempfile(u_char *name, ngx_uint_t persistent, ngx_uint_t access) +{ + ngx_fd_t fd; + + fd = open((const char *) name, O_CREAT|O_EXCL|O_RDWR, + access ? access : 0600); + + if (fd != -1 && !persistent) { + (void) unlink((const char *) name); + } + + return fd; +} + + +ssize_t +ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *cl, off_t offset, + ngx_pool_t *pool) +{ + ssize_t total, n; + ngx_iovec_t vec; + struct iovec iovs[NGX_IOVS_PREALLOCATE]; + + /* use pwrite() if there is the only buf in a chain */ + + if (cl->next == NULL) { + return ngx_write_file(file, cl->buf->pos, + (size_t) (cl->buf->last - cl->buf->pos), + offset); + } + + total = 0; + + vec.iovs = iovs; + vec.nalloc = NGX_IOVS_PREALLOCATE; + + do { + /* create the iovec and coalesce the neighbouring bufs */ + cl = ngx_chain_to_iovec(&vec, cl); + + /* use pwrite() if there is the only iovec buffer */ + + if (vec.count == 1) { + n = ngx_write_file(file, (u_char *) iovs[0].iov_base, + iovs[0].iov_len, offset); + + if (n == NGX_ERROR) { + return n; + } + + return total + n; + } + + n = ngx_writev_file(file, &vec, offset); + + if (n == NGX_ERROR) { + return n; + } + + offset += n; + total += n; + + } while (cl); + + return total; +} + + +static ngx_chain_t * +ngx_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *cl) +{ + size_t total, size; + u_char *prev; + ngx_uint_t n; + struct iovec *iov; + + iov = NULL; + prev = NULL; + total = 0; + n = 0; + + for ( /* void */ ; cl; cl = cl->next) { + + if (ngx_buf_special(cl->buf)) { + continue; + } + + size = cl->buf->last - cl->buf->pos; + + if (prev == cl->buf->pos) { + iov->iov_len += size; + + } else { + if (n == vec->nalloc) { + break; + } + + iov = &vec->iovs[n++]; + + iov->iov_base = (void *) cl->buf->pos; + iov->iov_len = size; + } + + prev = cl->buf->pos + size; + total += size; + } + + vec->count = n; + vec->size = total; + + return cl; +} + + +static ssize_t +ngx_writev_file(ngx_file_t *file, ngx_iovec_t *vec, off_t offset) +{ + ssize_t n; + ngx_err_t err; + + ngx_log_debug3(NGX_LOG_DEBUG_CORE, file->log, 0, + "writev: %d, %uz, %O", file->fd, vec->size, offset); + +#if (NGX_HAVE_PWRITEV) + +eintr: + + n = pwritev(file->fd, vec->iovs, vec->count, offset); + + if (n == -1) { + err = ngx_errno; + + if (err == NGX_EINTR) { + ngx_log_debug0(NGX_LOG_DEBUG_CORE, file->log, err, + "pwritev() was interrupted"); + goto eintr; + } + + ngx_log_error(NGX_LOG_CRIT, file->log, err, + "pwritev() \"%s\" failed", file->name.data); + return NGX_ERROR; + } + + if ((size_t) n != vec->size) { + ngx_log_error(NGX_LOG_CRIT, file->log, 0, + "pwritev() \"%s\" has written only %z of %uz", + file->name.data, n, vec->size); + return NGX_ERROR; + } + +#else + + if (file->sys_offset != offset) { + if (lseek(file->fd, offset, SEEK_SET) == -1) { + ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, + "lseek() \"%s\" failed", file->name.data); + return NGX_ERROR; + } + + file->sys_offset = offset; + } + +eintr: + + n = writev(file->fd, vec->iovs, vec->count); + + if (n == -1) { + err = ngx_errno; + + if (err == NGX_EINTR) { + ngx_log_debug0(NGX_LOG_DEBUG_CORE, file->log, err, + "writev() was interrupted"); + goto eintr; + } + + ngx_log_error(NGX_LOG_CRIT, file->log, err, + "writev() \"%s\" failed", file->name.data); + return NGX_ERROR; + } + + if ((size_t) n != vec->size) { + ngx_log_error(NGX_LOG_CRIT, file->log, 0, + "writev() \"%s\" has written only %z of %uz", + file->name.data, n, vec->size); + return NGX_ERROR; + } + + file->sys_offset += n; + +#endif + + file->offset += n; + + return n; +} + + +#if (NGX_THREADS) + +ssize_t +ngx_thread_write_chain_to_file(ngx_file_t *file, ngx_chain_t *cl, off_t offset, + ngx_pool_t *pool) +{ + ngx_thread_task_t *task; + ngx_thread_file_ctx_t *ctx; + + ngx_log_debug3(NGX_LOG_DEBUG_CORE, file->log, 0, + "thread write chain: %d, %p, %O", + file->fd, cl, offset); + + task = file->thread_task; + + if (task == NULL) { + task = ngx_thread_task_alloc(pool, + sizeof(ngx_thread_file_ctx_t)); + if (task == NULL) { + return NGX_ERROR; + } + + task->event.log = file->log; + + file->thread_task = task; + } + + ctx = task->ctx; + + if (task->event.complete) { + task->event.complete = 0; + + if (!ctx->write) { + ngx_log_error(NGX_LOG_ALERT, file->log, 0, + "invalid thread call, write instead of read"); + return NGX_ERROR; + } + + if (ctx->err || ctx->nbytes == 0) { + ngx_log_error(NGX_LOG_CRIT, file->log, ctx->err, + "pwritev() \"%s\" failed", file->name.data); + return NGX_ERROR; + } + + file->offset += ctx->nbytes; + return ctx->nbytes; + } + + task->handler = ngx_thread_write_chain_to_file_handler; + + ctx->write = 1; + + ctx->fd = file->fd; + ctx->chain = cl; + ctx->offset = offset; + + if (file->thread_handler(task, file) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_AGAIN; +} + + +static void +ngx_thread_write_chain_to_file_handler(void *data, ngx_log_t *log) +{ + ngx_thread_file_ctx_t *ctx = data; + +#if (NGX_HAVE_PWRITEV) + + off_t offset; + ssize_t n; + ngx_err_t err; + ngx_chain_t *cl; + ngx_iovec_t vec; + struct iovec iovs[NGX_IOVS_PREALLOCATE]; + + vec.iovs = iovs; + vec.nalloc = NGX_IOVS_PREALLOCATE; + + cl = ctx->chain; + offset = ctx->offset; + + ctx->nbytes = 0; + ctx->err = 0; + + do { + /* create the iovec and coalesce the neighbouring bufs */ + cl = ngx_chain_to_iovec(&vec, cl); + +eintr: + + n = pwritev(ctx->fd, iovs, vec.count, offset); + + if (n == -1) { + err = ngx_errno; + + if (err == NGX_EINTR) { + ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, err, + "pwritev() was interrupted"); + goto eintr; + } + + ctx->err = err; + return; + } + + if ((size_t) n != vec.size) { + ctx->nbytes = 0; + return; + } + + ctx->nbytes += n; + offset += n; + } while (cl); + +#else + + ctx->err = NGX_ENOSYS; + return; + +#endif +} + +#endif /* NGX_THREADS */ + + +ngx_int_t +ngx_set_file_time(u_char *name, ngx_fd_t fd, time_t s) +{ + struct timeval tv[2]; + + tv[0].tv_sec = ngx_time(); + tv[0].tv_usec = 0; + tv[1].tv_sec = s; + tv[1].tv_usec = 0; + + if (utimes((char *) name, tv) != -1) { + return NGX_OK; + } + + return NGX_ERROR; +} + + +ngx_int_t +ngx_create_file_mapping(ngx_file_mapping_t *fm) +{ + fm->fd = ngx_open_file(fm->name, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, + NGX_FILE_DEFAULT_ACCESS); + + if (fm->fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno, + ngx_open_file_n " \"%s\" failed", fm->name); + return NGX_ERROR; + } + + if (ftruncate(fm->fd, fm->size) == -1) { + ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno, + "ftruncate() \"%s\" failed", fm->name); + goto failed; + } + + fm->addr = mmap(NULL, fm->size, PROT_READ|PROT_WRITE, MAP_SHARED, + fm->fd, 0); + if (fm->addr != MAP_FAILED) { + return NGX_OK; + } + + ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno, + "mmap(%uz) \"%s\" failed", fm->size, fm->name); + +failed: + + if (ngx_close_file(fm->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", fm->name); + } + + return NGX_ERROR; +} + + +void +ngx_close_file_mapping(ngx_file_mapping_t *fm) +{ + if (munmap(fm->addr, fm->size) == -1) { + ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno, + "munmap(%uz) \"%s\" failed", fm->size, fm->name); + } + + if (ngx_close_file(fm->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", fm->name); + } +} + + +ngx_int_t +ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir) +{ + dir->dir = opendir((const char *) name->data); + + if (dir->dir == NULL) { + return NGX_ERROR; + } + + dir->valid_info = 0; + + return NGX_OK; +} + + +ngx_int_t +ngx_read_dir(ngx_dir_t *dir) +{ + dir->de = readdir(dir->dir); + + if (dir->de) { +#if (NGX_HAVE_D_TYPE) + dir->type = dir->de->d_type; +#else + dir->type = 0; +#endif + return NGX_OK; + } + + return NGX_ERROR; +} + + +ngx_int_t +ngx_open_glob(ngx_glob_t *gl) +{ + int n; + + n = glob((char *) gl->pattern, 0, NULL, &gl->pglob); + + if (n == 0) { + return NGX_OK; + } + +#ifdef GLOB_NOMATCH + + if (n == GLOB_NOMATCH && gl->test) { + return NGX_OK; + } + +#endif + + return NGX_ERROR; +} + + +ngx_int_t +ngx_read_glob(ngx_glob_t *gl, ngx_str_t *name) +{ + size_t count; + +#ifdef GLOB_NOMATCH + count = (size_t) gl->pglob.gl_pathc; +#else + count = (size_t) gl->pglob.gl_matchc; +#endif + + if (gl->n < count) { + + name->len = (size_t) ngx_strlen(gl->pglob.gl_pathv[gl->n]); + name->data = (u_char *) gl->pglob.gl_pathv[gl->n]; + gl->n++; + + return NGX_OK; + } + + return NGX_DONE; +} + + +void +ngx_close_glob(ngx_glob_t *gl) +{ + globfree(&gl->pglob); +} + + +ngx_err_t +ngx_trylock_fd(ngx_fd_t fd) +{ + struct flock fl; + + ngx_memzero(&fl, sizeof(struct flock)); + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + + if (fcntl(fd, F_SETLK, &fl) == -1) { + return ngx_errno; + } + + return 0; +} + + +ngx_err_t +ngx_lock_fd(ngx_fd_t fd) +{ + struct flock fl; + + ngx_memzero(&fl, sizeof(struct flock)); + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + + if (fcntl(fd, F_SETLKW, &fl) == -1) { + return ngx_errno; + } + + return 0; +} + + +ngx_err_t +ngx_unlock_fd(ngx_fd_t fd) +{ + struct flock fl; + + ngx_memzero(&fl, sizeof(struct flock)); + fl.l_type = F_UNLCK; + fl.l_whence = SEEK_SET; + + if (fcntl(fd, F_SETLK, &fl) == -1) { + return ngx_errno; + } + + return 0; +} + + +#if (NGX_HAVE_POSIX_FADVISE) && !(NGX_HAVE_F_READAHEAD) + +ngx_int_t +ngx_read_ahead(ngx_fd_t fd, size_t n) +{ + int err; + + err = posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL); + + if (err == 0) { + return 0; + } + + ngx_set_errno(err); + return NGX_FILE_ERROR; +} + +#endif + + +#if (NGX_HAVE_O_DIRECT) + +ngx_int_t +ngx_directio_on(ngx_fd_t fd) +{ + int flags; + + flags = fcntl(fd, F_GETFL); + + if (flags == -1) { + return NGX_FILE_ERROR; + } + + return fcntl(fd, F_SETFL, flags | O_DIRECT); +} + + +ngx_int_t +ngx_directio_off(ngx_fd_t fd) +{ + int flags; + + flags = fcntl(fd, F_GETFL); + + if (flags == -1) { + return NGX_FILE_ERROR; + } + + return fcntl(fd, F_SETFL, flags & ~O_DIRECT); +} + +#endif + + +#if (NGX_HAVE_STATFS) + +size_t +ngx_fs_bsize(u_char *name) +{ + struct statfs fs; + + if (statfs((char *) name, &fs) == -1) { + return 512; + } + + if ((fs.f_bsize % 512) != 0) { + return 512; + } + +#if (NGX_LINUX) + if ((size_t) fs.f_bsize > ngx_pagesize) { + return 512; + } +#endif + + return (size_t) fs.f_bsize; +} + + +off_t +ngx_fs_available(u_char *name) +{ + struct statfs fs; + + if (statfs((char *) name, &fs) == -1) { + return NGX_MAX_OFF_T_VALUE; + } + + return (off_t) fs.f_bavail * fs.f_bsize; +} + +#elif (NGX_HAVE_STATVFS) + +size_t +ngx_fs_bsize(u_char *name) +{ + struct statvfs fs; + + if (statvfs((char *) name, &fs) == -1) { + return 512; + } + + if ((fs.f_frsize % 512) != 0) { + return 512; + } + +#if (NGX_LINUX) + if ((size_t) fs.f_frsize > ngx_pagesize) { + return 512; + } +#endif + + return (size_t) fs.f_frsize; +} + + +off_t +ngx_fs_available(u_char *name) +{ + struct statvfs fs; + + if (statvfs((char *) name, &fs) == -1) { + return NGX_MAX_OFF_T_VALUE; + } + + return (off_t) fs.f_bavail * fs.f_frsize; +} + +#else + +size_t +ngx_fs_bsize(u_char *name) +{ + return 512; +} + + +off_t +ngx_fs_available(u_char *name) +{ + return NGX_MAX_OFF_T_VALUE; +} + +#endif diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_files.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_files.h new file mode 100644 index 000000000..d084713b6 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_files.h @@ -0,0 +1,396 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_FILES_H_INCLUDED_ +#define _NGX_FILES_H_INCLUDED_ + + +#include +#include + + +typedef int ngx_fd_t; +typedef struct stat ngx_file_info_t; +typedef ino_t ngx_file_uniq_t; + + +typedef struct { + u_char *name; + size_t size; + void *addr; + ngx_fd_t fd; + ngx_log_t *log; +} ngx_file_mapping_t; + + +typedef struct { + DIR *dir; + struct dirent *de; + struct stat info; + + unsigned type:8; + unsigned valid_info:1; +} ngx_dir_t; + + +typedef struct { + size_t n; + glob_t pglob; + u_char *pattern; + ngx_log_t *log; + ngx_uint_t test; +} ngx_glob_t; + + +#define NGX_INVALID_FILE -1 +#define NGX_FILE_ERROR -1 + + + +#ifdef __CYGWIN__ + +#ifndef NGX_HAVE_CASELESS_FILESYSTEM +#define NGX_HAVE_CASELESS_FILESYSTEM 1 +#endif + +#define ngx_open_file(name, mode, create, access) \ + open((const char *) name, mode|create|O_BINARY, access) + +#else + +#define ngx_open_file(name, mode, create, access) \ + open((const char *) name, mode|create, access) + +#endif + +#define ngx_open_file_n "open()" + +#define NGX_FILE_RDONLY O_RDONLY +#define NGX_FILE_WRONLY O_WRONLY +#define NGX_FILE_RDWR O_RDWR +#define NGX_FILE_CREATE_OR_OPEN O_CREAT +#define NGX_FILE_OPEN 0 +#define NGX_FILE_TRUNCATE (O_CREAT|O_TRUNC) +#define NGX_FILE_APPEND (O_WRONLY|O_APPEND) +#define NGX_FILE_NONBLOCK O_NONBLOCK + +#if (NGX_HAVE_OPENAT) +#define NGX_FILE_NOFOLLOW O_NOFOLLOW + +#if defined(O_DIRECTORY) +#define NGX_FILE_DIRECTORY O_DIRECTORY +#else +#define NGX_FILE_DIRECTORY 0 +#endif + +#if defined(O_SEARCH) +#define NGX_FILE_SEARCH (O_SEARCH|NGX_FILE_DIRECTORY) + +#elif defined(O_EXEC) +#define NGX_FILE_SEARCH (O_EXEC|NGX_FILE_DIRECTORY) + +#elif (NGX_HAVE_O_PATH) +#define NGX_FILE_SEARCH (O_PATH|O_RDONLY|NGX_FILE_DIRECTORY) + +#else +#define NGX_FILE_SEARCH (O_RDONLY|NGX_FILE_DIRECTORY) +#endif + +#endif /* NGX_HAVE_OPENAT */ + +#define NGX_FILE_DEFAULT_ACCESS 0644 +#define NGX_FILE_OWNER_ACCESS 0600 + + +#define ngx_close_file close +#define ngx_close_file_n "close()" + + +#define ngx_delete_file(name) unlink((const char *) name) +#define ngx_delete_file_n "unlink()" + + +ngx_fd_t ngx_open_tempfile(u_char *name, ngx_uint_t persistent, + ngx_uint_t access); +#define ngx_open_tempfile_n "open()" + + +ssize_t ngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset); +#if (NGX_HAVE_PREAD) +#define ngx_read_file_n "pread()" +#else +#define ngx_read_file_n "read()" +#endif + +ssize_t ngx_write_file(ngx_file_t *file, u_char *buf, size_t size, + off_t offset); + +ssize_t ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *ce, + off_t offset, ngx_pool_t *pool); + + +#define ngx_read_fd read +#define ngx_read_fd_n "read()" + +/* + * we use inlined function instead of simple #define + * because glibc 2.3 sets warn_unused_result attribute for write() + * and in this case gcc 4.3 ignores (void) cast + */ +static ngx_inline ssize_t +ngx_write_fd(ngx_fd_t fd, void *buf, size_t n) +{ + return write(fd, buf, n); +} + +#define ngx_write_fd_n "write()" + + +#define ngx_write_console ngx_write_fd + + +#define ngx_linefeed(p) *p++ = LF; +#define NGX_LINEFEED_SIZE 1 +#define NGX_LINEFEED "\x0a" + + +#define ngx_rename_file(o, n) rename((const char *) o, (const char *) n) +#define ngx_rename_file_n "rename()" + + +#define ngx_change_file_access(n, a) chmod((const char *) n, a) +#define ngx_change_file_access_n "chmod()" + + +ngx_int_t ngx_set_file_time(u_char *name, ngx_fd_t fd, time_t s); +#define ngx_set_file_time_n "utimes()" + + +#define ngx_file_info(file, sb) stat((const char *) file, sb) +#define ngx_file_info_n "stat()" + +#define ngx_fd_info(fd, sb) fstat(fd, sb) +#define ngx_fd_info_n "fstat()" + +#define ngx_link_info(file, sb) lstat((const char *) file, sb) +#define ngx_link_info_n "lstat()" + +#define ngx_is_dir(sb) (S_ISDIR((sb)->st_mode)) +#define ngx_is_file(sb) (S_ISREG((sb)->st_mode)) +#define ngx_is_link(sb) (S_ISLNK((sb)->st_mode)) +#define ngx_is_exec(sb) (((sb)->st_mode & S_IXUSR) == S_IXUSR) +#define ngx_file_access(sb) ((sb)->st_mode & 0777) +#define ngx_file_size(sb) (sb)->st_size +#define ngx_file_fs_size(sb) \ + (((sb)->st_blocks * 512 > (sb)->st_size \ + && (sb)->st_blocks * 512 < (sb)->st_size + 8 * (sb)->st_blksize) \ + ? (sb)->st_blocks * 512 : (sb)->st_size) +#define ngx_file_mtime(sb) (sb)->st_mtime +#define ngx_file_uniq(sb) (sb)->st_ino + + +ngx_int_t ngx_create_file_mapping(ngx_file_mapping_t *fm); +void ngx_close_file_mapping(ngx_file_mapping_t *fm); + + +#define ngx_realpath(p, r) (u_char *) realpath((char *) p, (char *) r) +#define ngx_realpath_n "realpath()" +#define ngx_getcwd(buf, size) (getcwd((char *) buf, size) != NULL) +#define ngx_getcwd_n "getcwd()" +#define ngx_path_separator(c) ((c) == '/') + + +#if defined(PATH_MAX) + +#define NGX_HAVE_MAX_PATH 1 +#define NGX_MAX_PATH PATH_MAX + +#else + +#define NGX_MAX_PATH 4096 + +#endif + + +ngx_int_t ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir); +#define ngx_open_dir_n "opendir()" + + +#define ngx_close_dir(d) closedir((d)->dir) +#define ngx_close_dir_n "closedir()" + + +ngx_int_t ngx_read_dir(ngx_dir_t *dir); +#define ngx_read_dir_n "readdir()" + + +#define ngx_create_dir(name, access) mkdir((const char *) name, access) +#define ngx_create_dir_n "mkdir()" + + +#define ngx_delete_dir(name) rmdir((const char *) name) +#define ngx_delete_dir_n "rmdir()" + + +#define ngx_dir_access(a) (a | (a & 0444) >> 2) + + +#define ngx_de_name(dir) ((u_char *) (dir)->de->d_name) +#if (NGX_HAVE_D_NAMLEN) +#define ngx_de_namelen(dir) (dir)->de->d_namlen +#else +#define ngx_de_namelen(dir) ngx_strlen((dir)->de->d_name) +#endif + +static ngx_inline ngx_int_t +ngx_de_info(u_char *name, ngx_dir_t *dir) +{ + dir->type = 0; + return stat((const char *) name, &dir->info); +} + +#define ngx_de_info_n "stat()" +#define ngx_de_link_info(name, dir) lstat((const char *) name, &(dir)->info) +#define ngx_de_link_info_n "lstat()" + +#if (NGX_HAVE_D_TYPE) + +/* + * some file systems (e.g. XFS on Linux and CD9660 on FreeBSD) + * do not set dirent.d_type + */ + +#define ngx_de_is_dir(dir) \ + (((dir)->type) ? ((dir)->type == DT_DIR) : (S_ISDIR((dir)->info.st_mode))) +#define ngx_de_is_file(dir) \ + (((dir)->type) ? ((dir)->type == DT_REG) : (S_ISREG((dir)->info.st_mode))) +#define ngx_de_is_link(dir) \ + (((dir)->type) ? ((dir)->type == DT_LNK) : (S_ISLNK((dir)->info.st_mode))) + +#else + +#define ngx_de_is_dir(dir) (S_ISDIR((dir)->info.st_mode)) +#define ngx_de_is_file(dir) (S_ISREG((dir)->info.st_mode)) +#define ngx_de_is_link(dir) (S_ISLNK((dir)->info.st_mode)) + +#endif + +#define ngx_de_access(dir) (((dir)->info.st_mode) & 0777) +#define ngx_de_size(dir) (dir)->info.st_size +#define ngx_de_fs_size(dir) \ + ngx_max((dir)->info.st_size, (dir)->info.st_blocks * 512) +#define ngx_de_mtime(dir) (dir)->info.st_mtime + + +ngx_int_t ngx_open_glob(ngx_glob_t *gl); +#define ngx_open_glob_n "glob()" +ngx_int_t ngx_read_glob(ngx_glob_t *gl, ngx_str_t *name); +void ngx_close_glob(ngx_glob_t *gl); + + +ngx_err_t ngx_trylock_fd(ngx_fd_t fd); +ngx_err_t ngx_lock_fd(ngx_fd_t fd); +ngx_err_t ngx_unlock_fd(ngx_fd_t fd); + +#define ngx_trylock_fd_n "fcntl(F_SETLK, F_WRLCK)" +#define ngx_lock_fd_n "fcntl(F_SETLKW, F_WRLCK)" +#define ngx_unlock_fd_n "fcntl(F_SETLK, F_UNLCK)" + + +#if (NGX_HAVE_F_READAHEAD) + +#define NGX_HAVE_READ_AHEAD 1 + +#define ngx_read_ahead(fd, n) fcntl(fd, F_READAHEAD, (int) n) +#define ngx_read_ahead_n "fcntl(fd, F_READAHEAD)" + +#elif (NGX_HAVE_POSIX_FADVISE) + +#define NGX_HAVE_READ_AHEAD 1 + +ngx_int_t ngx_read_ahead(ngx_fd_t fd, size_t n); +#define ngx_read_ahead_n "posix_fadvise(POSIX_FADV_SEQUENTIAL)" + +#else + +#define ngx_read_ahead(fd, n) 0 +#define ngx_read_ahead_n "ngx_read_ahead_n" + +#endif + + +#if (NGX_HAVE_O_DIRECT) + +ngx_int_t ngx_directio_on(ngx_fd_t fd); +#define ngx_directio_on_n "fcntl(O_DIRECT)" + +ngx_int_t ngx_directio_off(ngx_fd_t fd); +#define ngx_directio_off_n "fcntl(!O_DIRECT)" + +#elif (NGX_HAVE_F_NOCACHE) + +#define ngx_directio_on(fd) fcntl(fd, F_NOCACHE, 1) +#define ngx_directio_on_n "fcntl(F_NOCACHE, 1)" + +#elif (NGX_HAVE_DIRECTIO) + +#define ngx_directio_on(fd) directio(fd, DIRECTIO_ON) +#define ngx_directio_on_n "directio(DIRECTIO_ON)" + +#else + +#define ngx_directio_on(fd) 0 +#define ngx_directio_on_n "ngx_directio_on_n" + +#endif + +size_t ngx_fs_bsize(u_char *name); +off_t ngx_fs_available(u_char *name); + + +#if (NGX_HAVE_OPENAT) + +#define ngx_openat_file(fd, name, mode, create, access) \ + openat(fd, (const char *) name, mode|create, access) + +#define ngx_openat_file_n "openat()" + +#define ngx_file_at_info(fd, name, sb, flag) \ + fstatat(fd, (const char *) name, sb, flag) + +#define ngx_file_at_info_n "fstatat()" + +#define NGX_AT_FDCWD (ngx_fd_t) AT_FDCWD + +#endif + + +#define ngx_stdout STDOUT_FILENO +#define ngx_stderr STDERR_FILENO +#define ngx_set_stderr(fd) dup2(fd, STDERR_FILENO) +#define ngx_set_stderr_n "dup2(STDERR_FILENO)" + + +#if (NGX_HAVE_FILE_AIO) + +ngx_int_t ngx_file_aio_init(ngx_file_t *file, ngx_pool_t *pool); +ssize_t ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size, + off_t offset, ngx_pool_t *pool); + +extern ngx_uint_t ngx_file_aio; + +#endif + +#if (NGX_THREADS) +ssize_t ngx_thread_read(ngx_file_t *file, u_char *buf, size_t size, + off_t offset, ngx_pool_t *pool); +ssize_t ngx_thread_write_chain_to_file(ngx_file_t *file, ngx_chain_t *cl, + off_t offset, ngx_pool_t *pool); +#endif + + +#endif /* _NGX_FILES_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_freebsd.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_freebsd.h new file mode 100644 index 000000000..4f93da55c --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_freebsd.h @@ -0,0 +1,25 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_FREEBSD_H_INCLUDED_ +#define _NGX_FREEBSD_H_INCLUDED_ + + +void ngx_debug_init(void); +ngx_chain_t *ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); + +extern int ngx_freebsd_kern_osreldate; +extern int ngx_freebsd_hw_ncpu; +extern u_long ngx_freebsd_net_inet_tcp_sendspace; + +extern ngx_uint_t ngx_freebsd_sendfile_nbytes_bug; +extern ngx_uint_t ngx_freebsd_use_tcp_nopush; +extern ngx_uint_t ngx_debug_malloc; + + +#endif /* _NGX_FREEBSD_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_freebsd_config.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_freebsd_config.h new file mode 100644 index 000000000..815342974 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_freebsd_config.h @@ -0,0 +1,129 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_FREEBSD_CONFIG_H_INCLUDED_ +#define _NGX_FREEBSD_CONFIG_H_INCLUDED_ + + +#include +#include +#include +#include +#include /* offsetof() */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* ALIGN() */ +#include /* statfs() */ + +#include /* FIONBIO */ +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include /* TCP_NODELAY, TCP_NOPUSH */ +#include +#include +#include + +#include /* setproctitle() before 4.1 */ +#include +#include + +#include + + +#if __FreeBSD_version < 400017 + +/* + * FreeBSD 3.x has no CMSG_SPACE() and CMSG_LEN() and has the broken CMSG_DATA() + */ + +#undef CMSG_SPACE +#define CMSG_SPACE(l) (ALIGN(sizeof(struct cmsghdr)) + ALIGN(l)) + +#undef CMSG_LEN +#define CMSG_LEN(l) (ALIGN(sizeof(struct cmsghdr)) + (l)) + +#undef CMSG_DATA +#define CMSG_DATA(cmsg) ((u_char *)(cmsg) + ALIGN(sizeof(struct cmsghdr))) + +#endif + + +#include + + +#if (NGX_HAVE_POSIX_SEM) +#include +#endif + + +#if (NGX_HAVE_POLL) +#include +#endif + + +#if (NGX_HAVE_KQUEUE) +#include +#endif + + +#if (NGX_HAVE_FILE_AIO) + +#include +typedef struct aiocb ngx_aiocb_t; + +#if (__FreeBSD_version < 700005 && !defined __DragonFly__) +#define sival_ptr sigval_ptr +#endif + +#endif + + +#define NGX_LISTEN_BACKLOG -1 + + +#if (defined __DragonFly__ && __DragonFly_version < 500702) +#define NGX_KEEPALIVE_FACTOR 1000 +#endif + + +#ifndef IOV_MAX +#define IOV_MAX 1024 +#endif + + +#ifndef NGX_HAVE_INHERITED_NONBLOCK +#define NGX_HAVE_INHERITED_NONBLOCK 1 +#endif + + +#define NGX_HAVE_OS_SPECIFIC_INIT 1 +#define NGX_HAVE_DEBUG_MALLOC 1 + + +extern char **environ; +extern char *malloc_options; + + +#endif /* _NGX_FREEBSD_CONFIG_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_freebsd_init.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_freebsd_init.c new file mode 100644 index 000000000..1823f0281 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_freebsd_init.c @@ -0,0 +1,262 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +/* FreeBSD 3.0 at least */ +char ngx_freebsd_kern_ostype[16]; +char ngx_freebsd_kern_osrelease[128]; +int ngx_freebsd_kern_osreldate; +int ngx_freebsd_hw_ncpu; +int ngx_freebsd_kern_ipc_somaxconn; +u_long ngx_freebsd_net_inet_tcp_sendspace; + +/* FreeBSD 4.9 */ +int ngx_freebsd_machdep_hlt_logical_cpus; + + +ngx_uint_t ngx_freebsd_sendfile_nbytes_bug; +ngx_uint_t ngx_freebsd_use_tcp_nopush; + +ngx_uint_t ngx_debug_malloc; + + +static ngx_os_io_t ngx_freebsd_io = { + ngx_unix_recv, + ngx_readv_chain, + ngx_udp_unix_recv, + ngx_unix_send, + ngx_udp_unix_send, + ngx_udp_unix_sendmsg_chain, +#if (NGX_HAVE_SENDFILE) + ngx_freebsd_sendfile_chain, + NGX_IO_SENDFILE +#else + ngx_writev_chain, + 0 +#endif +}; + + +typedef struct { + char *name; + void *value; + size_t size; + ngx_uint_t exists; +} sysctl_t; + + +sysctl_t sysctls[] = { + { "hw.ncpu", + &ngx_freebsd_hw_ncpu, + sizeof(ngx_freebsd_hw_ncpu), 0 }, + + { "machdep.hlt_logical_cpus", + &ngx_freebsd_machdep_hlt_logical_cpus, + sizeof(ngx_freebsd_machdep_hlt_logical_cpus), 0 }, + + { "net.inet.tcp.sendspace", + &ngx_freebsd_net_inet_tcp_sendspace, + sizeof(ngx_freebsd_net_inet_tcp_sendspace), 0 }, + + { "kern.ipc.somaxconn", + &ngx_freebsd_kern_ipc_somaxconn, + sizeof(ngx_freebsd_kern_ipc_somaxconn), 0 }, + + { NULL, NULL, 0, 0 } +}; + + +void +ngx_debug_init(void) +{ +#if (NGX_DEBUG_MALLOC) + +#if __FreeBSD_version >= 500014 && __FreeBSD_version < 1000011 + _malloc_options = "J"; +#elif __FreeBSD_version < 500014 + malloc_options = "J"; +#endif + + ngx_debug_malloc = 1; + +#else + char *mo; + + mo = getenv("MALLOC_OPTIONS"); + + if (mo && ngx_strchr(mo, 'J')) { + ngx_debug_malloc = 1; + } +#endif +} + + +ngx_int_t +ngx_os_specific_init(ngx_log_t *log) +{ + int version; + size_t size; + ngx_err_t err; + ngx_uint_t i; + + size = sizeof(ngx_freebsd_kern_ostype); + if (sysctlbyname("kern.ostype", + ngx_freebsd_kern_ostype, &size, NULL, 0) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "sysctlbyname(kern.ostype) failed"); + + if (ngx_errno != NGX_ENOMEM) { + return NGX_ERROR; + } + + ngx_freebsd_kern_ostype[size - 1] = '\0'; + } + + size = sizeof(ngx_freebsd_kern_osrelease); + if (sysctlbyname("kern.osrelease", + ngx_freebsd_kern_osrelease, &size, NULL, 0) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "sysctlbyname(kern.osrelease) failed"); + + if (ngx_errno != NGX_ENOMEM) { + return NGX_ERROR; + } + + ngx_freebsd_kern_osrelease[size - 1] = '\0'; + } + + + size = sizeof(int); + if (sysctlbyname("kern.osreldate", + &ngx_freebsd_kern_osreldate, &size, NULL, 0) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "sysctlbyname(kern.osreldate) failed"); + return NGX_ERROR; + } + + version = ngx_freebsd_kern_osreldate; + + +#if (NGX_HAVE_SENDFILE) + + /* + * The determination of the sendfile() "nbytes bug" is complex enough. + * There are two sendfile() syscalls: a new #393 has no bug while + * an old #336 has the bug in some versions and has not in others. + * Besides libc_r wrapper also emulates the bug in some versions. + * There is no way to say exactly if syscall #336 in FreeBSD circa 4.6 + * has the bug. We use the algorithm that is correct at least for + * RELEASEs and for syscalls only (not libc_r wrapper). + * + * 4.6.1-RELEASE and below have the bug + * 4.6.2-RELEASE and above have the new syscall + * + * We detect the new sendfile() syscall available at the compile time + * to allow an old binary to run correctly on an updated FreeBSD system. + */ + +#if (__FreeBSD__ == 4 && __FreeBSD_version >= 460102) \ + || __FreeBSD_version == 460002 || __FreeBSD_version >= 500039 + + /* a new syscall without the bug */ + + ngx_freebsd_sendfile_nbytes_bug = 0; + +#else + + /* an old syscall that may have the bug */ + + ngx_freebsd_sendfile_nbytes_bug = 1; + +#endif + +#endif /* NGX_HAVE_SENDFILE */ + + + if ((version < 500000 && version >= 440003) || version >= 500017) { + ngx_freebsd_use_tcp_nopush = 1; + } + + + for (i = 0; sysctls[i].name; i++) { + size = sysctls[i].size; + + if (sysctlbyname(sysctls[i].name, sysctls[i].value, &size, NULL, 0) + == 0) + { + sysctls[i].exists = 1; + continue; + } + + err = ngx_errno; + + if (err == NGX_ENOENT) { + continue; + } + + ngx_log_error(NGX_LOG_ALERT, log, err, + "sysctlbyname(%s) failed", sysctls[i].name); + return NGX_ERROR; + } + + if (ngx_freebsd_machdep_hlt_logical_cpus) { + ngx_ncpu = ngx_freebsd_hw_ncpu / 2; + + } else { + ngx_ncpu = ngx_freebsd_hw_ncpu; + } + + if (version < 600008 && ngx_freebsd_kern_ipc_somaxconn > 32767) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "sysctl kern.ipc.somaxconn must be less than 32768"); + return NGX_ERROR; + } + + ngx_tcp_nodelay_and_tcp_nopush = 1; + + ngx_os_io = ngx_freebsd_io; + + return NGX_OK; +} + + +void +ngx_os_specific_status(ngx_log_t *log) +{ + u_long value; + ngx_uint_t i; + + ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s", + ngx_freebsd_kern_ostype, ngx_freebsd_kern_osrelease); + +#ifdef __DragonFly_version + ngx_log_error(NGX_LOG_NOTICE, log, 0, + "kern.osreldate: %d, built on %d", + ngx_freebsd_kern_osreldate, __DragonFly_version); +#else + ngx_log_error(NGX_LOG_NOTICE, log, 0, + "kern.osreldate: %d, built on %d", + ngx_freebsd_kern_osreldate, __FreeBSD_version); +#endif + + for (i = 0; sysctls[i].name; i++) { + if (sysctls[i].exists) { + if (sysctls[i].size == sizeof(long)) { + value = *(long *) sysctls[i].value; + + } else { + value = *(int *) sysctls[i].value; + } + + ngx_log_error(NGX_LOG_NOTICE, log, 0, "%s: %l", + sysctls[i].name, value); + } + } +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_freebsd_sendfile_chain.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_freebsd_sendfile_chain.c new file mode 100644 index 000000000..5c6a830d8 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_freebsd_sendfile_chain.c @@ -0,0 +1,308 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +/* + * Although FreeBSD sendfile() allows to pass a header and a trailer, + * it cannot send a header with a part of the file in one packet until + * FreeBSD 5.3. Besides, over the fast ethernet connection sendfile() + * may send the partially filled packets, i.e. the 8 file pages may be sent + * as the 11 full 1460-bytes packets, then one incomplete 324-bytes packet, + * and then again the 11 full 1460-bytes packets. + * + * Therefore we use the TCP_NOPUSH option (similar to Linux's TCP_CORK) + * to postpone the sending - it not only sends a header and the first part of + * the file in one packet, but also sends the file pages in the full packets. + * + * But until FreeBSD 4.5 turning TCP_NOPUSH off does not flush a pending + * data that less than MSS, so that data may be sent with 5 second delay. + * So we do not use TCP_NOPUSH on FreeBSD prior to 4.5, although it can be used + * for non-keepalive HTTP connections. + */ + + +ngx_chain_t * +ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) +{ + int rc, flags; + off_t send, prev_send, sent; + size_t file_size; + ssize_t n; + ngx_err_t err; + ngx_buf_t *file; + ngx_uint_t eintr, eagain; +#if (NGX_HAVE_SENDFILE_NODISKIO) + ngx_uint_t ebusy; +#endif + ngx_event_t *wev; + ngx_chain_t *cl; + ngx_iovec_t header, trailer; + struct sf_hdtr hdtr; + struct iovec headers[NGX_IOVS_PREALLOCATE]; + struct iovec trailers[NGX_IOVS_PREALLOCATE]; + + wev = c->write; + + if (!wev->ready) { + return in; + } + +#if (NGX_HAVE_KQUEUE) + + if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) { + (void) ngx_connection_error(c, wev->kq_errno, + "kevent() reported about an closed connection"); + wev->error = 1; + return NGX_CHAIN_ERROR; + } + +#endif + + /* the maximum limit size is the maximum size_t value - the page size */ + + if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) { + limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize; + } + + send = 0; + eagain = 0; + flags = 0; + + header.iovs = headers; + header.nalloc = NGX_IOVS_PREALLOCATE; + + trailer.iovs = trailers; + trailer.nalloc = NGX_IOVS_PREALLOCATE; + + for ( ;; ) { + eintr = 0; +#if (NGX_HAVE_SENDFILE_NODISKIO) + ebusy = 0; +#endif + prev_send = send; + + /* create the header iovec and coalesce the neighbouring bufs */ + + cl = ngx_output_chain_to_iovec(&header, in, limit - send, c->log); + + if (cl == NGX_CHAIN_ERROR) { + return NGX_CHAIN_ERROR; + } + + send += header.size; + + if (cl && cl->buf->in_file && send < limit) { + file = cl->buf; + + /* coalesce the neighbouring file bufs */ + + file_size = (size_t) ngx_chain_coalesce_file(&cl, limit - send); + + send += file_size; + + if (send < limit) { + + /* + * create the trailer iovec and coalesce the neighbouring bufs + */ + + cl = ngx_output_chain_to_iovec(&trailer, cl, limit - send, + c->log); + if (cl == NGX_CHAIN_ERROR) { + return NGX_CHAIN_ERROR; + } + + send += trailer.size; + + } else { + trailer.count = 0; + } + + if (ngx_freebsd_use_tcp_nopush + && c->tcp_nopush == NGX_TCP_NOPUSH_UNSET) + { + if (ngx_tcp_nopush(c->fd) == -1) { + err = ngx_socket_errno; + + /* + * there is a tiny chance to be interrupted, however, + * we continue a processing without the TCP_NOPUSH + */ + + if (err != NGX_EINTR) { + wev->error = 1; + (void) ngx_connection_error(c, err, + ngx_tcp_nopush_n " failed"); + return NGX_CHAIN_ERROR; + } + + } else { + c->tcp_nopush = NGX_TCP_NOPUSH_SET; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, + "tcp_nopush"); + } + } + + /* + * sendfile() does unneeded work if sf_hdtr's count is 0, + * but corresponding pointer is not NULL + */ + + hdtr.headers = header.count ? header.iovs : NULL; + hdtr.hdr_cnt = header.count; + hdtr.trailers = trailer.count ? trailer.iovs : NULL; + hdtr.trl_cnt = trailer.count; + + /* + * the "nbytes bug" of the old sendfile() syscall: + * http://bugs.freebsd.org/33771 + */ + + if (!ngx_freebsd_sendfile_nbytes_bug) { + header.size = 0; + } + + sent = 0; + +#if (NGX_HAVE_SENDFILE_NODISKIO) + + flags = (c->busy_count <= 2) ? SF_NODISKIO : 0; + + if (file->file->directio) { + flags |= SF_NOCACHE; + } + +#endif + + rc = sendfile(file->file->fd, c->fd, file->file_pos, + file_size + header.size, &hdtr, &sent, flags); + + if (rc == -1) { + err = ngx_errno; + + switch (err) { + case NGX_EAGAIN: + eagain = 1; + break; + + case NGX_EINTR: + eintr = 1; + break; + +#if (NGX_HAVE_SENDFILE_NODISKIO) + case NGX_EBUSY: + ebusy = 1; + break; +#endif + + default: + wev->error = 1; + (void) ngx_connection_error(c, err, "sendfile() failed"); + return NGX_CHAIN_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendfile() sent only %O bytes", sent); + + /* + * sendfile() in FreeBSD 3.x-4.x may return value >= 0 + * on success, although only 0 is documented + */ + + } else if (rc >= 0 && sent == 0) { + + /* + * if rc is OK and sent equal to zero, then someone + * has truncated the file, so the offset became beyond + * the end of the file + */ + + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "sendfile() reported that \"%s\" was truncated at %O", + file->file->name.data, file->file_pos); + + return NGX_CHAIN_ERROR; + } + + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendfile: %d, @%O %O:%uz", + rc, file->file_pos, sent, file_size + header.size); + + } else { + n = ngx_writev(c, &header); + + if (n == NGX_ERROR) { + return NGX_CHAIN_ERROR; + } + + sent = (n == NGX_AGAIN) ? 0 : n; + } + + c->sent += sent; + + in = ngx_chain_update_sent(in, sent); + +#if (NGX_HAVE_SENDFILE_NODISKIO) + + if (ebusy) { + if (sent == 0) { + c->busy_count++; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendfile() busy, count:%d", c->busy_count); + + } else { + c->busy_count = 0; + } + + if (wev->posted) { + ngx_delete_posted_event(wev); + } + + ngx_post_event(wev, &ngx_posted_next_events); + + wev->ready = 0; + return in; + } + + c->busy_count = 0; + +#endif + + if (eagain) { + + /* + * sendfile() may return EAGAIN, even if it has sent a whole file + * part, it indicates that the successive sendfile() call would + * return EAGAIN right away and would not send anything. + * We use it as a hint. + */ + + wev->ready = 0; + return in; + } + + if (eintr) { + send = prev_send + sent; + continue; + } + + if (send - prev_send != sent) { + wev->ready = 0; + return in; + } + + if (send >= limit || in == NULL) { + return in; + } + } +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_gcc_atomic_amd64.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_gcc_atomic_amd64.h new file mode 100644 index 000000000..159a29742 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_gcc_atomic_amd64.h @@ -0,0 +1,82 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#if (NGX_SMP) +#define NGX_SMP_LOCK "lock;" +#else +#define NGX_SMP_LOCK +#endif + + +/* + * "cmpxchgq r, [m]": + * + * if (rax == [m]) { + * zf = 1; + * [m] = r; + * } else { + * zf = 0; + * rax = [m]; + * } + * + * + * The "r" is any register, %rax (%r0) - %r16. + * The "=a" and "a" are the %rax register. + * Although we can return result in any register, we use "a" because it is + * used in cmpxchgq anyway. The result is actually in %al but not in $rax, + * however as the code is inlined gcc can test %al as well as %rax. + * + * The "cc" means that flags were changed. + */ + +static ngx_inline ngx_atomic_uint_t +ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old, + ngx_atomic_uint_t set) +{ + u_char res; + + __asm__ volatile ( + + NGX_SMP_LOCK + " cmpxchgq %3, %1; " + " sete %0; " + + : "=a" (res) : "m" (*lock), "a" (old), "r" (set) : "cc", "memory"); + + return res; +} + + +/* + * "xaddq r, [m]": + * + * temp = [m]; + * [m] += r; + * r = temp; + * + * + * The "+r" is any register, %rax (%r0) - %r16. + * The "cc" means that flags were changed. + */ + +static ngx_inline ngx_atomic_int_t +ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add) +{ + __asm__ volatile ( + + NGX_SMP_LOCK + " xaddq %0, %1; " + + : "+r" (add) : "m" (*value) : "cc", "memory"); + + return add; +} + + +#define ngx_memory_barrier() __asm__ volatile ("" ::: "memory") + +#define ngx_cpu_pause() __asm__ ("pause") diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_gcc_atomic_ppc.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_gcc_atomic_ppc.h new file mode 100644 index 000000000..45afc4b9e --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_gcc_atomic_ppc.h @@ -0,0 +1,155 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +/* + * The ppc assembler treats ";" as comment, so we have to use "\n". + * The minus in "bne-" is a hint for the branch prediction unit that + * this branch is unlikely to be taken. + * The "1b" means the nearest backward label "1" and the "1f" means + * the nearest forward label "1". + * + * The "b" means that the base registers can be used only, i.e. + * any register except r0. The r0 register always has a zero value and + * could not be used in "addi r0, r0, 1". + * The "=&b" means that no input registers can be used. + * + * "sync" read and write barriers + * "isync" read barrier, is faster than "sync" + * "eieio" write barrier, is faster than "sync" + * "lwsync" write barrier, is faster than "eieio" on ppc64 + */ + +#if (NGX_PTR_SIZE == 8) + +static ngx_inline ngx_atomic_uint_t +ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old, + ngx_atomic_uint_t set) +{ + ngx_atomic_uint_t res, temp; + + __asm__ volatile ( + + " li %0, 0 \n" /* preset "0" to "res" */ + " lwsync \n" /* write barrier */ + "1: \n" + " ldarx %1, 0, %2 \n" /* load from [lock] into "temp" */ + /* and store reservation */ + " cmpd %1, %3 \n" /* compare "temp" and "old" */ + " bne- 2f \n" /* not equal */ + " stdcx. %4, 0, %2 \n" /* store "set" into [lock] if reservation */ + /* is not cleared */ + " bne- 1b \n" /* the reservation was cleared */ + " isync \n" /* read barrier */ + " li %0, 1 \n" /* set "1" to "res" */ + "2: \n" + + : "=&b" (res), "=&b" (temp) + : "b" (lock), "b" (old), "b" (set) + : "cc", "memory"); + + return res; +} + + +static ngx_inline ngx_atomic_int_t +ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add) +{ + ngx_atomic_uint_t res, temp; + + __asm__ volatile ( + + " lwsync \n" /* write barrier */ + "1: ldarx %0, 0, %2 \n" /* load from [value] into "res" */ + /* and store reservation */ + " add %1, %0, %3 \n" /* "res" + "add" store in "temp" */ + " stdcx. %1, 0, %2 \n" /* store "temp" into [value] if reservation */ + /* is not cleared */ + " bne- 1b \n" /* try again if reservation was cleared */ + " isync \n" /* read barrier */ + + : "=&b" (res), "=&b" (temp) + : "b" (value), "b" (add) + : "cc", "memory"); + + return res; +} + + +#if (NGX_SMP) +#define ngx_memory_barrier() \ + __asm__ volatile ("isync \n lwsync \n" ::: "memory") +#else +#define ngx_memory_barrier() __asm__ volatile ("" ::: "memory") +#endif + +#else + +static ngx_inline ngx_atomic_uint_t +ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old, + ngx_atomic_uint_t set) +{ + ngx_atomic_uint_t res, temp; + + __asm__ volatile ( + + " li %0, 0 \n" /* preset "0" to "res" */ + " eieio \n" /* write barrier */ + "1: \n" + " lwarx %1, 0, %2 \n" /* load from [lock] into "temp" */ + /* and store reservation */ + " cmpw %1, %3 \n" /* compare "temp" and "old" */ + " bne- 2f \n" /* not equal */ + " stwcx. %4, 0, %2 \n" /* store "set" into [lock] if reservation */ + /* is not cleared */ + " bne- 1b \n" /* the reservation was cleared */ + " isync \n" /* read barrier */ + " li %0, 1 \n" /* set "1" to "res" */ + "2: \n" + + : "=&b" (res), "=&b" (temp) + : "b" (lock), "b" (old), "b" (set) + : "cc", "memory"); + + return res; +} + + +static ngx_inline ngx_atomic_int_t +ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add) +{ + ngx_atomic_uint_t res, temp; + + __asm__ volatile ( + + " eieio \n" /* write barrier */ + "1: lwarx %0, 0, %2 \n" /* load from [value] into "res" */ + /* and store reservation */ + " add %1, %0, %3 \n" /* "res" + "add" store in "temp" */ + " stwcx. %1, 0, %2 \n" /* store "temp" into [value] if reservation */ + /* is not cleared */ + " bne- 1b \n" /* try again if reservation was cleared */ + " isync \n" /* read barrier */ + + : "=&b" (res), "=&b" (temp) + : "b" (value), "b" (add) + : "cc", "memory"); + + return res; +} + + +#if (NGX_SMP) +#define ngx_memory_barrier() \ + __asm__ volatile ("isync \n eieio \n" ::: "memory") +#else +#define ngx_memory_barrier() __asm__ volatile ("" ::: "memory") +#endif + +#endif + + +#define ngx_cpu_pause() diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_gcc_atomic_sparc64.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_gcc_atomic_sparc64.h new file mode 100644 index 000000000..a84db3544 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_gcc_atomic_sparc64.h @@ -0,0 +1,82 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +/* + * "casa [r1] 0x80, r2, r0" and + * "casxa [r1] 0x80, r2, r0" do the following: + * + * if ([r1] == r2) { + * swap(r0, [r1]); + * } else { + * r0 = [r1]; + * } + * + * so "r0 == r2" means that the operation was successful. + * + * + * The "r" means the general register. + * The "+r" means the general register used for both input and output. + */ + + +#if (NGX_PTR_SIZE == 4) +#define NGX_CASA "casa" +#else +#define NGX_CASA "casxa" +#endif + + +static ngx_inline ngx_atomic_uint_t +ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old, + ngx_atomic_uint_t set) +{ + __asm__ volatile ( + + NGX_CASA " [%1] 0x80, %2, %0" + + : "+r" (set) : "r" (lock), "r" (old) : "memory"); + + return (set == old); +} + + +static ngx_inline ngx_atomic_int_t +ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add) +{ + ngx_atomic_uint_t old, res; + + old = *value; + + for ( ;; ) { + + res = old + add; + + __asm__ volatile ( + + NGX_CASA " [%1] 0x80, %2, %0" + + : "+r" (res) : "r" (value), "r" (old) : "memory"); + + if (res == old) { + return res; + } + + old = res; + } +} + + +#if (NGX_SMP) +#define ngx_memory_barrier() \ + __asm__ volatile ( \ + "membar #LoadLoad | #LoadStore | #StoreStore | #StoreLoad" \ + ::: "memory") +#else +#define ngx_memory_barrier() __asm__ volatile ("" ::: "memory") +#endif + +#define ngx_cpu_pause() diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_gcc_atomic_x86.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_gcc_atomic_x86.h new file mode 100644 index 000000000..54e01aebf --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_gcc_atomic_x86.h @@ -0,0 +1,127 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#if (NGX_SMP) +#define NGX_SMP_LOCK "lock;" +#else +#define NGX_SMP_LOCK +#endif + + +/* + * "cmpxchgl r, [m]": + * + * if (eax == [m]) { + * zf = 1; + * [m] = r; + * } else { + * zf = 0; + * eax = [m]; + * } + * + * + * The "r" means the general register. + * The "=a" and "a" are the %eax register. + * Although we can return result in any register, we use "a" because it is + * used in cmpxchgl anyway. The result is actually in %al but not in %eax, + * however, as the code is inlined gcc can test %al as well as %eax, + * and icc adds "movzbl %al, %eax" by itself. + * + * The "cc" means that flags were changed. + */ + +static ngx_inline ngx_atomic_uint_t +ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old, + ngx_atomic_uint_t set) +{ + u_char res; + + __asm__ volatile ( + + NGX_SMP_LOCK + " cmpxchgl %3, %1; " + " sete %0; " + + : "=a" (res) : "m" (*lock), "a" (old), "r" (set) : "cc", "memory"); + + return res; +} + + +/* + * "xaddl r, [m]": + * + * temp = [m]; + * [m] += r; + * r = temp; + * + * + * The "+r" means the general register. + * The "cc" means that flags were changed. + */ + + +#if !(( __GNUC__ == 2 && __GNUC_MINOR__ <= 7 ) || ( __INTEL_COMPILER >= 800 )) + +/* + * icc 8.1 and 9.0 compile broken code with -march=pentium4 option: + * ngx_atomic_fetch_add() always return the input "add" value, + * so we use the gcc 2.7 version. + * + * icc 8.1 and 9.0 with -march=pentiumpro option or icc 7.1 compile + * correct code. + */ + +static ngx_inline ngx_atomic_int_t +ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add) +{ + __asm__ volatile ( + + NGX_SMP_LOCK + " xaddl %0, %1; " + + : "+r" (add) : "m" (*value) : "cc", "memory"); + + return add; +} + + +#else + +/* + * gcc 2.7 does not support "+r", so we have to use the fixed + * %eax ("=a" and "a") and this adds two superfluous instructions in the end + * of code, something like this: "mov %eax, %edx / mov %edx, %eax". + */ + +static ngx_inline ngx_atomic_int_t +ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add) +{ + ngx_atomic_uint_t old; + + __asm__ volatile ( + + NGX_SMP_LOCK + " xaddl %2, %1; " + + : "=a" (old) : "m" (*value), "a" (add) : "cc", "memory"); + + return old; +} + +#endif + + +/* + * on x86 the write operations go in a program order, so we need only + * to disable the gcc reorder optimizations + */ + +#define ngx_memory_barrier() __asm__ volatile ("" ::: "memory") + +/* old "as" does not support "pause" opcode */ +#define ngx_cpu_pause() __asm__ (".byte 0xf3, 0x90") diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_linux.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_linux.h new file mode 100644 index 000000000..13d654e39 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_linux.h @@ -0,0 +1,16 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_LINUX_H_INCLUDED_ +#define _NGX_LINUX_H_INCLUDED_ + + +ngx_chain_t *ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); + + +#endif /* _NGX_LINUX_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_linux_aio_read.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_linux_aio_read.c new file mode 100644 index 000000000..9f0a6c124 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_linux_aio_read.c @@ -0,0 +1,148 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +extern int ngx_eventfd; +extern aio_context_t ngx_aio_ctx; + + +static void ngx_file_aio_event_handler(ngx_event_t *ev); + + +static int +io_submit(aio_context_t ctx, long n, struct iocb **paiocb) +{ + return syscall(SYS_io_submit, ctx, n, paiocb); +} + + +ngx_int_t +ngx_file_aio_init(ngx_file_t *file, ngx_pool_t *pool) +{ + ngx_event_aio_t *aio; + + aio = ngx_pcalloc(pool, sizeof(ngx_event_aio_t)); + if (aio == NULL) { + return NGX_ERROR; + } + + aio->file = file; + aio->fd = file->fd; + aio->event.data = aio; + aio->event.ready = 1; + aio->event.log = file->log; + + file->aio = aio; + + return NGX_OK; +} + + +ssize_t +ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset, + ngx_pool_t *pool) +{ + ngx_err_t err; + struct iocb *piocb[1]; + ngx_event_t *ev; + ngx_event_aio_t *aio; + + if (!ngx_file_aio) { + return ngx_read_file(file, buf, size, offset); + } + + if (file->aio == NULL && ngx_file_aio_init(file, pool) != NGX_OK) { + return NGX_ERROR; + } + + aio = file->aio; + ev = &aio->event; + + if (!ev->ready) { + ngx_log_error(NGX_LOG_ALERT, file->log, 0, + "second aio post for \"%V\"", &file->name); + return NGX_AGAIN; + } + + ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0, + "aio complete:%d @%O:%uz %V", + ev->complete, offset, size, &file->name); + + if (ev->complete) { + ev->active = 0; + ev->complete = 0; + + if (aio->res >= 0) { + ngx_set_errno(0); + return aio->res; + } + + ngx_set_errno(-aio->res); + + ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, + "aio read \"%s\" failed", file->name.data); + + return NGX_ERROR; + } + + ngx_memzero(&aio->aiocb, sizeof(struct iocb)); + + aio->aiocb.aio_data = (uint64_t) (uintptr_t) ev; + aio->aiocb.aio_lio_opcode = IOCB_CMD_PREAD; + aio->aiocb.aio_fildes = file->fd; + aio->aiocb.aio_buf = (uint64_t) (uintptr_t) buf; + aio->aiocb.aio_nbytes = size; + aio->aiocb.aio_offset = offset; + aio->aiocb.aio_flags = IOCB_FLAG_RESFD; + aio->aiocb.aio_resfd = ngx_eventfd; + + ev->handler = ngx_file_aio_event_handler; + + piocb[0] = &aio->aiocb; + + if (io_submit(ngx_aio_ctx, 1, piocb) == 1) { + ev->active = 1; + ev->ready = 0; + ev->complete = 0; + + return NGX_AGAIN; + } + + err = ngx_errno; + + if (err == NGX_EAGAIN) { + return ngx_read_file(file, buf, size, offset); + } + + ngx_log_error(NGX_LOG_CRIT, file->log, err, + "io_submit(\"%V\") failed", &file->name); + + if (err == NGX_ENOSYS) { + ngx_file_aio = 0; + return ngx_read_file(file, buf, size, offset); + } + + return NGX_ERROR; +} + + +static void +ngx_file_aio_event_handler(ngx_event_t *ev) +{ + ngx_event_aio_t *aio; + + aio = ev->data; + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, ev->log, 0, + "aio event handler fd:%d %V", aio->fd, &aio->file->name); + + aio->handler(ev); +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_linux_config.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_linux_config.h new file mode 100644 index 000000000..d99358c93 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_linux_config.h @@ -0,0 +1,136 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_LINUX_CONFIG_H_INCLUDED_ +#define _NGX_LINUX_CONFIG_H_INCLUDED_ + + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE /* pread(), pwrite(), gethostname() */ +#endif + +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include +#include +#include /* offsetof() */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* statfs() */ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include /* TCP_NODELAY, TCP_CORK */ +#include +#include +#include + +#include /* tzset() */ +#include /* memalign() */ +#include /* IOV_MAX */ +#include +#include /* uname() */ + +#include + + +#include + + +#if (NGX_HAVE_CRYPT_H) +#include +#endif + + +#if (NGX_HAVE_POSIX_SEM) +#include +#endif + + +#if (NGX_HAVE_SYS_PRCTL_H) +#include +#endif + + +#if (NGX_HAVE_SENDFILE64) +#include +#else +extern ssize_t sendfile(int s, int fd, int32_t *offset, size_t size); +#define NGX_SENDFILE_LIMIT 0x80000000 +#endif + + +#if (NGX_HAVE_POLL) +#include +#endif + + +#if (NGX_HAVE_EPOLL) +#include +#endif + + +#if (NGX_HAVE_SYS_EVENTFD_H) +#include +#endif +#include +#if (NGX_HAVE_FILE_AIO) +#include +typedef struct iocb ngx_aiocb_t; +#endif + + +#if (NGX_HAVE_CAPABILITIES) +#include +#endif + +#if (NGX_HAVE_UDP_SEGMENT) +#include +#endif + + +#define NGX_LISTEN_BACKLOG 511 + + +#ifndef NGX_HAVE_SO_SNDLOWAT +/* setsockopt(SO_SNDLOWAT) returns ENOPROTOOPT */ +#define NGX_HAVE_SO_SNDLOWAT 0 +#endif + + +#ifndef NGX_HAVE_INHERITED_NONBLOCK +#define NGX_HAVE_INHERITED_NONBLOCK 0 +#endif + + +#define NGX_HAVE_OS_SPECIFIC_INIT 1 +#define ngx_debug_init() + + +extern char **environ; + + +#endif /* _NGX_LINUX_CONFIG_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_linux_init.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_linux_init.c new file mode 100644 index 000000000..a8cf6a048 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_linux_init.c @@ -0,0 +1,60 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +u_char ngx_linux_kern_ostype[50]; +u_char ngx_linux_kern_osrelease[50]; + + +static ngx_os_io_t ngx_linux_io = { + ngx_unix_recv, + ngx_readv_chain, + ngx_udp_unix_recv, + ngx_unix_send, + ngx_udp_unix_send, + ngx_udp_unix_sendmsg_chain, +#if (NGX_HAVE_SENDFILE) + ngx_linux_sendfile_chain, + NGX_IO_SENDFILE +#else + ngx_writev_chain, + 0 +#endif +}; + + +ngx_int_t +ngx_os_specific_init(ngx_log_t *log) +{ + struct utsname u; + + if (uname(&u) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "uname() failed"); + return NGX_ERROR; + } + + (void) ngx_cpystrn(ngx_linux_kern_ostype, (u_char *) u.sysname, + sizeof(ngx_linux_kern_ostype)); + + (void) ngx_cpystrn(ngx_linux_kern_osrelease, (u_char *) u.release, + sizeof(ngx_linux_kern_osrelease)); + + ngx_os_io = ngx_linux_io; + + return NGX_OK; +} + + +void +ngx_os_specific_status(ngx_log_t *log) +{ + ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s", + ngx_linux_kern_ostype, ngx_linux_kern_osrelease); +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_linux_sendfile_chain.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_linux_sendfile_chain.c new file mode 100644 index 000000000..603f91722 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_linux_sendfile_chain.c @@ -0,0 +1,436 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +static ssize_t ngx_linux_sendfile(ngx_connection_t *c, ngx_buf_t *file, + size_t size); + +#if (NGX_THREADS) +#include + +#if !(NGX_HAVE_SENDFILE64) +#error sendfile64() is required! +#endif + +static ssize_t ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, + size_t size); +static void ngx_linux_sendfile_thread_handler(void *data, ngx_log_t *log); +#endif + + +/* + * On Linux up to 2.4.21 sendfile() (syscall #187) works with 32-bit + * offsets only, and the including breaks the compiling, + * if off_t is 64 bit wide. So we use own sendfile() definition, where offset + * parameter is int32_t, and use sendfile() for the file parts below 2G only, + * see src/os/unix/ngx_linux_config.h + * + * Linux 2.4.21 has the new sendfile64() syscall #239. + * + * On Linux up to 2.6.16 sendfile() does not allow to pass the count parameter + * more than 2G-1 bytes even on 64-bit platforms: it returns EINVAL, + * so we limit it to 2G-1 bytes. + * + * On Linux 2.6.16 and later, sendfile() silently limits the count parameter + * to 2G minus the page size, even on 64-bit platforms. + */ + +#define NGX_SENDFILE_MAXSIZE 2147483647L + + +ngx_chain_t * +ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) +{ + int tcp_nodelay; + off_t send, prev_send; + size_t file_size, sent; + ssize_t n; + ngx_err_t err; + ngx_buf_t *file; + ngx_event_t *wev; + ngx_chain_t *cl; + ngx_iovec_t header; + struct iovec headers[NGX_IOVS_PREALLOCATE]; + + wev = c->write; + + if (!wev->ready) { + return in; + } + + + /* the maximum limit size is 2G-1 - the page size */ + + if (limit == 0 || limit > (off_t) (NGX_SENDFILE_MAXSIZE - ngx_pagesize)) { + limit = NGX_SENDFILE_MAXSIZE - ngx_pagesize; + } + + + send = 0; + + header.iovs = headers; + header.nalloc = NGX_IOVS_PREALLOCATE; + + for ( ;; ) { + prev_send = send; + + /* create the iovec and coalesce the neighbouring bufs */ + + cl = ngx_output_chain_to_iovec(&header, in, limit - send, c->log); + + if (cl == NGX_CHAIN_ERROR) { + return NGX_CHAIN_ERROR; + } + + send += header.size; + + /* set TCP_CORK if there is a header before a file */ + + if (c->tcp_nopush == NGX_TCP_NOPUSH_UNSET + && header.count != 0 + && cl + && cl->buf->in_file) + { + /* the TCP_CORK and TCP_NODELAY are mutually exclusive */ + + if (c->tcp_nodelay == NGX_TCP_NODELAY_SET) { + + tcp_nodelay = 0; + + if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, + (const void *) &tcp_nodelay, sizeof(int)) == -1) + { + err = ngx_socket_errno; + + /* + * there is a tiny chance to be interrupted, however, + * we continue a processing with the TCP_NODELAY + * and without the TCP_CORK + */ + + if (err != NGX_EINTR) { + wev->error = 1; + ngx_connection_error(c, err, + "setsockopt(TCP_NODELAY) failed"); + return NGX_CHAIN_ERROR; + } + + } else { + c->tcp_nodelay = NGX_TCP_NODELAY_UNSET; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, + "no tcp_nodelay"); + } + } + + if (c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) { + + if (ngx_tcp_nopush(c->fd) == -1) { + err = ngx_socket_errno; + + /* + * there is a tiny chance to be interrupted, however, + * we continue a processing without the TCP_CORK + */ + + if (err != NGX_EINTR) { + wev->error = 1; + ngx_connection_error(c, err, + ngx_tcp_nopush_n " failed"); + return NGX_CHAIN_ERROR; + } + + } else { + c->tcp_nopush = NGX_TCP_NOPUSH_SET; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, + "tcp_nopush"); + } + } + } + + /* get the file buf */ + + if (header.count == 0 && cl && cl->buf->in_file && send < limit) { + file = cl->buf; + + /* coalesce the neighbouring file bufs */ + + file_size = (size_t) ngx_chain_coalesce_file(&cl, limit - send); + + send += file_size; +#if 1 + if (file_size == 0) { + ngx_debug_point(); + return NGX_CHAIN_ERROR; + } +#endif + + n = ngx_linux_sendfile(c, file, file_size); + + if (n == NGX_ERROR) { + return NGX_CHAIN_ERROR; + } + + if (n == NGX_DONE) { + /* thread task posted */ + return in; + } + + sent = (n == NGX_AGAIN) ? 0 : n; + + } else { + n = ngx_writev(c, &header); + + if (n == NGX_ERROR) { + return NGX_CHAIN_ERROR; + } + + sent = (n == NGX_AGAIN) ? 0 : n; + } + + c->sent += sent; + + in = ngx_chain_update_sent(in, sent); + + if (n == NGX_AGAIN) { + wev->ready = 0; + return in; + } + + if ((size_t) (send - prev_send) != sent) { + + /* + * sendfile() on Linux 4.3+ might be interrupted at any time, + * and provides no indication if it was interrupted or not, + * so we have to retry till an explicit EAGAIN + * + * sendfile() in threads can also report less bytes written + * than we are prepared to send now, since it was started in + * some point in the past, so we again have to retry + */ + + send = prev_send + sent; + } + + if (send >= limit || in == NULL) { + return in; + } + } +} + + +static ssize_t +ngx_linux_sendfile(ngx_connection_t *c, ngx_buf_t *file, size_t size) +{ +#if (NGX_HAVE_SENDFILE64) + off_t offset; +#else + int32_t offset; +#endif + ssize_t n; + ngx_err_t err; + +#if (NGX_THREADS) + + if (file->file->thread_handler) { + return ngx_linux_sendfile_thread(c, file, size); + } + +#endif + +#if (NGX_HAVE_SENDFILE64) + offset = file->file_pos; +#else + offset = (int32_t) file->file_pos; +#endif + +eintr: + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendfile: @%O %uz", file->file_pos, size); + + n = sendfile(c->fd, file->file->fd, &offset, size); + + if (n == -1) { + err = ngx_errno; + + switch (err) { + case NGX_EAGAIN: + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendfile() is not ready"); + return NGX_AGAIN; + + case NGX_EINTR: + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendfile() was interrupted"); + goto eintr; + + default: + c->write->error = 1; + ngx_connection_error(c, err, "sendfile() failed"); + return NGX_ERROR; + } + } + + if (n == 0) { + /* + * if sendfile returns zero, then someone has truncated the file, + * so the offset became beyond the end of the file + */ + + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "sendfile() reported that \"%s\" was truncated at %O", + file->file->name.data, file->file_pos); + + return NGX_ERROR; + } + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, "sendfile: %z of %uz @%O", + n, size, file->file_pos); + + return n; +} + + +#if (NGX_THREADS) + +typedef struct { + ngx_buf_t *file; + ngx_socket_t socket; + size_t size; + + size_t sent; + ngx_err_t err; +} ngx_linux_sendfile_ctx_t; + + +static ssize_t +ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, size_t size) +{ + ngx_event_t *wev; + ngx_thread_task_t *task; + ngx_linux_sendfile_ctx_t *ctx; + + ngx_log_debug3(NGX_LOG_DEBUG_CORE, c->log, 0, + "linux sendfile thread: %d, %uz, %O", + file->file->fd, size, file->file_pos); + + task = c->sendfile_task; + + if (task == NULL) { + task = ngx_thread_task_alloc(c->pool, sizeof(ngx_linux_sendfile_ctx_t)); + if (task == NULL) { + return NGX_ERROR; + } + + task->event.log = c->log; + task->handler = ngx_linux_sendfile_thread_handler; + + c->sendfile_task = task; + } + + ctx = task->ctx; + wev = c->write; + + if (task->event.complete) { + task->event.complete = 0; + + if (ctx->err == NGX_EAGAIN) { + /* + * if wev->complete is set, this means that a write event + * happened while we were waiting for the thread task, so + * we have to retry sending even on EAGAIN + */ + + if (wev->complete) { + return 0; + } + + return NGX_AGAIN; + } + + if (ctx->err) { + wev->error = 1; + ngx_connection_error(c, ctx->err, "sendfile() failed"); + return NGX_ERROR; + } + + if (ctx->sent == 0) { + /* + * if sendfile returns zero, then someone has truncated the file, + * so the offset became beyond the end of the file + */ + + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "sendfile() reported that \"%s\" was truncated at %O", + file->file->name.data, file->file_pos); + + return NGX_ERROR; + } + + return ctx->sent; + } + + ctx->file = file; + ctx->socket = c->fd; + ctx->size = size; + + wev->complete = 0; + + if (file->file->thread_handler(task, file->file) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_DONE; +} + + +static void +ngx_linux_sendfile_thread_handler(void *data, ngx_log_t *log) +{ + ngx_linux_sendfile_ctx_t *ctx = data; + + off_t offset; + ssize_t n; + ngx_buf_t *file; + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, "linux sendfile thread handler"); + + file = ctx->file; + offset = file->file_pos; + +again: + + n = sendfile(ctx->socket, file->file->fd, &offset, ctx->size); + + if (n == -1) { + ctx->err = ngx_errno; + + } else { + ctx->sent = n; + ctx->err = 0; + } + +#if 0 + ngx_time_update(); +#endif + + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, log, 0, + "sendfile: %z (err: %d) of %uz @%O", + n, ctx->err, ctx->size, file->file_pos); + + if (ctx->err == NGX_EINTR) { + goto again; + } +} + +#endif /* NGX_THREADS */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_os.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_os.h new file mode 100644 index 000000000..3b328191b --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_os.h @@ -0,0 +1,102 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_OS_H_INCLUDED_ +#define _NGX_OS_H_INCLUDED_ + + +#include +#include + + +#define NGX_IO_SENDFILE 1 + + +typedef ssize_t (*ngx_recv_pt)(ngx_connection_t *c, u_char *buf, size_t size); +typedef ssize_t (*ngx_recv_chain_pt)(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); +typedef ssize_t (*ngx_send_pt)(ngx_connection_t *c, u_char *buf, size_t size); +typedef ngx_chain_t *(*ngx_send_chain_pt)(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); + +typedef struct { + ngx_recv_pt recv; + ngx_recv_chain_pt recv_chain; + ngx_recv_pt udp_recv; + ngx_send_pt send; + ngx_send_pt udp_send; + ngx_send_chain_pt udp_send_chain; + ngx_send_chain_pt send_chain; + ngx_uint_t flags; +} ngx_os_io_t; + + +ngx_int_t ngx_os_init(ngx_log_t *log); +void ngx_os_status(ngx_log_t *log); +ngx_int_t ngx_os_specific_init(ngx_log_t *log); +void ngx_os_specific_status(ngx_log_t *log); +ngx_int_t ngx_daemon(ngx_log_t *log); +ngx_int_t ngx_os_signal_process(ngx_cycle_t *cycle, char *sig, ngx_pid_t pid); + + +ssize_t ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size); +ssize_t ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *entry, off_t limit); +ssize_t ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size); +ssize_t ngx_unix_send(ngx_connection_t *c, u_char *buf, size_t size); +ngx_chain_t *ngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); +ssize_t ngx_udp_unix_send(ngx_connection_t *c, u_char *buf, size_t size); +ngx_chain_t *ngx_udp_unix_sendmsg_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); + + +#if (IOV_MAX > 64) +#define NGX_IOVS_PREALLOCATE 64 +#else +#define NGX_IOVS_PREALLOCATE IOV_MAX +#endif + + +typedef struct { + struct iovec *iovs; + ngx_uint_t count; + size_t size; + ngx_uint_t nalloc; +} ngx_iovec_t; + +ngx_chain_t *ngx_output_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *in, + size_t limit, ngx_log_t *log); + + +ssize_t ngx_writev(ngx_connection_t *c, ngx_iovec_t *vec); + + +extern ngx_os_io_t ngx_os_io; +extern ngx_int_t ngx_ncpu; +extern ngx_int_t ngx_max_sockets; +extern ngx_uint_t ngx_inherited_nonblocking; +extern ngx_uint_t ngx_tcp_nodelay_and_tcp_nopush; + + +#if (NGX_FREEBSD) +#include + + +#elif (NGX_LINUX) +#include + + +#elif (NGX_SOLARIS) +#include + + +#elif (NGX_DARWIN) +#include +#endif + + +#endif /* _NGX_OS_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_posix_config.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_posix_config.h new file mode 100644 index 000000000..2a8c413e0 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_posix_config.h @@ -0,0 +1,151 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_POSIX_CONFIG_H_INCLUDED_ +#define _NGX_POSIX_CONFIG_H_INCLUDED_ + + +#if (NGX_HPUX) +#define _XOPEN_SOURCE +#define _XOPEN_SOURCE_EXTENDED 1 +#define _HPUX_ALT_XOPEN_SOCKET_API +#endif + + +#if (NGX_TRU64) +#define _REENTRANT +#endif + + +#if (NGX_GNU_HURD) +#ifndef _GNU_SOURCE +#define _GNU_SOURCE /* accept4() */ +#endif +#define _FILE_OFFSET_BITS 64 +#endif + + +#ifdef __CYGWIN__ +#define timezonevar /* timezone is variable */ +#define NGX_BROKEN_SCM_RIGHTS 1 +#endif + + +#include +#include +#if (NGX_HAVE_UNISTD_H) +#include +#endif +#if (NGX_HAVE_INTTYPES_H) +#include +#endif +#include +#include /* offsetof() */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if (NGX_HAVE_SYS_PARAM_H) +#include /* statfs() */ +#endif +#if (NGX_HAVE_SYS_MOUNT_H) +#include /* statfs() */ +#endif +#if (NGX_HAVE_SYS_STATVFS_H) +#include /* statvfs() */ +#endif + +#if (NGX_HAVE_SYS_FILIO_H) +#include /* FIONBIO */ +#endif +#include /* FIONBIO */ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include /* TCP_NODELAY */ +#include +#include +#include + +#if (NGX_HAVE_LIMITS_H) +#include /* IOV_MAX */ +#endif + +#ifdef __CYGWIN__ +#include /* memalign() */ +#endif + +#if (NGX_HAVE_CRYPT_H) +#include +#endif + + +#ifndef IOV_MAX +#define IOV_MAX 16 +#endif + + +#include + + +#if (NGX_HAVE_DLOPEN) +#include +#endif + + +#if (NGX_HAVE_POSIX_SEM) +#include +#endif + + +#if (NGX_HAVE_POLL) +#include +#endif + + +#if (NGX_HAVE_KQUEUE) +#include +#endif + + +#if (NGX_HAVE_DEVPOLL) && !(NGX_TEST_BUILD_DEVPOLL) +#include +#include +#endif + + +#if (NGX_HAVE_FILE_AIO) +#include +typedef struct aiocb ngx_aiocb_t; +#endif + + +#define NGX_LISTEN_BACKLOG 511 + +#define ngx_debug_init() + + +extern char **environ; + + +#endif /* _NGX_POSIX_CONFIG_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_posix_init.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_posix_init.c new file mode 100644 index 000000000..6ac931c0c --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_posix_init.c @@ -0,0 +1,147 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +ngx_int_t ngx_ncpu; +ngx_int_t ngx_max_sockets; +ngx_uint_t ngx_inherited_nonblocking; +ngx_uint_t ngx_tcp_nodelay_and_tcp_nopush; + + +struct rlimit rlmt; + + +ngx_os_io_t ngx_os_io = { + ngx_unix_recv, + ngx_readv_chain, + ngx_udp_unix_recv, + ngx_unix_send, + ngx_udp_unix_send, + ngx_udp_unix_sendmsg_chain, + ngx_writev_chain, + 0 +}; + + +ngx_int_t +ngx_os_init(ngx_log_t *log) +{ + ngx_time_t *tp; + ngx_uint_t n; +#if (NGX_HAVE_LEVEL1_DCACHE_LINESIZE) + long size; +#endif + +#if (NGX_HAVE_OS_SPECIFIC_INIT) + if (ngx_os_specific_init(log) != NGX_OK) { + return NGX_ERROR; + } +#endif + + if (ngx_init_setproctitle(log) != NGX_OK) { + return NGX_ERROR; + } + + ngx_pagesize = getpagesize(); + + if (ngx_cacheline_size == 0) { + ngx_cacheline_size = NGX_CPU_CACHE_LINE; + } + + for (n = ngx_pagesize; n >>= 1; ngx_pagesize_shift++) { /* void */ } + +#if (NGX_HAVE_SC_NPROCESSORS_ONLN) + if (ngx_ncpu == 0) { + ngx_ncpu = sysconf(_SC_NPROCESSORS_ONLN); + } +#endif + + if (ngx_ncpu < 1) { + ngx_ncpu = 1; + } + +#if (NGX_HAVE_LEVEL1_DCACHE_LINESIZE) + size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE); + if (size > 0) { + ngx_cacheline_size = size; + } +#endif + + ngx_cpuinfo(); + + if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, errno, + "getrlimit(RLIMIT_NOFILE) failed"); + return NGX_ERROR; + } + + ngx_max_sockets = (ngx_int_t) rlmt.rlim_cur; + +#if (NGX_HAVE_INHERITED_NONBLOCK || NGX_HAVE_ACCEPT4) + ngx_inherited_nonblocking = 1; +#else + ngx_inherited_nonblocking = 0; +#endif + + tp = ngx_timeofday(); + srandom(((unsigned) ngx_pid << 16) ^ tp->sec ^ tp->msec); + + return NGX_OK; +} + + +void +ngx_os_status(ngx_log_t *log) +{ + ngx_log_error(NGX_LOG_NOTICE, log, 0, NGINX_VER_BUILD); + +#ifdef NGX_COMPILER + ngx_log_error(NGX_LOG_NOTICE, log, 0, "built by " NGX_COMPILER); +#endif + +#if (NGX_HAVE_OS_SPECIFIC_INIT) + ngx_os_specific_status(log); +#endif + + ngx_log_error(NGX_LOG_NOTICE, log, 0, + "getrlimit(RLIMIT_NOFILE): %r:%r", + rlmt.rlim_cur, rlmt.rlim_max); +} + + +#if 0 + +ngx_int_t +ngx_posix_post_conf_init(ngx_log_t *log) +{ + ngx_fd_t pp[2]; + + if (pipe(pp) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "pipe() failed"); + return NGX_ERROR; + } + + if (dup2(pp[1], STDERR_FILENO) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, errno, "dup2(STDERR) failed"); + return NGX_ERROR; + } + + if (pp[1] > STDERR_FILENO) { + if (close(pp[1]) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, errno, "close() failed"); + return NGX_ERROR; + } + } + + return NGX_OK; +} + +#endif diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_process.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_process.c new file mode 100644 index 000000000..15680237a --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_process.c @@ -0,0 +1,648 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include + + +typedef struct { + int signo; + char *signame; + char *name; + void (*handler)(int signo, siginfo_t *siginfo, void *ucontext); +} ngx_signal_t; + + + +static void ngx_execute_proc(ngx_cycle_t *cycle, void *data); +static void ngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext); +static void ngx_process_get_status(void); +static void ngx_unlock_mutexes(ngx_pid_t pid); + + +int ngx_argc; +char **ngx_argv; +char **ngx_os_argv; + +ngx_int_t ngx_process_slot; +ngx_socket_t ngx_channel; +ngx_int_t ngx_last_process; +ngx_process_t ngx_processes[NGX_MAX_PROCESSES]; + + +ngx_signal_t signals[] = { + { ngx_signal_value(NGX_RECONFIGURE_SIGNAL), + "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL), + "reload", + ngx_signal_handler }, + + { ngx_signal_value(NGX_REOPEN_SIGNAL), + "SIG" ngx_value(NGX_REOPEN_SIGNAL), + "reopen", + ngx_signal_handler }, + + { ngx_signal_value(NGX_NOACCEPT_SIGNAL), + "SIG" ngx_value(NGX_NOACCEPT_SIGNAL), + "", + ngx_signal_handler }, + + { ngx_signal_value(NGX_TERMINATE_SIGNAL), + "SIG" ngx_value(NGX_TERMINATE_SIGNAL), + "stop", + ngx_signal_handler }, + + { ngx_signal_value(NGX_SHUTDOWN_SIGNAL), + "SIG" ngx_value(NGX_SHUTDOWN_SIGNAL), + "quit", + ngx_signal_handler }, + + { ngx_signal_value(NGX_CHANGEBIN_SIGNAL), + "SIG" ngx_value(NGX_CHANGEBIN_SIGNAL), + "", + ngx_signal_handler }, + + { SIGALRM, "SIGALRM", "", ngx_signal_handler }, + + { SIGINT, "SIGINT", "", ngx_signal_handler }, + + { SIGIO, "SIGIO", "", ngx_signal_handler }, + + { SIGCHLD, "SIGCHLD", "", ngx_signal_handler }, + + { SIGSYS, "SIGSYS, SIG_IGN", "", NULL }, + + { SIGPIPE, "SIGPIPE, SIG_IGN", "", NULL }, + + { 0, NULL, "", NULL } +}; + + +ngx_pid_t +ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data, + char *name, ngx_int_t respawn) +{ + u_long on; + ngx_pid_t pid; + ngx_int_t s; + + if (respawn >= 0) { + s = respawn; + + } else { + for (s = 0; s < ngx_last_process; s++) { + if (ngx_processes[s].pid == -1) { + break; + } + } + + if (s == NGX_MAX_PROCESSES) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "no more than %d processes can be spawned", + NGX_MAX_PROCESSES); + return NGX_INVALID_PID; + } + } + + + if (respawn != NGX_PROCESS_DETACHED) { + + /* Solaris 9 still has no AF_LOCAL */ + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "socketpair() failed while spawning \"%s\"", name); + return NGX_INVALID_PID; + } + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0, + "channel %d:%d", + ngx_processes[s].channel[0], + ngx_processes[s].channel[1]); + + if (ngx_nonblocking(ngx_processes[s].channel[0]) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_nonblocking_n " failed while spawning \"%s\"", + name); + ngx_close_channel(ngx_processes[s].channel, cycle->log); + return NGX_INVALID_PID; + } + + if (ngx_nonblocking(ngx_processes[s].channel[1]) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_nonblocking_n " failed while spawning \"%s\"", + name); + ngx_close_channel(ngx_processes[s].channel, cycle->log); + return NGX_INVALID_PID; + } + + on = 1; + if (ioctl(ngx_processes[s].channel[0], FIOASYNC, &on) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "ioctl(FIOASYNC) failed while spawning \"%s\"", name); + ngx_close_channel(ngx_processes[s].channel, cycle->log); + return NGX_INVALID_PID; + } + + if (fcntl(ngx_processes[s].channel[0], F_SETOWN, ngx_pid) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "fcntl(F_SETOWN) failed while spawning \"%s\"", name); + ngx_close_channel(ngx_processes[s].channel, cycle->log); + return NGX_INVALID_PID; + } + + if (fcntl(ngx_processes[s].channel[0], F_SETFD, FD_CLOEXEC) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "fcntl(FD_CLOEXEC) failed while spawning \"%s\"", + name); + ngx_close_channel(ngx_processes[s].channel, cycle->log); + return NGX_INVALID_PID; + } + + if (fcntl(ngx_processes[s].channel[1], F_SETFD, FD_CLOEXEC) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "fcntl(FD_CLOEXEC) failed while spawning \"%s\"", + name); + ngx_close_channel(ngx_processes[s].channel, cycle->log); + return NGX_INVALID_PID; + } + + ngx_channel = ngx_processes[s].channel[1]; + + } else { + ngx_processes[s].channel[0] = -1; + ngx_processes[s].channel[1] = -1; + } + + ngx_process_slot = s; + + + pid = fork(); + + switch (pid) { + + case -1: + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "fork() failed while spawning \"%s\"", name); + ngx_close_channel(ngx_processes[s].channel, cycle->log); + return NGX_INVALID_PID; + + case 0: + ngx_parent = ngx_pid; + ngx_pid = ngx_getpid(); + proc(cycle, data); + break; + + default: + break; + } + + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start %s %P", name, pid); + + ngx_processes[s].pid = pid; + ngx_processes[s].exited = 0; + + if (respawn >= 0) { + return pid; + } + + ngx_processes[s].proc = proc; + ngx_processes[s].data = data; + ngx_processes[s].name = name; + ngx_processes[s].exiting = 0; + + switch (respawn) { + + case NGX_PROCESS_NORESPAWN: + ngx_processes[s].respawn = 0; + ngx_processes[s].just_spawn = 0; + ngx_processes[s].detached = 0; + break; + + case NGX_PROCESS_JUST_SPAWN: + ngx_processes[s].respawn = 0; + ngx_processes[s].just_spawn = 1; + ngx_processes[s].detached = 0; + break; + + case NGX_PROCESS_RESPAWN: + ngx_processes[s].respawn = 1; + ngx_processes[s].just_spawn = 0; + ngx_processes[s].detached = 0; + break; + + case NGX_PROCESS_JUST_RESPAWN: + ngx_processes[s].respawn = 1; + ngx_processes[s].just_spawn = 1; + ngx_processes[s].detached = 0; + break; + + case NGX_PROCESS_DETACHED: + ngx_processes[s].respawn = 0; + ngx_processes[s].just_spawn = 0; + ngx_processes[s].detached = 1; + break; + } + + if (s == ngx_last_process) { + ngx_last_process++; + } + + return pid; +} + + +ngx_pid_t +ngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx) +{ + return ngx_spawn_process(cycle, ngx_execute_proc, ctx, ctx->name, + NGX_PROCESS_DETACHED); +} + + +static void +ngx_execute_proc(ngx_cycle_t *cycle, void *data) +{ + ngx_exec_ctx_t *ctx = data; + + if (execve(ctx->path, ctx->argv, ctx->envp) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "execve() failed while executing %s \"%s\"", + ctx->name, ctx->path); + } + + exit(1); +} + + +ngx_int_t +ngx_init_signals(ngx_log_t *log) +{ + ngx_signal_t *sig; + struct sigaction sa; + + for (sig = signals; sig->signo != 0; sig++) { + ngx_memzero(&sa, sizeof(struct sigaction)); + + if (sig->handler) { + sa.sa_sigaction = sig->handler; + sa.sa_flags = SA_SIGINFO; + + } else { + sa.sa_handler = SIG_IGN; + } + + sigemptyset(&sa.sa_mask); + if (sigaction(sig->signo, &sa, NULL) == -1) { +#if (NGX_VALGRIND) + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "sigaction(%s) failed, ignored", sig->signame); +#else + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "sigaction(%s) failed", sig->signame); + return NGX_ERROR; +#endif + } + } + + return NGX_OK; +} + + +static void +ngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext) +{ + char *action; + ngx_int_t ignore; + ngx_err_t err; + ngx_signal_t *sig; + + ignore = 0; + + err = ngx_errno; + + for (sig = signals; sig->signo != 0; sig++) { + if (sig->signo == signo) { + break; + } + } + + ngx_time_sigsafe_update(); + + action = ""; + + switch (ngx_process) { + + case NGX_PROCESS_MASTER: + case NGX_PROCESS_SINGLE: + switch (signo) { + + case ngx_signal_value(NGX_SHUTDOWN_SIGNAL): + ngx_quit = 1; + action = ", shutting down"; + break; + + case ngx_signal_value(NGX_TERMINATE_SIGNAL): + case SIGINT: + ngx_terminate = 1; + action = ", exiting"; + break; + + case ngx_signal_value(NGX_NOACCEPT_SIGNAL): + if (ngx_daemonized) { + ngx_noaccept = 1; + action = ", stop accepting connections"; + } + break; + + case ngx_signal_value(NGX_RECONFIGURE_SIGNAL): + ngx_reconfigure = 1; + action = ", reconfiguring"; + break; + + case ngx_signal_value(NGX_REOPEN_SIGNAL): + ngx_reopen = 1; + action = ", reopening logs"; + break; + + case ngx_signal_value(NGX_CHANGEBIN_SIGNAL): + if (ngx_getppid() == ngx_parent || ngx_new_binary > 0) { + + /* + * Ignore the signal in the new binary if its parent is + * not changed, i.e. the old binary's process is still + * running. Or ignore the signal in the old binary's + * process if the new binary's process is already running. + */ + + action = ", ignoring"; + ignore = 1; + break; + } + + ngx_change_binary = 1; + action = ", changing binary"; + break; + + case SIGALRM: + ngx_sigalrm = 1; + break; + + case SIGIO: + ngx_sigio = 1; + break; + + case SIGCHLD: + ngx_reap = 1; + break; + } + + break; + + case NGX_PROCESS_WORKER: + case NGX_PROCESS_HELPER: + switch (signo) { + + case ngx_signal_value(NGX_NOACCEPT_SIGNAL): + if (!ngx_daemonized) { + break; + } + ngx_debug_quit = 1; + /* fall through */ + case ngx_signal_value(NGX_SHUTDOWN_SIGNAL): + ngx_quit = 1; + action = ", shutting down"; + break; + + case ngx_signal_value(NGX_TERMINATE_SIGNAL): + case SIGINT: + ngx_terminate = 1; + action = ", exiting"; + break; + + case ngx_signal_value(NGX_REOPEN_SIGNAL): + ngx_reopen = 1; + action = ", reopening logs"; + break; + + case ngx_signal_value(NGX_RECONFIGURE_SIGNAL): + case ngx_signal_value(NGX_CHANGEBIN_SIGNAL): + case SIGIO: + action = ", ignoring"; + break; + } + + break; + } + + if (siginfo && siginfo->si_pid) { + ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, + "signal %d (%s) received from %P%s", + signo, sig->signame, siginfo->si_pid, action); + + } else { + ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, + "signal %d (%s) received%s", + signo, sig->signame, action); + } + + if (ignore) { + ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, 0, + "the changing binary signal is ignored: " + "you should shutdown or terminate " + "before either old or new binary's process"); + } + + if (signo == SIGCHLD) { + ngx_process_get_status(); + } + + ngx_set_errno(err); +} + + +static void +ngx_process_get_status(void) +{ + int status; + char *process; + ngx_pid_t pid; + ngx_err_t err; + ngx_int_t i; + ngx_uint_t one; + + one = 0; + + for ( ;; ) { + pid = waitpid(-1, &status, WNOHANG); + + if (pid == 0) { + return; + } + + if (pid == -1) { + err = ngx_errno; + + if (err == NGX_EINTR) { + continue; + } + + if (err == NGX_ECHILD && one) { + return; + } + + /* + * Solaris always calls the signal handler for each exited process + * despite waitpid() may be already called for this process. + * + * When several processes exit at the same time FreeBSD may + * erroneously call the signal handler for exited process + * despite waitpid() may be already called for this process. + */ + + if (err == NGX_ECHILD) { + ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, err, + "waitpid() failed"); + return; + } + + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err, + "waitpid() failed"); + return; + } + + + one = 1; + process = "unknown process"; + + for (i = 0; i < ngx_last_process; i++) { + if (ngx_processes[i].pid == pid) { + ngx_processes[i].status = status; + ngx_processes[i].exited = 1; + process = ngx_processes[i].name; + break; + } + } + + if (WTERMSIG(status)) { +#ifdef WCOREDUMP + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "%s %P exited on signal %d%s", + process, pid, WTERMSIG(status), + WCOREDUMP(status) ? " (core dumped)" : ""); +#else + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "%s %P exited on signal %d", + process, pid, WTERMSIG(status)); +#endif + + } else { + ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, + "%s %P exited with code %d", + process, pid, WEXITSTATUS(status)); + } + + if (WEXITSTATUS(status) == 2 && ngx_processes[i].respawn) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "%s %P exited with fatal code %d " + "and cannot be respawned", + process, pid, WEXITSTATUS(status)); + ngx_processes[i].respawn = 0; + } + + ngx_unlock_mutexes(pid); + } +} + + +static void +ngx_unlock_mutexes(ngx_pid_t pid) +{ + ngx_uint_t i; + ngx_shm_zone_t *shm_zone; + ngx_list_part_t *part; + ngx_slab_pool_t *sp; + + /* + * unlock the accept mutex if the abnormally exited process + * held it + */ + + if (ngx_accept_mutex_ptr) { + (void) ngx_shmtx_force_unlock(&ngx_accept_mutex, pid); + } + + /* + * unlock shared memory mutexes if held by the abnormally exited + * process + */ + + part = (ngx_list_part_t *) &ngx_cycle->shared_memory.part; + shm_zone = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + part = part->next; + shm_zone = part->elts; + i = 0; + } + + sp = (ngx_slab_pool_t *) shm_zone[i].shm.addr; + + if (ngx_shmtx_force_unlock(&sp->mutex, pid)) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "shared memory zone \"%V\" was locked by %P", + &shm_zone[i].shm.name, pid); + } + } +} + + +void +ngx_debug_point(void) +{ + ngx_core_conf_t *ccf; + + ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, + ngx_core_module); + + switch (ccf->debug_points) { + + case NGX_DEBUG_POINTS_STOP: + raise(SIGSTOP); + break; + + case NGX_DEBUG_POINTS_ABORT: + ngx_abort(); + } +} + + +ngx_int_t +ngx_os_signal_process(ngx_cycle_t *cycle, char *name, ngx_pid_t pid) +{ + ngx_signal_t *sig; + + for (sig = signals; sig->signo != 0; sig++) { + if (ngx_strcmp(name, sig->name) == 0) { + if (kill(pid, sig->signo) != -1) { + return 0; + } + + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "kill(%P, %d) failed", pid, sig->signo); + } + } + + return 1; +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_process.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_process.h new file mode 100644 index 000000000..3986639b4 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_process.h @@ -0,0 +1,90 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_PROCESS_H_INCLUDED_ +#define _NGX_PROCESS_H_INCLUDED_ + + +#include +#include + + +typedef pid_t ngx_pid_t; + +#define NGX_INVALID_PID -1 + +typedef void (*ngx_spawn_proc_pt) (ngx_cycle_t *cycle, void *data); + +typedef struct { + ngx_pid_t pid; + int status; + ngx_socket_t channel[2]; + + ngx_spawn_proc_pt proc; + void *data; + char *name; + + unsigned respawn:1; + unsigned just_spawn:1; + unsigned detached:1; + unsigned exiting:1; + unsigned exited:1; +} ngx_process_t; + + +typedef struct { + char *path; + char *name; + char *const *argv; + char *const *envp; +} ngx_exec_ctx_t; + + +#define NGX_MAX_PROCESSES 1024 + +#define NGX_PROCESS_NORESPAWN -1 +#define NGX_PROCESS_JUST_SPAWN -2 +#define NGX_PROCESS_RESPAWN -3 +#define NGX_PROCESS_JUST_RESPAWN -4 +#define NGX_PROCESS_DETACHED -5 + + +#define ngx_getpid getpid +#define ngx_getppid getppid + +#ifndef ngx_log_pid +#define ngx_log_pid ngx_pid +#endif + + +ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle, + ngx_spawn_proc_pt proc, void *data, char *name, ngx_int_t respawn); +ngx_pid_t ngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx); +ngx_int_t ngx_init_signals(ngx_log_t *log); +void ngx_debug_point(void); + + +#if (NGX_HAVE_SCHED_YIELD) +#define ngx_sched_yield() sched_yield() +#else +#define ngx_sched_yield() usleep(1) +#endif + + +extern int ngx_argc; +extern char **ngx_argv; +extern char **ngx_os_argv; + +extern ngx_pid_t ngx_pid; +extern ngx_pid_t ngx_parent; +extern ngx_socket_t ngx_channel; +extern ngx_int_t ngx_process_slot; +extern ngx_int_t ngx_last_process; +extern ngx_process_t ngx_processes[NGX_MAX_PROCESSES]; + + +#endif /* _NGX_PROCESS_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_process_cycle.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_process_cycle.c new file mode 100644 index 000000000..5bc5ce979 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_process_cycle.c @@ -0,0 +1,1191 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include + + +static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, + ngx_int_t type); +static void ngx_start_cache_manager_processes(ngx_cycle_t *cycle, + ngx_uint_t respawn); +static void ngx_pass_open_channel(ngx_cycle_t *cycle); +static void ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo); +static ngx_uint_t ngx_reap_children(ngx_cycle_t *cycle); +static void ngx_master_process_exit(ngx_cycle_t *cycle); +static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data); +static void ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker); +static void ngx_worker_process_exit(ngx_cycle_t *cycle); +static void ngx_channel_handler(ngx_event_t *ev); +static void ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data); +static void ngx_cache_manager_process_handler(ngx_event_t *ev); +static void ngx_cache_loader_process_handler(ngx_event_t *ev); + + +ngx_uint_t ngx_process; +ngx_uint_t ngx_worker; +ngx_pid_t ngx_pid; +ngx_pid_t ngx_parent; + +sig_atomic_t ngx_reap; +sig_atomic_t ngx_sigio; +sig_atomic_t ngx_sigalrm; +sig_atomic_t ngx_terminate; +sig_atomic_t ngx_quit; +sig_atomic_t ngx_debug_quit; +ngx_uint_t ngx_exiting; +sig_atomic_t ngx_reconfigure; +sig_atomic_t ngx_reopen; + +sig_atomic_t ngx_change_binary; +ngx_pid_t ngx_new_binary; +ngx_uint_t ngx_inherited; +ngx_uint_t ngx_daemonized; + +sig_atomic_t ngx_noaccept; +ngx_uint_t ngx_noaccepting; +ngx_uint_t ngx_restart; + + +static u_char master_process[] = "master process"; + + +static ngx_cache_manager_ctx_t ngx_cache_manager_ctx = { + ngx_cache_manager_process_handler, "cache manager process", 0 +}; + +static ngx_cache_manager_ctx_t ngx_cache_loader_ctx = { + ngx_cache_loader_process_handler, "cache loader process", 60000 +}; + + +static ngx_cycle_t ngx_exit_cycle; +static ngx_log_t ngx_exit_log; +static ngx_open_file_t ngx_exit_log_file; + + +void +ngx_master_process_cycle(ngx_cycle_t *cycle) +{ + char *title; + u_char *p; + size_t size; + ngx_int_t i; + ngx_uint_t sigio; + sigset_t set; + struct itimerval itv; + ngx_uint_t live; + ngx_msec_t delay; + ngx_core_conf_t *ccf; + + sigemptyset(&set); + sigaddset(&set, SIGCHLD); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGIO); + sigaddset(&set, SIGINT); + sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL)); + sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL)); + sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL)); + sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL)); + sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); + sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL)); + + if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "sigprocmask() failed"); + } + + sigemptyset(&set); + + + size = sizeof(master_process); + + for (i = 0; i < ngx_argc; i++) { + size += ngx_strlen(ngx_argv[i]) + 1; + } + + title = ngx_pnalloc(cycle->pool, size); + if (title == NULL) { + /* fatal */ + exit(2); + } + + p = ngx_cpymem(title, master_process, sizeof(master_process) - 1); + for (i = 0; i < ngx_argc; i++) { + *p++ = ' '; + p = ngx_cpystrn(p, (u_char *) ngx_argv[i], size); + } + + ngx_setproctitle(title); + + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + ngx_start_worker_processes(cycle, ccf->worker_processes, + NGX_PROCESS_RESPAWN); + ngx_start_cache_manager_processes(cycle, 0); + + ngx_new_binary = 0; + delay = 0; + sigio = 0; + live = 1; + + for ( ;; ) { + if (delay) { + if (ngx_sigalrm) { + sigio = 0; + delay *= 2; + ngx_sigalrm = 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "termination cycle: %M", delay); + + itv.it_interval.tv_sec = 0; + itv.it_interval.tv_usec = 0; + itv.it_value.tv_sec = delay / 1000; + itv.it_value.tv_usec = (delay % 1000 ) * 1000; + + if (setitimer(ITIMER_REAL, &itv, NULL) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "setitimer() failed"); + } + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "sigsuspend"); + + sigsuspend(&set); + + ngx_time_update(); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "wake up, sigio %i", sigio); + + if (ngx_reap) { + ngx_reap = 0; + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children"); + + live = ngx_reap_children(cycle); + } + + if (!live && (ngx_terminate || ngx_quit)) { + ngx_master_process_exit(cycle); + } + + if (ngx_terminate) { + if (delay == 0) { + delay = 50; + } + + if (sigio) { + sigio--; + continue; + } + + sigio = ccf->worker_processes + 2 /* cache processes */; + + if (delay > 1000) { + ngx_signal_worker_processes(cycle, SIGKILL); + } else { + ngx_signal_worker_processes(cycle, + ngx_signal_value(NGX_TERMINATE_SIGNAL)); + } + + continue; + } + + if (ngx_quit) { + ngx_signal_worker_processes(cycle, + ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); + ngx_close_listening_sockets(cycle); + + continue; + } + + if (ngx_reconfigure) { + ngx_reconfigure = 0; + + if (ngx_new_binary) { + ngx_start_worker_processes(cycle, ccf->worker_processes, + NGX_PROCESS_RESPAWN); + ngx_start_cache_manager_processes(cycle, 0); + ngx_noaccepting = 0; + + continue; + } + + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring"); + + cycle = ngx_init_cycle(cycle); + if (cycle == NULL) { + cycle = (ngx_cycle_t *) ngx_cycle; + continue; + } + + ngx_cycle = cycle; + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, + ngx_core_module); + ngx_start_worker_processes(cycle, ccf->worker_processes, + NGX_PROCESS_JUST_RESPAWN); + ngx_start_cache_manager_processes(cycle, 1); + + /* allow new processes to start */ + ngx_msleep(100); + + live = 1; + ngx_signal_worker_processes(cycle, + ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); + } + + if (ngx_restart) { + ngx_restart = 0; + ngx_start_worker_processes(cycle, ccf->worker_processes, + NGX_PROCESS_RESPAWN); + ngx_start_cache_manager_processes(cycle, 0); + live = 1; + } + + if (ngx_reopen) { + ngx_reopen = 0; + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs"); + ngx_reopen_files(cycle, ccf->user); + ngx_signal_worker_processes(cycle, + ngx_signal_value(NGX_REOPEN_SIGNAL)); + } + + if (ngx_change_binary) { + ngx_change_binary = 0; + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "changing binary"); + ngx_new_binary = ngx_exec_new_binary(cycle, ngx_argv); + } + + if (ngx_noaccept) { + ngx_noaccept = 0; + ngx_noaccepting = 1; + ngx_signal_worker_processes(cycle, + ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); + } + } +} + + +void +ngx_single_process_cycle(ngx_cycle_t *cycle) +{ + ngx_uint_t i; + + if (ngx_set_environment(cycle, NULL) == NULL) { + /* fatal */ + exit(2); + } + + for (i = 0; cycle->modules[i]; i++) { + if (cycle->modules[i]->init_process) { + if (cycle->modules[i]->init_process(cycle) == NGX_ERROR) { + /* fatal */ + exit(2); + } + } + } + + for ( ;; ) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle"); + + ngx_process_events_and_timers(cycle); + + if (ngx_terminate || ngx_quit) { + + for (i = 0; cycle->modules[i]; i++) { + if (cycle->modules[i]->exit_process) { + cycle->modules[i]->exit_process(cycle); + } + } + + ngx_master_process_exit(cycle); + } + + if (ngx_reconfigure) { + ngx_reconfigure = 0; + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring"); + + cycle = ngx_init_cycle(cycle); + if (cycle == NULL) { + cycle = (ngx_cycle_t *) ngx_cycle; + continue; + } + + ngx_cycle = cycle; + } + + if (ngx_reopen) { + ngx_reopen = 0; + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs"); + ngx_reopen_files(cycle, (ngx_uid_t) -1); + } + } +} + + +static void +ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type) +{ + ngx_int_t i; + + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start worker processes"); + + for (i = 0; i < n; i++) { + + ngx_spawn_process(cycle, ngx_worker_process_cycle, + (void *) (intptr_t) i, "worker process", type); + + ngx_pass_open_channel(cycle); + } +} + + +static void +ngx_start_cache_manager_processes(ngx_cycle_t *cycle, ngx_uint_t respawn) +{ + ngx_uint_t i, manager, loader; + ngx_path_t **path; + + manager = 0; + loader = 0; + + path = ngx_cycle->paths.elts; + for (i = 0; i < ngx_cycle->paths.nelts; i++) { + + if (path[i]->manager) { + manager = 1; + } + + if (path[i]->loader) { + loader = 1; + } + } + + if (manager == 0) { + return; + } + + ngx_spawn_process(cycle, ngx_cache_manager_process_cycle, + &ngx_cache_manager_ctx, "cache manager process", + respawn ? NGX_PROCESS_JUST_RESPAWN : NGX_PROCESS_RESPAWN); + + ngx_pass_open_channel(cycle); + + if (loader == 0) { + return; + } + + ngx_spawn_process(cycle, ngx_cache_manager_process_cycle, + &ngx_cache_loader_ctx, "cache loader process", + respawn ? NGX_PROCESS_JUST_SPAWN : NGX_PROCESS_NORESPAWN); + + ngx_pass_open_channel(cycle); +} + + +static void +ngx_pass_open_channel(ngx_cycle_t *cycle) +{ + ngx_int_t i; + ngx_channel_t ch; + + ngx_memzero(&ch, sizeof(ngx_channel_t)); + + ch.command = NGX_CMD_OPEN_CHANNEL; + ch.pid = ngx_processes[ngx_process_slot].pid; + ch.slot = ngx_process_slot; + ch.fd = ngx_processes[ngx_process_slot].channel[0]; + + for (i = 0; i < ngx_last_process; i++) { + + if (i == ngx_process_slot + || ngx_processes[i].pid == -1 + || ngx_processes[i].channel[0] == -1) + { + continue; + } + + ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0, + "pass channel s:%i pid:%P fd:%d to s:%i pid:%P fd:%d", + ch.slot, ch.pid, ch.fd, + i, ngx_processes[i].pid, + ngx_processes[i].channel[0]); + + /* TODO: NGX_AGAIN */ + + ngx_write_channel(ngx_processes[i].channel[0], + &ch, sizeof(ngx_channel_t), cycle->log); + } +} + + +static void +ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo) +{ + ngx_int_t i; + ngx_err_t err; + ngx_channel_t ch; + + ngx_memzero(&ch, sizeof(ngx_channel_t)); + +#if (NGX_BROKEN_SCM_RIGHTS) + + ch.command = 0; + +#else + + switch (signo) { + + case ngx_signal_value(NGX_SHUTDOWN_SIGNAL): + ch.command = NGX_CMD_QUIT; + break; + + case ngx_signal_value(NGX_TERMINATE_SIGNAL): + ch.command = NGX_CMD_TERMINATE; + break; + + case ngx_signal_value(NGX_REOPEN_SIGNAL): + ch.command = NGX_CMD_REOPEN; + break; + + default: + ch.command = 0; + } + +#endif + + ch.fd = -1; + + + for (i = 0; i < ngx_last_process; i++) { + + ngx_log_debug7(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "child: %i %P e:%d t:%d d:%d r:%d j:%d", + i, + ngx_processes[i].pid, + ngx_processes[i].exiting, + ngx_processes[i].exited, + ngx_processes[i].detached, + ngx_processes[i].respawn, + ngx_processes[i].just_spawn); + + if (ngx_processes[i].detached || ngx_processes[i].pid == -1) { + continue; + } + + if (ngx_processes[i].just_spawn) { + ngx_processes[i].just_spawn = 0; + continue; + } + + if (ngx_processes[i].exiting + && signo == ngx_signal_value(NGX_SHUTDOWN_SIGNAL)) + { + continue; + } + + if (ch.command) { + if (ngx_write_channel(ngx_processes[i].channel[0], + &ch, sizeof(ngx_channel_t), cycle->log) + == NGX_OK) + { + if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)) { + ngx_processes[i].exiting = 1; + } + + continue; + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0, + "kill (%P, %d)", ngx_processes[i].pid, signo); + + if (kill(ngx_processes[i].pid, signo) == -1) { + err = ngx_errno; + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, + "kill(%P, %d) failed", ngx_processes[i].pid, signo); + + if (err == NGX_ESRCH) { + ngx_processes[i].exited = 1; + ngx_processes[i].exiting = 0; + ngx_reap = 1; + } + + continue; + } + + if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)) { + ngx_processes[i].exiting = 1; + } + } +} + + +static ngx_uint_t +ngx_reap_children(ngx_cycle_t *cycle) +{ + ngx_int_t i, n; + ngx_uint_t live; + ngx_channel_t ch; + ngx_core_conf_t *ccf; + + ngx_memzero(&ch, sizeof(ngx_channel_t)); + + ch.command = NGX_CMD_CLOSE_CHANNEL; + ch.fd = -1; + + live = 0; + for (i = 0; i < ngx_last_process; i++) { + + ngx_log_debug7(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "child: %i %P e:%d t:%d d:%d r:%d j:%d", + i, + ngx_processes[i].pid, + ngx_processes[i].exiting, + ngx_processes[i].exited, + ngx_processes[i].detached, + ngx_processes[i].respawn, + ngx_processes[i].just_spawn); + + if (ngx_processes[i].pid == -1) { + continue; + } + + if (ngx_processes[i].exited) { + + if (!ngx_processes[i].detached) { + ngx_close_channel(ngx_processes[i].channel, cycle->log); + + ngx_processes[i].channel[0] = -1; + ngx_processes[i].channel[1] = -1; + + ch.pid = ngx_processes[i].pid; + ch.slot = i; + + for (n = 0; n < ngx_last_process; n++) { + if (ngx_processes[n].exited + || ngx_processes[n].pid == -1 + || ngx_processes[n].channel[0] == -1) + { + continue; + } + + ngx_log_debug3(NGX_LOG_DEBUG_CORE, cycle->log, 0, + "pass close channel s:%i pid:%P to:%P", + ch.slot, ch.pid, ngx_processes[n].pid); + + /* TODO: NGX_AGAIN */ + + ngx_write_channel(ngx_processes[n].channel[0], + &ch, sizeof(ngx_channel_t), cycle->log); + } + } + + if (ngx_processes[i].respawn + && !ngx_processes[i].exiting + && !ngx_terminate + && !ngx_quit) + { + if (ngx_spawn_process(cycle, ngx_processes[i].proc, + ngx_processes[i].data, + ngx_processes[i].name, i) + == NGX_INVALID_PID) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "could not respawn %s", + ngx_processes[i].name); + continue; + } + + + ngx_pass_open_channel(cycle); + + live = 1; + + continue; + } + + if (ngx_processes[i].pid == ngx_new_binary) { + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, + ngx_core_module); + + if (ngx_rename_file((char *) ccf->oldpid.data, + (char *) ccf->pid.data) + == NGX_FILE_ERROR) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_rename_file_n " %s back to %s failed " + "after the new binary process \"%s\" exited", + ccf->oldpid.data, ccf->pid.data, ngx_argv[0]); + } + + ngx_new_binary = 0; + if (ngx_noaccepting) { + ngx_restart = 1; + ngx_noaccepting = 0; + } + } + + if (i == ngx_last_process - 1) { + ngx_last_process--; + + } else { + ngx_processes[i].pid = -1; + } + + } else if (ngx_processes[i].exiting || !ngx_processes[i].detached) { + live = 1; + } + } + + return live; +} + + +static void +ngx_master_process_exit(ngx_cycle_t *cycle) +{ + ngx_uint_t i; + + ngx_delete_pidfile(cycle); + + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exit"); + + for (i = 0; cycle->modules[i]; i++) { + if (cycle->modules[i]->exit_master) { + cycle->modules[i]->exit_master(cycle); + } + } + + ngx_close_listening_sockets(cycle); + + /* + * Copy ngx_cycle->log related data to the special static exit cycle, + * log, and log file structures enough to allow a signal handler to log. + * The handler may be called when standard ngx_cycle->log allocated from + * ngx_cycle->pool is already destroyed. + */ + + + ngx_exit_log = *ngx_log_get_file_log(ngx_cycle->log); + + ngx_exit_log_file.fd = ngx_exit_log.file->fd; + ngx_exit_log.file = &ngx_exit_log_file; + ngx_exit_log.next = NULL; + ngx_exit_log.writer = NULL; + + ngx_exit_cycle.log = &ngx_exit_log; + ngx_exit_cycle.files = ngx_cycle->files; + ngx_exit_cycle.files_n = ngx_cycle->files_n; + ngx_cycle = &ngx_exit_cycle; + + ngx_destroy_pool(cycle->pool); + + exit(0); +} + + +static void +ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data) +{ + ngx_int_t worker = (intptr_t) data; + + ngx_process = NGX_PROCESS_WORKER; + ngx_worker = worker; + + ngx_worker_process_init(cycle, worker); + + ngx_setproctitle("worker process"); + + for ( ;; ) { + + if (ngx_exiting) { + if (ngx_event_no_timers_left() == NGX_OK) { + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting"); + ngx_worker_process_exit(cycle); + } + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle"); + + ngx_process_events_and_timers(cycle); + + if (ngx_terminate) { + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting"); + ngx_worker_process_exit(cycle); + } + + if (ngx_quit) { + ngx_quit = 0; + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, + "gracefully shutting down"); + ngx_setproctitle("worker process is shutting down"); + + if (!ngx_exiting) { + ngx_exiting = 1; + ngx_set_shutdown_timer(cycle); + ngx_close_listening_sockets(cycle); + ngx_close_idle_connections(cycle); + ngx_event_process_posted(cycle, &ngx_posted_events); + } + } + + if (ngx_reopen) { + ngx_reopen = 0; + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs"); + ngx_reopen_files(cycle, -1); + } + } +} + + +static void +ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker) +{ + sigset_t set; + ngx_int_t n; + ngx_time_t *tp; + ngx_uint_t i; + ngx_cpuset_t *cpu_affinity; + struct rlimit rlmt; + ngx_core_conf_t *ccf; + + if (ngx_set_environment(cycle, NULL) == NULL) { + /* fatal */ + exit(2); + } + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + if (worker >= 0 && ccf->priority != 0) { + if (setpriority(PRIO_PROCESS, 0, ccf->priority) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "setpriority(%d) failed", ccf->priority); + } + } + + if (ccf->rlimit_nofile != NGX_CONF_UNSET) { + rlmt.rlim_cur = (rlim_t) ccf->rlimit_nofile; + rlmt.rlim_max = (rlim_t) ccf->rlimit_nofile; + + if (setrlimit(RLIMIT_NOFILE, &rlmt) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "setrlimit(RLIMIT_NOFILE, %i) failed", + ccf->rlimit_nofile); + } + } + + if (ccf->rlimit_core != NGX_CONF_UNSET) { + rlmt.rlim_cur = (rlim_t) ccf->rlimit_core; + rlmt.rlim_max = (rlim_t) ccf->rlimit_core; + + if (setrlimit(RLIMIT_CORE, &rlmt) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "setrlimit(RLIMIT_CORE, %O) failed", + ccf->rlimit_core); + } + } + + if (geteuid() == 0) { + if (setgid(ccf->group) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "setgid(%d) failed", ccf->group); + /* fatal */ + exit(2); + } + + if (initgroups(ccf->username, ccf->group) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "initgroups(%s, %d) failed", + ccf->username, ccf->group); + } + +#if (NGX_HAVE_PR_SET_KEEPCAPS && NGX_HAVE_CAPABILITIES) + if (ccf->transparent && ccf->user) { + if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "prctl(PR_SET_KEEPCAPS, 1) failed"); + /* fatal */ + exit(2); + } + } +#endif + + if (setuid(ccf->user) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "setuid(%d) failed", ccf->user); + /* fatal */ + exit(2); + } + +#if (NGX_HAVE_CAPABILITIES) + if (ccf->transparent && ccf->user) { + struct __user_cap_data_struct data; + struct __user_cap_header_struct header; + + ngx_memzero(&header, sizeof(struct __user_cap_header_struct)); + ngx_memzero(&data, sizeof(struct __user_cap_data_struct)); + + header.version = _LINUX_CAPABILITY_VERSION_1; + data.effective = CAP_TO_MASK(CAP_NET_RAW); + data.permitted = data.effective; + + if (syscall(SYS_capset, &header, &data) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "capset() failed"); + /* fatal */ + exit(2); + } + } +#endif + } + + if (worker >= 0) { + cpu_affinity = ngx_get_cpu_affinity(worker); + + if (cpu_affinity) { + ngx_setaffinity(cpu_affinity, cycle->log); + } + } + +#if (NGX_HAVE_PR_SET_DUMPABLE) + + /* allow coredump after setuid() in Linux 2.4.x */ + + if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "prctl(PR_SET_DUMPABLE) failed"); + } + +#endif + + if (ccf->working_directory.len) { + if (chdir((char *) ccf->working_directory.data) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "chdir(\"%s\") failed", ccf->working_directory.data); + /* fatal */ + exit(2); + } + } + + sigemptyset(&set); + + if (sigprocmask(SIG_SETMASK, &set, NULL) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "sigprocmask() failed"); + } + + tp = ngx_timeofday(); + srandom(((unsigned) ngx_pid << 16) ^ tp->sec ^ tp->msec); + + for (i = 0; cycle->modules[i]; i++) { + if (cycle->modules[i]->init_process) { + if (cycle->modules[i]->init_process(cycle) == NGX_ERROR) { + /* fatal */ + exit(2); + } + } + } + + for (n = 0; n < ngx_last_process; n++) { + + if (ngx_processes[n].pid == -1) { + continue; + } + + if (n == ngx_process_slot) { + continue; + } + + if (ngx_processes[n].channel[1] == -1) { + continue; + } + + if (close(ngx_processes[n].channel[1]) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "close() channel failed"); + } + } + + if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "close() channel failed"); + } + +#if 0 + ngx_last_process = 0; +#endif + + if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT, + ngx_channel_handler) + == NGX_ERROR) + { + /* fatal */ + exit(2); + } +} + + +static void +ngx_worker_process_exit(ngx_cycle_t *cycle) +{ + ngx_uint_t i; + ngx_connection_t *c; + + for (i = 0; cycle->modules[i]; i++) { + if (cycle->modules[i]->exit_process) { + cycle->modules[i]->exit_process(cycle); + } + } + + if (ngx_exiting && !ngx_terminate) { + c = cycle->connections; + for (i = 0; i < cycle->connection_n; i++) { + if (c[i].fd != -1 + && c[i].read + && !c[i].read->accept + && !c[i].read->channel + && !c[i].read->resolver) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "*%uA open socket #%d left in connection %ui", + c[i].number, c[i].fd, i); + ngx_debug_quit = 1; + } + } + } + + if (ngx_debug_quit) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "aborting"); + ngx_debug_point(); + } + + /* + * Copy ngx_cycle->log related data to the special static exit cycle, + * log, and log file structures enough to allow a signal handler to log. + * The handler may be called when standard ngx_cycle->log allocated from + * ngx_cycle->pool is already destroyed. + */ + + ngx_exit_log = *ngx_log_get_file_log(ngx_cycle->log); + + ngx_exit_log_file.fd = ngx_exit_log.file->fd; + ngx_exit_log.file = &ngx_exit_log_file; + ngx_exit_log.next = NULL; + ngx_exit_log.writer = NULL; + + ngx_exit_cycle.log = &ngx_exit_log; + ngx_exit_cycle.files = ngx_cycle->files; + ngx_exit_cycle.files_n = ngx_cycle->files_n; + ngx_cycle = &ngx_exit_cycle; + + ngx_destroy_pool(cycle->pool); + + ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, "exit"); + + exit(0); +} + + +static void +ngx_channel_handler(ngx_event_t *ev) +{ + ngx_int_t n; + ngx_channel_t ch; + ngx_connection_t *c; + + if (ev->timedout) { + ev->timedout = 0; + return; + } + + c = ev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, ev->log, 0, "channel handler"); + + for ( ;; ) { + + n = ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log); + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, "channel: %i", n); + + if (n == NGX_ERROR) { + + if (ngx_event_flags & NGX_USE_EPOLL_EVENT) { + ngx_del_conn(c, 0); + } + + ngx_close_connection(c); + return; + } + + if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) { + if (ngx_add_event(ev, NGX_READ_EVENT, 0) == NGX_ERROR) { + return; + } + } + + if (n == NGX_AGAIN) { + return; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, + "channel command: %ui", ch.command); + + switch (ch.command) { + + case NGX_CMD_QUIT: + ngx_quit = 1; + break; + + case NGX_CMD_TERMINATE: + ngx_terminate = 1; + break; + + case NGX_CMD_REOPEN: + ngx_reopen = 1; + break; + + case NGX_CMD_OPEN_CHANNEL: + + ngx_log_debug3(NGX_LOG_DEBUG_CORE, ev->log, 0, + "get channel s:%i pid:%P fd:%d", + ch.slot, ch.pid, ch.fd); + + ngx_processes[ch.slot].pid = ch.pid; + ngx_processes[ch.slot].channel[0] = ch.fd; + break; + + case NGX_CMD_CLOSE_CHANNEL: + + ngx_log_debug4(NGX_LOG_DEBUG_CORE, ev->log, 0, + "close channel s:%i pid:%P our:%P fd:%d", + ch.slot, ch.pid, ngx_processes[ch.slot].pid, + ngx_processes[ch.slot].channel[0]); + + if (close(ngx_processes[ch.slot].channel[0]) == -1) { + ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, + "close() channel failed"); + } + + ngx_processes[ch.slot].channel[0] = -1; + break; + } + } +} + + +static void +ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data) +{ + ngx_cache_manager_ctx_t *ctx = data; + + void *ident[4]; + ngx_event_t ev; + + /* + * Set correct process type since closing listening Unix domain socket + * in a master process also removes the Unix domain socket file. + */ + ngx_process = NGX_PROCESS_HELPER; + + ngx_close_listening_sockets(cycle); + + /* Set a moderate number of connections for a helper process. */ + cycle->connection_n = 512; + + ngx_worker_process_init(cycle, -1); + + ngx_memzero(&ev, sizeof(ngx_event_t)); + ev.handler = ctx->handler; + ev.data = ident; + ev.log = cycle->log; + ident[3] = (void *) -1; + + ngx_use_accept_mutex = 0; + + ngx_setproctitle(ctx->name); + + ngx_add_timer(&ev, ctx->delay); + + for ( ;; ) { + + if (ngx_terminate || ngx_quit) { + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting"); + exit(0); + } + + if (ngx_reopen) { + ngx_reopen = 0; + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs"); + ngx_reopen_files(cycle, -1); + } + + ngx_process_events_and_timers(cycle); + } +} + + +static void +ngx_cache_manager_process_handler(ngx_event_t *ev) +{ + ngx_uint_t i; + ngx_msec_t next, n; + ngx_path_t **path; + + next = 60 * 60 * 1000; + + path = ngx_cycle->paths.elts; + for (i = 0; i < ngx_cycle->paths.nelts; i++) { + + if (path[i]->manager) { + n = path[i]->manager(path[i]->data); + + next = (n <= next) ? n : next; + + ngx_time_update(); + } + } + + if (next == 0) { + next = 1; + } + + ngx_add_timer(ev, next); +} + + +static void +ngx_cache_loader_process_handler(ngx_event_t *ev) +{ + ngx_uint_t i; + ngx_path_t **path; + ngx_cycle_t *cycle; + + cycle = (ngx_cycle_t *) ngx_cycle; + + path = cycle->paths.elts; + for (i = 0; i < cycle->paths.nelts; i++) { + + if (ngx_terminate || ngx_quit) { + break; + } + + if (path[i]->loader) { + path[i]->loader(path[i]->data); + ngx_time_update(); + } + } + + exit(0); +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_process_cycle.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_process_cycle.h new file mode 100644 index 000000000..69495d5f4 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_process_cycle.h @@ -0,0 +1,61 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_PROCESS_CYCLE_H_INCLUDED_ +#define _NGX_PROCESS_CYCLE_H_INCLUDED_ + + +#include +#include + + +#define NGX_CMD_OPEN_CHANNEL 1 +#define NGX_CMD_CLOSE_CHANNEL 2 +#define NGX_CMD_QUIT 3 +#define NGX_CMD_TERMINATE 4 +#define NGX_CMD_REOPEN 5 + + +#define NGX_PROCESS_SINGLE 0 +#define NGX_PROCESS_MASTER 1 +#define NGX_PROCESS_SIGNALLER 2 +#define NGX_PROCESS_WORKER 3 +#define NGX_PROCESS_HELPER 4 + + +typedef struct { + ngx_event_handler_pt handler; + char *name; + ngx_msec_t delay; +} ngx_cache_manager_ctx_t; + + +void ngx_master_process_cycle(ngx_cycle_t *cycle); +void ngx_single_process_cycle(ngx_cycle_t *cycle); + + +extern ngx_uint_t ngx_process; +extern ngx_uint_t ngx_worker; +extern ngx_pid_t ngx_pid; +extern ngx_pid_t ngx_new_binary; +extern ngx_uint_t ngx_inherited; +extern ngx_uint_t ngx_daemonized; +extern ngx_uint_t ngx_exiting; + +extern sig_atomic_t ngx_reap; +extern sig_atomic_t ngx_sigio; +extern sig_atomic_t ngx_sigalrm; +extern sig_atomic_t ngx_quit; +extern sig_atomic_t ngx_debug_quit; +extern sig_atomic_t ngx_terminate; +extern sig_atomic_t ngx_noaccept; +extern sig_atomic_t ngx_reconfigure; +extern sig_atomic_t ngx_reopen; +extern sig_atomic_t ngx_change_binary; + + +#endif /* _NGX_PROCESS_CYCLE_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_readv_chain.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_readv_chain.c new file mode 100644 index 000000000..48fa0a73d --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_readv_chain.c @@ -0,0 +1,252 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +ssize_t +ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain, off_t limit) +{ + u_char *prev; + ssize_t n, size; + ngx_err_t err; + ngx_array_t vec; + ngx_event_t *rev; + struct iovec *iov, iovs[NGX_IOVS_PREALLOCATE]; + + rev = c->read; + +#if (NGX_HAVE_KQUEUE) + + if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "readv: eof:%d, avail:%d, err:%d", + rev->pending_eof, rev->available, rev->kq_errno); + + if (rev->available == 0) { + if (rev->pending_eof) { + rev->ready = 0; + rev->eof = 1; + + ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno, + "kevent() reported about an closed connection"); + + if (rev->kq_errno) { + rev->error = 1; + ngx_set_socket_errno(rev->kq_errno); + return NGX_ERROR; + } + + return 0; + + } else { + rev->ready = 0; + return NGX_AGAIN; + } + } + } + +#endif + +#if (NGX_HAVE_EPOLLRDHUP) + + if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) + && ngx_use_epoll_rdhup) + { + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "readv: eof:%d, avail:%d", + rev->pending_eof, rev->available); + + if (rev->available == 0 && !rev->pending_eof) { + rev->ready = 0; + return NGX_AGAIN; + } + } + +#endif + + prev = NULL; + iov = NULL; + size = 0; + + vec.elts = iovs; + vec.nelts = 0; + vec.size = sizeof(struct iovec); + vec.nalloc = NGX_IOVS_PREALLOCATE; + vec.pool = c->pool; + + /* coalesce the neighbouring bufs */ + + while (chain) { + n = chain->buf->end - chain->buf->last; + + if (limit) { + if (size >= limit) { + break; + } + + if (size + n > limit) { + n = (ssize_t) (limit - size); + } + } + + if (prev == chain->buf->last) { + iov->iov_len += n; + + } else { + if (vec.nelts == vec.nalloc) { + break; + } + + iov = ngx_array_push(&vec); + if (iov == NULL) { + return NGX_ERROR; + } + + iov->iov_base = (void *) chain->buf->last; + iov->iov_len = n; + } + + size += n; + prev = chain->buf->end; + chain = chain->next; + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "readv: %ui, last:%uz", vec.nelts, iov->iov_len); + + do { + n = readv(c->fd, (struct iovec *) vec.elts, vec.nelts); + + if (n == 0) { + rev->ready = 0; + rev->eof = 1; + +#if (NGX_HAVE_KQUEUE) + + /* + * on FreeBSD readv() may return 0 on closed socket + * even if kqueue reported about available data + */ + + if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { + rev->available = 0; + } + +#endif + + return 0; + } + + if (n > 0) { + +#if (NGX_HAVE_KQUEUE) + + if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { + rev->available -= n; + + /* + * rev->available may be negative here because some additional + * bytes may be received between kevent() and readv() + */ + + if (rev->available <= 0) { + if (!rev->pending_eof) { + rev->ready = 0; + } + + rev->available = 0; + } + + return n; + } + +#endif + +#if (NGX_HAVE_FIONREAD) + + if (rev->available >= 0) { + rev->available -= n; + + /* + * negative rev->available means some additional bytes + * were received between kernel notification and readv(), + * and therefore ev->ready can be safely reset even for + * edge-triggered event methods + */ + + if (rev->available < 0) { + rev->available = 0; + rev->ready = 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "readv: avail:%d", rev->available); + + } else if (n == size) { + + if (ngx_socket_nread(c->fd, &rev->available) == -1) { + n = ngx_connection_error(c, ngx_socket_errno, + ngx_socket_nread_n " failed"); + break; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "readv: avail:%d", rev->available); + } + +#endif + +#if (NGX_HAVE_EPOLLRDHUP) + + if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) + && ngx_use_epoll_rdhup) + { + if (n < size) { + if (!rev->pending_eof) { + rev->ready = 0; + } + + rev->available = 0; + } + + return n; + } + +#endif + + if (n < size && !(ngx_event_flags & NGX_USE_GREEDY_EVENT)) { + rev->ready = 0; + } + + return n; + } + + err = ngx_socket_errno; + + if (err == NGX_EAGAIN || err == NGX_EINTR) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "readv() not ready"); + n = NGX_AGAIN; + + } else { + n = ngx_connection_error(c, err, "readv() failed"); + break; + } + + } while (err == NGX_EINTR); + + rev->ready = 0; + + if (n == NGX_ERROR) { + c->read->error = 1; + } + + return n; +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_recv.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_recv.c new file mode 100644 index 000000000..cc04a5fbb --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_recv.c @@ -0,0 +1,203 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +ssize_t +ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size) +{ + ssize_t n; + ngx_err_t err; + ngx_event_t *rev; + + rev = c->read; + +#if (NGX_HAVE_KQUEUE) + + if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "recv: eof:%d, avail:%d, err:%d", + rev->pending_eof, rev->available, rev->kq_errno); + + if (rev->available == 0) { + if (rev->pending_eof) { + rev->ready = 0; + rev->eof = 1; + + if (rev->kq_errno) { + rev->error = 1; + ngx_set_socket_errno(rev->kq_errno); + + return ngx_connection_error(c, rev->kq_errno, + "kevent() reported about an closed connection"); + } + + return 0; + + } else { + rev->ready = 0; + return NGX_AGAIN; + } + } + } + +#endif + +#if (NGX_HAVE_EPOLLRDHUP) + + if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) + && ngx_use_epoll_rdhup) + { + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "recv: eof:%d, avail:%d", + rev->pending_eof, rev->available); + + if (rev->available == 0 && !rev->pending_eof) { + rev->ready = 0; + return NGX_AGAIN; + } + } + +#endif + + do { + n = recv(c->fd, buf, size, 0); + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "recv: fd:%d %z of %uz", c->fd, n, size); + + if (n == 0) { + rev->ready = 0; + rev->eof = 1; + +#if (NGX_HAVE_KQUEUE) + + /* + * on FreeBSD recv() may return 0 on closed socket + * even if kqueue reported about available data + */ + + if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { + rev->available = 0; + } + +#endif + + return 0; + } + + if (n > 0) { + +#if (NGX_HAVE_KQUEUE) + + if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { + rev->available -= n; + + /* + * rev->available may be negative here because some additional + * bytes may be received between kevent() and recv() + */ + + if (rev->available <= 0) { + if (!rev->pending_eof) { + rev->ready = 0; + } + + rev->available = 0; + } + + return n; + } + +#endif + +#if (NGX_HAVE_FIONREAD) + + if (rev->available >= 0) { + rev->available -= n; + + /* + * negative rev->available means some additional bytes + * were received between kernel notification and recv(), + * and therefore ev->ready can be safely reset even for + * edge-triggered event methods + */ + + if (rev->available < 0) { + rev->available = 0; + rev->ready = 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "recv: avail:%d", rev->available); + + } else if ((size_t) n == size) { + + if (ngx_socket_nread(c->fd, &rev->available) == -1) { + n = ngx_connection_error(c, ngx_socket_errno, + ngx_socket_nread_n " failed"); + break; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "recv: avail:%d", rev->available); + } + +#endif + +#if (NGX_HAVE_EPOLLRDHUP) + + if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) + && ngx_use_epoll_rdhup) + { + if ((size_t) n < size) { + if (!rev->pending_eof) { + rev->ready = 0; + } + + rev->available = 0; + } + + return n; + } + +#endif + + if ((size_t) n < size + && !(ngx_event_flags & NGX_USE_GREEDY_EVENT)) + { + rev->ready = 0; + } + + return n; + } + + err = ngx_socket_errno; + + if (err == NGX_EAGAIN || err == NGX_EINTR) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "recv() not ready"); + n = NGX_AGAIN; + + } else { + n = ngx_connection_error(c, err, "recv() failed"); + break; + } + + } while (err == NGX_EINTR); + + rev->ready = 0; + + if (n == NGX_ERROR) { + rev->error = 1; + } + + return n; +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_send.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_send.c new file mode 100644 index 000000000..61ea20252 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_send.c @@ -0,0 +1,73 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +ssize_t +ngx_unix_send(ngx_connection_t *c, u_char *buf, size_t size) +{ + ssize_t n; + ngx_err_t err; + ngx_event_t *wev; + + wev = c->write; + +#if (NGX_HAVE_KQUEUE) + + if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) { + (void) ngx_connection_error(c, wev->kq_errno, + "kevent() reported about an closed connection"); + wev->error = 1; + return NGX_ERROR; + } + +#endif + + for ( ;; ) { + n = send(c->fd, buf, size, 0); + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "send: fd:%d %z of %uz", c->fd, n, size); + + if (n > 0) { + if (n < (ssize_t) size) { + wev->ready = 0; + } + + c->sent += n; + + return n; + } + + err = ngx_socket_errno; + + if (n == 0) { + ngx_log_error(NGX_LOG_ALERT, c->log, err, "send() returned zero"); + wev->ready = 0; + return n; + } + + if (err == NGX_EAGAIN || err == NGX_EINTR) { + wev->ready = 0; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "send() not ready"); + + if (err == NGX_EAGAIN) { + return NGX_AGAIN; + } + + } else { + wev->error = 1; + (void) ngx_connection_error(c, err, "send() failed"); + return NGX_ERROR; + } + } +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_setaffinity.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_setaffinity.c new file mode 100644 index 000000000..34ec390b7 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_setaffinity.c @@ -0,0 +1,53 @@ + +/* + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +#if (NGX_HAVE_CPUSET_SETAFFINITY) + +void +ngx_setaffinity(ngx_cpuset_t *cpu_affinity, ngx_log_t *log) +{ + ngx_uint_t i; + + for (i = 0; i < CPU_SETSIZE; i++) { + if (CPU_ISSET(i, cpu_affinity)) { + ngx_log_error(NGX_LOG_NOTICE, log, 0, + "cpuset_setaffinity(): using cpu #%ui", i); + } + } + + if (cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, + sizeof(cpuset_t), cpu_affinity) == -1) + { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "cpuset_setaffinity() failed"); + } +} + +#elif (NGX_HAVE_SCHED_SETAFFINITY) + +void +ngx_setaffinity(ngx_cpuset_t *cpu_affinity, ngx_log_t *log) +{ + ngx_uint_t i; + + for (i = 0; i < CPU_SETSIZE; i++) { + if (CPU_ISSET(i, cpu_affinity)) { + ngx_log_error(NGX_LOG_NOTICE, log, 0, + "sched_setaffinity(): using cpu #%ui", i); + } + } + + if (sched_setaffinity(0, sizeof(cpu_set_t), cpu_affinity) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "sched_setaffinity() failed"); + } +} + +#endif diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_setaffinity.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_setaffinity.h new file mode 100644 index 000000000..a4139edc8 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_setaffinity.h @@ -0,0 +1,37 @@ + +/* + * Copyright (C) Nginx, Inc. + */ + +#ifndef _NGX_SETAFFINITY_H_INCLUDED_ +#define _NGX_SETAFFINITY_H_INCLUDED_ + + +#if (NGX_HAVE_SCHED_SETAFFINITY || NGX_HAVE_CPUSET_SETAFFINITY) + +#define NGX_HAVE_CPU_AFFINITY 1 + +#if (NGX_HAVE_SCHED_SETAFFINITY) + +typedef cpu_set_t ngx_cpuset_t; + +#elif (NGX_HAVE_CPUSET_SETAFFINITY) + +#include + +typedef cpuset_t ngx_cpuset_t; + +#endif + +void ngx_setaffinity(ngx_cpuset_t *cpu_affinity, ngx_log_t *log); + +#else + +#define ngx_setaffinity(cpu_affinity, log) + +typedef uint64_t ngx_cpuset_t; + +#endif + + +#endif /* _NGX_SETAFFINITY_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_setproctitle.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_setproctitle.c new file mode 100644 index 000000000..91afa5191 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_setproctitle.c @@ -0,0 +1,135 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +#if (NGX_SETPROCTITLE_USES_ENV) + +/* + * To change the process title in Linux and Solaris we have to set argv[1] + * to NULL and to copy the title to the same place where the argv[0] points to. + * However, argv[0] may be too small to hold a new title. Fortunately, Linux + * and Solaris store argv[] and environ[] one after another. So we should + * ensure that is the continuous memory and then we allocate the new memory + * for environ[] and copy it. After this we could use the memory starting + * from argv[0] for our process title. + * + * The Solaris's standard /bin/ps does not show the changed process title. + * You have to use "/usr/ucb/ps -w" instead. Besides, the UCB ps does not + * show a new title if its length less than the origin command line length. + * To avoid it we append to a new title the origin command line in the + * parenthesis. + */ + +extern char **environ; + +static char *ngx_os_argv_last; + +ngx_int_t +ngx_init_setproctitle(ngx_log_t *log) +{ + u_char *p; + size_t size; + ngx_uint_t i; + + size = 0; + + for (i = 0; environ[i]; i++) { + size += ngx_strlen(environ[i]) + 1; + } + + p = ngx_alloc(size, log); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_os_argv_last = ngx_os_argv[0]; + + for (i = 0; ngx_os_argv[i]; i++) { + if (ngx_os_argv_last == ngx_os_argv[i]) { + ngx_os_argv_last = ngx_os_argv[i] + ngx_strlen(ngx_os_argv[i]) + 1; + } + } + + for (i = 0; environ[i]; i++) { + if (ngx_os_argv_last == environ[i]) { + + size = ngx_strlen(environ[i]) + 1; + ngx_os_argv_last = environ[i] + size; + + ngx_cpystrn(p, (u_char *) environ[i], size); + environ[i] = (char *) p; + p += size; + } + } + + ngx_os_argv_last--; + + return NGX_OK; +} + + +void +ngx_setproctitle(char *title) +{ + u_char *p; + +#if (NGX_SOLARIS) + + ngx_int_t i; + size_t size; + +#endif + + ngx_os_argv[1] = NULL; + + p = ngx_cpystrn((u_char *) ngx_os_argv[0], (u_char *) "nginx: ", + ngx_os_argv_last - ngx_os_argv[0]); + + p = ngx_cpystrn(p, (u_char *) title, ngx_os_argv_last - (char *) p); + +#if (NGX_SOLARIS) + + size = 0; + + for (i = 0; i < ngx_argc; i++) { + size += ngx_strlen(ngx_argv[i]) + 1; + } + + if (size > (size_t) ((char *) p - ngx_os_argv[0])) { + + /* + * ngx_setproctitle() is too rare operation so we use + * the non-optimized copies + */ + + p = ngx_cpystrn(p, (u_char *) " (", ngx_os_argv_last - (char *) p); + + for (i = 0; i < ngx_argc; i++) { + p = ngx_cpystrn(p, (u_char *) ngx_argv[i], + ngx_os_argv_last - (char *) p); + p = ngx_cpystrn(p, (u_char *) " ", ngx_os_argv_last - (char *) p); + } + + if (*(p - 1) == ' ') { + *(p - 1) = ')'; + } + } + +#endif + + if (ngx_os_argv_last - (char *) p) { + ngx_memset(p, NGX_SETPROCTITLE_PAD, ngx_os_argv_last - (char *) p); + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, + "setproctitle: \"%s\"", ngx_os_argv[0]); +} + +#endif /* NGX_SETPROCTITLE_USES_ENV */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_setproctitle.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_setproctitle.h new file mode 100644 index 000000000..c3636628b --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_setproctitle.h @@ -0,0 +1,52 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_SETPROCTITLE_H_INCLUDED_ +#define _NGX_SETPROCTITLE_H_INCLUDED_ + + +#if (NGX_HAVE_SETPROCTITLE) + +/* FreeBSD, NetBSD, OpenBSD */ + +#define ngx_init_setproctitle(log) NGX_OK +#define ngx_setproctitle(title) setproctitle("%s", title) + + +#else /* !NGX_HAVE_SETPROCTITLE */ + +#if !defined NGX_SETPROCTITLE_USES_ENV + +#if (NGX_SOLARIS) + +#define NGX_SETPROCTITLE_USES_ENV 1 +#define NGX_SETPROCTITLE_PAD ' ' + +ngx_int_t ngx_init_setproctitle(ngx_log_t *log); +void ngx_setproctitle(char *title); + +#elif (NGX_LINUX) || (NGX_DARWIN) + +#define NGX_SETPROCTITLE_USES_ENV 1 +#define NGX_SETPROCTITLE_PAD '\0' + +ngx_int_t ngx_init_setproctitle(ngx_log_t *log); +void ngx_setproctitle(char *title); + +#else + +#define ngx_init_setproctitle(log) NGX_OK +#define ngx_setproctitle(title) + +#endif /* OSes */ + +#endif /* NGX_SETPROCTITLE_USES_ENV */ + +#endif /* NGX_HAVE_SETPROCTITLE */ + + +#endif /* _NGX_SETPROCTITLE_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_shmem.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_shmem.c new file mode 100644 index 000000000..3ec7cbf1f --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_shmem.c @@ -0,0 +1,126 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +#if (NGX_HAVE_MAP_ANON) + +ngx_int_t +ngx_shm_alloc(ngx_shm_t *shm) +{ + shm->addr = (u_char *) mmap(NULL, shm->size, + PROT_READ|PROT_WRITE, + MAP_ANON|MAP_SHARED, -1, 0); + + if (shm->addr == MAP_FAILED) { + ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, + "mmap(MAP_ANON|MAP_SHARED, %uz) failed", shm->size); + return NGX_ERROR; + } + + return NGX_OK; +} + + +void +ngx_shm_free(ngx_shm_t *shm) +{ + if (munmap((void *) shm->addr, shm->size) == -1) { + ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, + "munmap(%p, %uz) failed", shm->addr, shm->size); + } +} + +#elif (NGX_HAVE_MAP_DEVZERO) + +ngx_int_t +ngx_shm_alloc(ngx_shm_t *shm) +{ + ngx_fd_t fd; + + fd = open("/dev/zero", O_RDWR); + + if (fd == -1) { + ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, + "open(\"/dev/zero\") failed"); + return NGX_ERROR; + } + + shm->addr = (u_char *) mmap(NULL, shm->size, PROT_READ|PROT_WRITE, + MAP_SHARED, fd, 0); + + if (shm->addr == MAP_FAILED) { + ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, + "mmap(/dev/zero, MAP_SHARED, %uz) failed", shm->size); + } + + if (close(fd) == -1) { + ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, + "close(\"/dev/zero\") failed"); + } + + return (shm->addr == MAP_FAILED) ? NGX_ERROR : NGX_OK; +} + + +void +ngx_shm_free(ngx_shm_t *shm) +{ + if (munmap((void *) shm->addr, shm->size) == -1) { + ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, + "munmap(%p, %uz) failed", shm->addr, shm->size); + } +} + +#elif (NGX_HAVE_SYSVSHM) + +#include +#include + + +ngx_int_t +ngx_shm_alloc(ngx_shm_t *shm) +{ + int id; + + id = shmget(IPC_PRIVATE, shm->size, (SHM_R|SHM_W|IPC_CREAT)); + + if (id == -1) { + ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, + "shmget(%uz) failed", shm->size); + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, shm->log, 0, "shmget id: %d", id); + + shm->addr = shmat(id, NULL, 0); + + if (shm->addr == (void *) -1) { + ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, "shmat() failed"); + } + + if (shmctl(id, IPC_RMID, NULL) == -1) { + ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, + "shmctl(IPC_RMID) failed"); + } + + return (shm->addr == (void *) -1) ? NGX_ERROR : NGX_OK; +} + + +void +ngx_shm_free(ngx_shm_t *shm) +{ + if (shmdt(shm->addr) == -1) { + ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, + "shmdt(%p) failed", shm->addr); + } +} + +#endif diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_shmem.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_shmem.h new file mode 100644 index 000000000..566a7d330 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_shmem.h @@ -0,0 +1,29 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_SHMEM_H_INCLUDED_ +#define _NGX_SHMEM_H_INCLUDED_ + + +#include +#include + + +typedef struct { + u_char *addr; + size_t size; + ngx_str_t name; + ngx_log_t *log; + ngx_uint_t exists; /* unsigned exists:1; */ +} ngx_shm_t; + + +ngx_int_t ngx_shm_alloc(ngx_shm_t *shm); +void ngx_shm_free(ngx_shm_t *shm); + + +#endif /* _NGX_SHMEM_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_socket.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_socket.c new file mode 100644 index 000000000..3978f655c --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_socket.c @@ -0,0 +1,116 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +/* + * ioctl(FIONBIO) sets a non-blocking mode with the single syscall + * while fcntl(F_SETFL, O_NONBLOCK) needs to learn the current state + * using fcntl(F_GETFL). + * + * ioctl() and fcntl() are syscalls at least in FreeBSD 2.x, Linux 2.2 + * and Solaris 7. + * + * ioctl() in Linux 2.4 and 2.6 uses BKL, however, fcntl(F_SETFL) uses it too. + */ + + +#if (NGX_HAVE_FIONBIO) + +int +ngx_nonblocking(ngx_socket_t s) +{ + int nb; + + nb = 1; + + return ioctl(s, FIONBIO, &nb); +} + + +int +ngx_blocking(ngx_socket_t s) +{ + int nb; + + nb = 0; + + return ioctl(s, FIONBIO, &nb); +} + +#endif + + +#if (NGX_FREEBSD) + +int +ngx_tcp_nopush(ngx_socket_t s) +{ + int tcp_nopush; + + tcp_nopush = 1; + + return setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, + (const void *) &tcp_nopush, sizeof(int)); +} + + +int +ngx_tcp_push(ngx_socket_t s) +{ + int tcp_nopush; + + tcp_nopush = 0; + + return setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, + (const void *) &tcp_nopush, sizeof(int)); +} + +#elif (NGX_LINUX) + + +int +ngx_tcp_nopush(ngx_socket_t s) +{ + int cork; + + cork = 1; + + return setsockopt(s, IPPROTO_TCP, TCP_CORK, + (const void *) &cork, sizeof(int)); +} + + +int +ngx_tcp_push(ngx_socket_t s) +{ + int cork; + + cork = 0; + + return setsockopt(s, IPPROTO_TCP, TCP_CORK, + (const void *) &cork, sizeof(int)); +} + +#else + +int +ngx_tcp_nopush(ngx_socket_t s) +{ + return 0; +} + + +int +ngx_tcp_push(ngx_socket_t s) +{ + return 0; +} + +#endif diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_socket.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_socket.h new file mode 100644 index 000000000..4480adca2 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_socket.h @@ -0,0 +1,73 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_SOCKET_H_INCLUDED_ +#define _NGX_SOCKET_H_INCLUDED_ + + +#include + + +#define NGX_WRITE_SHUTDOWN SHUT_WR +#define NGX_READ_SHUTDOWN SHUT_RD +#define NGX_RDWR_SHUTDOWN SHUT_RDWR + +typedef int ngx_socket_t; + +#define ngx_socket socket +#define ngx_socket_n "socket()" + + +#if (NGX_HAVE_FIONBIO) + +int ngx_nonblocking(ngx_socket_t s); +int ngx_blocking(ngx_socket_t s); + +#define ngx_nonblocking_n "ioctl(FIONBIO)" +#define ngx_blocking_n "ioctl(!FIONBIO)" + +#else + +#define ngx_nonblocking(s) fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK) +#define ngx_nonblocking_n "fcntl(O_NONBLOCK)" + +#define ngx_blocking(s) fcntl(s, F_SETFL, fcntl(s, F_GETFL) & ~O_NONBLOCK) +#define ngx_blocking_n "fcntl(!O_NONBLOCK)" + +#endif + +#if (NGX_HAVE_FIONREAD) + +#define ngx_socket_nread(s, n) ioctl(s, FIONREAD, n) +#define ngx_socket_nread_n "ioctl(FIONREAD)" + +#endif + +int ngx_tcp_nopush(ngx_socket_t s); +int ngx_tcp_push(ngx_socket_t s); + +#if (NGX_LINUX) + +#define ngx_tcp_nopush_n "setsockopt(TCP_CORK)" +#define ngx_tcp_push_n "setsockopt(!TCP_CORK)" + +#else + +#define ngx_tcp_nopush_n "setsockopt(TCP_NOPUSH)" +#define ngx_tcp_push_n "setsockopt(!TCP_NOPUSH)" + +#endif + + +#define ngx_shutdown_socket shutdown +#define ngx_shutdown_socket_n "shutdown()" + +#define ngx_close_socket close +#define ngx_close_socket_n "close() socket" + + +#endif /* _NGX_SOCKET_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_solaris.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_solaris.h new file mode 100644 index 000000000..7b167d830 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_solaris.h @@ -0,0 +1,16 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_SOLARIS_H_INCLUDED_ +#define _NGX_SOLARIS_H_INCLUDED_ + + +ngx_chain_t *ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); + + +#endif /* _NGX_SOLARIS_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_solaris_config.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_solaris_config.h new file mode 100644 index 000000000..ffa01c8b7 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_solaris_config.h @@ -0,0 +1,112 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_SOLARIS_CONFIG_H_INCLUDED_ +#define _NGX_SOLARIS_CONFIG_H_INCLUDED_ + + +#ifndef _REENTRANT +#define _REENTRANT +#endif + +#define _FILE_OFFSET_BITS 64 /* must be before */ + +#include +#include +#include +#include +#include /* offsetof() */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* statvfs() */ + +#include /* FIONBIO */ +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include /* TCP_NODELAY */ +#include +#include +#include + +#include +#include /* IOV_MAX */ +#include +#include + +#include + +#define NGX_ALIGNMENT _MAX_ALIGNMENT + +#include + + +#if (NGX_HAVE_POSIX_SEM) +#include +#endif + + +#if (NGX_HAVE_POLL) +#include +#endif + + +#if (NGX_HAVE_DEVPOLL) +#include +#include +#endif + + +#if (NGX_HAVE_EVENTPORT) +#include +#endif + + +#if (NGX_HAVE_SENDFILE) +#include +#endif + + +#define NGX_LISTEN_BACKLOG 511 + + +#ifndef NGX_HAVE_INHERITED_NONBLOCK +#define NGX_HAVE_INHERITED_NONBLOCK 1 +#endif + + +#ifndef NGX_HAVE_SO_SNDLOWAT +/* setsockopt(SO_SNDLOWAT) returns ENOPROTOOPT */ +#define NGX_HAVE_SO_SNDLOWAT 0 +#endif + + +#define NGX_HAVE_OS_SPECIFIC_INIT 1 +#define ngx_debug_init() + + +extern char **environ; + + +#endif /* _NGX_SOLARIS_CONFIG_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_solaris_init.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_solaris_init.c new file mode 100644 index 000000000..65d787539 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_solaris_init.c @@ -0,0 +1,77 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +char ngx_solaris_sysname[20]; +char ngx_solaris_release[10]; +char ngx_solaris_version[50]; + + +static ngx_os_io_t ngx_solaris_io = { + ngx_unix_recv, + ngx_readv_chain, + ngx_udp_unix_recv, + ngx_unix_send, + ngx_udp_unix_send, + ngx_udp_unix_sendmsg_chain, +#if (NGX_HAVE_SENDFILE) + ngx_solaris_sendfilev_chain, + NGX_IO_SENDFILE +#else + ngx_writev_chain, + 0 +#endif +}; + + +ngx_int_t +ngx_os_specific_init(ngx_log_t *log) +{ + if (sysinfo(SI_SYSNAME, ngx_solaris_sysname, sizeof(ngx_solaris_sysname)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "sysinfo(SI_SYSNAME) failed"); + return NGX_ERROR; + } + + if (sysinfo(SI_RELEASE, ngx_solaris_release, sizeof(ngx_solaris_release)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "sysinfo(SI_RELEASE) failed"); + return NGX_ERROR; + } + + if (sysinfo(SI_VERSION, ngx_solaris_version, sizeof(ngx_solaris_version)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "sysinfo(SI_SYSNAME) failed"); + return NGX_ERROR; + } + + + ngx_os_io = ngx_solaris_io; + + return NGX_OK; +} + + +void +ngx_os_specific_status(ngx_log_t *log) +{ + + ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s", + ngx_solaris_sysname, ngx_solaris_release); + + ngx_log_error(NGX_LOG_NOTICE, log, 0, "version: %s", + ngx_solaris_version); +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_solaris_sendfilev_chain.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_solaris_sendfilev_chain.c new file mode 100644 index 000000000..39bcafaaf --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_solaris_sendfilev_chain.c @@ -0,0 +1,228 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +#if (NGX_TEST_BUILD_SOLARIS_SENDFILEV) + +/* Solaris declarations */ + +typedef struct sendfilevec { + int sfv_fd; + u_int sfv_flag; + off_t sfv_off; + size_t sfv_len; +} sendfilevec_t; + +#define SFV_FD_SELF -2 + +static ssize_t sendfilev(int fd, const struct sendfilevec *vec, + int sfvcnt, size_t *xferred) +{ + return -1; +} + +ngx_chain_t *ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); + +#endif + + +#define NGX_SENDFILEVECS NGX_IOVS_PREALLOCATE + + +ngx_chain_t * +ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) +{ + int fd; + u_char *prev; + off_t size, send, prev_send, aligned, fprev; + size_t sent; + ssize_t n; + ngx_int_t eintr; + ngx_err_t err; + ngx_buf_t *file; + ngx_uint_t nsfv; + sendfilevec_t *sfv, sfvs[NGX_SENDFILEVECS]; + ngx_event_t *wev; + ngx_chain_t *cl; + + wev = c->write; + + if (!wev->ready) { + return in; + } + + if (!c->sendfile) { + return ngx_writev_chain(c, in, limit); + } + + + /* the maximum limit size is the maximum size_t value - the page size */ + + if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) { + limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize; + } + + + send = 0; + + for ( ;; ) { + fd = SFV_FD_SELF; + prev = NULL; + fprev = 0; + file = NULL; + sfv = NULL; + eintr = 0; + sent = 0; + prev_send = send; + + nsfv = 0; + + /* create the sendfilevec and coalesce the neighbouring bufs */ + + for (cl = in; cl && send < limit; cl = cl->next) { + + if (ngx_buf_special(cl->buf)) { + continue; + } + + if (ngx_buf_in_memory_only(cl->buf)) { + fd = SFV_FD_SELF; + + size = cl->buf->last - cl->buf->pos; + + if (send + size > limit) { + size = limit - send; + } + + if (prev == cl->buf->pos) { + sfv->sfv_len += (size_t) size; + + } else { + if (nsfv == NGX_SENDFILEVECS) { + break; + } + + sfv = &sfvs[nsfv++]; + + sfv->sfv_fd = SFV_FD_SELF; + sfv->sfv_flag = 0; + sfv->sfv_off = (off_t) (uintptr_t) cl->buf->pos; + sfv->sfv_len = (size_t) size; + } + + prev = cl->buf->pos + (size_t) size; + send += size; + + } else { + prev = NULL; + + size = cl->buf->file_last - cl->buf->file_pos; + + if (send + size > limit) { + size = limit - send; + + aligned = (cl->buf->file_pos + size + ngx_pagesize - 1) + & ~((off_t) ngx_pagesize - 1); + + if (aligned <= cl->buf->file_last) { + size = aligned - cl->buf->file_pos; + } + } + + if (fd == cl->buf->file->fd && fprev == cl->buf->file_pos) { + sfv->sfv_len += (size_t) size; + + } else { + if (nsfv == NGX_SENDFILEVECS) { + break; + } + + sfv = &sfvs[nsfv++]; + + fd = cl->buf->file->fd; + sfv->sfv_fd = fd; + sfv->sfv_flag = 0; + sfv->sfv_off = cl->buf->file_pos; + sfv->sfv_len = (size_t) size; + } + + file = cl->buf; + fprev = cl->buf->file_pos + size; + send += size; + } + } + + n = sendfilev(c->fd, sfvs, nsfv, &sent); + + if (n == -1) { + err = ngx_errno; + + switch (err) { + case NGX_EAGAIN: + break; + + case NGX_EINTR: + eintr = 1; + break; + + default: + wev->error = 1; + ngx_connection_error(c, err, "sendfilev() failed"); + return NGX_CHAIN_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendfilev() sent only %uz bytes", sent); + + } else if (n == 0 && sent == 0) { + + /* + * sendfilev() is documented to return -1 with errno + * set to EINVAL if svf_len is greater than the file size, + * but at least Solaris 11 returns 0 instead + */ + + if (file) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "sendfilev() reported that \"%s\" was truncated at %O", + file->file->name.data, file->file_pos); + + } else { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "sendfilev() returned 0 with memory buffers"); + } + + return NGX_CHAIN_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendfilev: %z %z", n, sent); + + c->sent += sent; + + in = ngx_chain_update_sent(in, sent); + + if (eintr) { + send = prev_send + sent; + continue; + } + + if (send - prev_send != (off_t) sent) { + wev->ready = 0; + return in; + } + + if (send >= limit || in == NULL) { + return in; + } + } +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_sunpro_amd64.il b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_sunpro_amd64.il new file mode 100644 index 000000000..07f32103b --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_sunpro_amd64.il @@ -0,0 +1,43 @@ +/ +/ Copyright (C) Igor Sysoev +/ Copyright (C) Nginx, Inc. +/ + +/ ngx_atomic_uint_t ngx_atomic_cmp_set(ngx_atomic_t *lock, +/ ngx_atomic_uint_t old, ngx_atomic_uint_t set); +/ +/ the arguments are passed in %rdi, %rsi, %rdx +/ the result is returned in the %rax + + .inline ngx_atomic_cmp_set,0 + movq %rsi, %rax + lock + cmpxchgq %rdx, (%rdi) + setz %al + movzbq %al, %rax + .end + + +/ ngx_atomic_int_t ngx_atomic_fetch_add(ngx_atomic_t *value, +/ ngx_atomic_int_t add); +/ +/ the arguments are passed in %rdi, %rsi +/ the result is returned in the %rax + + .inline ngx_atomic_fetch_add,0 + movq %rsi, %rax + lock + xaddq %rax, (%rdi) + .end + + +/ ngx_cpu_pause() +/ +/ the "rep; nop" is used instead of "pause" to avoid the "[ PAUSE ]" hardware +/ capability added by linker because Solaris/amd64 does not know about it: +/ +/ ld.so.1: nginx: fatal: hardware capability unsupported: 0x2000 [ PAUSE ] + + .inline ngx_cpu_pause,0 + rep; nop + .end diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_sunpro_atomic_sparc64.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_sunpro_atomic_sparc64.h new file mode 100644 index 000000000..5f280553c --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_sunpro_atomic_sparc64.h @@ -0,0 +1,61 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#if (NGX_PTR_SIZE == 4) +#define NGX_CASA ngx_casa +#else +#define NGX_CASA ngx_casxa +#endif + + +ngx_atomic_uint_t +ngx_casa(ngx_atomic_uint_t set, ngx_atomic_uint_t old, ngx_atomic_t *lock); + +ngx_atomic_uint_t +ngx_casxa(ngx_atomic_uint_t set, ngx_atomic_uint_t old, ngx_atomic_t *lock); + +/* the code in src/os/unix/ngx_sunpro_sparc64.il */ + + +static ngx_inline ngx_atomic_uint_t +ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old, + ngx_atomic_uint_t set) +{ + set = NGX_CASA(set, old, lock); + + return (set == old); +} + + +static ngx_inline ngx_atomic_int_t +ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add) +{ + ngx_atomic_uint_t old, res; + + old = *value; + + for ( ;; ) { + + res = old + add; + + res = NGX_CASA(res, old, value); + + if (res == old) { + return res; + } + + old = res; + } +} + + +#define ngx_memory_barrier() \ + __asm (".volatile"); \ + __asm ("membar #LoadLoad | #LoadStore | #StoreStore | #StoreLoad"); \ + __asm (".nonvolatile") + +#define ngx_cpu_pause() diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_sunpro_sparc64.il b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_sunpro_sparc64.il new file mode 100644 index 000000000..bdeef6125 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_sunpro_sparc64.il @@ -0,0 +1,36 @@ +/ +/ Copyright (C) Igor Sysoev +/ Copyright (C) Nginx, Inc. +/ + + +/ "casa [%o2] 0x80, %o1, %o0" and +/ "casxa [%o2] 0x80, %o1, %o0" do the following: +/ +/ if ([%o2] == %o1) { +/ swap(%o0, [%o2]); +/ } else { +/ %o0 = [%o2]; +/ } + + +/ ngx_atomic_uint_t ngx_casa(ngx_atomic_uint_t set, ngx_atomic_uint_t old, +/ ngx_atomic_t *lock); +/ +/ the arguments are passed in the %o0, %o1, %o2 +/ the result is returned in the %o0 + + .inline ngx_casa,0 + casa [%o2] 0x80, %o1, %o0 + .end + + +/ ngx_atomic_uint_t ngx_casxa(ngx_atomic_uint_t set, ngx_atomic_uint_t old, +/ ngx_atomic_t *lock); +/ +/ the arguments are passed in the %o0, %o1, %o2 +/ the result is returned in the %o0 + + .inline ngx_casxa,0 + casxa [%o2] 0x80, %o1, %o0 + .end diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_sunpro_x86.il b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_sunpro_x86.il new file mode 100644 index 000000000..d7e127cc4 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_sunpro_x86.il @@ -0,0 +1,44 @@ +/ +/ Copyright (C) Igor Sysoev +/ Copyright (C) Nginx, Inc. +/ + +/ ngx_atomic_uint_t ngx_atomic_cmp_set(ngx_atomic_t *lock, +/ ngx_atomic_uint_t old, ngx_atomic_uint_t set); +/ +/ the arguments are passed on stack (%esp), 4(%esp), 8(%esp) + + .inline ngx_atomic_cmp_set,0 + movl (%esp), %ecx + movl 4(%esp), %eax + movl 8(%esp), %edx + lock + cmpxchgl %edx, (%ecx) + setz %al + movzbl %al, %eax + .end + + +/ ngx_atomic_int_t ngx_atomic_fetch_add(ngx_atomic_t *value, +/ ngx_atomic_int_t add); +/ +/ the arguments are passed on stack (%esp), 4(%esp) + + .inline ngx_atomic_fetch_add,0 + movl (%esp), %ecx + movl 4(%esp), %eax + lock + xaddl %eax, (%ecx) + .end + + +/ ngx_cpu_pause() +/ +/ the "rep; nop" is used instead of "pause" to avoid the "[ PAUSE ]" hardware +/ capability added by linker because Solaris/i386 does not know about it: +/ +/ ld.so.1: nginx: fatal: hardware capability unsupported: 0x2000 [ PAUSE ] + + .inline ngx_cpu_pause,0 + rep; nop + .end diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_thread.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_thread.h new file mode 100644 index 000000000..e3b5e8134 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_thread.h @@ -0,0 +1,71 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_THREAD_H_INCLUDED_ +#define _NGX_THREAD_H_INCLUDED_ + + +#include +#include + +#if (NGX_THREADS) + +#include + + +typedef pthread_mutex_t ngx_thread_mutex_t; + +ngx_int_t ngx_thread_mutex_create(ngx_thread_mutex_t *mtx, ngx_log_t *log); +ngx_int_t ngx_thread_mutex_destroy(ngx_thread_mutex_t *mtx, ngx_log_t *log); +ngx_int_t ngx_thread_mutex_lock(ngx_thread_mutex_t *mtx, ngx_log_t *log); +ngx_int_t ngx_thread_mutex_unlock(ngx_thread_mutex_t *mtx, ngx_log_t *log); + + +typedef pthread_cond_t ngx_thread_cond_t; + +ngx_int_t ngx_thread_cond_create(ngx_thread_cond_t *cond, ngx_log_t *log); +ngx_int_t ngx_thread_cond_destroy(ngx_thread_cond_t *cond, ngx_log_t *log); +ngx_int_t ngx_thread_cond_signal(ngx_thread_cond_t *cond, ngx_log_t *log); +ngx_int_t ngx_thread_cond_wait(ngx_thread_cond_t *cond, ngx_thread_mutex_t *mtx, + ngx_log_t *log); + + +#if (NGX_LINUX) + +typedef pid_t ngx_tid_t; +#define NGX_TID_T_FMT "%P" + +#elif (NGX_FREEBSD) + +typedef uint32_t ngx_tid_t; +#define NGX_TID_T_FMT "%uD" + +#elif (NGX_DARWIN) + +typedef uint64_t ngx_tid_t; +#define NGX_TID_T_FMT "%uL" + +#else + +typedef uint64_t ngx_tid_t; +#define NGX_TID_T_FMT "%uL" + +#endif + +ngx_tid_t ngx_thread_tid(void); + +#define ngx_log_tid ngx_thread_tid() + +#else + +#define ngx_log_tid 0 +#define NGX_TID_T_FMT "%d" + +#endif + + +#endif /* _NGX_THREAD_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_thread_cond.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_thread_cond.c new file mode 100644 index 000000000..2ad51b809 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_thread_cond.c @@ -0,0 +1,76 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +ngx_int_t +ngx_thread_cond_create(ngx_thread_cond_t *cond, ngx_log_t *log) +{ + ngx_err_t err; + + err = pthread_cond_init(cond, NULL); + if (err == 0) { + return NGX_OK; + } + + ngx_log_error(NGX_LOG_EMERG, log, err, "pthread_cond_init() failed"); + return NGX_ERROR; +} + + +ngx_int_t +ngx_thread_cond_destroy(ngx_thread_cond_t *cond, ngx_log_t *log) +{ + ngx_err_t err; + + err = pthread_cond_destroy(cond); + if (err == 0) { + return NGX_OK; + } + + ngx_log_error(NGX_LOG_EMERG, log, err, "pthread_cond_destroy() failed"); + return NGX_ERROR; +} + + +ngx_int_t +ngx_thread_cond_signal(ngx_thread_cond_t *cond, ngx_log_t *log) +{ + ngx_err_t err; + + err = pthread_cond_signal(cond); + if (err == 0) { + return NGX_OK; + } + + ngx_log_error(NGX_LOG_EMERG, log, err, "pthread_cond_signal() failed"); + return NGX_ERROR; +} + + +ngx_int_t +ngx_thread_cond_wait(ngx_thread_cond_t *cond, ngx_thread_mutex_t *mtx, + ngx_log_t *log) +{ + ngx_err_t err; + + err = pthread_cond_wait(cond, mtx); + +#if 0 + ngx_time_update(); +#endif + + if (err == 0) { + return NGX_OK; + } + + ngx_log_error(NGX_LOG_ALERT, log, err, "pthread_cond_wait() failed"); + + return NGX_ERROR; +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_thread_id.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_thread_id.c new file mode 100644 index 000000000..5174f1abc --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_thread_id.c @@ -0,0 +1,70 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +#if (NGX_LINUX) + +/* + * Linux thread id is a pid of thread created by clone(2), + * glibc does not provide a wrapper for gettid(). + */ + +ngx_tid_t +ngx_thread_tid(void) +{ + return syscall(SYS_gettid); +} + +#elif (NGX_FREEBSD) && (__FreeBSD_version >= 900031) + +#include + +ngx_tid_t +ngx_thread_tid(void) +{ + return pthread_getthreadid_np(); +} + +#elif (NGX_DARWIN) + +/* + * MacOSX thread has two thread ids: + * + * 1) MacOSX 10.6 (Snow Leoprad) has pthread_threadid_np() returning + * an uint64_t value, which is obtained using the __thread_selfid() + * syscall. It is a number above 300,000. + */ + +ngx_tid_t +ngx_thread_tid(void) +{ + uint64_t tid; + + (void) pthread_threadid_np(NULL, &tid); + return tid; +} + +/* + * 2) Kernel thread mach_port_t returned by pthread_mach_thread_np(). + * It is a number in range 100-100,000. + * + * return pthread_mach_thread_np(pthread_self()); + */ + +#else + +ngx_tid_t +ngx_thread_tid(void) +{ + return (uint64_t) (uintptr_t) pthread_self(); +} + +#endif diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_thread_mutex.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_thread_mutex.c new file mode 100644 index 000000000..4886f49d7 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_thread_mutex.c @@ -0,0 +1,165 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + +#include +#include + + +/* + * All modern pthread mutex implementations try to acquire a lock + * atomically in userland before going to sleep in kernel. Some + * spins before the sleeping. + * + * In Solaris since version 8 all mutex types spin before sleeping. + * The default spin count is 1000. It can be overridden using + * _THREAD_ADAPTIVE_SPIN=100 environment variable. + * + * In MacOSX all mutex types spin to acquire a lock protecting a mutex's + * internals. If the mutex is busy, thread calls Mach semaphore_wait(). + * + * + * PTHREAD_MUTEX_NORMAL lacks deadlock detection and is the fastest + * mutex type. + * + * Linux: No spinning. The internal name PTHREAD_MUTEX_TIMED_NP + * remains from the times when pthread_mutex_timedlock() was + * non-standard extension. Alias name: PTHREAD_MUTEX_FAST_NP. + * FreeBSD: No spinning. + * + * + * PTHREAD_MUTEX_ERRORCHECK is usually as fast as PTHREAD_MUTEX_NORMAL + * yet has lightweight deadlock detection. + * + * Linux: No spinning. The internal name: PTHREAD_MUTEX_ERRORCHECK_NP. + * FreeBSD: No spinning. + * + * + * PTHREAD_MUTEX_RECURSIVE allows recursive locking. + * + * Linux: No spinning. The internal name: PTHREAD_MUTEX_RECURSIVE_NP. + * FreeBSD: No spinning. + * + * + * PTHREAD_MUTEX_ADAPTIVE_NP spins on SMP systems before sleeping. + * + * Linux: No deadlock detection. Dynamically changes a spin count + * for each mutex from 10 to 100 based on spin count taken + * previously. + * FreeBSD: Deadlock detection. The default spin count is 2000. + * It can be overridden using LIBPTHREAD_SPINLOOPS environment + * variable or by pthread_mutex_setspinloops_np(). If a lock + * is still busy, sched_yield() can be called on both UP and + * SMP systems. The default yield loop count is zero, but + * it can be set by LIBPTHREAD_YIELDLOOPS environment + * variable or by pthread_mutex_setyieldloops_np(). + * Solaris: No PTHREAD_MUTEX_ADAPTIVE_NP. + * MacOSX: No PTHREAD_MUTEX_ADAPTIVE_NP. + * + * + * PTHREAD_MUTEX_ELISION_NP is a Linux extension to elide locks using + * Intel Restricted Transactional Memory. It is the most suitable for + * rwlock pattern access because it allows simultaneous reads without lock. + * Supported since glibc 2.18. + * + * + * PTHREAD_MUTEX_DEFAULT is default mutex type. + * + * Linux: PTHREAD_MUTEX_NORMAL. + * FreeBSD: PTHREAD_MUTEX_ERRORCHECK. + * Solaris: PTHREAD_MUTEX_NORMAL. + * MacOSX: PTHREAD_MUTEX_NORMAL. + */ + + +ngx_int_t +ngx_thread_mutex_create(ngx_thread_mutex_t *mtx, ngx_log_t *log) +{ + ngx_err_t err; + pthread_mutexattr_t attr; + + err = pthread_mutexattr_init(&attr); + if (err != 0) { + ngx_log_error(NGX_LOG_EMERG, log, err, + "pthread_mutexattr_init() failed"); + return NGX_ERROR; + } + + err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); + if (err != 0) { + ngx_log_error(NGX_LOG_EMERG, log, err, + "pthread_mutexattr_settype" + "(PTHREAD_MUTEX_ERRORCHECK) failed"); + return NGX_ERROR; + } + + err = pthread_mutex_init(mtx, &attr); + if (err != 0) { + ngx_log_error(NGX_LOG_EMERG, log, err, + "pthread_mutex_init() failed"); + return NGX_ERROR; + } + + err = pthread_mutexattr_destroy(&attr); + if (err != 0) { + ngx_log_error(NGX_LOG_ALERT, log, err, + "pthread_mutexattr_destroy() failed"); + } + + return NGX_OK; +} + + +ngx_int_t +ngx_thread_mutex_destroy(ngx_thread_mutex_t *mtx, ngx_log_t *log) +{ + ngx_err_t err; + + err = pthread_mutex_destroy(mtx); + if (err != 0) { + ngx_log_error(NGX_LOG_ALERT, log, err, + "pthread_mutex_destroy() failed"); + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_thread_mutex_lock(ngx_thread_mutex_t *mtx, ngx_log_t *log) +{ + ngx_err_t err; + + err = pthread_mutex_lock(mtx); + if (err == 0) { + return NGX_OK; + } + + ngx_log_error(NGX_LOG_ALERT, log, err, "pthread_mutex_lock() failed"); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_thread_mutex_unlock(ngx_thread_mutex_t *mtx, ngx_log_t *log) +{ + ngx_err_t err; + + err = pthread_mutex_unlock(mtx); + +#if 0 + ngx_time_update(); +#endif + + if (err == 0) { + return NGX_OK; + } + + ngx_log_error(NGX_LOG_ALERT, log, err, "pthread_mutex_unlock() failed"); + + return NGX_ERROR; +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_time.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_time.c new file mode 100644 index 000000000..c97bae2ed --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_time.c @@ -0,0 +1,104 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +/* + * FreeBSD does not test /etc/localtime change, however, we can workaround it + * by calling tzset() with TZ and then without TZ to update timezone. + * The trick should work since FreeBSD 2.1.0. + * + * Linux does not test /etc/localtime change in localtime(), + * but may stat("/etc/localtime") several times in every strftime(), + * therefore we use it to update timezone. + * + * Solaris does not test /etc/TIMEZONE change too and no workaround available. + */ + +void +ngx_timezone_update(void) +{ +#if (NGX_FREEBSD) + + if (getenv("TZ")) { + return; + } + + putenv("TZ=UTC"); + + tzset(); + + unsetenv("TZ"); + + tzset(); + +#elif (NGX_LINUX) + time_t s; + struct tm *t; + char buf[4]; + + s = time(NULL); + + t = localtime(&s); + + strftime(buf, 4, "%H", t); + +#endif +} + + +void +ngx_localtime(time_t s, ngx_tm_t *tm) +{ +#if (NGX_HAVE_LOCALTIME_R) + (void) localtime_r(&s, tm); + +#else + ngx_tm_t *t; + + t = localtime(&s); + *tm = *t; + +#endif + + tm->ngx_tm_mon++; + tm->ngx_tm_year += 1900; +} + + +void +ngx_libc_localtime(time_t s, struct tm *tm) +{ +#if (NGX_HAVE_LOCALTIME_R) + (void) localtime_r(&s, tm); + +#else + struct tm *t; + + t = localtime(&s); + *tm = *t; + +#endif +} + + +void +ngx_libc_gmtime(time_t s, struct tm *tm) +{ +#if (NGX_HAVE_LOCALTIME_R) + (void) gmtime_r(&s, tm); + +#else + struct tm *t; + + t = gmtime(&s); + *tm = *t; + +#endif +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_time.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_time.h new file mode 100644 index 000000000..c128c9aa0 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_time.h @@ -0,0 +1,66 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_TIME_H_INCLUDED_ +#define _NGX_TIME_H_INCLUDED_ + + +#include +#include + + +typedef ngx_rbtree_key_t ngx_msec_t; +typedef ngx_rbtree_key_int_t ngx_msec_int_t; + +typedef struct tm ngx_tm_t; + +#define ngx_tm_sec tm_sec +#define ngx_tm_min tm_min +#define ngx_tm_hour tm_hour +#define ngx_tm_mday tm_mday +#define ngx_tm_mon tm_mon +#define ngx_tm_year tm_year +#define ngx_tm_wday tm_wday +#define ngx_tm_isdst tm_isdst + +#define ngx_tm_sec_t int +#define ngx_tm_min_t int +#define ngx_tm_hour_t int +#define ngx_tm_mday_t int +#define ngx_tm_mon_t int +#define ngx_tm_year_t int +#define ngx_tm_wday_t int + + +#if (NGX_HAVE_GMTOFF) +#define ngx_tm_gmtoff tm_gmtoff +#define ngx_tm_zone tm_zone +#endif + + +#if (NGX_SOLARIS) + +#define ngx_timezone(isdst) (- (isdst ? altzone : timezone) / 60) + +#else + +#define ngx_timezone(isdst) (- (isdst ? timezone + 3600 : timezone) / 60) + +#endif + + +void ngx_timezone_update(void); +void ngx_localtime(time_t s, ngx_tm_t *tm); +void ngx_libc_localtime(time_t s, struct tm *tm); +void ngx_libc_gmtime(time_t s, struct tm *tm); + +#define ngx_gettimeofday(tp) (void) gettimeofday(tp, NULL); +#define ngx_msleep(ms) (void) usleep(ms * 1000) +#define ngx_sleep(s) (void) sleep(s) + + +#endif /* _NGX_TIME_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_udp_recv.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_udp_recv.c new file mode 100644 index 000000000..6d544c2c0 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_udp_recv.c @@ -0,0 +1,72 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +ssize_t +ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size) +{ + ssize_t n; + ngx_err_t err; + ngx_event_t *rev; + + rev = c->read; + + do { + n = recv(c->fd, buf, size, 0); + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "recv: fd:%d %z of %uz", c->fd, n, size); + + if (n >= 0) { + +#if (NGX_HAVE_KQUEUE) + + if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { + rev->available -= n; + + /* + * rev->available may be negative here because some additional + * bytes may be received between kevent() and recv() + */ + + if (rev->available <= 0) { + rev->ready = 0; + rev->available = 0; + } + } + +#endif + + return n; + } + + err = ngx_socket_errno; + + if (err == NGX_EAGAIN || err == NGX_EINTR) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "recv() not ready"); + n = NGX_AGAIN; + + } else { + n = ngx_connection_error(c, err, "recv() failed"); + break; + } + + } while (err == NGX_EINTR); + + rev->ready = 0; + + if (n == NGX_ERROR) { + rev->error = 1; + } + + return n; +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_udp_send.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_udp_send.c new file mode 100644 index 000000000..aabbc8e8b --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_udp_send.c @@ -0,0 +1,56 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +ssize_t +ngx_udp_unix_send(ngx_connection_t *c, u_char *buf, size_t size) +{ + ssize_t n; + ngx_err_t err; + ngx_event_t *wev; + + wev = c->write; + + for ( ;; ) { + n = sendto(c->fd, buf, size, 0, c->sockaddr, c->socklen); + + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendto: fd:%d %z of %uz to \"%V\"", + c->fd, n, size, &c->addr_text); + + if (n >= 0) { + if ((size_t) n != size) { + wev->error = 1; + (void) ngx_connection_error(c, 0, "sendto() incomplete"); + return NGX_ERROR; + } + + c->sent += n; + + return n; + } + + err = ngx_socket_errno; + + if (err == NGX_EAGAIN) { + wev->ready = 0; + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, NGX_EAGAIN, + "sendto() not ready"); + return NGX_AGAIN; + } + + if (err != NGX_EINTR) { + wev->error = 1; + (void) ngx_connection_error(c, err, "sendto() failed"); + return NGX_ERROR; + } + } +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_udp_sendmsg_chain.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_udp_sendmsg_chain.c new file mode 100644 index 000000000..fa917b569 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_udp_sendmsg_chain.c @@ -0,0 +1,432 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +static ngx_chain_t *ngx_udp_output_chain_to_iovec(ngx_iovec_t *vec, + ngx_chain_t *in, ngx_log_t *log); +static ssize_t ngx_sendmsg_vec(ngx_connection_t *c, ngx_iovec_t *vec); + + +ngx_chain_t * +ngx_udp_unix_sendmsg_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) +{ + ssize_t n; + off_t send; + ngx_chain_t *cl; + ngx_event_t *wev; + ngx_iovec_t vec; + struct iovec iovs[NGX_IOVS_PREALLOCATE]; + + wev = c->write; + + if (!wev->ready) { + return in; + } + +#if (NGX_HAVE_KQUEUE) + + if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) { + (void) ngx_connection_error(c, wev->kq_errno, + "kevent() reported about an closed connection"); + wev->error = 1; + return NGX_CHAIN_ERROR; + } + +#endif + + /* the maximum limit size is the maximum size_t value - the page size */ + + if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) { + limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize; + } + + send = 0; + + vec.iovs = iovs; + vec.nalloc = NGX_IOVS_PREALLOCATE; + + for ( ;; ) { + + /* create the iovec and coalesce the neighbouring bufs */ + + cl = ngx_udp_output_chain_to_iovec(&vec, in, c->log); + + if (cl == NGX_CHAIN_ERROR) { + return NGX_CHAIN_ERROR; + } + + if (cl && cl->buf->in_file) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "file buf in sendmsg " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + cl->buf->temporary, + cl->buf->recycled, + cl->buf->in_file, + cl->buf->start, + cl->buf->pos, + cl->buf->last, + cl->buf->file, + cl->buf->file_pos, + cl->buf->file_last); + + ngx_debug_point(); + + return NGX_CHAIN_ERROR; + } + + if (cl == in) { + return in; + } + + send += vec.size; + + n = ngx_sendmsg_vec(c, &vec); + + if (n == NGX_ERROR) { + return NGX_CHAIN_ERROR; + } + + if (n == NGX_AGAIN) { + wev->ready = 0; + return in; + } + + c->sent += n; + + in = ngx_chain_update_sent(in, n); + + if (send >= limit || in == NULL) { + return in; + } + } +} + + +static ngx_chain_t * +ngx_udp_output_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *in, ngx_log_t *log) +{ + size_t total, size; + u_char *prev; + ngx_uint_t n, flush; + ngx_chain_t *cl; + struct iovec *iov; + + cl = in; + iov = NULL; + prev = NULL; + total = 0; + n = 0; + flush = 0; + + for ( /* void */ ; in && !flush; in = in->next) { + + if (in->buf->flush || in->buf->last_buf) { + flush = 1; + } + + if (ngx_buf_special(in->buf)) { + continue; + } + + if (in->buf->in_file) { + break; + } + + if (!ngx_buf_in_memory(in->buf)) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "bad buf in output chain " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + in->buf->temporary, + in->buf->recycled, + in->buf->in_file, + in->buf->start, + in->buf->pos, + in->buf->last, + in->buf->file, + in->buf->file_pos, + in->buf->file_last); + + ngx_debug_point(); + + return NGX_CHAIN_ERROR; + } + + size = in->buf->last - in->buf->pos; + + if (prev == in->buf->pos) { + iov->iov_len += size; + + } else { + if (n == vec->nalloc) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "too many parts in a datagram"); + return NGX_CHAIN_ERROR; + } + + iov = &vec->iovs[n++]; + + iov->iov_base = (void *) in->buf->pos; + iov->iov_len = size; + } + + prev = in->buf->pos + size; + total += size; + } + + if (!flush) { +#if (NGX_SUPPRESS_WARN) + vec->size = 0; + vec->count = 0; +#endif + return cl; + } + + /* zero-sized datagram; pretend to have at least 1 iov */ + if (n == 0) { + iov = &vec->iovs[n++]; + iov->iov_base = NULL; + iov->iov_len = 0; + } + + vec->count = n; + vec->size = total; + + return in; +} + + +static ssize_t +ngx_sendmsg_vec(ngx_connection_t *c, ngx_iovec_t *vec) +{ + struct msghdr msg; + +#if (NGX_HAVE_ADDRINFO_CMSG) + struct cmsghdr *cmsg; + u_char msg_control[CMSG_SPACE(sizeof(ngx_addrinfo_t))]; +#endif + + ngx_memzero(&msg, sizeof(struct msghdr)); + + if (c->socklen) { + msg.msg_name = c->sockaddr; + msg.msg_namelen = c->socklen; + } + + msg.msg_iov = vec->iovs; + msg.msg_iovlen = vec->count; + +#if (NGX_HAVE_ADDRINFO_CMSG) + if (c->listening && c->listening->wildcard && c->local_sockaddr) { + + msg.msg_control = msg_control; + msg.msg_controllen = sizeof(msg_control); + ngx_memzero(msg_control, sizeof(msg_control)); + + cmsg = CMSG_FIRSTHDR(&msg); + + msg.msg_controllen = ngx_set_srcaddr_cmsg(cmsg, c->local_sockaddr); + } +#endif + + return ngx_sendmsg(c, &msg, 0); +} + + +#if (NGX_HAVE_ADDRINFO_CMSG) + +size_t +ngx_set_srcaddr_cmsg(struct cmsghdr *cmsg, struct sockaddr *local_sockaddr) +{ + size_t len; +#if (NGX_HAVE_IP_SENDSRCADDR) + struct in_addr *addr; + struct sockaddr_in *sin; +#elif (NGX_HAVE_IP_PKTINFO) + struct in_pktinfo *pkt; + struct sockaddr_in *sin; +#endif + +#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO) + struct in6_pktinfo *pkt6; + struct sockaddr_in6 *sin6; +#endif + + +#if (NGX_HAVE_IP_SENDSRCADDR) || (NGX_HAVE_IP_PKTINFO) + + if (local_sockaddr->sa_family == AF_INET) { + + cmsg->cmsg_level = IPPROTO_IP; + +#if (NGX_HAVE_IP_SENDSRCADDR) + + cmsg->cmsg_type = IP_SENDSRCADDR; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); + len = CMSG_SPACE(sizeof(struct in_addr)); + + sin = (struct sockaddr_in *) local_sockaddr; + + addr = (struct in_addr *) CMSG_DATA(cmsg); + *addr = sin->sin_addr; + +#elif (NGX_HAVE_IP_PKTINFO) + + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + len = CMSG_SPACE(sizeof(struct in_pktinfo)); + + sin = (struct sockaddr_in *) local_sockaddr; + + pkt = (struct in_pktinfo *) CMSG_DATA(cmsg); + ngx_memzero(pkt, sizeof(struct in_pktinfo)); + pkt->ipi_spec_dst = sin->sin_addr; + +#endif + return len; + } + +#endif + +#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO) + if (local_sockaddr->sa_family == AF_INET6) { + + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + len = CMSG_SPACE(sizeof(struct in6_pktinfo)); + + sin6 = (struct sockaddr_in6 *) local_sockaddr; + + pkt6 = (struct in6_pktinfo *) CMSG_DATA(cmsg); + ngx_memzero(pkt6, sizeof(struct in6_pktinfo)); + pkt6->ipi6_addr = sin6->sin6_addr; + + return len; + } +#endif + + return 0; +} + + +ngx_int_t +ngx_get_srcaddr_cmsg(struct cmsghdr *cmsg, struct sockaddr *local_sockaddr) +{ + +#if (NGX_HAVE_IP_RECVDSTADDR) + struct in_addr *addr; + struct sockaddr_in *sin; +#elif (NGX_HAVE_IP_PKTINFO) + struct in_pktinfo *pkt; + struct sockaddr_in *sin; +#endif + +#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO) + struct in6_pktinfo *pkt6; + struct sockaddr_in6 *sin6; +#endif + + +#if (NGX_HAVE_IP_RECVDSTADDR) + + if (cmsg->cmsg_level == IPPROTO_IP + && cmsg->cmsg_type == IP_RECVDSTADDR + && local_sockaddr->sa_family == AF_INET) + { + addr = (struct in_addr *) CMSG_DATA(cmsg); + sin = (struct sockaddr_in *) local_sockaddr; + sin->sin_addr = *addr; + + return NGX_OK; + } + +#elif (NGX_HAVE_IP_PKTINFO) + + if (cmsg->cmsg_level == IPPROTO_IP + && cmsg->cmsg_type == IP_PKTINFO + && local_sockaddr->sa_family == AF_INET) + { + pkt = (struct in_pktinfo *) CMSG_DATA(cmsg); + sin = (struct sockaddr_in *) local_sockaddr; + sin->sin_addr = pkt->ipi_addr; + + return NGX_OK; + } + +#endif + +#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO) + + if (cmsg->cmsg_level == IPPROTO_IPV6 + && cmsg->cmsg_type == IPV6_PKTINFO + && local_sockaddr->sa_family == AF_INET6) + { + pkt6 = (struct in6_pktinfo *) CMSG_DATA(cmsg); + sin6 = (struct sockaddr_in6 *) local_sockaddr; + sin6->sin6_addr = pkt6->ipi6_addr; + + return NGX_OK; + } + +#endif + + return NGX_DECLINED; +} + +#endif + + +ssize_t +ngx_sendmsg(ngx_connection_t *c, struct msghdr *msg, int flags) +{ + ssize_t n; + ngx_err_t err; +#if (NGX_DEBUG) + size_t size; + ngx_uint_t i; +#endif + +eintr: + + n = sendmsg(c->fd, msg, flags); + + if (n == -1) { + err = ngx_errno; + + switch (err) { + case NGX_EAGAIN: + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendmsg() not ready"); + return NGX_AGAIN; + + case NGX_EINTR: + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendmsg() was interrupted"); + goto eintr; + + default: + c->write->error = 1; + ngx_connection_error(c, err, "sendmsg() failed"); + return NGX_ERROR; + } + } + +#if (NGX_DEBUG) + for (i = 0, size = 0; i < (size_t) msg->msg_iovlen; i++) { + size += msg->msg_iov[i].iov_len; + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendmsg: %z of %uz", n, size); +#endif + + return n; +} diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_user.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_user.c new file mode 100644 index 000000000..8c769ed93 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_user.c @@ -0,0 +1,84 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +#if (NGX_CRYPT) + +#if (NGX_HAVE_GNU_CRYPT_R) + +ngx_int_t +ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted) +{ + char *value; + size_t len; + struct crypt_data cd; + + cd.initialized = 0; + + value = crypt_r((char *) key, (char *) salt, &cd); + + if (value) { + len = ngx_strlen(value) + 1; + + *encrypted = ngx_pnalloc(pool, len); + if (*encrypted == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(*encrypted, value, len); + return NGX_OK; + } + + ngx_log_error(NGX_LOG_CRIT, pool->log, ngx_errno, "crypt_r() failed"); + + return NGX_ERROR; +} + +#elif (NGX_HAVE_CRYPT) + +ngx_int_t +ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted) +{ + char *value; + size_t len; + ngx_err_t err; + + value = crypt((char *) key, (char *) salt); + + if (value) { + len = ngx_strlen(value) + 1; + + *encrypted = ngx_pnalloc(pool, len); + if (*encrypted == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(*encrypted, value, len); + return NGX_OK; + } + + err = ngx_errno; + + ngx_log_error(NGX_LOG_CRIT, pool->log, err, "crypt() failed"); + + return NGX_ERROR; +} + +#else + +ngx_int_t +ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted) +{ + return NGX_ERROR; +} + +#endif + +#endif /* NGX_CRYPT */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_user.h b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_user.h new file mode 100644 index 000000000..6e82204f4 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_user.h @@ -0,0 +1,24 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_USER_H_INCLUDED_ +#define _NGX_USER_H_INCLUDED_ + + +#include +#include + + +typedef uid_t ngx_uid_t; +typedef gid_t ngx_gid_t; + + +ngx_int_t ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, + u_char **encrypted); + + +#endif /* _NGX_USER_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/unix/ngx_writev_chain.c b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_writev_chain.c new file mode 100644 index 000000000..e38a3aae0 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/unix/ngx_writev_chain.c @@ -0,0 +1,216 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +ngx_chain_t * +ngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) +{ + ssize_t n, sent; + off_t send, prev_send; + ngx_chain_t *cl; + ngx_event_t *wev; + ngx_iovec_t vec; + struct iovec iovs[NGX_IOVS_PREALLOCATE]; + + wev = c->write; + + if (!wev->ready) { + return in; + } + +#if (NGX_HAVE_KQUEUE) + + if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) { + (void) ngx_connection_error(c, wev->kq_errno, + "kevent() reported about an closed connection"); + wev->error = 1; + return NGX_CHAIN_ERROR; + } + +#endif + + /* the maximum limit size is the maximum size_t value - the page size */ + + if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) { + limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize; + } + + send = 0; + + vec.iovs = iovs; + vec.nalloc = NGX_IOVS_PREALLOCATE; + + for ( ;; ) { + prev_send = send; + + /* create the iovec and coalesce the neighbouring bufs */ + + cl = ngx_output_chain_to_iovec(&vec, in, limit - send, c->log); + + if (cl == NGX_CHAIN_ERROR) { + return NGX_CHAIN_ERROR; + } + + if (cl && cl->buf->in_file) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "file buf in writev " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + cl->buf->temporary, + cl->buf->recycled, + cl->buf->in_file, + cl->buf->start, + cl->buf->pos, + cl->buf->last, + cl->buf->file, + cl->buf->file_pos, + cl->buf->file_last); + + ngx_debug_point(); + + return NGX_CHAIN_ERROR; + } + + send += vec.size; + + n = ngx_writev(c, &vec); + + if (n == NGX_ERROR) { + return NGX_CHAIN_ERROR; + } + + sent = (n == NGX_AGAIN) ? 0 : n; + + c->sent += sent; + + in = ngx_chain_update_sent(in, sent); + + if (send - prev_send != sent) { + wev->ready = 0; + return in; + } + + if (send >= limit || in == NULL) { + return in; + } + } +} + + +ngx_chain_t * +ngx_output_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *in, size_t limit, + ngx_log_t *log) +{ + size_t total, size; + u_char *prev; + ngx_uint_t n; + struct iovec *iov; + + iov = NULL; + prev = NULL; + total = 0; + n = 0; + + for ( /* void */ ; in && total < limit; in = in->next) { + + if (ngx_buf_special(in->buf)) { + continue; + } + + if (in->buf->in_file) { + break; + } + + if (!ngx_buf_in_memory(in->buf)) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "bad buf in output chain " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + in->buf->temporary, + in->buf->recycled, + in->buf->in_file, + in->buf->start, + in->buf->pos, + in->buf->last, + in->buf->file, + in->buf->file_pos, + in->buf->file_last); + + ngx_debug_point(); + + return NGX_CHAIN_ERROR; + } + + size = in->buf->last - in->buf->pos; + + if (size > limit - total) { + size = limit - total; + } + + if (prev == in->buf->pos) { + iov->iov_len += size; + + } else { + if (n == vec->nalloc) { + break; + } + + iov = &vec->iovs[n++]; + + iov->iov_base = (void *) in->buf->pos; + iov->iov_len = size; + } + + prev = in->buf->pos + size; + total += size; + } + + vec->count = n; + vec->size = total; + + return in; +} + + +ssize_t +ngx_writev(ngx_connection_t *c, ngx_iovec_t *vec) +{ + ssize_t n; + ngx_err_t err; + +eintr: + + n = writev(c->fd, vec->iovs, vec->count); + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "writev: %z of %uz", n, vec->size); + + if (n == -1) { + err = ngx_errno; + + switch (err) { + case NGX_EAGAIN: + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "writev() not ready"); + return NGX_AGAIN; + + case NGX_EINTR: + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "writev() was interrupted"); + goto eintr; + + default: + c->write->error = 1; + ngx_connection_error(c, err, "writev() failed"); + return NGX_ERROR; + } + } + + return n; +} diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/nginx.ico b/src/deps/src/nginx-1.30.1/src/os/win32/nginx.ico new file mode 100644 index 000000000..70f79db1c Binary files /dev/null and b/src/deps/src/nginx-1.30.1/src/os/win32/nginx.ico differ diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/nginx.rc b/src/deps/src/nginx-1.30.1/src/os/win32/nginx.rc new file mode 100644 index 000000000..dc8b7ab94 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/nginx.rc @@ -0,0 +1,6 @@ + +// Copyright (C) Igor Sysoev +// Copyright (C) Nginx, Inc. + + +nginx icon discardable "src\\os\\win32\\nginx.ico" diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/nginx_icon16.xpm b/src/deps/src/nginx-1.30.1/src/os/win32/nginx_icon16.xpm new file mode 100644 index 000000000..45e4bad37 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/nginx_icon16.xpm @@ -0,0 +1,24 @@ +/* XPM */ +static char * nginx_xpm[] = { +"16 16 2 2", +/* colors */ +" c none", +"GG c #009900", +/* pixels */ +" ", +" GGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGG ", +" GGGGGG GGGGGG ", +" GGGGGG GGGGGG ", +" GGGGGG ", +" GGGGGG GGGGGGGGGGGGGGGG ", +" GGGGGG GGGGGGGGGGGGGGGGGG ", +" GGGGGG GGGGGGGGGGGGGG ", +" GGGGGG GGGGGG ", +" GGGGGG GGGGGG ", +" GGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG ", +" " +}; diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/nginx_icon32.xpm b/src/deps/src/nginx-1.30.1/src/os/win32/nginx_icon32.xpm new file mode 100644 index 000000000..eb2663884 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/nginx_icon32.xpm @@ -0,0 +1,39 @@ +/* XPM */ +static char * nginx_xpm[] = { +"32 32 2 2", +/* colors */ +" c none", +"GG c #009900", +/* pixels */ +" ", +" ", +" ", +" ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGG GGGGGGGGGG ", +" GGGGGGGGGG GGGGGGGGGG ", +" GGGGGGGGGG GGGGGGGGGG ", +" GGGGGGGGGG GGGGGGGGGG ", +" GGGGGGGGGG ", +" GGGGGGGGGG ", +" GGGGGGGGGG GGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGG GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGG GGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGG GGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGG GGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGG GGGGGGGGGG ", +" GGGGGGGGGG GGGGGGGGGG ", +" GGGGGGGGGG GGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" ", +" ", +" ", +" " diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/nginx_icon48.xpm b/src/deps/src/nginx-1.30.1/src/os/win32/nginx_icon48.xpm new file mode 100644 index 000000000..c25ba0f2f --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/nginx_icon48.xpm @@ -0,0 +1,55 @@ +/* XPM */ +static char * nginx_xpm[] = { +"48 48 2 2", +/* colors */ +" c none", +"GG c #009900", +/* pixels */ +" ", +" ", +" ", +" ", +" ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG ", +" ", +" ", +" ", +" ", +" ", diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_alloc.c b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_alloc.c new file mode 100644 index 000000000..0c0ef30f3 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_alloc.c @@ -0,0 +1,44 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +ngx_uint_t ngx_pagesize; +ngx_uint_t ngx_pagesize_shift; +ngx_uint_t ngx_cacheline_size; + + +void *ngx_alloc(size_t size, ngx_log_t *log) +{ + void *p; + + p = malloc(size); + if (p == NULL) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "malloc(%uz) failed", size); + } + + ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, "malloc: %p:%uz", p, size); + + return p; +} + + +void *ngx_calloc(size_t size, ngx_log_t *log) +{ + void *p; + + p = ngx_alloc(size, log); + + if (p) { + ngx_memzero(p, size); + } + + return p; +} diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_alloc.h b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_alloc.h new file mode 100644 index 000000000..5a0fa3f29 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_alloc.h @@ -0,0 +1,27 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_ALLOC_H_INCLUDED_ +#define _NGX_ALLOC_H_INCLUDED_ + + +#include +#include + + +void *ngx_alloc(size_t size, ngx_log_t *log); +void *ngx_calloc(size_t size, ngx_log_t *log); + +#define ngx_free free +#define ngx_memalign(alignment, size, log) ngx_alloc(size, log) + +extern ngx_uint_t ngx_pagesize; +extern ngx_uint_t ngx_pagesize_shift; +extern ngx_uint_t ngx_cacheline_size; + + +#endif /* _NGX_ALLOC_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_atomic.h b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_atomic.h new file mode 100644 index 000000000..113f56161 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_atomic.h @@ -0,0 +1,69 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_ATOMIC_H_INCLUDED_ +#define _NGX_ATOMIC_H_INCLUDED_ + + +#include +#include + + +#define NGX_HAVE_ATOMIC_OPS 1 + +typedef int32_t ngx_atomic_int_t; +typedef uint32_t ngx_atomic_uint_t; +typedef volatile ngx_atomic_uint_t ngx_atomic_t; +#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1) + + +#if defined( __WATCOMC__ ) || defined( __BORLANDC__ ) || defined(__GNUC__) \ + || ( _MSC_VER >= 1300 ) + +/* the new SDK headers */ + +#define ngx_atomic_cmp_set(lock, old, set) \ + ((ngx_atomic_uint_t) InterlockedCompareExchange((long *) lock, set, old) \ + == old) + +#else + +/* the old MS VC6.0SP2 SDK headers */ + +#define ngx_atomic_cmp_set(lock, old, set) \ + (InterlockedCompareExchange((void **) lock, (void *) set, (void *) old) \ + == (void *) old) + +#endif + + +#define ngx_atomic_fetch_add(p, add) InterlockedExchangeAdd((long *) p, add) + + +#define ngx_memory_barrier() + + +#if defined( __BORLANDC__ ) || ( __WATCOMC__ < 1230 ) + +/* + * Borland C++ 5.5 (tasm32) and Open Watcom C prior to 1.3 + * do not understand the "pause" instruction + */ + +#define ngx_cpu_pause() +#else +#define ngx_cpu_pause() __asm { pause } +#endif + + +void ngx_spinlock(ngx_atomic_t *lock, ngx_atomic_int_t value, ngx_uint_t spin); + +#define ngx_trylock(lock) (*(lock) == 0 && ngx_atomic_cmp_set(lock, 0, 1)) +#define ngx_unlock(lock) *(lock) = 0 + + +#endif /* _NGX_ATOMIC_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_dlopen.c b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_dlopen.c new file mode 100644 index 000000000..804f49d13 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_dlopen.c @@ -0,0 +1,22 @@ + +/* + * Copyright (C) Maxim Dounin + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +char * +ngx_dlerror(void) +{ + u_char *p; + static u_char errstr[NGX_MAX_ERROR_STR]; + + p = ngx_strerror(ngx_errno, errstr, NGX_MAX_ERROR_STR); + *p = '\0'; + + return (char *) errstr; +} diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_dlopen.h b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_dlopen.h new file mode 100644 index 000000000..0d6b40524 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_dlopen.h @@ -0,0 +1,32 @@ + +/* + * Copyright (C) Maxim Dounin + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_DLOPEN_H_INCLUDED_ +#define _NGX_DLOPEN_H_INCLUDED_ + + +#include +#include + + +#define NGX_HAVE_DLOPEN 1 + + +#define ngx_dlopen(path) LoadLibrary((char *) path) +#define ngx_dlopen_n "LoadLibrary()" + +#define ngx_dlsym(handle, symbol) (void *) GetProcAddress(handle, symbol) +#define ngx_dlsym_n "GetProcAddress()" + +#define ngx_dlclose(handle) (FreeLibrary(handle) ? 0 : -1) +#define ngx_dlclose_n "FreeLibrary()" + + +char *ngx_dlerror(void); + + +#endif /* _NGX_DLOPEN_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_errno.c b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_errno.c new file mode 100644 index 000000000..966d3def2 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_errno.c @@ -0,0 +1,60 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +u_char * +ngx_strerror(ngx_err_t err, u_char *errstr, size_t size) +{ + u_int len; + static u_long lang = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US); + + if (size == 0) { + return errstr; + } + + len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + NULL, err, lang, (char *) errstr, size, NULL); + + if (len == 0 && lang) { + + /* + * Try to use English messages first and fallback to a language, + * based on locale: non-English Windows have no English messages + * at all. This way allows to use English messages at least on + * Windows with MUI. + */ + + lang = 0; + + len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + NULL, err, lang, (char *) errstr, size, NULL); + } + + if (len == 0) { + return ngx_snprintf(errstr, size, + "FormatMessage() error:(%d)", GetLastError()); + } + + /* remove ".\r\n\0" */ + while (errstr[len] == '\0' || errstr[len] == CR + || errstr[len] == LF || errstr[len] == '.') + { + --len; + } + + return &errstr[++len]; +} + + +ngx_int_t +ngx_strerror_init(void) +{ + return NGX_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_errno.h b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_errno.h new file mode 100644 index 000000000..1e73a832b --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_errno.h @@ -0,0 +1,72 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_ERRNO_H_INCLUDED_ +#define _NGX_ERRNO_H_INCLUDED_ + + +#include +#include + + +typedef DWORD ngx_err_t; + +#define ngx_errno GetLastError() +#define ngx_set_errno(err) SetLastError(err) +#define ngx_socket_errno WSAGetLastError() +#define ngx_set_socket_errno(err) WSASetLastError(err) + +#define NGX_EPERM ERROR_ACCESS_DENIED +#define NGX_ENOENT ERROR_FILE_NOT_FOUND +#define NGX_ENOPATH ERROR_PATH_NOT_FOUND +#define NGX_ENOMEM ERROR_NOT_ENOUGH_MEMORY +#define NGX_EACCES ERROR_ACCESS_DENIED +/* + * there are two EEXIST error codes: + * ERROR_FILE_EXISTS used by CreateFile(CREATE_NEW), + * and ERROR_ALREADY_EXISTS used by CreateDirectory(); + * MoveFile() uses both + */ +#define NGX_EEXIST ERROR_ALREADY_EXISTS +#define NGX_EEXIST_FILE ERROR_FILE_EXISTS +#define NGX_EXDEV ERROR_NOT_SAME_DEVICE +#define NGX_ENOTDIR ERROR_PATH_NOT_FOUND +#define NGX_EISDIR ERROR_CANNOT_MAKE +#define NGX_ENOSPC ERROR_DISK_FULL +#define NGX_EPIPE EPIPE +#define NGX_EAGAIN WSAEWOULDBLOCK +#define NGX_EINPROGRESS WSAEINPROGRESS +#define NGX_ENOPROTOOPT WSAENOPROTOOPT +#define NGX_EOPNOTSUPP WSAEOPNOTSUPP +#define NGX_EADDRINUSE WSAEADDRINUSE +#define NGX_ECONNABORTED WSAECONNABORTED +#define NGX_ECONNRESET WSAECONNRESET +#define NGX_ENOTCONN WSAENOTCONN +#define NGX_ETIMEDOUT WSAETIMEDOUT +#define NGX_ECONNREFUSED WSAECONNREFUSED +#define NGX_ENAMETOOLONG ERROR_BAD_PATHNAME +#define NGX_ENETDOWN WSAENETDOWN +#define NGX_ENETUNREACH WSAENETUNREACH +#define NGX_EHOSTDOWN WSAEHOSTDOWN +#define NGX_EHOSTUNREACH WSAEHOSTUNREACH +#define NGX_ENOMOREFILES ERROR_NO_MORE_FILES +#define NGX_EILSEQ ERROR_NO_UNICODE_TRANSLATION +#define NGX_ELOOP 0 +#define NGX_EBADF WSAEBADF +#define NGX_EMSGSIZE WSAEMSGSIZE + +#define NGX_EALREADY WSAEALREADY +#define NGX_EINVAL WSAEINVAL +#define NGX_EMFILE WSAEMFILE +#define NGX_ENFILE WSAEMFILE + + +u_char *ngx_strerror(ngx_err_t err, u_char *errstr, size_t size); +ngx_int_t ngx_strerror_init(void); + + +#endif /* _NGX_ERRNO_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_event_log.c b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_event_log.c new file mode 100644 index 000000000..e11ed1e82 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_event_log.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +#define NGX_MAX_ERROR_STR 2048 + + +void ngx_cdecl +ngx_event_log(ngx_err_t err, const char *fmt, ...) +{ + u_char *p, *last; + long types; + HKEY key; + HANDLE ev; + va_list args; + u_char text[NGX_MAX_ERROR_STR]; + const char *msgarg[9]; + static u_char netmsg[] = "%SystemRoot%\\System32\\netmsg.dll"; + + last = text + NGX_MAX_ERROR_STR; + p = text + GetModuleFileName(NULL, (char *) text, NGX_MAX_ERROR_STR - 50); + + *p++ = ':'; + ngx_linefeed(p); + + va_start(args, fmt); + p = ngx_vslprintf(p, last, fmt, args); + va_end(args); + + if (err) { + p = ngx_log_errno(p, last, err); + } + + if (p > last - NGX_LINEFEED_SIZE - 1) { + p = last - NGX_LINEFEED_SIZE - 1; + } + + ngx_linefeed(p); + + *p = '\0'; + + /* + * we do not log errors here since we use + * Event Log only to log our own logs open errors + */ + + if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, + "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\nginx", + 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &key, NULL) + != 0) + { + return; + } + + if (RegSetValueEx(key, "EventMessageFile", 0, REG_EXPAND_SZ, + netmsg, sizeof(netmsg) - 1) + != 0) + { + return; + } + + types = EVENTLOG_ERROR_TYPE; + + if (RegSetValueEx(key, "TypesSupported", 0, REG_DWORD, + (u_char *) &types, sizeof(long)) + != 0) + { + return; + } + + RegCloseKey(key); + + ev = RegisterEventSource(NULL, "nginx"); + + msgarg[0] = (char *) text; + msgarg[1] = NULL; + msgarg[2] = NULL; + msgarg[3] = NULL; + msgarg[4] = NULL; + msgarg[5] = NULL; + msgarg[6] = NULL; + msgarg[7] = NULL; + msgarg[8] = NULL; + + /* + * the 3299 event id in netmsg.dll has the generic message format: + * "%1 %2 %3 %4 %5 %6 %7 %8 %9" + */ + + ReportEvent(ev, EVENTLOG_ERROR_TYPE, 0, 3299, NULL, 9, 0, msgarg, NULL); + + DeregisterEventSource(ev); +} diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_files.c b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_files.c new file mode 100644 index 000000000..90644ad9c --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_files.c @@ -0,0 +1,1459 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +#define NGX_UTF16_BUFLEN 256 +#define NGX_UTF8_BUFLEN 512 + +static ngx_int_t ngx_win32_check_filename(u_short *u, size_t len, + ngx_uint_t dirname); +static u_short *ngx_utf8_to_utf16(u_short *utf16, u_char *utf8, size_t *len, + size_t reserved); +static u_char *ngx_utf16_to_utf8(u_char *utf8, u_short *utf16, size_t *len, + size_t *allocated); +uint32_t ngx_utf16_decode(u_short **u, size_t n); + + +/* FILE_FLAG_BACKUP_SEMANTICS allows to obtain a handle to a directory */ + +ngx_fd_t +ngx_open_file(u_char *name, u_long mode, u_long create, u_long access) +{ + size_t len; + u_short *u; + ngx_fd_t fd; + ngx_err_t err; + u_short utf16[NGX_UTF16_BUFLEN]; + + len = NGX_UTF16_BUFLEN; + u = ngx_utf8_to_utf16(utf16, name, &len, 0); + + if (u == NULL) { + return INVALID_HANDLE_VALUE; + } + + fd = INVALID_HANDLE_VALUE; + + if (create == NGX_FILE_OPEN + && ngx_win32_check_filename(u, len, 0) != NGX_OK) + { + goto failed; + } + + fd = CreateFileW(u, mode, + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + NULL, create, FILE_FLAG_BACKUP_SEMANTICS, NULL); + +failed: + + if (u != utf16) { + err = ngx_errno; + ngx_free(u); + ngx_set_errno(err); + } + + return fd; +} + + +ngx_fd_t +ngx_open_tempfile(u_char *name, ngx_uint_t persistent, ngx_uint_t access) +{ + size_t len; + u_short *u; + ngx_fd_t fd; + ngx_err_t err; + u_short utf16[NGX_UTF16_BUFLEN]; + + len = NGX_UTF16_BUFLEN; + u = ngx_utf8_to_utf16(utf16, name, &len, 0); + + if (u == NULL) { + return INVALID_HANDLE_VALUE; + } + + fd = CreateFileW(u, + GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + NULL, + CREATE_NEW, + persistent ? 0: + FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE, + NULL); + + if (u != utf16) { + err = ngx_errno; + ngx_free(u); + ngx_set_errno(err); + } + + return fd; +} + + +ssize_t +ngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset) +{ + u_long n; + ngx_err_t err; + OVERLAPPED ovlp, *povlp; + + ovlp.Internal = 0; + ovlp.InternalHigh = 0; + ovlp.Offset = (u_long) offset; + ovlp.OffsetHigh = (u_long) (offset >> 32); + ovlp.hEvent = NULL; + + povlp = &ovlp; + + if (ReadFile(file->fd, buf, size, &n, povlp) == 0) { + err = ngx_errno; + + if (err == ERROR_HANDLE_EOF) { + return 0; + } + + ngx_log_error(NGX_LOG_ERR, file->log, err, + "ReadFile() \"%s\" failed", file->name.data); + return NGX_ERROR; + } + + file->offset += n; + + return n; +} + + +ssize_t +ngx_write_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset) +{ + u_long n; + OVERLAPPED ovlp, *povlp; + + ovlp.Internal = 0; + ovlp.InternalHigh = 0; + ovlp.Offset = (u_long) offset; + ovlp.OffsetHigh = (u_long) (offset >> 32); + ovlp.hEvent = NULL; + + povlp = &ovlp; + + if (WriteFile(file->fd, buf, size, &n, povlp) == 0) { + ngx_log_error(NGX_LOG_ERR, file->log, ngx_errno, + "WriteFile() \"%s\" failed", file->name.data); + return NGX_ERROR; + } + + if (n != size) { + ngx_log_error(NGX_LOG_CRIT, file->log, 0, + "WriteFile() \"%s\" has written only %ul of %uz", + file->name.data, n, size); + return NGX_ERROR; + } + + file->offset += n; + + return n; +} + + +ssize_t +ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *cl, off_t offset, + ngx_pool_t *pool) +{ + u_char *buf, *prev; + size_t size; + ssize_t total, n; + + total = 0; + + while (cl) { + buf = cl->buf->pos; + prev = buf; + size = 0; + + /* coalesce the neighbouring bufs */ + + while (cl && prev == cl->buf->pos) { + size += cl->buf->last - cl->buf->pos; + prev = cl->buf->last; + cl = cl->next; + } + + n = ngx_write_file(file, buf, size, offset); + + if (n == NGX_ERROR) { + return NGX_ERROR; + } + + total += n; + offset += n; + } + + return total; +} + + +ssize_t +ngx_read_fd(ngx_fd_t fd, void *buf, size_t size) +{ + u_long n; + + if (ReadFile(fd, buf, size, &n, NULL) != 0) { + return (size_t) n; + } + + return -1; +} + + +ssize_t +ngx_write_fd(ngx_fd_t fd, void *buf, size_t size) +{ + u_long n; + + if (WriteFile(fd, buf, size, &n, NULL) != 0) { + return (size_t) n; + } + + return -1; +} + + +ssize_t +ngx_write_console(ngx_fd_t fd, void *buf, size_t size) +{ + u_long n; + + (void) CharToOemBuff(buf, buf, size); + + if (WriteFile(fd, buf, size, &n, NULL) != 0) { + return (size_t) n; + } + + return -1; +} + + +ngx_int_t +ngx_delete_file(u_char *name) +{ + long rc; + size_t len; + u_short *u; + ngx_err_t err; + u_short utf16[NGX_UTF16_BUFLEN]; + + len = NGX_UTF16_BUFLEN; + u = ngx_utf8_to_utf16(utf16, name, &len, 0); + + if (u == NULL) { + return NGX_FILE_ERROR; + } + + rc = NGX_FILE_ERROR; + + if (ngx_win32_check_filename(u, len, 0) != NGX_OK) { + goto failed; + } + + rc = DeleteFileW(u); + +failed: + + if (u != utf16) { + err = ngx_errno; + ngx_free(u); + ngx_set_errno(err); + } + + return rc; +} + + +ngx_int_t +ngx_rename_file(u_char *from, u_char *to) +{ + long rc; + size_t len; + u_short *fu, *tu; + ngx_err_t err; + u_short utf16f[NGX_UTF16_BUFLEN]; + u_short utf16t[NGX_UTF16_BUFLEN]; + + len = NGX_UTF16_BUFLEN; + fu = ngx_utf8_to_utf16(utf16f, from, &len, 0); + + if (fu == NULL) { + return NGX_FILE_ERROR; + } + + rc = NGX_FILE_ERROR; + tu = NULL; + + if (ngx_win32_check_filename(fu, len, 0) != NGX_OK) { + goto failed; + } + + len = NGX_UTF16_BUFLEN; + tu = ngx_utf8_to_utf16(utf16t, to, &len, 0); + + if (tu == NULL) { + goto failed; + } + + if (ngx_win32_check_filename(tu, len, 1) != NGX_OK) { + goto failed; + } + + rc = MoveFileW(fu, tu); + +failed: + + if (fu != utf16f) { + err = ngx_errno; + ngx_free(fu); + ngx_set_errno(err); + } + + if (tu && tu != utf16t) { + err = ngx_errno; + ngx_free(tu); + ngx_set_errno(err); + } + + return rc; +} + + +ngx_err_t +ngx_win32_rename_file(ngx_str_t *from, ngx_str_t *to, ngx_log_t *log) +{ + u_char *name; + ngx_err_t err; + ngx_uint_t collision; + ngx_atomic_uint_t num; + + name = ngx_alloc(to->len + 1 + NGX_ATOMIC_T_LEN + 1 + sizeof("DELETE"), + log); + if (name == NULL) { + return NGX_ENOMEM; + } + + ngx_memcpy(name, to->data, to->len); + + collision = 0; + + /* mutex_lock() (per cache or single ?) */ + + for ( ;; ) { + num = ngx_next_temp_number(collision); + + ngx_sprintf(name + to->len, ".%0muA.DELETE%Z", num); + + if (ngx_rename_file(to->data, name) != NGX_FILE_ERROR) { + break; + } + + err = ngx_errno; + + if (err == NGX_EEXIST || err == NGX_EEXIST_FILE) { + collision = 1; + continue; + } + + ngx_log_error(NGX_LOG_CRIT, log, err, + "MoveFile() \"%s\" to \"%s\" failed", to->data, name); + goto failed; + } + + if (ngx_rename_file(from->data, to->data) == NGX_FILE_ERROR) { + err = ngx_errno; + + } else { + err = 0; + } + + if (ngx_delete_file(name) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, log, ngx_errno, + "DeleteFile() \"%s\" failed", name); + } + +failed: + + /* mutex_unlock() */ + + ngx_free(name); + + return err; +} + + +ngx_int_t +ngx_file_info(u_char *file, ngx_file_info_t *sb) +{ + size_t len; + long rc; + u_short *u; + ngx_err_t err; + WIN32_FILE_ATTRIBUTE_DATA fa; + u_short utf16[NGX_UTF16_BUFLEN]; + + len = NGX_UTF16_BUFLEN; + + u = ngx_utf8_to_utf16(utf16, file, &len, 0); + + if (u == NULL) { + return NGX_FILE_ERROR; + } + + rc = NGX_FILE_ERROR; + + if (ngx_win32_check_filename(u, len, 0) != NGX_OK) { + goto failed; + } + + rc = GetFileAttributesExW(u, GetFileExInfoStandard, &fa); + + sb->dwFileAttributes = fa.dwFileAttributes; + sb->ftCreationTime = fa.ftCreationTime; + sb->ftLastAccessTime = fa.ftLastAccessTime; + sb->ftLastWriteTime = fa.ftLastWriteTime; + sb->nFileSizeHigh = fa.nFileSizeHigh; + sb->nFileSizeLow = fa.nFileSizeLow; + +failed: + + if (u != utf16) { + err = ngx_errno; + ngx_free(u); + ngx_set_errno(err); + } + + return rc; +} + + +ngx_int_t +ngx_set_file_time(u_char *name, ngx_fd_t fd, time_t s) +{ + uint64_t intervals; + FILETIME ft; + + /* 116444736000000000 is commented in src/os/win32/ngx_time.c */ + + intervals = s * 10000000 + 116444736000000000; + + ft.dwLowDateTime = (DWORD) intervals; + ft.dwHighDateTime = (DWORD) (intervals >> 32); + + if (SetFileTime(fd, NULL, NULL, &ft) != 0) { + return NGX_OK; + } + + return NGX_ERROR; +} + + +ngx_int_t +ngx_create_file_mapping(ngx_file_mapping_t *fm) +{ + LARGE_INTEGER size; + + fm->fd = ngx_open_file(fm->name, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, + NGX_FILE_DEFAULT_ACCESS); + + if (fm->fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno, + ngx_open_file_n " \"%s\" failed", fm->name); + return NGX_ERROR; + } + + fm->handle = NULL; + + size.QuadPart = fm->size; + + if (SetFilePointerEx(fm->fd, size, NULL, FILE_BEGIN) == 0) { + ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno, + "SetFilePointerEx(\"%s\", %uz) failed", + fm->name, fm->size); + goto failed; + } + + if (SetEndOfFile(fm->fd) == 0) { + ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno, + "SetEndOfFile() \"%s\" failed", fm->name); + goto failed; + } + + fm->handle = CreateFileMapping(fm->fd, NULL, PAGE_READWRITE, + (u_long) ((off_t) fm->size >> 32), + (u_long) ((off_t) fm->size & 0xffffffff), + NULL); + if (fm->handle == NULL) { + ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno, + "CreateFileMapping(%s, %uz) failed", + fm->name, fm->size); + goto failed; + } + + fm->addr = MapViewOfFile(fm->handle, FILE_MAP_WRITE, 0, 0, 0); + + if (fm->addr != NULL) { + return NGX_OK; + } + + ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno, + "MapViewOfFile(%uz) of file mapping \"%s\" failed", + fm->size, fm->name); + +failed: + + if (fm->handle) { + if (CloseHandle(fm->handle) == 0) { + ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno, + "CloseHandle() of file mapping \"%s\" failed", + fm->name); + } + } + + if (ngx_close_file(fm->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", fm->name); + } + + return NGX_ERROR; +} + + +void +ngx_close_file_mapping(ngx_file_mapping_t *fm) +{ + if (UnmapViewOfFile(fm->addr) == 0) { + ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno, + "UnmapViewOfFile(%p) of file mapping \"%s\" failed", + fm->addr, &fm->name); + } + + if (CloseHandle(fm->handle) == 0) { + ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno, + "CloseHandle() of file mapping \"%s\" failed", + &fm->name); + } + + if (ngx_close_file(fm->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", fm->name); + } +} + + +u_char * +ngx_realpath(u_char *path, u_char *resolved) +{ + /* STUB */ + return path; +} + + +size_t +ngx_getcwd(u_char *buf, size_t size) +{ + u_char *p; + size_t n; + u_short utf16[NGX_MAX_PATH]; + + n = GetCurrentDirectoryW(NGX_MAX_PATH, utf16); + + if (n == 0) { + return 0; + } + + if (n > NGX_MAX_PATH) { + ngx_set_errno(ERROR_INSUFFICIENT_BUFFER); + return 0; + } + + p = ngx_utf16_to_utf8(buf, utf16, &size, NULL); + + if (p == NULL) { + return 0; + } + + if (p != buf) { + ngx_free(p); + ngx_set_errno(ERROR_INSUFFICIENT_BUFFER); + return 0; + } + + return size - 1; +} + + +ngx_int_t +ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir) +{ + size_t len; + u_short *u, *p; + ngx_err_t err; + u_short utf16[NGX_UTF16_BUFLEN]; + + len = NGX_UTF16_BUFLEN - 2; + u = ngx_utf8_to_utf16(utf16, name->data, &len, 2); + + if (u == NULL) { + return NGX_ERROR; + } + + if (ngx_win32_check_filename(u, len, 0) != NGX_OK) { + goto failed; + } + + p = &u[len - 1]; + + *p++ = '/'; + *p++ = '*'; + *p = '\0'; + + dir->dir = FindFirstFileW(u, &dir->finddata); + + if (dir->dir == INVALID_HANDLE_VALUE) { + goto failed; + } + + if (u != utf16) { + ngx_free(u); + } + + dir->valid_info = 1; + dir->ready = 1; + dir->name = NULL; + dir->allocated = 0; + + return NGX_OK; + +failed: + + if (u != utf16) { + err = ngx_errno; + ngx_free(u); + ngx_set_errno(err); + } + + return NGX_ERROR; +} + + +ngx_int_t +ngx_read_dir(ngx_dir_t *dir) +{ + u_char *name; + size_t len, allocated; + + if (dir->ready) { + dir->ready = 0; + goto convert; + } + + if (FindNextFileW(dir->dir, &dir->finddata) != 0) { + dir->type = 1; + goto convert; + } + + return NGX_ERROR; + +convert: + + name = dir->name; + len = dir->allocated; + + name = ngx_utf16_to_utf8(name, dir->finddata.cFileName, &len, &allocated); + + if (name == NULL) { + return NGX_ERROR; + } + + if (name != dir->name) { + + if (dir->name) { + ngx_free(dir->name); + } + + dir->name = name; + dir->allocated = allocated; + } + + dir->namelen = len - 1; + + return NGX_OK; +} + + +ngx_int_t +ngx_close_dir(ngx_dir_t *dir) +{ + if (dir->name) { + ngx_free(dir->name); + } + + if (FindClose(dir->dir) == 0) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_create_dir(u_char *name, ngx_uint_t access) +{ + long rc; + size_t len; + u_short *u; + ngx_err_t err; + u_short utf16[NGX_UTF16_BUFLEN]; + + len = NGX_UTF16_BUFLEN; + u = ngx_utf8_to_utf16(utf16, name, &len, 0); + + if (u == NULL) { + return NGX_FILE_ERROR; + } + + rc = NGX_FILE_ERROR; + + if (ngx_win32_check_filename(u, len, 1) != NGX_OK) { + goto failed; + } + + rc = CreateDirectoryW(u, NULL); + +failed: + + if (u != utf16) { + err = ngx_errno; + ngx_free(u); + ngx_set_errno(err); + } + + return rc; +} + + +ngx_int_t +ngx_delete_dir(u_char *name) +{ + long rc; + size_t len; + u_short *u; + ngx_err_t err; + u_short utf16[NGX_UTF16_BUFLEN]; + + len = NGX_UTF16_BUFLEN; + u = ngx_utf8_to_utf16(utf16, name, &len, 0); + + if (u == NULL) { + return NGX_FILE_ERROR; + } + + rc = NGX_FILE_ERROR; + + if (ngx_win32_check_filename(u, len, 0) != NGX_OK) { + goto failed; + } + + rc = RemoveDirectoryW(u); + +failed: + + if (u != utf16) { + err = ngx_errno; + ngx_free(u); + ngx_set_errno(err); + } + + return rc; +} + + +ngx_int_t +ngx_open_glob(ngx_glob_t *gl) +{ + u_char *p; + size_t len; + u_short *u; + ngx_err_t err; + u_short utf16[NGX_UTF16_BUFLEN]; + + len = NGX_UTF16_BUFLEN; + u = ngx_utf8_to_utf16(utf16, gl->pattern, &len, 0); + + if (u == NULL) { + return NGX_ERROR; + } + + gl->dir = FindFirstFileW(u, &gl->finddata); + + if (gl->dir == INVALID_HANDLE_VALUE) { + + err = ngx_errno; + + if (u != utf16) { + ngx_free(u); + } + + if ((err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) + && gl->test) + { + gl->no_match = 1; + return NGX_OK; + } + + ngx_set_errno(err); + + return NGX_ERROR; + } + + for (p = gl->pattern; *p; p++) { + if (*p == '/') { + gl->last = p + 1 - gl->pattern; + } + } + + if (u != utf16) { + ngx_free(u); + } + + gl->ready = 1; + + return NGX_OK; +} + + +ngx_int_t +ngx_read_glob(ngx_glob_t *gl, ngx_str_t *name) +{ + u_char *p; + size_t len; + ngx_err_t err; + u_char utf8[NGX_UTF8_BUFLEN]; + + if (gl->no_match) { + return NGX_DONE; + } + + if (gl->ready) { + gl->ready = 0; + goto convert; + } + + ngx_free(gl->name.data); + gl->name.data = NULL; + + if (FindNextFileW(gl->dir, &gl->finddata) != 0) { + goto convert; + } + + err = ngx_errno; + + if (err == NGX_ENOMOREFILES) { + return NGX_DONE; + } + + ngx_log_error(NGX_LOG_ALERT, gl->log, err, + "FindNextFile(%s) failed", gl->pattern); + + return NGX_ERROR; + +convert: + + len = NGX_UTF8_BUFLEN; + p = ngx_utf16_to_utf8(utf8, gl->finddata.cFileName, &len, NULL); + + if (p == NULL) { + return NGX_ERROR; + } + + gl->name.len = gl->last + len - 1; + + gl->name.data = ngx_alloc(gl->name.len + 1, gl->log); + if (gl->name.data == NULL) { + goto failed; + } + + ngx_memcpy(gl->name.data, gl->pattern, gl->last); + ngx_cpystrn(gl->name.data + gl->last, p, len); + + if (p != utf8) { + ngx_free(p); + } + + *name = gl->name; + + return NGX_OK; + +failed: + + if (p != utf8) { + err = ngx_errno; + ngx_free(p); + ngx_set_errno(err); + } + + return NGX_ERROR; +} + + +void +ngx_close_glob(ngx_glob_t *gl) +{ + if (gl->name.data) { + ngx_free(gl->name.data); + } + + if (gl->dir == INVALID_HANDLE_VALUE) { + return; + } + + if (FindClose(gl->dir) == 0) { + ngx_log_error(NGX_LOG_ALERT, gl->log, ngx_errno, + "FindClose(%s) failed", gl->pattern); + } +} + + +ngx_int_t +ngx_de_info(u_char *name, ngx_dir_t *dir) +{ + return NGX_OK; +} + + +ngx_int_t +ngx_de_link_info(u_char *name, ngx_dir_t *dir) +{ + return NGX_OK; +} + + +ngx_int_t +ngx_read_ahead(ngx_fd_t fd, size_t n) +{ + return ~NGX_FILE_ERROR; +} + + +ngx_int_t +ngx_directio_on(ngx_fd_t fd) +{ + return ~NGX_FILE_ERROR; +} + + +ngx_int_t +ngx_directio_off(ngx_fd_t fd) +{ + return ~NGX_FILE_ERROR; +} + + +size_t +ngx_fs_bsize(u_char *name) +{ + u_long sc, bs, nfree, ncl; + size_t len; + u_short *u; + u_short utf16[NGX_UTF16_BUFLEN]; + + len = NGX_UTF16_BUFLEN; + u = ngx_utf8_to_utf16(utf16, name, &len, 0); + + if (u == NULL) { + return 512; + } + + if (GetDiskFreeSpaceW(u, &sc, &bs, &nfree, &ncl) == 0) { + + if (u != utf16) { + ngx_free(u); + } + + return 512; + } + + if (u != utf16) { + ngx_free(u); + } + + return sc * bs; +} + + +off_t +ngx_fs_available(u_char *name) +{ + size_t len; + u_short *u; + ULARGE_INTEGER navail; + u_short utf16[NGX_UTF16_BUFLEN]; + + len = NGX_UTF16_BUFLEN; + u = ngx_utf8_to_utf16(utf16, name, &len, 0); + + if (u == NULL) { + return NGX_MAX_OFF_T_VALUE; + } + + if (GetDiskFreeSpaceExW(u, &navail, NULL, NULL) == 0) { + + if (u != utf16) { + ngx_free(u); + } + + return NGX_MAX_OFF_T_VALUE; + } + + if (u != utf16) { + ngx_free(u); + } + + return (off_t) navail.QuadPart; +} + + +static ngx_int_t +ngx_win32_check_filename(u_short *u, size_t len, ngx_uint_t dirname) +{ + u_long n; + u_short *lu, *p, *slash, ch; + ngx_err_t err; + enum { + sw_start = 0, + sw_normal, + sw_after_slash, + sw_after_colon, + sw_after_dot + } state; + + /* check for NTFS streams (":"), trailing dots and spaces */ + + lu = NULL; + slash = NULL; + state = sw_start; + +#if (NGX_SUPPRESS_WARN) + ch = 0; +#endif + + for (p = u; *p; p++) { + ch = *p; + + switch (state) { + + case sw_start: + + /* + * skip till first "/" to allow paths starting with drive and + * relative path, like "c:html/" + */ + + if (ch == '/' || ch == '\\') { + state = sw_after_slash; + slash = p; + } + + break; + + case sw_normal: + + if (ch == ':') { + state = sw_after_colon; + break; + } + + if (ch == '.' || ch == ' ') { + state = sw_after_dot; + break; + } + + if (ch == '/' || ch == '\\') { + state = sw_after_slash; + slash = p; + break; + } + + break; + + case sw_after_slash: + + if (ch == '/' || ch == '\\') { + break; + } + + if (ch == '.') { + break; + } + + if (ch == ':') { + state = sw_after_colon; + break; + } + + state = sw_normal; + break; + + case sw_after_colon: + + if (ch == '/' || ch == '\\') { + state = sw_after_slash; + slash = p; + break; + } + + goto invalid; + + case sw_after_dot: + + if (ch == '/' || ch == '\\') { + goto invalid; + } + + if (ch == ':') { + goto invalid; + } + + if (ch == '.' || ch == ' ') { + break; + } + + state = sw_normal; + break; + } + } + + if (state == sw_after_dot) { + goto invalid; + } + + if (dirname && slash) { + ch = *slash; + *slash = '\0'; + len = slash - u + 1; + } + + /* check if long name match */ + + lu = malloc(len * 2); + if (lu == NULL) { + return NGX_ERROR; + } + + n = GetLongPathNameW(u, lu, len); + + if (n == 0) { + + if (dirname && slash && ngx_errno == NGX_ENOENT) { + ngx_set_errno(NGX_ENOPATH); + } + + goto failed; + } + + if (n != len - 1 || _wcsicmp(u, lu) != 0) { + goto invalid; + } + + if (dirname && slash) { + *slash = ch; + } + + ngx_free(lu); + + return NGX_OK; + +invalid: + + ngx_set_errno(NGX_ENOENT); + +failed: + + if (dirname && slash) { + *slash = ch; + } + + if (lu) { + err = ngx_errno; + ngx_free(lu); + ngx_set_errno(err); + } + + return NGX_ERROR; +} + + +static u_short * +ngx_utf8_to_utf16(u_short *utf16, u_char *utf8, size_t *len, size_t reserved) +{ + u_char *p; + u_short *u, *last; + uint32_t n; + + p = utf8; + u = utf16; + last = utf16 + *len; + + while (u < last) { + + if (*p < 0x80) { + *u++ = (u_short) *p; + + if (*p == 0) { + *len = u - utf16; + return utf16; + } + + p++; + + continue; + } + + if (u + 1 == last) { + *len = u - utf16; + break; + } + + n = ngx_utf8_decode(&p, 4); + + if (n > 0x10ffff) { + ngx_set_errno(NGX_EILSEQ); + return NULL; + } + + if (n > 0xffff) { + n -= 0x10000; + *u++ = (u_short) (0xd800 + (n >> 10)); + *u++ = (u_short) (0xdc00 + (n & 0x03ff)); + continue; + } + + *u++ = (u_short) n; + } + + /* the given buffer is not enough, allocate a new one */ + + u = malloc(((p - utf8) + ngx_strlen(p) + 1 + reserved) * sizeof(u_short)); + if (u == NULL) { + return NULL; + } + + ngx_memcpy(u, utf16, *len * 2); + + utf16 = u; + u += *len; + + for ( ;; ) { + + if (*p < 0x80) { + *u++ = (u_short) *p; + + if (*p == 0) { + *len = u - utf16; + return utf16; + } + + p++; + + continue; + } + + n = ngx_utf8_decode(&p, 4); + + if (n > 0x10ffff) { + ngx_free(utf16); + ngx_set_errno(NGX_EILSEQ); + return NULL; + } + + if (n > 0xffff) { + n -= 0x10000; + *u++ = (u_short) (0xd800 + (n >> 10)); + *u++ = (u_short) (0xdc00 + (n & 0x03ff)); + continue; + } + + *u++ = (u_short) n; + } + + /* unreachable */ +} + + +static u_char * +ngx_utf16_to_utf8(u_char *utf8, u_short *utf16, size_t *len, size_t *allocated) +{ + u_char *p, *last; + u_short *u, *j; + uint32_t n; + + u = utf16; + p = utf8; + last = utf8 + *len; + + while (p < last) { + + if (*u < 0x80) { + *p++ = (u_char) *u; + + if (*u == 0) { + *len = p - utf8; + return utf8; + } + + u++; + + continue; + } + + if (p >= last - 4) { + *len = p - utf8; + break; + } + + n = ngx_utf16_decode(&u, 2); + + if (n > 0x10ffff) { + ngx_set_errno(NGX_EILSEQ); + return NULL; + } + + if (n >= 0x10000) { + *p++ = (u_char) (0xf0 + (n >> 18)); + *p++ = (u_char) (0x80 + ((n >> 12) & 0x3f)); + *p++ = (u_char) (0x80 + ((n >> 6) & 0x3f)); + *p++ = (u_char) (0x80 + (n & 0x3f)); + continue; + } + + if (n >= 0x0800) { + *p++ = (u_char) (0xe0 + (n >> 12)); + *p++ = (u_char) (0x80 + ((n >> 6) & 0x3f)); + *p++ = (u_char) (0x80 + (n & 0x3f)); + continue; + } + + *p++ = (u_char) (0xc0 + (n >> 6)); + *p++ = (u_char) (0x80 + (n & 0x3f)); + } + + /* the given buffer is not enough, allocate a new one */ + + for (j = u; *j; j++) { /* void */ } + + p = malloc((j - utf16) * 4 + 1); + if (p == NULL) { + return NULL; + } + + if (allocated) { + *allocated = (j - utf16) * 4 + 1; + } + + ngx_memcpy(p, utf8, *len); + + utf8 = p; + p += *len; + + for ( ;; ) { + + if (*u < 0x80) { + *p++ = (u_char) *u; + + if (*u == 0) { + *len = p - utf8; + return utf8; + } + + u++; + + continue; + } + + n = ngx_utf16_decode(&u, 2); + + if (n > 0x10ffff) { + ngx_free(utf8); + ngx_set_errno(NGX_EILSEQ); + return NULL; + } + + if (n >= 0x10000) { + *p++ = (u_char) (0xf0 + (n >> 18)); + *p++ = (u_char) (0x80 + ((n >> 12) & 0x3f)); + *p++ = (u_char) (0x80 + ((n >> 6) & 0x3f)); + *p++ = (u_char) (0x80 + (n & 0x3f)); + continue; + } + + if (n >= 0x0800) { + *p++ = (u_char) (0xe0 + (n >> 12)); + *p++ = (u_char) (0x80 + ((n >> 6) & 0x3f)); + *p++ = (u_char) (0x80 + (n & 0x3f)); + continue; + } + + *p++ = (u_char) (0xc0 + (n >> 6)); + *p++ = (u_char) (0x80 + (n & 0x3f)); + } + + /* unreachable */ +} + + +/* + * ngx_utf16_decode() decodes one or two UTF-16 code units + * the return values: + * 0x80 - 0x10ffff valid character + * 0x110000 - 0xfffffffd invalid sequence + * 0xfffffffe incomplete sequence + * 0xffffffff error + */ + +uint32_t +ngx_utf16_decode(u_short **u, size_t n) +{ + uint32_t k, m; + + k = **u; + + if (k < 0xd800 || k > 0xdfff) { + (*u)++; + return k; + } + + if (k > 0xdbff) { + (*u)++; + return 0xffffffff; + } + + if (n < 2) { + return 0xfffffffe; + } + + (*u)++; + + m = *(*u)++; + + if (m < 0xdc00 || m > 0xdfff) { + return 0xffffffff; + + } + + return 0x10000 + ((k - 0xd800) << 10) + (m - 0xdc00); +} diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_files.h b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_files.h new file mode 100644 index 000000000..78ba13a0e --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_files.h @@ -0,0 +1,272 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_FILES_H_INCLUDED_ +#define _NGX_FILES_H_INCLUDED_ + + +#include +#include + + +typedef HANDLE ngx_fd_t; +typedef BY_HANDLE_FILE_INFORMATION ngx_file_info_t; +typedef uint64_t ngx_file_uniq_t; + + +typedef struct { + u_char *name; + size_t size; + void *addr; + ngx_fd_t fd; + HANDLE handle; + ngx_log_t *log; +} ngx_file_mapping_t; + + +typedef struct { + HANDLE dir; + WIN32_FIND_DATAW finddata; + + u_char *name; + size_t namelen; + size_t allocated; + + unsigned valid_info:1; + unsigned type:1; + unsigned ready:1; +} ngx_dir_t; + + +typedef struct { + HANDLE dir; + WIN32_FIND_DATAW finddata; + + unsigned ready:1; + unsigned test:1; + unsigned no_match:1; + + u_char *pattern; + ngx_str_t name; + size_t last; + ngx_log_t *log; +} ngx_glob_t; + + + +/* INVALID_FILE_ATTRIBUTES is specified but not defined at least in MSVC6SP2 */ +#ifndef INVALID_FILE_ATTRIBUTES +#define INVALID_FILE_ATTRIBUTES 0xffffffff +#endif + +/* INVALID_SET_FILE_POINTER is not defined at least in MSVC6SP2 */ +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER 0xffffffff +#endif + + +#define NGX_INVALID_FILE INVALID_HANDLE_VALUE +#define NGX_FILE_ERROR 0 + + +ngx_fd_t ngx_open_file(u_char *name, u_long mode, u_long create, u_long access); +#define ngx_open_file_n "CreateFile()" + +#define NGX_FILE_RDONLY GENERIC_READ +#define NGX_FILE_WRONLY GENERIC_WRITE +#define NGX_FILE_RDWR GENERIC_READ|GENERIC_WRITE +#define NGX_FILE_APPEND FILE_APPEND_DATA|SYNCHRONIZE +#define NGX_FILE_NONBLOCK 0 + +#define NGX_FILE_CREATE_OR_OPEN OPEN_ALWAYS +#define NGX_FILE_OPEN OPEN_EXISTING +#define NGX_FILE_TRUNCATE CREATE_ALWAYS + +#define NGX_FILE_DEFAULT_ACCESS 0 +#define NGX_FILE_OWNER_ACCESS 0 + + +ngx_fd_t ngx_open_tempfile(u_char *name, ngx_uint_t persistent, + ngx_uint_t access); +#define ngx_open_tempfile_n "CreateFile()" + + +#define ngx_close_file CloseHandle +#define ngx_close_file_n "CloseHandle()" + + +ssize_t ngx_read_fd(ngx_fd_t fd, void *buf, size_t size); +#define ngx_read_fd_n "ReadFile()" + + +ssize_t ngx_write_fd(ngx_fd_t fd, void *buf, size_t size); +#define ngx_write_fd_n "WriteFile()" + + +ssize_t ngx_write_console(ngx_fd_t fd, void *buf, size_t size); + + +#define ngx_linefeed(p) *p++ = CR; *p++ = LF; +#define NGX_LINEFEED_SIZE 2 +#define NGX_LINEFEED CRLF + + +ngx_int_t ngx_delete_file(u_char *name); +#define ngx_delete_file_n "DeleteFile()" + + +ngx_int_t ngx_rename_file(u_char *from, u_char *to); +#define ngx_rename_file_n "MoveFile()" +ngx_err_t ngx_win32_rename_file(ngx_str_t *from, ngx_str_t *to, ngx_log_t *log); + + + +ngx_int_t ngx_set_file_time(u_char *name, ngx_fd_t fd, time_t s); +#define ngx_set_file_time_n "SetFileTime()" + + +ngx_int_t ngx_file_info(u_char *filename, ngx_file_info_t *fi); +#define ngx_file_info_n "GetFileAttributesEx()" + + +#define ngx_fd_info(fd, fi) GetFileInformationByHandle(fd, fi) +#define ngx_fd_info_n "GetFileInformationByHandle()" + + +#define ngx_link_info(name, fi) ngx_file_info(name, fi) +#define ngx_link_info_n "GetFileAttributesEx()" + + +#define ngx_is_dir(fi) \ + (((fi)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) +#define ngx_is_file(fi) \ + (((fi)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) +#define ngx_is_link(fi) 0 +#define ngx_is_exec(fi) 0 + +#define ngx_file_access(fi) 0 + +#define ngx_file_size(fi) \ + (((off_t) (fi)->nFileSizeHigh << 32) | (fi)->nFileSizeLow) +#define ngx_file_fs_size(fi) ngx_file_size(fi) + +#define ngx_file_uniq(fi) \ + (((ngx_file_uniq_t) (fi)->nFileIndexHigh << 32) | (fi)->nFileIndexLow) + + +/* 116444736000000000 is commented in src/os/win32/ngx_time.c */ + +#define ngx_file_mtime(fi) \ + (time_t) (((((unsigned __int64) (fi)->ftLastWriteTime.dwHighDateTime << 32) \ + | (fi)->ftLastWriteTime.dwLowDateTime) \ + - 116444736000000000) / 10000000) + +ngx_int_t ngx_create_file_mapping(ngx_file_mapping_t *fm); +void ngx_close_file_mapping(ngx_file_mapping_t *fm); + + +u_char *ngx_realpath(u_char *path, u_char *resolved); +#define ngx_realpath_n "" + + +size_t ngx_getcwd(u_char *buf, size_t size); +#define ngx_getcwd_n "GetCurrentDirectory()" + + +#define ngx_path_separator(c) ((c) == '/' || (c) == '\\') + +#define NGX_HAVE_MAX_PATH 1 +#define NGX_MAX_PATH MAX_PATH + + +ngx_int_t ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir); +#define ngx_open_dir_n "FindFirstFile()" + + +ngx_int_t ngx_read_dir(ngx_dir_t *dir); +#define ngx_read_dir_n "FindNextFile()" + + +ngx_int_t ngx_close_dir(ngx_dir_t *dir); +#define ngx_close_dir_n "FindClose()" + + +ngx_int_t ngx_create_dir(u_char *name, ngx_uint_t access); +#define ngx_create_dir_n "CreateDirectory()" + + +ngx_int_t ngx_delete_dir(u_char *name); +#define ngx_delete_dir_n "RemoveDirectory()" + + +#define ngx_dir_access(a) (a) + + +#define ngx_de_name(dir) (dir)->name +#define ngx_de_namelen(dir) (dir)->namelen + +ngx_int_t ngx_de_info(u_char *name, ngx_dir_t *dir); +#define ngx_de_info_n "dummy()" + +ngx_int_t ngx_de_link_info(u_char *name, ngx_dir_t *dir); +#define ngx_de_link_info_n "dummy()" + +#define ngx_de_is_dir(dir) \ + (((dir)->finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) +#define ngx_de_is_file(dir) \ + (((dir)->finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) +#define ngx_de_is_link(dir) 0 +#define ngx_de_access(dir) 0 +#define ngx_de_size(dir) \ + (((off_t) (dir)->finddata.nFileSizeHigh << 32) | (dir)->finddata.nFileSizeLow) +#define ngx_de_fs_size(dir) ngx_de_size(dir) + +/* 116444736000000000 is commented in src/os/win32/ngx_time.c */ + +#define ngx_de_mtime(dir) \ + (time_t) (((((unsigned __int64) \ + (dir)->finddata.ftLastWriteTime.dwHighDateTime << 32) \ + | (dir)->finddata.ftLastWriteTime.dwLowDateTime) \ + - 116444736000000000) / 10000000) + + +ngx_int_t ngx_open_glob(ngx_glob_t *gl); +#define ngx_open_glob_n "FindFirstFile()" + +ngx_int_t ngx_read_glob(ngx_glob_t *gl, ngx_str_t *name); +void ngx_close_glob(ngx_glob_t *gl); + + +ssize_t ngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset); +#define ngx_read_file_n "ReadFile()" + +ssize_t ngx_write_file(ngx_file_t *file, u_char *buf, size_t size, + off_t offset); + +ssize_t ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *ce, + off_t offset, ngx_pool_t *pool); + +ngx_int_t ngx_read_ahead(ngx_fd_t fd, size_t n); +#define ngx_read_ahead_n "ngx_read_ahead_n" + +ngx_int_t ngx_directio_on(ngx_fd_t fd); +#define ngx_directio_on_n "ngx_directio_on_n" + +ngx_int_t ngx_directio_off(ngx_fd_t fd); +#define ngx_directio_off_n "ngx_directio_off_n" + +size_t ngx_fs_bsize(u_char *name); +off_t ngx_fs_available(u_char *name); + + +#define ngx_stdout GetStdHandle(STD_OUTPUT_HANDLE) +#define ngx_stderr GetStdHandle(STD_ERROR_HANDLE) +#define ngx_set_stderr(fd) SetStdHandle(STD_ERROR_HANDLE, fd) +#define ngx_set_stderr_n "SetStdHandle(STD_ERROR_HANDLE)" + + +#endif /* _NGX_FILES_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_os.h b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_os.h new file mode 100644 index 000000000..15f5aa036 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_os.h @@ -0,0 +1,68 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_OS_H_INCLUDED_ +#define _NGX_OS_H_INCLUDED_ + + +#include +#include + + +#define NGX_IO_SENDFILE 1 + + +typedef ssize_t (*ngx_recv_pt)(ngx_connection_t *c, u_char *buf, size_t size); +typedef ssize_t (*ngx_recv_chain_pt)(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); +typedef ssize_t (*ngx_send_pt)(ngx_connection_t *c, u_char *buf, size_t size); +typedef ngx_chain_t *(*ngx_send_chain_pt)(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); + +typedef struct { + ngx_recv_pt recv; + ngx_recv_chain_pt recv_chain; + ngx_recv_pt udp_recv; + ngx_send_pt send; + ngx_send_pt udp_send; + ngx_send_chain_pt udp_send_chain; + ngx_send_chain_pt send_chain; + ngx_uint_t flags; +} ngx_os_io_t; + + +ngx_int_t ngx_os_init(ngx_log_t *log); +void ngx_os_status(ngx_log_t *log); +ngx_int_t ngx_os_signal_process(ngx_cycle_t *cycle, char *sig, ngx_pid_t pid); + +ssize_t ngx_wsarecv(ngx_connection_t *c, u_char *buf, size_t size); +ssize_t ngx_overlapped_wsarecv(ngx_connection_t *c, u_char *buf, size_t size); +ssize_t ngx_udp_wsarecv(ngx_connection_t *c, u_char *buf, size_t size); +ssize_t ngx_udp_overlapped_wsarecv(ngx_connection_t *c, u_char *buf, + size_t size); +ssize_t ngx_wsarecv_chain(ngx_connection_t *c, ngx_chain_t *chain, off_t limit); +ssize_t ngx_wsasend(ngx_connection_t *c, u_char *buf, size_t size); +ssize_t ngx_overlapped_wsasend(ngx_connection_t *c, u_char *buf, size_t size); +ngx_chain_t *ngx_wsasend_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); +ngx_chain_t *ngx_overlapped_wsasend_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); + +void ngx_cdecl ngx_event_log(ngx_err_t err, const char *fmt, ...); + + +extern ngx_os_io_t ngx_os_io; +extern ngx_uint_t ngx_ncpu; +extern ngx_uint_t ngx_max_wsabufs; +extern ngx_int_t ngx_max_sockets; +extern ngx_uint_t ngx_inherited_nonblocking; +extern ngx_uint_t ngx_tcp_nodelay_and_tcp_nopush; +extern ngx_uint_t ngx_win32_version; +extern char ngx_unique[]; + + +#endif /* _NGX_OS_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_process.c b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_process.c new file mode 100644 index 000000000..57b1ae9f4 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_process.c @@ -0,0 +1,238 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +int ngx_argc; +char **ngx_argv; +char **ngx_os_argv; + +ngx_int_t ngx_last_process; +ngx_process_t ngx_processes[NGX_MAX_PROCESSES]; + + +ngx_pid_t +ngx_spawn_process(ngx_cycle_t *cycle, char *name, ngx_int_t respawn) +{ + u_long rc, n, code; + ngx_int_t s; + ngx_pid_t pid; + ngx_exec_ctx_t ctx; + HANDLE events[2]; + char file[MAX_PATH + 1]; + + if (respawn >= 0) { + s = respawn; + + } else { + for (s = 0; s < ngx_last_process; s++) { + if (ngx_processes[s].handle == NULL) { + break; + } + } + + if (s == NGX_MAX_PROCESSES) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "no more than %d processes can be spawned", + NGX_MAX_PROCESSES); + return NGX_INVALID_PID; + } + } + + n = GetModuleFileName(NULL, file, MAX_PATH); + + if (n == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "GetModuleFileName() failed"); + return NGX_INVALID_PID; + } + + file[n] = '\0'; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0, + "GetModuleFileName: \"%s\"", file); + + ctx.path = file; + ctx.name = name; + ctx.args = GetCommandLine(); + ctx.argv = NULL; + ctx.envp = NULL; + + pid = ngx_execute(cycle, &ctx); + + if (pid == NGX_INVALID_PID) { + return pid; + } + + ngx_memzero(&ngx_processes[s], sizeof(ngx_process_t)); + + ngx_processes[s].handle = ctx.child; + ngx_processes[s].pid = pid; + ngx_processes[s].name = name; + + ngx_sprintf(ngx_processes[s].term_event, "ngx_%s_term_%P%Z", name, pid); + ngx_sprintf(ngx_processes[s].quit_event, "ngx_%s_quit_%P%Z", name, pid); + ngx_sprintf(ngx_processes[s].reopen_event, "ngx_%s_reopen_%P%Z", + name, pid); + + events[0] = ngx_master_process_event; + events[1] = ctx.child; + + rc = WaitForMultipleObjects(2, events, 0, 5000); + + ngx_time_update(); + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0, + "WaitForMultipleObjects: %ul", rc); + + switch (rc) { + + case WAIT_OBJECT_0: + + ngx_processes[s].term = OpenEvent(EVENT_MODIFY_STATE, 0, + (char *) ngx_processes[s].term_event); + if (ngx_processes[s].term == NULL) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "OpenEvent(\"%s\") failed", + ngx_processes[s].term_event); + goto failed; + } + + ngx_processes[s].quit = OpenEvent(EVENT_MODIFY_STATE, 0, + (char *) ngx_processes[s].quit_event); + if (ngx_processes[s].quit == NULL) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "OpenEvent(\"%s\") failed", + ngx_processes[s].quit_event); + goto failed; + } + + ngx_processes[s].reopen = OpenEvent(EVENT_MODIFY_STATE, 0, + (char *) ngx_processes[s].reopen_event); + if (ngx_processes[s].reopen == NULL) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "OpenEvent(\"%s\") failed", + ngx_processes[s].reopen_event); + goto failed; + } + + if (ResetEvent(ngx_master_process_event) == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "ResetEvent(\"%s\") failed", + ngx_master_process_event_name); + goto failed; + } + + break; + + case WAIT_OBJECT_0 + 1: + if (GetExitCodeProcess(ctx.child, &code) == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "GetExitCodeProcess(%P) failed", pid); + } + + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "%s process %P exited with code %Xl", + name, pid, code); + + goto failed; + + case WAIT_TIMEOUT: + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "the event \"%s\" was not signaled for 5s", + ngx_master_process_event_name); + goto failed; + + case WAIT_FAILED: + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "WaitForSingleObject(\"%s\") failed", + ngx_master_process_event_name); + + goto failed; + } + + if (respawn >= 0) { + return pid; + } + + switch (respawn) { + + case NGX_PROCESS_RESPAWN: + ngx_processes[s].just_spawn = 0; + break; + + case NGX_PROCESS_JUST_RESPAWN: + ngx_processes[s].just_spawn = 1; + break; + } + + if (s == ngx_last_process) { + ngx_last_process++; + } + + return pid; + +failed: + + if (ngx_processes[s].reopen) { + ngx_close_handle(ngx_processes[s].reopen); + } + + if (ngx_processes[s].quit) { + ngx_close_handle(ngx_processes[s].quit); + } + + if (ngx_processes[s].term) { + ngx_close_handle(ngx_processes[s].term); + } + + TerminateProcess(ngx_processes[s].handle, 2); + + if (ngx_processes[s].handle) { + ngx_close_handle(ngx_processes[s].handle); + ngx_processes[s].handle = NULL; + } + + return NGX_INVALID_PID; +} + + +ngx_pid_t +ngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx) +{ + STARTUPINFO si; + PROCESS_INFORMATION pi; + + ngx_memzero(&si, sizeof(STARTUPINFO)); + si.cb = sizeof(STARTUPINFO); + + ngx_memzero(&pi, sizeof(PROCESS_INFORMATION)); + + if (CreateProcess(ctx->path, ctx->args, + NULL, NULL, 0, CREATE_NO_WINDOW, NULL, NULL, &si, &pi) + == 0) + { + ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_errno, + "CreateProcess(\"%s\") failed", ngx_argv[0]); + + return 0; + } + + ctx->child = pi.hProcess; + + if (CloseHandle(pi.hThread) == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "CloseHandle(pi.hThread) failed"); + } + + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, + "start %s process %P", ctx->name, pi.dwProcessId); + + return pi.dwProcessId; +} diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_process.h b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_process.h new file mode 100644 index 000000000..7ec4cd92c --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_process.h @@ -0,0 +1,80 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_PROCESS_H_INCLUDED_ +#define _NGX_PROCESS_H_INCLUDED_ + + +typedef DWORD ngx_pid_t; +#define NGX_INVALID_PID 0 + + +#define ngx_getpid GetCurrentProcessId +#define ngx_getppid() 0 +#define ngx_log_pid ngx_pid + + +#define NGX_PROCESS_SYNC_NAME \ + (sizeof("ngx_cache_manager_mutex_") + NGX_INT32_LEN) + + +typedef uint64_t ngx_cpuset_t; + + +typedef struct { + HANDLE handle; + ngx_pid_t pid; + char *name; + + HANDLE term; + HANDLE quit; + HANDLE reopen; + + u_char term_event[NGX_PROCESS_SYNC_NAME]; + u_char quit_event[NGX_PROCESS_SYNC_NAME]; + u_char reopen_event[NGX_PROCESS_SYNC_NAME]; + + unsigned just_spawn:1; + unsigned exiting:1; +} ngx_process_t; + + +typedef struct { + char *path; + char *name; + char *args; + char *const *argv; + char *const *envp; + HANDLE child; +} ngx_exec_ctx_t; + + +ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle, char *name, ngx_int_t respawn); +ngx_pid_t ngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx); + +#define ngx_debug_point() +#define ngx_sched_yield() SwitchToThread() + + +#define NGX_MAX_PROCESSES (MAXIMUM_WAIT_OBJECTS - 4) + +#define NGX_PROCESS_RESPAWN -2 +#define NGX_PROCESS_JUST_RESPAWN -3 + + +extern int ngx_argc; +extern char **ngx_argv; +extern char **ngx_os_argv; + +extern ngx_int_t ngx_last_process; +extern ngx_process_t ngx_processes[NGX_MAX_PROCESSES]; + +extern ngx_pid_t ngx_pid; +extern ngx_pid_t ngx_parent; + + +#endif /* _NGX_PROCESS_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_process_cycle.c b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_process_cycle.c new file mode 100644 index 000000000..a39335fd1 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_process_cycle.c @@ -0,0 +1,1044 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include + + +static void ngx_console_init(ngx_cycle_t *cycle); +static int __stdcall ngx_console_handler(u_long type); +static ngx_int_t ngx_create_signal_events(ngx_cycle_t *cycle); +static ngx_int_t ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t type); +static void ngx_reopen_worker_processes(ngx_cycle_t *cycle); +static void ngx_quit_worker_processes(ngx_cycle_t *cycle, ngx_uint_t old); +static void ngx_terminate_worker_processes(ngx_cycle_t *cycle); +static ngx_uint_t ngx_reap_worker(ngx_cycle_t *cycle, HANDLE h); +static void ngx_master_process_exit(ngx_cycle_t *cycle); +static void ngx_worker_process_cycle(ngx_cycle_t *cycle, char *mevn); +static void ngx_worker_process_exit(ngx_cycle_t *cycle); +static ngx_thread_value_t __stdcall ngx_worker_thread(void *data); +static ngx_thread_value_t __stdcall ngx_cache_manager_thread(void *data); +static void ngx_cache_manager_process_handler(void); +static ngx_thread_value_t __stdcall ngx_cache_loader_thread(void *data); + + +ngx_uint_t ngx_process; +ngx_uint_t ngx_worker; +ngx_pid_t ngx_pid; +ngx_pid_t ngx_parent; + +ngx_uint_t ngx_inherited; +ngx_pid_t ngx_new_binary; + +sig_atomic_t ngx_terminate; +sig_atomic_t ngx_quit; +sig_atomic_t ngx_reopen; +sig_atomic_t ngx_reconfigure; +ngx_uint_t ngx_exiting; + + +HANDLE ngx_master_process_event; +char ngx_master_process_event_name[NGX_PROCESS_SYNC_NAME]; + +static HANDLE ngx_stop_event; +static char ngx_stop_event_name[NGX_PROCESS_SYNC_NAME]; +static HANDLE ngx_quit_event; +static char ngx_quit_event_name[NGX_PROCESS_SYNC_NAME]; +static HANDLE ngx_reopen_event; +static char ngx_reopen_event_name[NGX_PROCESS_SYNC_NAME]; +static HANDLE ngx_reload_event; +static char ngx_reload_event_name[NGX_PROCESS_SYNC_NAME]; + +HANDLE ngx_cache_manager_mutex; +char ngx_cache_manager_mutex_name[NGX_PROCESS_SYNC_NAME]; +HANDLE ngx_cache_manager_event; + + +void +ngx_master_process_cycle(ngx_cycle_t *cycle) +{ + u_long nev, ev, timeout; + ngx_err_t err; + ngx_int_t n; + ngx_msec_t timer; + ngx_uint_t live; + HANDLE events[MAXIMUM_WAIT_OBJECTS]; + + ngx_sprintf((u_char *) ngx_master_process_event_name, + "ngx_master_%s%Z", ngx_unique); + + if (ngx_process == NGX_PROCESS_WORKER) { + ngx_worker_process_cycle(cycle, ngx_master_process_event_name); + return; + } + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, cycle->log, 0, "master started"); + + ngx_console_init(cycle); + + SetEnvironmentVariable("ngx_unique", ngx_unique); + + ngx_master_process_event = CreateEvent(NULL, 1, 0, + ngx_master_process_event_name); + if (ngx_master_process_event == NULL) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "CreateEvent(\"%s\") failed", + ngx_master_process_event_name); + exit(2); + } + + if (ngx_create_signal_events(cycle) != NGX_OK) { + exit(2); + } + + ngx_sprintf((u_char *) ngx_cache_manager_mutex_name, + "ngx_cache_manager_mutex_%s%Z", ngx_unique); + + ngx_cache_manager_mutex = CreateMutex(NULL, 0, + ngx_cache_manager_mutex_name); + if (ngx_cache_manager_mutex == NULL) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "CreateMutex(\"%s\") failed", ngx_cache_manager_mutex_name); + exit(2); + } + + + events[0] = ngx_stop_event; + events[1] = ngx_quit_event; + events[2] = ngx_reopen_event; + events[3] = ngx_reload_event; + + ngx_close_listening_sockets(cycle); + + if (ngx_start_worker_processes(cycle, NGX_PROCESS_RESPAWN) == 0) { + exit(2); + } + + timer = 0; + timeout = INFINITE; + + for ( ;; ) { + + nev = 4; + for (n = 0; n < ngx_last_process; n++) { + if (ngx_processes[n].handle) { + events[nev++] = ngx_processes[n].handle; + } + } + + if (timer) { + timeout = timer > ngx_current_msec ? timer - ngx_current_msec : 0; + } + + ev = WaitForMultipleObjects(nev, events, 0, timeout); + + err = ngx_errno; + ngx_time_update(); + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0, + "master WaitForMultipleObjects: %ul", ev); + + if (ev == WAIT_OBJECT_0) { + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting"); + + if (ResetEvent(ngx_stop_event) == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "ResetEvent(\"%s\") failed", ngx_stop_event_name); + } + + if (timer == 0) { + timer = ngx_current_msec + 5000; + } + + ngx_terminate = 1; + ngx_quit_worker_processes(cycle, 0); + + continue; + } + + if (ev == WAIT_OBJECT_0 + 1) { + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "shutting down"); + + if (ResetEvent(ngx_quit_event) == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "ResetEvent(\"%s\") failed", ngx_quit_event_name); + } + + ngx_quit = 1; + ngx_quit_worker_processes(cycle, 0); + + continue; + } + + if (ev == WAIT_OBJECT_0 + 2) { + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs"); + + if (ResetEvent(ngx_reopen_event) == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "ResetEvent(\"%s\") failed", + ngx_reopen_event_name); + } + + ngx_reopen_files(cycle, -1); + ngx_reopen_worker_processes(cycle); + + continue; + } + + if (ev == WAIT_OBJECT_0 + 3) { + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring"); + + if (ResetEvent(ngx_reload_event) == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "ResetEvent(\"%s\") failed", + ngx_reload_event_name); + } + + cycle = ngx_init_cycle(cycle); + if (cycle == NULL) { + cycle = (ngx_cycle_t *) ngx_cycle; + continue; + } + + ngx_cycle = cycle; + + ngx_close_listening_sockets(cycle); + + if (ngx_start_worker_processes(cycle, NGX_PROCESS_JUST_RESPAWN)) { + ngx_quit_worker_processes(cycle, 1); + } + + continue; + } + + if (ev > WAIT_OBJECT_0 + 3 && ev < WAIT_OBJECT_0 + nev) { + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, cycle->log, 0, "reap worker"); + + live = ngx_reap_worker(cycle, events[ev]); + + if (!live && (ngx_terminate || ngx_quit)) { + ngx_master_process_exit(cycle); + } + + continue; + } + + if (ev == WAIT_TIMEOUT) { + ngx_terminate_worker_processes(cycle); + + ngx_master_process_exit(cycle); + } + + if (ev == WAIT_FAILED) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, + "WaitForMultipleObjects() failed"); + + continue; + } + + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "WaitForMultipleObjects() returned unexpected value %ul", ev); + } +} + + +static void +ngx_console_init(ngx_cycle_t *cycle) +{ + ngx_core_conf_t *ccf; + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + if (ccf->daemon) { + if (FreeConsole() == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "FreeConsole() failed"); + } + + return; + } + + if (SetConsoleCtrlHandler(ngx_console_handler, 1) == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "SetConsoleCtrlHandler() failed"); + } +} + + +static int __stdcall +ngx_console_handler(u_long type) +{ + char *msg; + + switch (type) { + + case CTRL_C_EVENT: + msg = "Ctrl-C pressed, exiting"; + break; + + case CTRL_BREAK_EVENT: + msg = "Ctrl-Break pressed, exiting"; + break; + + case CTRL_CLOSE_EVENT: + msg = "console closing, exiting"; + break; + + case CTRL_LOGOFF_EVENT: + msg = "user logs off, exiting"; + break; + + default: + return 0; + } + + ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, msg); + + if (ngx_stop_event == NULL) { + return 1; + } + + if (SetEvent(ngx_stop_event) == 0) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "SetEvent(\"%s\") failed", ngx_stop_event_name); + } + + return 1; +} + + +static ngx_int_t +ngx_create_signal_events(ngx_cycle_t *cycle) +{ + ngx_sprintf((u_char *) ngx_stop_event_name, + "Global\\ngx_stop_%s%Z", ngx_unique); + + ngx_stop_event = CreateEvent(NULL, 1, 0, ngx_stop_event_name); + if (ngx_stop_event == NULL) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "CreateEvent(\"%s\") failed", ngx_stop_event_name); + return NGX_ERROR; + } + + + ngx_sprintf((u_char *) ngx_quit_event_name, + "Global\\ngx_quit_%s%Z", ngx_unique); + + ngx_quit_event = CreateEvent(NULL, 1, 0, ngx_quit_event_name); + if (ngx_quit_event == NULL) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "CreateEvent(\"%s\") failed", ngx_quit_event_name); + return NGX_ERROR; + } + + + ngx_sprintf((u_char *) ngx_reopen_event_name, + "Global\\ngx_reopen_%s%Z", ngx_unique); + + ngx_reopen_event = CreateEvent(NULL, 1, 0, ngx_reopen_event_name); + if (ngx_reopen_event == NULL) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "CreateEvent(\"%s\") failed", ngx_reopen_event_name); + return NGX_ERROR; + } + + + ngx_sprintf((u_char *) ngx_reload_event_name, + "Global\\ngx_reload_%s%Z", ngx_unique); + + ngx_reload_event = CreateEvent(NULL, 1, 0, ngx_reload_event_name); + if (ngx_reload_event == NULL) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "CreateEvent(\"%s\") failed", ngx_reload_event_name); + return NGX_ERROR; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t type) +{ + ngx_int_t n; + ngx_core_conf_t *ccf; + + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start worker processes"); + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + for (n = 0; n < ccf->worker_processes; n++) { + if (ngx_spawn_process(cycle, "worker", type) == NGX_INVALID_PID) { + break; + } + } + + return n; +} + + +static void +ngx_reopen_worker_processes(ngx_cycle_t *cycle) +{ + ngx_int_t n; + + for (n = 0; n < ngx_last_process; n++) { + + if (ngx_processes[n].handle == NULL) { + continue; + } + + if (SetEvent(ngx_processes[n].reopen) == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "SetEvent(\"%s\") failed", + ngx_processes[n].reopen_event); + } + } +} + + +static void +ngx_quit_worker_processes(ngx_cycle_t *cycle, ngx_uint_t old) +{ + ngx_int_t n; + + for (n = 0; n < ngx_last_process; n++) { + + ngx_log_debug5(NGX_LOG_DEBUG_CORE, cycle->log, 0, + "process: %d %P %p e:%d j:%d", + n, + ngx_processes[n].pid, + ngx_processes[n].handle, + ngx_processes[n].exiting, + ngx_processes[n].just_spawn); + + if (old && ngx_processes[n].just_spawn) { + ngx_processes[n].just_spawn = 0; + continue; + } + + if (ngx_processes[n].handle == NULL) { + continue; + } + + if (SetEvent(ngx_processes[n].quit) == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "SetEvent(\"%s\") failed", + ngx_processes[n].quit_event); + } + + ngx_processes[n].exiting = 1; + } +} + + +static void +ngx_terminate_worker_processes(ngx_cycle_t *cycle) +{ + ngx_int_t n; + + for (n = 0; n < ngx_last_process; n++) { + + if (ngx_processes[n].handle == NULL) { + continue; + } + + if (TerminateProcess(ngx_processes[n].handle, 0) == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "TerminateProcess(\"%p\") failed", + ngx_processes[n].handle); + } + + ngx_processes[n].exiting = 1; + + ngx_close_handle(ngx_processes[n].reopen); + ngx_close_handle(ngx_processes[n].quit); + ngx_close_handle(ngx_processes[n].term); + ngx_close_handle(ngx_processes[n].handle); + } +} + + +static ngx_uint_t +ngx_reap_worker(ngx_cycle_t *cycle, HANDLE h) +{ + u_long code; + ngx_int_t n; + + for (n = 0; n < ngx_last_process; n++) { + + if (ngx_processes[n].handle != h) { + continue; + } + + if (GetExitCodeProcess(h, &code) == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "GetExitCodeProcess(%P) failed", + ngx_processes[n].pid); + } + + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, + "%s process %P exited with code %Xl", + ngx_processes[n].name, ngx_processes[n].pid, code); + + ngx_close_handle(ngx_processes[n].reopen); + ngx_close_handle(ngx_processes[n].quit); + ngx_close_handle(ngx_processes[n].term); + ngx_close_handle(h); + + ngx_processes[n].handle = NULL; + ngx_processes[n].term = NULL; + ngx_processes[n].quit = NULL; + ngx_processes[n].reopen = NULL; + + if (!ngx_processes[n].exiting && !ngx_terminate && !ngx_quit) { + + if (ngx_spawn_process(cycle, ngx_processes[n].name, n) + == NGX_INVALID_PID) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "could not respawn %s", ngx_processes[n].name); + + if (n == ngx_last_process - 1) { + ngx_last_process--; + } + } + } + + goto found; + } + + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "unknown process handle %p", h); + +found: + + for (n = 0; n < ngx_last_process; n++) { + + ngx_log_debug5(NGX_LOG_DEBUG_CORE, cycle->log, 0, + "process: %d %P %p e:%d j:%d", + n, + ngx_processes[n].pid, + ngx_processes[n].handle, + ngx_processes[n].exiting, + ngx_processes[n].just_spawn); + + if (ngx_processes[n].handle) { + return 1; + } + } + + return 0; +} + + +static void +ngx_master_process_exit(ngx_cycle_t *cycle) +{ + ngx_uint_t i; + + ngx_delete_pidfile(cycle); + + ngx_close_handle(ngx_cache_manager_mutex); + ngx_close_handle(ngx_stop_event); + ngx_close_handle(ngx_quit_event); + ngx_close_handle(ngx_reopen_event); + ngx_close_handle(ngx_reload_event); + ngx_close_handle(ngx_master_process_event); + + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exit"); + + for (i = 0; cycle->modules[i]; i++) { + if (cycle->modules[i]->exit_master) { + cycle->modules[i]->exit_master(cycle); + } + } + + ngx_destroy_pool(cycle->pool); + + exit(0); +} + + +static void +ngx_worker_process_cycle(ngx_cycle_t *cycle, char *mevn) +{ + char wtevn[NGX_PROCESS_SYNC_NAME]; + char wqevn[NGX_PROCESS_SYNC_NAME]; + char wroevn[NGX_PROCESS_SYNC_NAME]; + HANDLE mev, events[3]; + u_long nev, ev; + ngx_err_t err; + ngx_tid_t wtid, cmtid, cltid; + ngx_log_t *log; + + log = cycle->log; + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, "worker started"); + + ngx_sprintf((u_char *) wtevn, "ngx_worker_term_%P%Z", ngx_pid); + events[0] = CreateEvent(NULL, 1, 0, wtevn); + if (events[0] == NULL) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "CreateEvent(\"%s\") failed", wtevn); + goto failed; + } + + ngx_sprintf((u_char *) wqevn, "ngx_worker_quit_%P%Z", ngx_pid); + events[1] = CreateEvent(NULL, 1, 0, wqevn); + if (events[1] == NULL) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "CreateEvent(\"%s\") failed", wqevn); + goto failed; + } + + ngx_sprintf((u_char *) wroevn, "ngx_worker_reopen_%P%Z", ngx_pid); + events[2] = CreateEvent(NULL, 1, 0, wroevn); + if (events[2] == NULL) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "CreateEvent(\"%s\") failed", wroevn); + goto failed; + } + + mev = OpenEvent(EVENT_MODIFY_STATE, 0, mevn); + if (mev == NULL) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "OpenEvent(\"%s\") failed", mevn); + goto failed; + } + + if (SetEvent(mev) == 0) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "SetEvent(\"%s\") failed", mevn); + goto failed; + } + + + ngx_sprintf((u_char *) ngx_cache_manager_mutex_name, + "ngx_cache_manager_mutex_%s%Z", ngx_unique); + + ngx_cache_manager_mutex = OpenMutex(SYNCHRONIZE, 0, + ngx_cache_manager_mutex_name); + if (ngx_cache_manager_mutex == NULL) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "OpenMutex(\"%s\") failed", ngx_cache_manager_mutex_name); + goto failed; + } + + ngx_cache_manager_event = CreateEvent(NULL, 1, 0, NULL); + if (ngx_cache_manager_event == NULL) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "CreateEvent(\"ngx_cache_manager_event\") failed"); + goto failed; + } + + + if (ngx_create_thread(&wtid, ngx_worker_thread, NULL, log) != 0) { + goto failed; + } + + if (ngx_create_thread(&cmtid, ngx_cache_manager_thread, NULL, log) != 0) { + goto failed; + } + + if (ngx_create_thread(&cltid, ngx_cache_loader_thread, NULL, log) != 0) { + goto failed; + } + + for ( ;; ) { + ev = WaitForMultipleObjects(3, events, 0, INFINITE); + + err = ngx_errno; + ngx_time_update(); + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, + "worker WaitForMultipleObjects: %ul", ev); + + if (ev == WAIT_OBJECT_0) { + ngx_terminate = 1; + ngx_log_error(NGX_LOG_NOTICE, log, 0, "exiting"); + + if (ResetEvent(events[0]) == 0) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "ResetEvent(\"%s\") failed", wtevn); + } + + break; + } + + if (ev == WAIT_OBJECT_0 + 1) { + ngx_quit = 1; + ngx_log_error(NGX_LOG_NOTICE, log, 0, "gracefully shutting down"); + break; + } + + if (ev == WAIT_OBJECT_0 + 2) { + ngx_reopen = 1; + ngx_log_error(NGX_LOG_NOTICE, log, 0, "reopening logs"); + + if (ResetEvent(events[2]) == 0) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "ResetEvent(\"%s\") failed", wroevn); + } + + continue; + } + + if (ev == WAIT_FAILED) { + ngx_log_error(NGX_LOG_ALERT, log, err, + "WaitForMultipleObjects() failed"); + + goto failed; + } + } + + /* wait threads */ + + if (SetEvent(ngx_cache_manager_event) == 0) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "SetEvent(\"ngx_cache_manager_event\") failed"); + } + + events[1] = wtid; + events[2] = cmtid; + + nev = 3; + + for ( ;; ) { + ev = WaitForMultipleObjects(nev, events, 0, INFINITE); + + err = ngx_errno; + ngx_time_update(); + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, + "worker exit WaitForMultipleObjects: %ul", ev); + + if (ev == WAIT_OBJECT_0) { + break; + } + + if (ev == WAIT_OBJECT_0 + 1) { + if (nev == 2) { + break; + } + + events[1] = events[2]; + nev = 2; + continue; + } + + if (ev == WAIT_OBJECT_0 + 2) { + nev = 2; + continue; + } + + if (ev == WAIT_FAILED) { + ngx_log_error(NGX_LOG_ALERT, log, err, + "WaitForMultipleObjects() failed"); + break; + } + } + + ngx_close_handle(ngx_cache_manager_event); + ngx_close_handle(events[0]); + ngx_close_handle(events[1]); + ngx_close_handle(events[2]); + ngx_close_handle(mev); + + ngx_worker_process_exit(cycle); + +failed: + + exit(2); +} + + +static ngx_thread_value_t __stdcall +ngx_worker_thread(void *data) +{ + ngx_int_t n; + ngx_time_t *tp; + ngx_cycle_t *cycle; + + tp = ngx_timeofday(); + srand((ngx_pid << 16) ^ (unsigned) tp->sec ^ tp->msec); + + cycle = (ngx_cycle_t *) ngx_cycle; + + for (n = 0; cycle->modules[n]; n++) { + if (cycle->modules[n]->init_process) { + if (cycle->modules[n]->init_process(cycle) == NGX_ERROR) { + /* fatal */ + exit(2); + } + } + } + + while (!ngx_quit) { + + if (ngx_exiting) { + if (ngx_event_no_timers_left() == NGX_OK) { + break; + } + } + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, cycle->log, 0, "worker cycle"); + + ngx_process_events_and_timers(cycle); + + if (ngx_terminate) { + return 0; + } + + if (ngx_quit) { + ngx_quit = 0; + + if (!ngx_exiting) { + ngx_exiting = 1; + ngx_set_shutdown_timer(cycle); + ngx_close_listening_sockets(cycle); + ngx_close_idle_connections(cycle); + ngx_event_process_posted(cycle, &ngx_posted_events); + } + } + + if (ngx_reopen) { + ngx_reopen = 0; + ngx_reopen_files(cycle, -1); + } + } + + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting"); + + return 0; +} + + +static void +ngx_worker_process_exit(ngx_cycle_t *cycle) +{ + ngx_uint_t i; + ngx_connection_t *c; + + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exit"); + + for (i = 0; cycle->modules[i]; i++) { + if (cycle->modules[i]->exit_process) { + cycle->modules[i]->exit_process(cycle); + } + } + + if (ngx_exiting && !ngx_terminate) { + c = cycle->connections; + for (i = 0; i < cycle->connection_n; i++) { + if (c[i].fd != (ngx_socket_t) -1 + && c[i].read + && !c[i].read->accept + && !c[i].read->channel + && !c[i].read->resolver) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "*%uA open socket #%d left in connection %ui", + c[i].number, c[i].fd, i); + } + } + } + + ngx_destroy_pool(cycle->pool); + + exit(0); +} + + +static ngx_thread_value_t __stdcall +ngx_cache_manager_thread(void *data) +{ + u_long ev; + HANDLE events[2]; + ngx_err_t err; + ngx_cycle_t *cycle; + + cycle = (ngx_cycle_t *) ngx_cycle; + + events[0] = ngx_cache_manager_event; + events[1] = ngx_cache_manager_mutex; + + for ( ;; ) { + ev = WaitForMultipleObjects(2, events, 0, INFINITE); + + err = ngx_errno; + ngx_time_update(); + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0, + "cache manager WaitForMultipleObjects: %ul", ev); + + if (ev == WAIT_FAILED) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, err, + "WaitForMultipleObjects() failed"); + } + + /* + * ev == WAIT_OBJECT_0 + * ev == WAIT_OBJECT_0 + 1 + * ev == WAIT_ABANDONED_0 + 1 + */ + + if (ngx_terminate || ngx_quit || ngx_exiting) { + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting"); + return 0; + } + + break; + } + + for ( ;; ) { + + if (ngx_terminate || ngx_quit || ngx_exiting) { + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting"); + break; + } + + ngx_cache_manager_process_handler(); + } + + if (ReleaseMutex(ngx_cache_manager_mutex) == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "ReleaseMutex() failed"); + } + + return 0; +} + + +static void +ngx_cache_manager_process_handler(void) +{ + u_long ev; + ngx_uint_t i; + ngx_msec_t next, n; + ngx_path_t **path; + + next = 60 * 60 * 1000; + + path = ngx_cycle->paths.elts; + for (i = 0; i < ngx_cycle->paths.nelts; i++) { + + if (path[i]->manager) { + n = path[i]->manager(path[i]->data); + + next = (n <= next) ? n : next; + + ngx_time_update(); + } + } + + if (next == 0) { + next = 1; + } + + ev = WaitForSingleObject(ngx_cache_manager_event, (u_long) next); + + if (ev != WAIT_TIMEOUT) { + + ngx_time_update(); + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, + "cache manager WaitForSingleObject: %ul", ev); + } +} + + +static ngx_thread_value_t __stdcall +ngx_cache_loader_thread(void *data) +{ + ngx_uint_t i; + ngx_path_t **path; + ngx_cycle_t *cycle; + + ngx_msleep(60000); + + cycle = (ngx_cycle_t *) ngx_cycle; + + path = cycle->paths.elts; + for (i = 0; i < cycle->paths.nelts; i++) { + + if (ngx_terminate || ngx_quit || ngx_exiting) { + break; + } + + if (path[i]->loader) { + path[i]->loader(path[i]->data); + ngx_time_update(); + } + } + + return 0; +} + + +void +ngx_single_process_cycle(ngx_cycle_t *cycle) +{ + ngx_tid_t tid; + + ngx_console_init(cycle); + + if (ngx_create_signal_events(cycle) != NGX_OK) { + exit(2); + } + + if (ngx_create_thread(&tid, ngx_worker_thread, NULL, cycle->log) != 0) { + /* fatal */ + exit(2); + } + + /* STUB */ + WaitForSingleObject(ngx_stop_event, INFINITE); +} + + +ngx_int_t +ngx_os_signal_process(ngx_cycle_t *cycle, char *sig, ngx_pid_t pid) +{ + HANDLE ev; + ngx_int_t rc; + char evn[NGX_PROCESS_SYNC_NAME]; + + ngx_sprintf((u_char *) evn, "Global\\ngx_%s_%P%Z", sig, pid); + + ev = OpenEvent(EVENT_MODIFY_STATE, 0, evn); + if (ev == NULL) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, + "OpenEvent(\"%s\") failed", evn); + return 1; + } + + if (SetEvent(ev) == 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + "SetEvent(\"%s\") failed", evn); + rc = 1; + + } else { + rc = 0; + } + + ngx_close_handle(ev); + + return rc; +} + + +void +ngx_close_handle(HANDLE h) +{ + if (CloseHandle(h) == 0) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno, + "CloseHandle(%p) failed", h); + } +} diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_process_cycle.h b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_process_cycle.h new file mode 100644 index 000000000..95d274350 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_process_cycle.h @@ -0,0 +1,44 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_PROCESS_CYCLE_H_INCLUDED_ +#define _NGX_PROCESS_CYCLE_H_INCLUDED_ + + +#include +#include + + +#define NGX_PROCESS_SINGLE 0 +#define NGX_PROCESS_MASTER 1 +#define NGX_PROCESS_SIGNALLER 2 +#define NGX_PROCESS_WORKER 3 + + +void ngx_master_process_cycle(ngx_cycle_t *cycle); +void ngx_single_process_cycle(ngx_cycle_t *cycle); +void ngx_close_handle(HANDLE h); + + +extern ngx_uint_t ngx_process; +extern ngx_uint_t ngx_worker; +extern ngx_pid_t ngx_pid; +extern ngx_uint_t ngx_exiting; + +extern sig_atomic_t ngx_quit; +extern sig_atomic_t ngx_terminate; +extern sig_atomic_t ngx_reopen; + +extern ngx_uint_t ngx_inherited; +extern ngx_pid_t ngx_new_binary; + + +extern HANDLE ngx_master_process_event; +extern char ngx_master_process_event_name[]; + + +#endif /* _NGX_PROCESS_CYCLE_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_service.c b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_service.c new file mode 100644 index 000000000..835d9cf00 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_service.c @@ -0,0 +1,134 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + + +#define NGX_SERVICE_CONTROL_SHUTDOWN 128 +#define NGX_SERVICE_CONTROL_REOPEN 129 + + +SERVICE_TABLE_ENTRY st[] = { + { "nginx", service_main }, + { NULL, NULL } +}; + + +ngx_int_t +ngx_service(ngx_log_t *log) +{ + /* primary thread */ + + /* StartServiceCtrlDispatcher() should be called within 30 seconds */ + + if (StartServiceCtrlDispatcher(st) == 0) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "StartServiceCtrlDispatcher() failed"); + return NGX_ERROR; + } + + return NGX_OK; +} + + +void +service_main(u_int argc, char **argv) +{ + SERVICE_STATUS status; + SERVICE_STATUS_HANDLE service; + + /* thread spawned by SCM */ + + service = RegisterServiceCtrlHandlerEx("nginx", service_handler, ctx); + if (service == INVALID_HANDLE_VALUE) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "RegisterServiceCtrlHandlerEx() failed"); + return; + } + + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + status.dwCurrentState = SERVICE_START_PENDING; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP + |SERVICE_ACCEPT_PARAMCHANGE; + status.dwWin32ExitCode = NO_ERROR; + status.dwServiceSpecificExitCode = 0; + status.dwCheckPoint = 1; + status.dwWaitHint = 2000; + + /* SetServiceStatus() should be called within 80 seconds */ + + if (SetServiceStatus(service, &status) == 0) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "SetServiceStatus() failed"); + return; + } + + /* init */ + + status.dwCurrentState = SERVICE_RUNNING; + status.dwCheckPoint = 0; + status.dwWaitHint = 0; + + if (SetServiceStatus(service, &status) == 0) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "SetServiceStatus() failed"); + return; + } + + /* call master or worker loop */ + + /* + * master should use event notification and look status + * single should use iocp to get notifications from service handler + */ + +} + + +u_int +service_handler(u_int control, u_int type, void *data, void *ctx) +{ + /* primary thread */ + + switch (control) { + + case SERVICE_CONTROL_INTERROGATE: + status = NGX_IOCP_INTERROGATE; + break; + + case SERVICE_CONTROL_STOP: + status = NGX_IOCP_STOP; + break; + + case SERVICE_CONTROL_PARAMCHANGE: + status = NGX_IOCP_RECONFIGURE; + break; + + case NGX_SERVICE_CONTROL_SHUTDOWN: + status = NGX_IOCP_REOPEN; + break; + + case NGX_SERVICE_CONTROL_REOPEN: + status = NGX_IOCP_REOPEN; + break; + + default: + return ERROR_CALL_NOT_IMPLEMENTED; + } + + if (ngx_single) { + if (PostQueuedCompletionStatus(iocp, ... status, ...) == 0) { + err = ngx_errno; + ngx_log_error(NGX_LOG_ALERT, log, err, + "PostQueuedCompletionStatus() failed"); + return err; + } + + } else { + Event + } + + return NO_ERROR; +} diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_shmem.c b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_shmem.c new file mode 100644 index 000000000..4932764e8 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_shmem.c @@ -0,0 +1,161 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +/* + * Base addresses selected by system for shared memory mappings are likely + * to be different on Windows Vista and later versions due to address space + * layout randomization. This is however incompatible with storing absolute + * addresses within the shared memory. + * + * To make it possible to store absolute addresses we create mappings + * at the same address in all processes by starting mappings at predefined + * addresses. The addresses were selected somewhat randomly in order to + * minimize the probability that some other library doing something similar + * conflicts with us. The addresses are from the following typically free + * blocks: + * + * - 0x10000000 .. 0x70000000 (about 1.5 GB in total) on 32-bit platforms + * - 0x000000007fff0000 .. 0x000007f68e8b0000 (about 8 TB) on 64-bit platforms + * + * Additionally, we allow to change the mapping address once it was detected + * to be different from one originally used. This is needed to support + * reconfiguration. + */ + + +#ifdef _WIN64 +#define NGX_SHMEM_BASE 0x0000047047e00000 +#else +#define NGX_SHMEM_BASE 0x2efe0000 +#endif + + +ngx_uint_t ngx_allocation_granularity; + + +ngx_int_t +ngx_shm_alloc(ngx_shm_t *shm) +{ + u_char *name; + uint64_t size; + static u_char *base = (u_char *) NGX_SHMEM_BASE; + + name = ngx_alloc(shm->name.len + 2 + NGX_INT32_LEN, shm->log); + if (name == NULL) { + return NGX_ERROR; + } + + (void) ngx_sprintf(name, "%V_%s%Z", &shm->name, ngx_unique); + + ngx_set_errno(0); + + size = shm->size; + + shm->handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, + (u_long) (size >> 32), + (u_long) (size & 0xffffffff), + (char *) name); + + if (shm->handle == NULL) { + ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, + "CreateFileMapping(%uz, %s) failed", + shm->size, name); + ngx_free(name); + + return NGX_ERROR; + } + + ngx_free(name); + + if (ngx_errno == ERROR_ALREADY_EXISTS) { + shm->exists = 1; + } + + shm->addr = MapViewOfFileEx(shm->handle, FILE_MAP_WRITE, 0, 0, 0, base); + + if (shm->addr != NULL) { + base += ngx_align(shm->size, ngx_allocation_granularity); + return NGX_OK; + } + + ngx_log_debug3(NGX_LOG_DEBUG_CORE, shm->log, ngx_errno, + "MapViewOfFileEx(%uz, %p) of file mapping \"%V\" failed, " + "retry without a base address", + shm->size, base, &shm->name); + + /* + * Order of shared memory zones may be different in the master process + * and worker processes after reconfiguration. As a result, the above + * may fail due to a conflict with a previously created mapping remapped + * to a different address. Additionally, there may be a conflict with + * some other uses of the memory. In this case we retry without a base + * address to let the system assign the address itself. + */ + + shm->addr = MapViewOfFile(shm->handle, FILE_MAP_WRITE, 0, 0, 0); + + if (shm->addr != NULL) { + return NGX_OK; + } + + ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, + "MapViewOfFile(%uz) of file mapping \"%V\" failed", + shm->size, &shm->name); + + if (CloseHandle(shm->handle) == 0) { + ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, + "CloseHandle() of file mapping \"%V\" failed", + &shm->name); + } + + return NGX_ERROR; +} + + +ngx_int_t +ngx_shm_remap(ngx_shm_t *shm, u_char *addr) +{ + if (UnmapViewOfFile(shm->addr) == 0) { + ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, + "UnmapViewOfFile(%p) of file mapping \"%V\" failed", + shm->addr, &shm->name); + return NGX_ERROR; + } + + shm->addr = MapViewOfFileEx(shm->handle, FILE_MAP_WRITE, 0, 0, 0, addr); + + if (shm->addr != NULL) { + return NGX_OK; + } + + ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, + "MapViewOfFileEx(%uz, %p) of file mapping \"%V\" failed", + shm->size, addr, &shm->name); + + return NGX_ERROR; +} + + +void +ngx_shm_free(ngx_shm_t *shm) +{ + if (UnmapViewOfFile(shm->addr) == 0) { + ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, + "UnmapViewOfFile(%p) of file mapping \"%V\" failed", + shm->addr, &shm->name); + } + + if (CloseHandle(shm->handle) == 0) { + ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, + "CloseHandle() of file mapping \"%V\" failed", + &shm->name); + } +} diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_shmem.h b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_shmem.h new file mode 100644 index 000000000..ee4742933 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_shmem.h @@ -0,0 +1,33 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_SHMEM_H_INCLUDED_ +#define _NGX_SHMEM_H_INCLUDED_ + + +#include +#include + + +typedef struct { + u_char *addr; + size_t size; + ngx_str_t name; + HANDLE handle; + ngx_log_t *log; + ngx_uint_t exists; /* unsigned exists:1; */ +} ngx_shm_t; + + +ngx_int_t ngx_shm_alloc(ngx_shm_t *shm); +ngx_int_t ngx_shm_remap(ngx_shm_t *shm, u_char *addr); +void ngx_shm_free(ngx_shm_t *shm); + +extern ngx_uint_t ngx_allocation_granularity; + + +#endif /* _NGX_SHMEM_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_socket.c b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_socket.c new file mode 100644 index 000000000..b1b4afb8a --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_socket.c @@ -0,0 +1,49 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +int +ngx_nonblocking(ngx_socket_t s) +{ + unsigned long nb = 1; + + return ioctlsocket(s, FIONBIO, &nb); +} + + +int +ngx_blocking(ngx_socket_t s) +{ + unsigned long nb = 0; + + return ioctlsocket(s, FIONBIO, &nb); +} + + +int +ngx_socket_nread(ngx_socket_t s, int *n) +{ + unsigned long nread; + + if (ioctlsocket(s, FIONREAD, &nread) == -1) { + return -1; + } + + *n = nread; + + return 0; +} + + +int +ngx_tcp_push(ngx_socket_t s) +{ + return 0; +} diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_socket.h b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_socket.h new file mode 100644 index 000000000..5b6138944 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_socket.h @@ -0,0 +1,255 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_SOCKET_H_INCLUDED_ +#define _NGX_SOCKET_H_INCLUDED_ + + +#include +#include + + +#define NGX_WRITE_SHUTDOWN SD_SEND +#define NGX_READ_SHUTDOWN SD_RECEIVE +#define NGX_RDWR_SHUTDOWN SD_BOTH + + +typedef SOCKET ngx_socket_t; +typedef int socklen_t; + + +#define ngx_socket(af, type, proto) \ + WSASocketW(af, type, proto, NULL, 0, WSA_FLAG_OVERLAPPED) + +#define ngx_socket_n "WSASocketW()" + +int ngx_nonblocking(ngx_socket_t s); +int ngx_blocking(ngx_socket_t s); + +#define ngx_nonblocking_n "ioctlsocket(FIONBIO)" +#define ngx_blocking_n "ioctlsocket(!FIONBIO)" + +int ngx_socket_nread(ngx_socket_t s, int *n); +#define ngx_socket_nread_n "ioctlsocket(FIONREAD)" + +#define ngx_shutdown_socket shutdown +#define ngx_shutdown_socket_n "shutdown()" + +#define ngx_close_socket closesocket +#define ngx_close_socket_n "closesocket()" + + +#ifndef WSAID_ACCEPTEX + +typedef BOOL (PASCAL FAR * LPFN_ACCEPTEX)( + IN SOCKET sListenSocket, + IN SOCKET sAcceptSocket, + IN PVOID lpOutputBuffer, + IN DWORD dwReceiveDataLength, + IN DWORD dwLocalAddressLength, + IN DWORD dwRemoteAddressLength, + OUT LPDWORD lpdwBytesReceived, + IN LPOVERLAPPED lpOverlapped + ); + +#define WSAID_ACCEPTEX \ + {0xb5367df1,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} + +#endif + + +#ifndef WSAID_GETACCEPTEXSOCKADDRS + +typedef VOID (PASCAL FAR * LPFN_GETACCEPTEXSOCKADDRS)( + IN PVOID lpOutputBuffer, + IN DWORD dwReceiveDataLength, + IN DWORD dwLocalAddressLength, + IN DWORD dwRemoteAddressLength, + OUT struct sockaddr **LocalSockaddr, + OUT LPINT LocalSockaddrLength, + OUT struct sockaddr **RemoteSockaddr, + OUT LPINT RemoteSockaddrLength + ); + +#define WSAID_GETACCEPTEXSOCKADDRS \ + {0xb5367df2,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} + +#endif + + +#ifndef WSAID_TRANSMITFILE + +#ifndef TF_DISCONNECT + +#define TF_DISCONNECT 1 +#define TF_REUSE_SOCKET 2 +#define TF_WRITE_BEHIND 4 +#define TF_USE_DEFAULT_WORKER 0 +#define TF_USE_SYSTEM_THREAD 16 +#define TF_USE_KERNEL_APC 32 + +typedef struct _TRANSMIT_FILE_BUFFERS { + LPVOID Head; + DWORD HeadLength; + LPVOID Tail; + DWORD TailLength; +} TRANSMIT_FILE_BUFFERS, *PTRANSMIT_FILE_BUFFERS, FAR *LPTRANSMIT_FILE_BUFFERS; + +#endif + +typedef BOOL (PASCAL FAR * LPFN_TRANSMITFILE)( + IN SOCKET hSocket, + IN HANDLE hFile, + IN DWORD nNumberOfBytesToWrite, + IN DWORD nNumberOfBytesPerSend, + IN LPOVERLAPPED lpOverlapped, + IN LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers, + IN DWORD dwReserved + ); + +#define WSAID_TRANSMITFILE \ + {0xb5367df0,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} + +#endif + + +#ifndef WSAID_TRANSMITPACKETS + +/* OpenWatcom has a swapped TP_ELEMENT_FILE and TP_ELEMENT_MEMORY definition */ + +#ifndef TP_ELEMENT_FILE + +#ifdef _MSC_VER +#pragma warning(disable:4201) /* Nonstandard extension, nameless struct/union */ +#endif + +typedef struct _TRANSMIT_PACKETS_ELEMENT { + ULONG dwElFlags; +#define TP_ELEMENT_MEMORY 1 +#define TP_ELEMENT_FILE 2 +#define TP_ELEMENT_EOP 4 + ULONG cLength; + union { + struct { + LARGE_INTEGER nFileOffset; + HANDLE hFile; + }; + PVOID pBuffer; + }; +} TRANSMIT_PACKETS_ELEMENT, *PTRANSMIT_PACKETS_ELEMENT, + FAR *LPTRANSMIT_PACKETS_ELEMENT; + +#ifdef _MSC_VER +#pragma warning(default:4201) +#endif + +#endif + +typedef BOOL (PASCAL FAR * LPFN_TRANSMITPACKETS) ( + SOCKET hSocket, + TRANSMIT_PACKETS_ELEMENT *lpPacketArray, + DWORD nElementCount, + DWORD nSendSize, + LPOVERLAPPED lpOverlapped, + DWORD dwFlags + ); + +#define WSAID_TRANSMITPACKETS \ + {0xd9689da0,0x1f90,0x11d3,{0x99,0x71,0x00,0xc0,0x4f,0x68,0xc8,0x76}} + +#endif + + +#ifndef WSAID_CONNECTEX + +typedef BOOL (PASCAL FAR * LPFN_CONNECTEX) ( + IN SOCKET s, + IN const struct sockaddr FAR *name, + IN int namelen, + IN PVOID lpSendBuffer OPTIONAL, + IN DWORD dwSendDataLength, + OUT LPDWORD lpdwBytesSent, + IN LPOVERLAPPED lpOverlapped + ); + +#define WSAID_CONNECTEX \ + {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}} + +#endif + + +#ifndef WSAID_DISCONNECTEX + +typedef BOOL (PASCAL FAR * LPFN_DISCONNECTEX) ( + IN SOCKET s, + IN LPOVERLAPPED lpOverlapped, + IN DWORD dwFlags, + IN DWORD dwReserved + ); + +#define WSAID_DISCONNECTEX \ + {0x7fda2e11,0x8630,0x436f,{0xa0,0x31,0xf5,0x36,0xa6,0xee,0xc1,0x57}} + +#endif + + +extern LPFN_ACCEPTEX ngx_acceptex; +extern LPFN_GETACCEPTEXSOCKADDRS ngx_getacceptexsockaddrs; +extern LPFN_TRANSMITFILE ngx_transmitfile; +extern LPFN_TRANSMITPACKETS ngx_transmitpackets; +extern LPFN_CONNECTEX ngx_connectex; +extern LPFN_DISCONNECTEX ngx_disconnectex; + + +#if (NGX_HAVE_POLL && !defined POLLIN) + +/* + * WSAPoll() is only available if _WIN32_WINNT >= 0x0600. + * If it is not available during compilation, we try to + * load it dynamically at runtime. + */ + +#define NGX_LOAD_WSAPOLL 1 + +#define POLLRDNORM 0x0100 +#define POLLRDBAND 0x0200 +#define POLLIN (POLLRDNORM | POLLRDBAND) +#define POLLPRI 0x0400 + +#define POLLWRNORM 0x0010 +#define POLLOUT (POLLWRNORM) +#define POLLWRBAND 0x0020 + +#define POLLERR 0x0001 +#define POLLHUP 0x0002 +#define POLLNVAL 0x0004 + +typedef struct pollfd { + + SOCKET fd; + SHORT events; + SHORT revents; + +} WSAPOLLFD, *PWSAPOLLFD, FAR *LPWSAPOLLFD; + +typedef int (WSAAPI *ngx_wsapoll_pt)( + LPWSAPOLLFD fdArray, + ULONG fds, + INT timeout + ); + +extern ngx_wsapoll_pt WSAPoll; +extern ngx_uint_t ngx_have_wsapoll; + +#endif + + +int ngx_tcp_push(ngx_socket_t s); +#define ngx_tcp_push_n "tcp_push()" + + +#endif /* _NGX_SOCKET_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_stat.c b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_stat.c new file mode 100644 index 000000000..51bcd9629 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_stat.c @@ -0,0 +1,34 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +int ngx_file_type(char *file, ngx_file_info_t *sb) +{ + sb->dwFileAttributes = GetFileAttributes(file); + + if (sb->dwFileAttributes == INVALID_FILE_ATTRIBUTES) { + return -1; + } + + return 0; +} + +/* +int ngx_stat(char *file, ngx_stat_t *sb) +{ + *sb = GetFileAttributes(file); + + if (*sb == INVALID_FILE_ATTRIBUTES) { + return -1; + } + + return 0; +} +*/ diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_thread.c b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_thread.c new file mode 100644 index 000000000..a13de2d09 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_thread.c @@ -0,0 +1,30 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +ngx_err_t +ngx_create_thread(ngx_tid_t *tid, + ngx_thread_value_t (__stdcall *func)(void *arg), void *arg, ngx_log_t *log) +{ + u_long id; + ngx_err_t err; + + *tid = CreateThread(NULL, 0, func, arg, 0, &id); + + if (*tid != NULL) { + ngx_log_error(NGX_LOG_NOTICE, log, 0, + "create thread " NGX_TID_T_FMT, id); + return 0; + } + + err = ngx_errno; + ngx_log_error(NGX_LOG_ALERT, log, err, "CreateThread() failed"); + return err; +} diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_thread.h b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_thread.h new file mode 100644 index 000000000..40122768c --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_thread.h @@ -0,0 +1,27 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_THREAD_H_INCLUDED_ +#define _NGX_THREAD_H_INCLUDED_ + + +#include +#include + + +typedef HANDLE ngx_tid_t; +typedef DWORD ngx_thread_value_t; + + +ngx_err_t ngx_create_thread(ngx_tid_t *tid, + ngx_thread_value_t (__stdcall *func)(void *arg), void *arg, ngx_log_t *log); + +#define ngx_log_tid GetCurrentThreadId() +#define NGX_TID_T_FMT "%ud" + + +#endif /* _NGX_THREAD_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_time.c b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_time.c new file mode 100644 index 000000000..79149b2c7 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_time.c @@ -0,0 +1,83 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +void +ngx_gettimeofday(struct timeval *tp) +{ + uint64_t intervals; + FILETIME ft; + + GetSystemTimeAsFileTime(&ft); + + /* + * A file time is a 64-bit value that represents the number + * of 100-nanosecond intervals that have elapsed since + * January 1, 1601 12:00 A.M. UTC. + * + * Between January 1, 1970 (Epoch) and January 1, 1601 there were + * 134774 days, + * 11644473600 seconds or + * 11644473600,000,000,0 100-nanosecond intervals. + * + * See also MSKB Q167296. + */ + + intervals = ((uint64_t) ft.dwHighDateTime << 32) | ft.dwLowDateTime; + intervals -= 116444736000000000; + + tp->tv_sec = (long) (intervals / 10000000); + tp->tv_usec = (long) ((intervals % 10000000) / 10); +} + + +void +ngx_libc_localtime(time_t s, struct tm *tm) +{ + struct tm *t; + + t = localtime(&s); + *tm = *t; +} + + +void +ngx_libc_gmtime(time_t s, struct tm *tm) +{ + struct tm *t; + + t = gmtime(&s); + *tm = *t; +} + + +ngx_int_t +ngx_gettimezone(void) +{ + u_long n; + TIME_ZONE_INFORMATION tz; + + n = GetTimeZoneInformation(&tz); + + switch (n) { + + case TIME_ZONE_ID_UNKNOWN: + return -tz.Bias; + + case TIME_ZONE_ID_STANDARD: + return -(tz.Bias + tz.StandardBias); + + case TIME_ZONE_ID_DAYLIGHT: + return -(tz.Bias + tz.DaylightBias); + + default: /* TIME_ZONE_ID_INVALID */ + return 0; + } +} diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_time.h b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_time.h new file mode 100644 index 000000000..6c2f806f3 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_time.h @@ -0,0 +1,51 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_TIME_H_INCLUDED_ +#define _NGX_TIME_H_INCLUDED_ + + +#include +#include + + +typedef ngx_rbtree_key_t ngx_msec_t; +typedef ngx_rbtree_key_int_t ngx_msec_int_t; + +typedef SYSTEMTIME ngx_tm_t; +typedef FILETIME ngx_mtime_t; + +#define ngx_tm_sec wSecond +#define ngx_tm_min wMinute +#define ngx_tm_hour wHour +#define ngx_tm_mday wDay +#define ngx_tm_mon wMonth +#define ngx_tm_year wYear +#define ngx_tm_wday wDayOfWeek + +#define ngx_tm_sec_t u_short +#define ngx_tm_min_t u_short +#define ngx_tm_hour_t u_short +#define ngx_tm_mday_t u_short +#define ngx_tm_mon_t u_short +#define ngx_tm_year_t u_short +#define ngx_tm_wday_t u_short + + +#define ngx_msleep Sleep + +#define NGX_HAVE_GETTIMEZONE 1 + +#define ngx_timezone_update() + +ngx_int_t ngx_gettimezone(void); +void ngx_libc_localtime(time_t s, struct tm *tm); +void ngx_libc_gmtime(time_t s, struct tm *tm); +void ngx_gettimeofday(struct timeval *tp); + + +#endif /* _NGX_TIME_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_udp_wsarecv.c b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_udp_wsarecv.c new file mode 100644 index 000000000..5424375a4 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_udp_wsarecv.c @@ -0,0 +1,149 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +ssize_t +ngx_udp_wsarecv(ngx_connection_t *c, u_char *buf, size_t size) +{ + int rc; + u_long bytes, flags; + WSABUF wsabuf[1]; + ngx_err_t err; + ngx_event_t *rev; + + wsabuf[0].buf = (char *) buf; + wsabuf[0].len = size; + flags = 0; + bytes = 0; + + rc = WSARecv(c->fd, wsabuf, 1, &bytes, &flags, NULL, NULL); + + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSARecv: fd:%d rc:%d %ul of %z", c->fd, rc, bytes, size); + + rev = c->read; + + if (rc == -1) { + rev->ready = 0; + err = ngx_socket_errno; + + if (err == WSAEWOULDBLOCK) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "WSARecv() not ready"); + return NGX_AGAIN; + } + + rev->error = 1; + ngx_connection_error(c, err, "WSARecv() failed"); + + return NGX_ERROR; + } + + return bytes; +} + + +ssize_t +ngx_udp_overlapped_wsarecv(ngx_connection_t *c, u_char *buf, size_t size) +{ + int rc; + u_long bytes, flags; + WSABUF wsabuf[1]; + ngx_err_t err; + ngx_event_t *rev; + LPWSAOVERLAPPED ovlp; + + rev = c->read; + + if (!rev->ready) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, "second wsa post"); + return NGX_AGAIN; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "rev->complete: %d", rev->complete); + + if (rev->complete) { + rev->complete = 0; + + if (ngx_event_flags & NGX_USE_IOCP_EVENT) { + if (rev->ovlp.error) { + ngx_connection_error(c, rev->ovlp.error, "WSARecv() failed"); + return NGX_ERROR; + } + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSARecv ovlp: fd:%d %ul of %z", + c->fd, rev->available, size); + + return rev->available; + } + + if (WSAGetOverlappedResult(c->fd, (LPWSAOVERLAPPED) &rev->ovlp, + &bytes, 0, NULL) + == 0) + { + ngx_connection_error(c, ngx_socket_errno, + "WSARecv() or WSAGetOverlappedResult() failed"); + return NGX_ERROR; + } + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSARecv: fd:%d %ul of %z", c->fd, bytes, size); + + return bytes; + } + + ovlp = (LPWSAOVERLAPPED) &rev->ovlp; + ngx_memzero(ovlp, sizeof(WSAOVERLAPPED)); + wsabuf[0].buf = (char *) buf; + wsabuf[0].len = size; + flags = 0; + bytes = 0; + + rc = WSARecv(c->fd, wsabuf, 1, &bytes, &flags, ovlp, NULL); + + rev->complete = 0; + + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSARecv ovlp: fd:%d rc:%d %ul of %z", + c->fd, rc, bytes, size); + + if (rc == -1) { + err = ngx_socket_errno; + if (err == WSA_IO_PENDING) { + rev->active = 1; + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "WSARecv() posted"); + return NGX_AGAIN; + } + + rev->error = 1; + ngx_connection_error(c, err, "WSARecv() failed"); + return NGX_ERROR; + } + + if (ngx_event_flags & NGX_USE_IOCP_EVENT) { + + /* + * if a socket was bound with I/O completion port + * then GetQueuedCompletionStatus() would anyway return its status + * despite that WSARecv() was already complete + */ + + rev->active = 1; + return NGX_AGAIN; + } + + rev->active = 0; + + return bytes; +} diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_user.c b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_user.c new file mode 100644 index 000000000..ea6da5a30 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_user.c @@ -0,0 +1,23 @@ +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +#if (NGX_CRYPT) + +ngx_int_t +ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted) +{ + /* STUB: a plain text password */ + + *encrypted = key; + + return NGX_OK; +} + +#endif /* NGX_CRYPT */ diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_user.h b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_user.h new file mode 100644 index 000000000..61408e450 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_user.h @@ -0,0 +1,25 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_USER_H_INCLUDED_ +#define _NGX_USER_H_INCLUDED_ + + +#include +#include + + +/* STUB */ +#define ngx_uid_t ngx_int_t +#define ngx_gid_t ngx_int_t + + +ngx_int_t ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, + u_char **encrypted); + + +#endif /* _NGX_USER_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_win32_config.h b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_win32_config.h new file mode 100644 index 000000000..704561355 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_win32_config.h @@ -0,0 +1,291 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_WIN32_CONFIG_H_INCLUDED_ +#define _NGX_WIN32_CONFIG_H_INCLUDED_ + + +#undef WIN32 +#define WIN32 0x0400 +#define _WIN32_WINNT 0x0501 + + +#define STRICT +#define WIN32_LEAN_AND_MEAN + +/* enable getenv() and gmtime() in msvc8 */ +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE + +/* enable gethostbyname() in msvc2015 */ +#if !(NGX_HAVE_INET6) +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#endif + +/* + * we need to include explicitly before because + * the warning 4201 is enabled in + */ +#include + +#ifdef _MSC_VER +#pragma warning(disable:4201) +#endif + +#include +#include /* ipv6 */ +#include +#include +#include /* offsetof() */ + +#ifdef __MINGW64_VERSION_MAJOR + +/* GCC MinGW-w64 supports _FILE_OFFSET_BITS */ +#define _FILE_OFFSET_BITS 64 + +#elif defined __GNUC__ + +/* GCC MinGW's stdio.h includes sys/types.h */ +#define _OFF_T_ +#define __have_typedef_off_t + +#endif + +#include +#include +#include +#ifdef __GNUC__ +#include +#endif +#include +#include + +#ifdef __WATCOMC__ +#define _TIME_T_DEFINED +typedef long time_t; +/* OpenWatcom defines time_t as "unsigned long" */ +#endif + +#include /* localtime(), strftime() */ + + +#ifdef _MSC_VER + +/* the end of the precompiled headers */ +#pragma hdrstop + +#pragma warning(default:4201) + +/* 'type cast': from function pointer to data pointer */ +#pragma warning(disable:4054) + +/* 'type cast': from data pointer to function pointer */ +#pragma warning(disable:4055) + +/* 'function' : different 'const' qualifiers */ +#pragma warning(disable:4090) + +/* unreferenced formal parameter */ +#pragma warning(disable:4100) + +/* FD_SET() and FD_CLR(): conditional expression is constant */ +#pragma warning(disable:4127) + +/* conversion from 'type1' to 'type2', possible loss of data */ +#pragma warning(disable:4244) + +/* conversion from 'size_t' to 'type', possible loss of data */ +#pragma warning(disable:4267) + +/* array is too small to include a terminating null character */ +#pragma warning(disable:4295) + +/* conversion from 'type1' to 'type2' of greater size */ +#pragma warning(disable:4306) + +#endif + + +#ifdef __WATCOMC__ + +/* symbol 'ngx_rbtree_min' has been defined, but not referenced */ +#pragma disable_message(202) + +#endif + + +#ifdef __BORLANDC__ + +/* the end of the precompiled headers */ +#pragma hdrstop + +/* functions containing (for|while|some if) are not expanded inline */ +#pragma warn -8027 + +/* unreferenced formal parameter */ +#pragma warn -8057 + +/* suspicious pointer arithmetic */ +#pragma warn -8072 + +#endif + + +#include + + +#define ngx_inline __inline +#define ngx_cdecl __cdecl + + +#ifdef _MSC_VER +typedef unsigned __int32 uint32_t; +typedef __int32 int32_t; +typedef unsigned __int16 uint16_t; +#define ngx_libc_cdecl __cdecl + +#elif defined __BORLANDC__ +typedef unsigned __int32 uint32_t; +typedef __int32 int32_t; +typedef unsigned __int16 uint16_t; +#define ngx_libc_cdecl __cdecl + +#else /* __WATCOMC__ */ +typedef unsigned int uint32_t; +typedef int int32_t; +typedef unsigned short int uint16_t; +#define ngx_libc_cdecl + +#endif + +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; + +#if __BORLANDC__ +typedef int intptr_t; +typedef u_int uintptr_t; +#endif + + +#ifndef __MINGW64_VERSION_MAJOR + +/* Windows defines off_t as long, which is 32-bit */ +typedef __int64 off_t; +#define _OFF_T_DEFINED + +#endif + + +#ifdef __WATCOMC__ + +/* off_t is redefined by sys/types.h used by zlib.h */ +#define __TYPES_H_INCLUDED +typedef int dev_t; +typedef unsigned int ino_t; + +#elif __BORLANDC__ + +/* off_t is redefined by sys/types.h used by zlib.h */ +#define __TYPES_H + +typedef int dev_t; +typedef unsigned int ino_t; + +#endif + + +#ifndef __GNUC__ +#ifdef _WIN64 +typedef __int64 ssize_t; +#else +typedef int ssize_t; +#endif +#endif + + +typedef uint32_t in_addr_t; +typedef u_short in_port_t; +typedef int sig_atomic_t; + + +#ifdef _WIN64 + +#define NGX_PTR_SIZE 8 +#define NGX_SIZE_T_LEN (sizeof("-9223372036854775808") - 1) +#define NGX_MAX_SIZE_T_VALUE 9223372036854775807 +#define NGX_TIME_T_LEN (sizeof("-9223372036854775808") - 1) +#define NGX_TIME_T_SIZE 8 +#define NGX_MAX_TIME_T_VALUE 9223372036854775807 + +#else + +#define NGX_PTR_SIZE 4 +#define NGX_SIZE_T_LEN (sizeof("-2147483648") - 1) +#define NGX_MAX_SIZE_T_VALUE 2147483647 +#define NGX_TIME_T_LEN (sizeof("-2147483648") - 1) +#define NGX_TIME_T_SIZE 4 +#define NGX_MAX_TIME_T_VALUE 2147483647 + +#endif + + +#define NGX_OFF_T_LEN (sizeof("-9223372036854775807") - 1) +#define NGX_MAX_OFF_T_VALUE 9223372036854775807 +#define NGX_SIG_ATOMIC_T_SIZE 4 + + +#define NGX_HAVE_LITTLE_ENDIAN 1 +#define NGX_HAVE_NONALIGNED 1 + + +#define NGX_WIN_NT 200000 + + +#define NGX_LISTEN_BACKLOG 511 + + +#ifndef NGX_HAVE_INHERITED_NONBLOCK +#define NGX_HAVE_INHERITED_NONBLOCK 1 +#endif + +#ifndef NGX_HAVE_CASELESS_FILESYSTEM +#define NGX_HAVE_CASELESS_FILESYSTEM 1 +#endif + +#ifndef NGX_HAVE_WIN32_TRANSMITPACKETS +#define NGX_HAVE_WIN32_TRANSMITPACKETS 1 +#define NGX_HAVE_WIN32_TRANSMITFILE 0 +#endif + +#ifndef NGX_HAVE_WIN32_TRANSMITFILE +#define NGX_HAVE_WIN32_TRANSMITFILE 1 +#endif + +#if (NGX_HAVE_WIN32_TRANSMITPACKETS) || (NGX_HAVE_WIN32_TRANSMITFILE) +#define NGX_HAVE_SENDFILE 1 +#endif + +#ifndef NGX_HAVE_SO_SNDLOWAT +/* setsockopt(SO_SNDLOWAT) returns error WSAENOPROTOOPT */ +#define NGX_HAVE_SO_SNDLOWAT 0 +#endif + +#ifndef NGX_HAVE_FIONREAD +#define NGX_HAVE_FIONREAD 1 +#endif + +#define NGX_HAVE_GETADDRINFO 1 + +#define ngx_random() \ + ((long) (0x7fffffff & ( ((uint32_t) rand() << 16) \ + ^ ((uint32_t) rand() << 8) \ + ^ ((uint32_t) rand()) ))) + +#define ngx_debug_init() + + +#endif /* _NGX_WIN32_CONFIG_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_win32_init.c b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_win32_init.c new file mode 100644 index 000000000..de66a44eb --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_win32_init.c @@ -0,0 +1,329 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +ngx_uint_t ngx_win32_version; +ngx_uint_t ngx_ncpu; +ngx_uint_t ngx_max_wsabufs; +ngx_int_t ngx_max_sockets; +ngx_uint_t ngx_inherited_nonblocking = 1; +ngx_uint_t ngx_tcp_nodelay_and_tcp_nopush; + +char ngx_unique[NGX_INT32_LEN + 1]; + + +ngx_os_io_t ngx_os_io = { + ngx_wsarecv, + ngx_wsarecv_chain, + ngx_udp_wsarecv, + ngx_wsasend, + NULL, + NULL, + ngx_wsasend_chain, + 0 +}; + + +typedef struct { + WORD wServicePackMinor; + WORD wSuiteMask; + BYTE wProductType; +} ngx_osviex_stub_t; + + +static u_int osviex; +static OSVERSIONINFOEX osvi; + +/* Should these pointers be per protocol ? */ +LPFN_ACCEPTEX ngx_acceptex; +LPFN_GETACCEPTEXSOCKADDRS ngx_getacceptexsockaddrs; +LPFN_TRANSMITFILE ngx_transmitfile; +LPFN_TRANSMITPACKETS ngx_transmitpackets; +LPFN_CONNECTEX ngx_connectex; +LPFN_DISCONNECTEX ngx_disconnectex; + +static GUID ax_guid = WSAID_ACCEPTEX; +static GUID as_guid = WSAID_GETACCEPTEXSOCKADDRS; +static GUID tf_guid = WSAID_TRANSMITFILE; +static GUID tp_guid = WSAID_TRANSMITPACKETS; +static GUID cx_guid = WSAID_CONNECTEX; +static GUID dx_guid = WSAID_DISCONNECTEX; + + +#if (NGX_LOAD_WSAPOLL) +ngx_wsapoll_pt WSAPoll; +ngx_uint_t ngx_have_wsapoll; +#endif + + +ngx_int_t +ngx_os_init(ngx_log_t *log) +{ + DWORD bytes; + SOCKET s; + WSADATA wsd; + ngx_err_t err; + ngx_time_t *tp; + ngx_uint_t n; + SYSTEM_INFO si; + + /* get Windows version */ + + ngx_memzero(&osvi, sizeof(OSVERSIONINFOEX)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + +#ifdef _MSC_VER +#pragma warning(disable:4996) +#endif + + osviex = GetVersionEx((OSVERSIONINFO *) &osvi); + + if (osviex == 0) { + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + if (GetVersionEx((OSVERSIONINFO *) &osvi) == 0) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "GetVersionEx() failed"); + return NGX_ERROR; + } + } + +#ifdef _MSC_VER +#pragma warning(default:4996) +#endif + + /* + * Windows 3.1 Win32s 0xxxxx + * + * Windows 95 140000 + * Windows 98 141000 + * Windows ME 149000 + * Windows NT 3.51 235100 + * Windows NT 4.0 240000 + * Windows NT 4.0 SP5 240050 + * Windows 2000 250000 + * Windows XP 250100 + * Windows 2003 250200 + * Windows Vista/2008 260000 + * + * Windows CE x.x 3xxxxx + */ + + ngx_win32_version = osvi.dwPlatformId * 100000 + + osvi.dwMajorVersion * 10000 + + osvi.dwMinorVersion * 100; + + if (osviex) { + ngx_win32_version += osvi.wServicePackMajor * 10 + + osvi.wServicePackMinor; + } + + GetSystemInfo(&si); + ngx_pagesize = si.dwPageSize; + ngx_allocation_granularity = si.dwAllocationGranularity; + ngx_ncpu = si.dwNumberOfProcessors; + ngx_cacheline_size = NGX_CPU_CACHE_LINE; + + for (n = ngx_pagesize; n >>= 1; ngx_pagesize_shift++) { /* void */ } + + /* delete default "C" locale for _wcsicmp() */ + setlocale(LC_ALL, ""); + + + /* init Winsock */ + + if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + "WSAStartup() failed"); + return NGX_ERROR; + } + + if (ngx_win32_version < NGX_WIN_NT) { + ngx_max_wsabufs = 16; + return NGX_OK; + } + + /* STUB: ngx_uint_t max */ + ngx_max_wsabufs = 1024 * 1024; + + /* + * get AcceptEx(), GetAcceptExSockAddrs(), TransmitFile(), + * TransmitPackets(), ConnectEx(), and DisconnectEx() addresses + */ + + s = ngx_socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + if (s == (ngx_socket_t) -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + ngx_socket_n " failed"); + return NGX_ERROR; + } + + if (WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &ax_guid, sizeof(GUID), + &ngx_acceptex, sizeof(LPFN_ACCEPTEX), &bytes, NULL, NULL) + == -1) + { + ngx_log_error(NGX_LOG_NOTICE, log, ngx_socket_errno, + "WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER, " + "WSAID_ACCEPTEX) failed"); + } + + if (WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &as_guid, sizeof(GUID), + &ngx_getacceptexsockaddrs, sizeof(LPFN_GETACCEPTEXSOCKADDRS), + &bytes, NULL, NULL) + == -1) + { + ngx_log_error(NGX_LOG_NOTICE, log, ngx_socket_errno, + "WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER, " + "WSAID_GETACCEPTEXSOCKADDRS) failed"); + } + + if (WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &tf_guid, sizeof(GUID), + &ngx_transmitfile, sizeof(LPFN_TRANSMITFILE), &bytes, + NULL, NULL) + == -1) + { + ngx_log_error(NGX_LOG_NOTICE, log, ngx_socket_errno, + "WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER, " + "WSAID_TRANSMITFILE) failed"); + } + + if (WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &tp_guid, sizeof(GUID), + &ngx_transmitpackets, sizeof(LPFN_TRANSMITPACKETS), &bytes, + NULL, NULL) + == -1) + { + ngx_log_error(NGX_LOG_NOTICE, log, ngx_socket_errno, + "WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER, " + "WSAID_TRANSMITPACKETS) failed"); + } + + if (WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &cx_guid, sizeof(GUID), + &ngx_connectex, sizeof(LPFN_CONNECTEX), &bytes, + NULL, NULL) + == -1) + { + ngx_log_error(NGX_LOG_NOTICE, log, ngx_socket_errno, + "WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER, " + "WSAID_CONNECTEX) failed"); + } + + if (WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &dx_guid, sizeof(GUID), + &ngx_disconnectex, sizeof(LPFN_DISCONNECTEX), &bytes, + NULL, NULL) + == -1) + { + ngx_log_error(NGX_LOG_NOTICE, log, ngx_socket_errno, + "WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER, " + "WSAID_DISCONNECTEX) failed"); + } + + if (ngx_close_socket(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno, + ngx_close_socket_n " failed"); + } + +#if (NGX_LOAD_WSAPOLL) + { + HMODULE hmod; + + hmod = GetModuleHandle("ws2_32.dll"); + if (hmod == NULL) { + ngx_log_error(NGX_LOG_NOTICE, log, ngx_errno, + "GetModuleHandle(\"ws2_32.dll\") failed"); + goto nopoll; + } + + WSAPoll = (ngx_wsapoll_pt) (void *) GetProcAddress(hmod, "WSAPoll"); + if (WSAPoll == NULL) { + ngx_log_error(NGX_LOG_NOTICE, log, ngx_errno, + "GetProcAddress(\"WSAPoll\") failed"); + goto nopoll; + } + + ngx_have_wsapoll = 1; + + } + +nopoll: + +#endif + + if (GetEnvironmentVariable("ngx_unique", ngx_unique, NGX_INT32_LEN + 1) + != 0) + { + ngx_process = NGX_PROCESS_WORKER; + + } else { + err = ngx_errno; + + if (err != ERROR_ENVVAR_NOT_FOUND) { + ngx_log_error(NGX_LOG_EMERG, log, err, + "GetEnvironmentVariable(\"ngx_unique\") failed"); + return NGX_ERROR; + } + + ngx_sprintf((u_char *) ngx_unique, "%P%Z", ngx_pid); + } + + tp = ngx_timeofday(); + srand((ngx_pid << 16) ^ (unsigned) tp->sec ^ tp->msec); + + return NGX_OK; +} + + +void +ngx_os_status(ngx_log_t *log) +{ + ngx_osviex_stub_t *osviex_stub; + + ngx_log_error(NGX_LOG_NOTICE, log, 0, NGINX_VER_BUILD); + + if (osviex) { + + /* + * the MSVC 6.0 SP2 defines wSuiteMask and wProductType + * as WORD wReserved[2] + */ + osviex_stub = (ngx_osviex_stub_t *) &osvi.wServicePackMinor; + + ngx_log_error(NGX_LOG_INFO, log, 0, + "OS: %ui build:%ud, \"%s\", suite:%Xd, type:%ud", + ngx_win32_version, osvi.dwBuildNumber, osvi.szCSDVersion, + osviex_stub->wSuiteMask, osviex_stub->wProductType); + + } else { + if (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { + + /* Win9x build */ + + ngx_log_error(NGX_LOG_INFO, log, 0, + "OS: %ui build:%ud.%ud.%ud, \"%s\"", + ngx_win32_version, + osvi.dwBuildNumber >> 24, + (osvi.dwBuildNumber >> 16) & 0xff, + osvi.dwBuildNumber & 0xffff, + osvi.szCSDVersion); + + } else { + + /* + * VER_PLATFORM_WIN32_NT + * + * we do not currently support VER_PLATFORM_WIN32_CE + * and we do not support VER_PLATFORM_WIN32s at all + */ + + ngx_log_error(NGX_LOG_INFO, log, 0, "OS: %ui build:%ud, \"%s\"", + ngx_win32_version, osvi.dwBuildNumber, + osvi.szCSDVersion); + } + } +} diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_wsarecv.c b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_wsarecv.c new file mode 100644 index 000000000..b01405e26 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_wsarecv.c @@ -0,0 +1,215 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +ssize_t +ngx_wsarecv(ngx_connection_t *c, u_char *buf, size_t size) +{ + int rc; + u_long bytes, flags; + WSABUF wsabuf[1]; + ngx_err_t err; + ngx_int_t n; + ngx_event_t *rev; + + wsabuf[0].buf = (char *) buf; + wsabuf[0].len = size; + flags = 0; + bytes = 0; + + rc = WSARecv(c->fd, wsabuf, 1, &bytes, &flags, NULL, NULL); + + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSARecv: fd:%d rc:%d %ul of %z", c->fd, rc, bytes, size); + + rev = c->read; + + if (rc == -1) { + rev->ready = 0; + err = ngx_socket_errno; + + if (err == WSAEWOULDBLOCK) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "WSARecv() not ready"); + return NGX_AGAIN; + } + + n = ngx_connection_error(c, err, "WSARecv() failed"); + + if (n == NGX_ERROR) { + rev->error = 1; + } + + return n; + } + +#if (NGX_HAVE_FIONREAD) + + if (rev->available >= 0 && bytes > 0) { + rev->available -= bytes; + + /* + * negative rev->available means some additional bytes + * were received between kernel notification and WSARecv(), + * and therefore ev->ready can be safely reset even for + * edge-triggered event methods + */ + + if (rev->available < 0) { + rev->available = 0; + rev->ready = 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSARecv: avail:%d", rev->available); + + } else if (bytes == size) { + + if (ngx_socket_nread(c->fd, &rev->available) == -1) { + n = ngx_connection_error(c, ngx_socket_errno, + ngx_socket_nread_n " failed"); + + if (n == NGX_ERROR) { + rev->ready = 0; + rev->error = 1; + } + + return n; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSARecv: avail:%d", rev->available); + } + +#endif + + if (bytes < size) { + rev->ready = 0; + } + + if (bytes == 0) { + rev->ready = 0; + rev->eof = 1; + } + + return bytes; +} + + +ssize_t +ngx_overlapped_wsarecv(ngx_connection_t *c, u_char *buf, size_t size) +{ + int rc; + u_long bytes, flags; + WSABUF wsabuf[1]; + ngx_err_t err; + ngx_int_t n; + ngx_event_t *rev; + LPWSAOVERLAPPED ovlp; + + rev = c->read; + + if (!rev->ready) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, "second wsa post"); + return NGX_AGAIN; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "rev->complete: %d", rev->complete); + + if (rev->complete) { + rev->complete = 0; + + if (ngx_event_flags & NGX_USE_IOCP_EVENT) { + if (rev->ovlp.error) { + ngx_connection_error(c, rev->ovlp.error, "WSARecv() failed"); + return NGX_ERROR; + } + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSARecv ovlp: fd:%d %ul of %z", + c->fd, rev->available, size); + + return rev->available; + } + + if (WSAGetOverlappedResult(c->fd, (LPWSAOVERLAPPED) &rev->ovlp, + &bytes, 0, NULL) + == 0) + { + ngx_connection_error(c, ngx_socket_errno, + "WSARecv() or WSAGetOverlappedResult() failed"); + return NGX_ERROR; + } + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSARecv: fd:%d %ul of %z", c->fd, bytes, size); + + return bytes; + } + + ovlp = (LPWSAOVERLAPPED) &rev->ovlp; + ngx_memzero(ovlp, sizeof(WSAOVERLAPPED)); + wsabuf[0].buf = (char *) buf; + wsabuf[0].len = size; + flags = 0; + bytes = 0; + + rc = WSARecv(c->fd, wsabuf, 1, &bytes, &flags, ovlp, NULL); + + rev->complete = 0; + + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSARecv ovlp: fd:%d rc:%d %ul of %z", + c->fd, rc, bytes, size); + + if (rc == -1) { + err = ngx_socket_errno; + if (err == WSA_IO_PENDING) { + rev->active = 1; + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "WSARecv() posted"); + return NGX_AGAIN; + } + + n = ngx_connection_error(c, err, "WSARecv() failed"); + + if (n == NGX_ERROR) { + rev->error = 1; + } + + return n; + } + + if (ngx_event_flags & NGX_USE_IOCP_EVENT) { + + /* + * if a socket was bound with I/O completion port + * then GetQueuedCompletionStatus() would anyway return its status + * despite that WSARecv() was already complete + */ + + rev->active = 1; + return NGX_AGAIN; + } + + if (bytes == 0) { + rev->eof = 1; + rev->ready = 0; + + } else { + rev->ready = 1; + } + + rev->active = 0; + + return bytes; +} diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_wsarecv_chain.c b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_wsarecv_chain.c new file mode 100644 index 000000000..e60389bb7 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_wsarecv_chain.c @@ -0,0 +1,147 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +#define NGX_WSABUFS 64 + + +ssize_t +ngx_wsarecv_chain(ngx_connection_t *c, ngx_chain_t *chain, off_t limit) +{ + int rc; + u_char *prev; + u_long bytes, flags; + size_t n, size; + ngx_err_t err; + ngx_array_t vec; + ngx_event_t *rev; + LPWSABUF wsabuf; + WSABUF wsabufs[NGX_WSABUFS]; + + prev = NULL; + wsabuf = NULL; + flags = 0; + size = 0; + bytes = 0; + + vec.elts = wsabufs; + vec.nelts = 0; + vec.size = sizeof(WSABUF); + vec.nalloc = NGX_WSABUFS; + vec.pool = c->pool; + + /* coalesce the neighbouring bufs */ + + while (chain) { + n = chain->buf->end - chain->buf->last; + + if (limit) { + if (size >= (size_t) limit) { + break; + } + + if (size + n > (size_t) limit) { + n = (size_t) limit - size; + } + } + + if (prev == chain->buf->last) { + wsabuf->len += n; + + } else { + if (vec.nelts == vec.nalloc) { + break; + } + + wsabuf = ngx_array_push(&vec); + if (wsabuf == NULL) { + return NGX_ERROR; + } + + wsabuf->buf = (char *) chain->buf->last; + wsabuf->len = n; + } + + size += n; + prev = chain->buf->end; + chain = chain->next; + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSARecv: %d:%d", vec.nelts, wsabuf->len); + + + rc = WSARecv(c->fd, vec.elts, vec.nelts, &bytes, &flags, NULL, NULL); + + rev = c->read; + + if (rc == -1) { + rev->ready = 0; + err = ngx_socket_errno; + + if (err == WSAEWOULDBLOCK) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "WSARecv() not ready"); + return NGX_AGAIN; + } + + rev->error = 1; + ngx_connection_error(c, err, "WSARecv() failed"); + return NGX_ERROR; + } + +#if (NGX_HAVE_FIONREAD) + + if (rev->available >= 0 && bytes > 0) { + rev->available -= bytes; + + /* + * negative rev->available means some additional bytes + * were received between kernel notification and WSARecv(), + * and therefore ev->ready can be safely reset even for + * edge-triggered event methods + */ + + if (rev->available < 0) { + rev->available = 0; + rev->ready = 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSARecv: avail:%d", rev->available); + + } else if (bytes == size) { + + if (ngx_socket_nread(c->fd, &rev->available) == -1) { + rev->ready = 0; + rev->error = 1; + ngx_connection_error(c, ngx_socket_errno, + ngx_socket_nread_n " failed"); + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSARecv: avail:%d", rev->available); + } + +#endif + + if (bytes < size) { + rev->ready = 0; + } + + if (bytes == 0) { + rev->ready = 0; + rev->eof = 1; + } + + return bytes; +} diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_wsasend.c b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_wsasend.c new file mode 100644 index 000000000..d6a23d17b --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_wsasend.c @@ -0,0 +1,185 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +ssize_t +ngx_wsasend(ngx_connection_t *c, u_char *buf, size_t size) +{ + int n; + u_long sent; + ngx_err_t err; + ngx_event_t *wev; + WSABUF wsabuf; + + wev = c->write; + + if (!wev->ready) { + return NGX_AGAIN; + } + + /* + * WSABUF must be 4-byte aligned otherwise + * WSASend() will return undocumented WSAEINVAL error. + */ + + wsabuf.buf = (char *) buf; + wsabuf.len = size; + + sent = 0; + + n = WSASend(c->fd, &wsabuf, 1, &sent, 0, NULL, NULL); + + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSASend: fd:%d, %d, %ul of %uz", c->fd, n, sent, size); + + if (n == 0) { + if (sent < size) { + wev->ready = 0; + } + + c->sent += sent; + + return sent; + } + + err = ngx_socket_errno; + + if (err == WSAEWOULDBLOCK) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, "WSASend() not ready"); + wev->ready = 0; + return NGX_AGAIN; + } + + wev->error = 1; + ngx_connection_error(c, err, "WSASend() failed"); + + return NGX_ERROR; +} + + +ssize_t +ngx_overlapped_wsasend(ngx_connection_t *c, u_char *buf, size_t size) +{ + int n; + u_long sent; + ngx_err_t err; + ngx_event_t *wev; + LPWSAOVERLAPPED ovlp; + WSABUF wsabuf; + + wev = c->write; + + if (!wev->ready) { + return NGX_AGAIN; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "wev->complete: %d", wev->complete); + + if (!wev->complete) { + + /* post the overlapped WSASend() */ + + /* + * WSABUFs must be 4-byte aligned otherwise + * WSASend() will return undocumented WSAEINVAL error. + */ + + wsabuf.buf = (char *) buf; + wsabuf.len = size; + + sent = 0; + + ovlp = (LPWSAOVERLAPPED) &c->write->ovlp; + ngx_memzero(ovlp, sizeof(WSAOVERLAPPED)); + + n = WSASend(c->fd, &wsabuf, 1, &sent, 0, ovlp, NULL); + + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSASend: fd:%d, %d, %ul of %uz", c->fd, n, sent, size); + + wev->complete = 0; + + if (n == 0) { + if (ngx_event_flags & NGX_USE_IOCP_EVENT) { + + /* + * if a socket was bound with I/O completion port then + * GetQueuedCompletionStatus() would anyway return its status + * despite that WSASend() was already complete + */ + + wev->active = 1; + return NGX_AGAIN; + } + + if (sent < size) { + wev->ready = 0; + } + + c->sent += sent; + + return sent; + } + + err = ngx_socket_errno; + + if (err == WSA_IO_PENDING) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "WSASend() posted"); + wev->active = 1; + return NGX_AGAIN; + } + + wev->error = 1; + ngx_connection_error(c, err, "WSASend() failed"); + + return NGX_ERROR; + } + + /* the overlapped WSASend() complete */ + + wev->complete = 0; + wev->active = 0; + + if (ngx_event_flags & NGX_USE_IOCP_EVENT) { + + if (wev->ovlp.error) { + ngx_connection_error(c, wev->ovlp.error, "WSASend() failed"); + return NGX_ERROR; + } + + sent = wev->available; + + } else { + if (WSAGetOverlappedResult(c->fd, (LPWSAOVERLAPPED) &wev->ovlp, + &sent, 0, NULL) + == 0) + { + ngx_connection_error(c, ngx_socket_errno, + "WSASend() or WSAGetOverlappedResult() failed"); + + return NGX_ERROR; + } + } + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSAGetOverlappedResult: fd:%d, %ul of %uz", + c->fd, sent, size); + + if (sent < size) { + wev->ready = 0; + } + + c->sent += sent; + + return sent; +} diff --git a/src/deps/src/nginx-1.30.1/src/os/win32/ngx_wsasend_chain.c b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_wsasend_chain.c new file mode 100644 index 000000000..cd50e710c --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/os/win32/ngx_wsasend_chain.c @@ -0,0 +1,296 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +#define NGX_WSABUFS 64 + + +ngx_chain_t * +ngx_wsasend_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) +{ + int rc; + u_char *prev; + u_long size, sent, send, prev_send; + ngx_err_t err; + ngx_event_t *wev; + ngx_array_t vec; + ngx_chain_t *cl; + LPWSABUF wsabuf; + WSABUF wsabufs[NGX_WSABUFS]; + + wev = c->write; + + if (!wev->ready) { + return in; + } + + /* the maximum limit size is the maximum u_long value - the page size */ + + if (limit == 0 || limit > (off_t) (NGX_MAX_UINT32_VALUE - ngx_pagesize)) { + limit = NGX_MAX_UINT32_VALUE - ngx_pagesize; + } + + send = 0; + + /* + * WSABUFs must be 4-byte aligned otherwise + * WSASend() will return undocumented WSAEINVAL error. + */ + + vec.elts = wsabufs; + vec.size = sizeof(WSABUF); + vec.nalloc = ngx_min(NGX_WSABUFS, ngx_max_wsabufs); + vec.pool = c->pool; + + for ( ;; ) { + prev = NULL; + wsabuf = NULL; + prev_send = send; + + vec.nelts = 0; + + /* create the WSABUF and coalesce the neighbouring bufs */ + + for (cl = in; cl && send < limit; cl = cl->next) { + + if (ngx_buf_special(cl->buf)) { + continue; + } + + size = cl->buf->last - cl->buf->pos; + + if (send + size > limit) { + size = (u_long) (limit - send); + } + + if (prev == cl->buf->pos) { + wsabuf->len += cl->buf->last - cl->buf->pos; + + } else { + if (vec.nelts == vec.nalloc) { + break; + } + + wsabuf = ngx_array_push(&vec); + if (wsabuf == NULL) { + return NGX_CHAIN_ERROR; + } + + wsabuf->buf = (char *) cl->buf->pos; + wsabuf->len = cl->buf->last - cl->buf->pos; + } + + prev = cl->buf->last; + send += size; + } + + sent = 0; + + rc = WSASend(c->fd, vec.elts, vec.nelts, &sent, 0, NULL, NULL); + + if (rc == -1) { + err = ngx_errno; + + if (err == WSAEWOULDBLOCK) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "WSASend() not ready"); + + } else { + wev->error = 1; + ngx_connection_error(c, err, "WSASend() failed"); + return NGX_CHAIN_ERROR; + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSASend: fd:%d, s:%ul", c->fd, sent); + + c->sent += sent; + + in = ngx_chain_update_sent(in, sent); + + if (send - prev_send != sent) { + wev->ready = 0; + return in; + } + + if (send >= limit || in == NULL) { + return in; + } + } +} + + +ngx_chain_t * +ngx_overlapped_wsasend_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) +{ + int rc; + u_char *prev; + u_long size, send, sent; + ngx_err_t err; + ngx_event_t *wev; + ngx_array_t vec; + ngx_chain_t *cl; + LPWSAOVERLAPPED ovlp; + LPWSABUF wsabuf; + WSABUF wsabufs[NGX_WSABUFS]; + + wev = c->write; + + if (!wev->ready) { + return in; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "wev->complete: %d", wev->complete); + + if (!wev->complete) { + + /* post the overlapped WSASend() */ + + /* the maximum limit size is the maximum u_long value - the page size */ + + if (limit == 0 || limit > (off_t) (NGX_MAX_UINT32_VALUE - ngx_pagesize)) + { + limit = NGX_MAX_UINT32_VALUE - ngx_pagesize; + } + + /* + * WSABUFs must be 4-byte aligned otherwise + * WSASend() will return undocumented WSAEINVAL error. + */ + + vec.elts = wsabufs; + vec.nelts = 0; + vec.size = sizeof(WSABUF); + vec.nalloc = ngx_min(NGX_WSABUFS, ngx_max_wsabufs); + vec.pool = c->pool; + + send = 0; + prev = NULL; + wsabuf = NULL; + + /* create the WSABUF and coalesce the neighbouring bufs */ + + for (cl = in; cl && send < limit; cl = cl->next) { + + if (ngx_buf_special(cl->buf)) { + continue; + } + + size = cl->buf->last - cl->buf->pos; + + if (send + size > limit) { + size = (u_long) (limit - send); + } + + if (prev == cl->buf->pos) { + wsabuf->len += cl->buf->last - cl->buf->pos; + + } else { + if (vec.nelts == vec.nalloc) { + break; + } + + wsabuf = ngx_array_push(&vec); + if (wsabuf == NULL) { + return NGX_CHAIN_ERROR; + } + + wsabuf->buf = (char *) cl->buf->pos; + wsabuf->len = cl->buf->last - cl->buf->pos; + } + + prev = cl->buf->last; + send += size; + } + + ovlp = (LPWSAOVERLAPPED) &c->write->ovlp; + ngx_memzero(ovlp, sizeof(WSAOVERLAPPED)); + + rc = WSASend(c->fd, vec.elts, vec.nelts, &sent, 0, ovlp, NULL); + + wev->complete = 0; + + if (rc == -1) { + err = ngx_errno; + + if (err == WSA_IO_PENDING) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "WSASend() posted"); + wev->active = 1; + return in; + + } else { + wev->error = 1; + ngx_connection_error(c, err, "WSASend() failed"); + return NGX_CHAIN_ERROR; + } + + } else if (ngx_event_flags & NGX_USE_IOCP_EVENT) { + + /* + * if a socket was bound with I/O completion port then + * GetQueuedCompletionStatus() would anyway return its status + * despite that WSASend() was already complete + */ + + wev->active = 1; + return in; + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSASend: fd:%d, s:%ul", c->fd, sent); + + } else { + + /* the overlapped WSASend() complete */ + + wev->complete = 0; + wev->active = 0; + + if (ngx_event_flags & NGX_USE_IOCP_EVENT) { + if (wev->ovlp.error) { + ngx_connection_error(c, wev->ovlp.error, "WSASend() failed"); + return NGX_CHAIN_ERROR; + } + + sent = wev->available; + + } else { + if (WSAGetOverlappedResult(c->fd, (LPWSAOVERLAPPED) &wev->ovlp, + &sent, 0, NULL) + == 0) + { + ngx_connection_error(c, ngx_socket_errno, + "WSASend() or WSAGetOverlappedResult() failed"); + + return NGX_CHAIN_ERROR; + } + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "WSASend ovlp: fd:%d, s:%ul", c->fd, sent); + + c->sent += sent; + + in = ngx_chain_update_sent(in, sent); + + if (in) { + wev->ready = 0; + + } else { + wev->ready = 1; + } + + return in; +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream.c new file mode 100644 index 000000000..508ce6082 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream.c @@ -0,0 +1,1176 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include + + +static char *ngx_stream_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static ngx_int_t ngx_stream_init_phases(ngx_conf_t *cf, + ngx_stream_core_main_conf_t *cmcf); +static ngx_int_t ngx_stream_init_phase_handlers(ngx_conf_t *cf, + ngx_stream_core_main_conf_t *cmcf); + +static ngx_int_t ngx_stream_add_addresses(ngx_conf_t *cf, + ngx_stream_core_srv_conf_t *cscf, ngx_stream_conf_port_t *port, + ngx_stream_listen_opt_t *lsopt); +static ngx_int_t ngx_stream_add_address(ngx_conf_t *cf, + ngx_stream_core_srv_conf_t *cscf, ngx_stream_conf_port_t *port, + ngx_stream_listen_opt_t *lsopt); +static ngx_int_t ngx_stream_add_server(ngx_conf_t *cf, + ngx_stream_core_srv_conf_t *cscf, ngx_stream_conf_addr_t *addr); + +static ngx_int_t ngx_stream_optimize_servers(ngx_conf_t *cf, + ngx_stream_core_main_conf_t *cmcf, ngx_array_t *ports); +static ngx_int_t ngx_stream_server_names(ngx_conf_t *cf, + ngx_stream_core_main_conf_t *cmcf, ngx_stream_conf_addr_t *addr); +static ngx_int_t ngx_stream_cmp_conf_addrs(const void *one, const void *two); +static int ngx_libc_cdecl ngx_stream_cmp_dns_wildcards(const void *one, + const void *two); + +static ngx_int_t ngx_stream_init_listening(ngx_conf_t *cf, + ngx_stream_conf_port_t *port); +static ngx_listening_t *ngx_stream_add_listening(ngx_conf_t *cf, + ngx_stream_conf_addr_t *addr); +static ngx_int_t ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *stport, + ngx_stream_conf_addr_t *addr); +#if (NGX_HAVE_INET6) +static ngx_int_t ngx_stream_add_addrs6(ngx_conf_t *cf, + ngx_stream_port_t *stport, ngx_stream_conf_addr_t *addr); +#endif + + +ngx_uint_t ngx_stream_max_module; + + +ngx_stream_filter_pt ngx_stream_top_filter; + + +static ngx_command_t ngx_stream_commands[] = { + + { ngx_string("stream"), + NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_stream_block, + 0, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_core_module_t ngx_stream_module_ctx = { + ngx_string("stream"), + NULL, + NULL +}; + + +ngx_module_t ngx_stream_module = { + NGX_MODULE_V1, + &ngx_stream_module_ctx, /* module context */ + ngx_stream_commands, /* module directives */ + NGX_CORE_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static char * +ngx_stream_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *rv; + ngx_uint_t mi, m, s; + ngx_conf_t pcf; + ngx_stream_module_t *module; + ngx_stream_conf_ctx_t *ctx; + ngx_stream_core_srv_conf_t **cscfp; + ngx_stream_core_main_conf_t *cmcf; + + if (*(ngx_stream_conf_ctx_t **) conf) { + return "is duplicate"; + } + + /* the main stream context */ + + ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_conf_ctx_t)); + if (ctx == NULL) { + return NGX_CONF_ERROR; + } + + *(ngx_stream_conf_ctx_t **) conf = ctx; + + /* count the number of the stream modules and set up their indices */ + + ngx_stream_max_module = ngx_count_modules(cf->cycle, NGX_STREAM_MODULE); + + + /* the stream main_conf context, it's the same in the all stream contexts */ + + ctx->main_conf = ngx_pcalloc(cf->pool, + sizeof(void *) * ngx_stream_max_module); + if (ctx->main_conf == NULL) { + return NGX_CONF_ERROR; + } + + + /* + * the stream null srv_conf context, it is used to merge + * the server{}s' srv_conf's + */ + + ctx->srv_conf = ngx_pcalloc(cf->pool, + sizeof(void *) * ngx_stream_max_module); + if (ctx->srv_conf == NULL) { + return NGX_CONF_ERROR; + } + + + /* + * create the main_conf's and the null srv_conf's of the all stream modules + */ + + for (m = 0; cf->cycle->modules[m]; m++) { + if (cf->cycle->modules[m]->type != NGX_STREAM_MODULE) { + continue; + } + + module = cf->cycle->modules[m]->ctx; + mi = cf->cycle->modules[m]->ctx_index; + + if (module->create_main_conf) { + ctx->main_conf[mi] = module->create_main_conf(cf); + if (ctx->main_conf[mi] == NULL) { + return NGX_CONF_ERROR; + } + } + + if (module->create_srv_conf) { + ctx->srv_conf[mi] = module->create_srv_conf(cf); + if (ctx->srv_conf[mi] == NULL) { + return NGX_CONF_ERROR; + } + } + } + + + pcf = *cf; + cf->ctx = ctx; + + for (m = 0; cf->cycle->modules[m]; m++) { + if (cf->cycle->modules[m]->type != NGX_STREAM_MODULE) { + continue; + } + + module = cf->cycle->modules[m]->ctx; + + if (module->preconfiguration) { + if (module->preconfiguration(cf) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + } + + + /* parse inside the stream{} block */ + + cf->module_type = NGX_STREAM_MODULE; + cf->cmd_type = NGX_STREAM_MAIN_CONF; + rv = ngx_conf_parse(cf, NULL); + + if (rv != NGX_CONF_OK) { + *cf = pcf; + return rv; + } + + + /* init stream{} main_conf's, merge the server{}s' srv_conf's */ + + cmcf = ctx->main_conf[ngx_stream_core_module.ctx_index]; + cscfp = cmcf->servers.elts; + + for (m = 0; cf->cycle->modules[m]; m++) { + if (cf->cycle->modules[m]->type != NGX_STREAM_MODULE) { + continue; + } + + module = cf->cycle->modules[m]->ctx; + mi = cf->cycle->modules[m]->ctx_index; + + /* init stream{} main_conf's */ + + cf->ctx = ctx; + + if (module->init_main_conf) { + rv = module->init_main_conf(cf, ctx->main_conf[mi]); + if (rv != NGX_CONF_OK) { + *cf = pcf; + return rv; + } + } + + for (s = 0; s < cmcf->servers.nelts; s++) { + + /* merge the server{}s' srv_conf's */ + + cf->ctx = cscfp[s]->ctx; + + if (module->merge_srv_conf) { + rv = module->merge_srv_conf(cf, + ctx->srv_conf[mi], + cscfp[s]->ctx->srv_conf[mi]); + if (rv != NGX_CONF_OK) { + *cf = pcf; + return rv; + } + } + } + } + + if (ngx_stream_init_phases(cf, cmcf) != NGX_OK) { + return NGX_CONF_ERROR; + } + + for (m = 0; cf->cycle->modules[m]; m++) { + if (cf->cycle->modules[m]->type != NGX_STREAM_MODULE) { + continue; + } + + module = cf->cycle->modules[m]->ctx; + + if (module->postconfiguration) { + if (module->postconfiguration(cf) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + } + + if (ngx_stream_variables_init_vars(cf) != NGX_OK) { + return NGX_CONF_ERROR; + } + + *cf = pcf; + + if (ngx_stream_init_phase_handlers(cf, cmcf) != NGX_OK) { + return NGX_CONF_ERROR; + } + + /* optimize the lists of ports, addresses and server names */ + + if (ngx_stream_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_stream_init_phases(ngx_conf_t *cf, ngx_stream_core_main_conf_t *cmcf) +{ + if (ngx_array_init(&cmcf->phases[NGX_STREAM_POST_ACCEPT_PHASE].handlers, + cf->pool, 1, sizeof(ngx_stream_handler_pt)) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_array_init(&cmcf->phases[NGX_STREAM_PREACCESS_PHASE].handlers, + cf->pool, 1, sizeof(ngx_stream_handler_pt)) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_array_init(&cmcf->phases[NGX_STREAM_ACCESS_PHASE].handlers, + cf->pool, 1, sizeof(ngx_stream_handler_pt)) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_array_init(&cmcf->phases[NGX_STREAM_SSL_PHASE].handlers, + cf->pool, 1, sizeof(ngx_stream_handler_pt)) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_array_init(&cmcf->phases[NGX_STREAM_PREREAD_PHASE].handlers, + cf->pool, 1, sizeof(ngx_stream_handler_pt)) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_array_init(&cmcf->phases[NGX_STREAM_LOG_PHASE].handlers, + cf->pool, 1, sizeof(ngx_stream_handler_pt)) + != NGX_OK) + { + return NGX_ERROR; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_init_phase_handlers(ngx_conf_t *cf, + ngx_stream_core_main_conf_t *cmcf) +{ + ngx_int_t j; + ngx_uint_t i, n; + ngx_stream_handler_pt *h; + ngx_stream_phase_handler_t *ph; + ngx_stream_phase_handler_pt checker; + + n = 1 /* content phase */; + + for (i = 0; i < NGX_STREAM_LOG_PHASE; i++) { + n += cmcf->phases[i].handlers.nelts; + } + + ph = ngx_pcalloc(cf->pool, + n * sizeof(ngx_stream_phase_handler_t) + sizeof(void *)); + if (ph == NULL) { + return NGX_ERROR; + } + + cmcf->phase_engine.handlers = ph; + n = 0; + + for (i = 0; i < NGX_STREAM_LOG_PHASE; i++) { + h = cmcf->phases[i].handlers.elts; + + switch (i) { + + case NGX_STREAM_PREREAD_PHASE: + checker = ngx_stream_core_preread_phase; + break; + + case NGX_STREAM_CONTENT_PHASE: + ph->checker = ngx_stream_core_content_phase; + n++; + ph++; + + continue; + + default: + checker = ngx_stream_core_generic_phase; + } + + n += cmcf->phases[i].handlers.nelts; + + for (j = cmcf->phases[i].handlers.nelts - 1; j >= 0; j--) { + ph->checker = checker; + ph->handler = h[j]; + ph->next = n; + ph++; + } + } + + return NGX_OK; +} + + +ngx_int_t +ngx_stream_add_listen(ngx_conf_t *cf, ngx_stream_core_srv_conf_t *cscf, + ngx_stream_listen_opt_t *lsopt) +{ + in_port_t p; + ngx_uint_t i; + struct sockaddr *sa; + ngx_stream_conf_port_t *port; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + if (cmcf->ports == NULL) { + cmcf->ports = ngx_array_create(cf->temp_pool, 2, + sizeof(ngx_stream_conf_port_t)); + if (cmcf->ports == NULL) { + return NGX_ERROR; + } + } + + sa = lsopt->sockaddr; + p = ngx_inet_get_port(sa); + + port = cmcf->ports->elts; + for (i = 0; i < cmcf->ports->nelts; i++) { + + if (p != port[i].port + || lsopt->type != port[i].type + || sa->sa_family != port[i].family) + { + continue; + } + + /* a port is already in the port list */ + + return ngx_stream_add_addresses(cf, cscf, &port[i], lsopt); + } + + /* add a port to the port list */ + + port = ngx_array_push(cmcf->ports); + if (port == NULL) { + return NGX_ERROR; + } + + port->family = sa->sa_family; + port->type = lsopt->type; + port->port = p; + port->addrs.elts = NULL; + + return ngx_stream_add_address(cf, cscf, port, lsopt); +} + + +static ngx_int_t +ngx_stream_add_addresses(ngx_conf_t *cf, ngx_stream_core_srv_conf_t *cscf, + ngx_stream_conf_port_t *port, ngx_stream_listen_opt_t *lsopt) +{ + ngx_uint_t i, default_server, proxy_protocol, + protocols, protocols_prev; + ngx_stream_conf_addr_t *addr; +#if (NGX_STREAM_SSL) + ngx_uint_t ssl; +#endif + + /* + * we cannot compare whole sockaddr struct's as kernel + * may fill some fields in inherited sockaddr struct's + */ + + addr = port->addrs.elts; + + for (i = 0; i < port->addrs.nelts; i++) { + + if (ngx_cmp_sockaddr(lsopt->sockaddr, lsopt->socklen, + addr[i].opt.sockaddr, + addr[i].opt.socklen, 0) + != NGX_OK) + { + continue; + } + + /* the address is already in the address list */ + + if (ngx_stream_add_server(cf, cscf, &addr[i]) != NGX_OK) { + return NGX_ERROR; + } + + /* preserve default_server bit during listen options overwriting */ + default_server = addr[i].opt.default_server; + + proxy_protocol = lsopt->proxy_protocol || addr[i].opt.proxy_protocol; + protocols = lsopt->proxy_protocol; + protocols_prev = addr[i].opt.proxy_protocol; + +#if (NGX_STREAM_SSL) + ssl = lsopt->ssl || addr[i].opt.ssl; + protocols |= lsopt->ssl << 1; + protocols_prev |= addr[i].opt.ssl << 1; +#endif + + if (lsopt->set) { + + if (addr[i].opt.set) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate listen options for %V", + &addr[i].opt.addr_text); + return NGX_ERROR; + } + + addr[i].opt = *lsopt; + } + + /* check the duplicate "default" server for this address:port */ + + if (lsopt->default_server) { + + if (default_server) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "a duplicate default server for %V", + &addr[i].opt.addr_text); + return NGX_ERROR; + } + + default_server = 1; + addr[i].default_server = cscf; + } + + /* check for conflicting protocol options */ + + if ((protocols | protocols_prev) != protocols_prev) { + + /* options added */ + + if ((addr[i].opt.set && !lsopt->set) + || addr[i].protocols_changed + || (protocols | protocols_prev) != protocols) + { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "protocol options redefined for %V", + &addr[i].opt.addr_text); + } + + addr[i].protocols = protocols_prev; + addr[i].protocols_set = 1; + addr[i].protocols_changed = 1; + + } else if ((protocols_prev | protocols) != protocols) { + + /* options removed */ + + if (lsopt->set + || (addr[i].protocols_set && protocols != addr[i].protocols)) + { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "protocol options redefined for %V", + &addr[i].opt.addr_text); + } + + addr[i].protocols = protocols; + addr[i].protocols_set = 1; + addr[i].protocols_changed = 1; + + } else { + + /* the same options */ + + if ((lsopt->set && addr[i].protocols_changed) + || (addr[i].protocols_set && protocols != addr[i].protocols)) + { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "protocol options redefined for %V", + &addr[i].opt.addr_text); + } + + addr[i].protocols = protocols; + addr[i].protocols_set = 1; + } + + addr[i].opt.default_server = default_server; + addr[i].opt.proxy_protocol = proxy_protocol; +#if (NGX_STREAM_SSL) + addr[i].opt.ssl = ssl; +#endif + + return NGX_OK; + } + + /* add the address to the addresses list that bound to this port */ + + return ngx_stream_add_address(cf, cscf, port, lsopt); +} + + +/* + * add the server address, the server names and the server core module + * configurations to the port list + */ + +static ngx_int_t +ngx_stream_add_address(ngx_conf_t *cf, ngx_stream_core_srv_conf_t *cscf, + ngx_stream_conf_port_t *port, ngx_stream_listen_opt_t *lsopt) +{ + ngx_stream_conf_addr_t *addr; + + if (port->addrs.elts == NULL) { + if (ngx_array_init(&port->addrs, cf->temp_pool, 4, + sizeof(ngx_stream_conf_addr_t)) + != NGX_OK) + { + return NGX_ERROR; + } + } + + addr = ngx_array_push(&port->addrs); + if (addr == NULL) { + return NGX_ERROR; + } + + addr->opt = *lsopt; + addr->protocols = 0; + addr->protocols_set = 0; + addr->protocols_changed = 0; + addr->hash.buckets = NULL; + addr->hash.size = 0; + addr->wc_head = NULL; + addr->wc_tail = NULL; +#if (NGX_PCRE) + addr->nregex = 0; + addr->regex = NULL; +#endif + addr->default_server = cscf; + addr->servers.elts = NULL; + + return ngx_stream_add_server(cf, cscf, addr); +} + + +/* add the server core module configuration to the address:port */ + +static ngx_int_t +ngx_stream_add_server(ngx_conf_t *cf, ngx_stream_core_srv_conf_t *cscf, + ngx_stream_conf_addr_t *addr) +{ + ngx_uint_t i; + ngx_stream_core_srv_conf_t **server; + + if (addr->servers.elts == NULL) { + if (ngx_array_init(&addr->servers, cf->temp_pool, 4, + sizeof(ngx_stream_core_srv_conf_t *)) + != NGX_OK) + { + return NGX_ERROR; + } + + } else { + server = addr->servers.elts; + for (i = 0; i < addr->servers.nelts; i++) { + if (server[i] == cscf) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "a duplicate listen %V", + &addr->opt.addr_text); + return NGX_ERROR; + } + } + } + + server = ngx_array_push(&addr->servers); + if (server == NULL) { + return NGX_ERROR; + } + + *server = cscf; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_stream_core_main_conf_t *cmcf, + ngx_array_t *ports) +{ + ngx_uint_t p, a; + ngx_stream_conf_port_t *port; + ngx_stream_conf_addr_t *addr; + + if (ports == NULL) { + return NGX_OK; + } + + port = ports->elts; + for (p = 0; p < ports->nelts; p++) { + + ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts, + sizeof(ngx_stream_conf_addr_t), ngx_stream_cmp_conf_addrs); + + /* + * check whether all name-based servers have the same + * configuration as a default server for given address:port + */ + + addr = port[p].addrs.elts; + for (a = 0; a < port[p].addrs.nelts; a++) { + + if (addr[a].servers.nelts > 1 +#if (NGX_PCRE) + || addr[a].default_server->captures +#endif + ) + { + if (ngx_stream_server_names(cf, cmcf, &addr[a]) != NGX_OK) { + return NGX_ERROR; + } + } + } + + if (ngx_stream_init_listening(cf, &port[p]) != NGX_OK) { + return NGX_ERROR; + } + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_server_names(ngx_conf_t *cf, ngx_stream_core_main_conf_t *cmcf, + ngx_stream_conf_addr_t *addr) +{ + ngx_int_t rc; + ngx_uint_t n, s; + ngx_hash_init_t hash; + ngx_hash_keys_arrays_t ha; + ngx_stream_server_name_t *name; + ngx_stream_core_srv_conf_t **cscfp; +#if (NGX_PCRE) + ngx_uint_t regex, i; + + regex = 0; +#endif + + ngx_memzero(&ha, sizeof(ngx_hash_keys_arrays_t)); + + ha.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log); + if (ha.temp_pool == NULL) { + return NGX_ERROR; + } + + ha.pool = cf->pool; + + if (ngx_hash_keys_array_init(&ha, NGX_HASH_LARGE) != NGX_OK) { + goto failed; + } + + cscfp = addr->servers.elts; + + for (s = 0; s < addr->servers.nelts; s++) { + + name = cscfp[s]->server_names.elts; + + for (n = 0; n < cscfp[s]->server_names.nelts; n++) { + +#if (NGX_PCRE) + if (name[n].regex) { + regex++; + continue; + } +#endif + + rc = ngx_hash_add_key(&ha, &name[n].name, name[n].server, + NGX_HASH_WILDCARD_KEY); + + if (rc == NGX_ERROR) { + goto failed; + } + + if (rc == NGX_DECLINED) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "invalid server name or wildcard \"%V\" on %V", + &name[n].name, &addr->opt.addr_text); + goto failed; + } + + if (rc == NGX_BUSY) { + ngx_log_error(NGX_LOG_WARN, cf->log, 0, + "conflicting server name \"%V\" on %V, ignored", + &name[n].name, &addr->opt.addr_text); + } + } + } + + hash.key = ngx_hash_key_lc; + hash.max_size = cmcf->server_names_hash_max_size; + hash.bucket_size = cmcf->server_names_hash_bucket_size; + hash.name = "server_names_hash"; + hash.pool = cf->pool; + + if (ha.keys.nelts) { + hash.hash = &addr->hash; + hash.temp_pool = NULL; + + if (ngx_hash_init(&hash, ha.keys.elts, ha.keys.nelts) != NGX_OK) { + goto failed; + } + } + + if (ha.dns_wc_head.nelts) { + + ngx_qsort(ha.dns_wc_head.elts, (size_t) ha.dns_wc_head.nelts, + sizeof(ngx_hash_key_t), ngx_stream_cmp_dns_wildcards); + + hash.hash = NULL; + hash.temp_pool = ha.temp_pool; + + if (ngx_hash_wildcard_init(&hash, ha.dns_wc_head.elts, + ha.dns_wc_head.nelts) + != NGX_OK) + { + goto failed; + } + + addr->wc_head = (ngx_hash_wildcard_t *) hash.hash; + } + + if (ha.dns_wc_tail.nelts) { + + ngx_qsort(ha.dns_wc_tail.elts, (size_t) ha.dns_wc_tail.nelts, + sizeof(ngx_hash_key_t), ngx_stream_cmp_dns_wildcards); + + hash.hash = NULL; + hash.temp_pool = ha.temp_pool; + + if (ngx_hash_wildcard_init(&hash, ha.dns_wc_tail.elts, + ha.dns_wc_tail.nelts) + != NGX_OK) + { + goto failed; + } + + addr->wc_tail = (ngx_hash_wildcard_t *) hash.hash; + } + + ngx_destroy_pool(ha.temp_pool); + +#if (NGX_PCRE) + + if (regex == 0) { + return NGX_OK; + } + + addr->nregex = regex; + addr->regex = ngx_palloc(cf->pool, + regex * sizeof(ngx_stream_server_name_t)); + if (addr->regex == NULL) { + return NGX_ERROR; + } + + i = 0; + + for (s = 0; s < addr->servers.nelts; s++) { + + name = cscfp[s]->server_names.elts; + + for (n = 0; n < cscfp[s]->server_names.nelts; n++) { + if (name[n].regex) { + addr->regex[i++] = name[n]; + } + } + } + +#endif + + return NGX_OK; + +failed: + + ngx_destroy_pool(ha.temp_pool); + + return NGX_ERROR; +} + + +static ngx_int_t +ngx_stream_cmp_conf_addrs(const void *one, const void *two) +{ + ngx_stream_conf_addr_t *first, *second; + + first = (ngx_stream_conf_addr_t *) one; + second = (ngx_stream_conf_addr_t *) two; + + if (first->opt.wildcard) { + /* a wildcard address must be the last resort, shift it to the end */ + return 1; + } + + if (second->opt.wildcard) { + /* a wildcard address must be the last resort, shift it to the end */ + return -1; + } + + if (first->opt.bind && !second->opt.bind) { + /* shift explicit bind()ed addresses to the start */ + return -1; + } + + if (!first->opt.bind && second->opt.bind) { + /* shift explicit bind()ed addresses to the start */ + return 1; + } + + /* do not sort by default */ + + return 0; +} + + +static int ngx_libc_cdecl +ngx_stream_cmp_dns_wildcards(const void *one, const void *two) +{ + ngx_hash_key_t *first, *second; + + first = (ngx_hash_key_t *) one; + second = (ngx_hash_key_t *) two; + + return ngx_dns_strcmp(first->key.data, second->key.data); +} + + +static ngx_int_t +ngx_stream_init_listening(ngx_conf_t *cf, ngx_stream_conf_port_t *port) +{ + ngx_uint_t i, last, bind_wildcard; + ngx_listening_t *ls; + ngx_stream_port_t *stport; + ngx_stream_conf_addr_t *addr; + + addr = port->addrs.elts; + last = port->addrs.nelts; + + /* + * If there is a binding to an "*:port" then we need to bind() to + * the "*:port" only and ignore other implicit bindings. The bindings + * have been already sorted: explicit bindings are on the start, then + * implicit bindings go, and wildcard binding is in the end. + */ + + if (addr[last - 1].opt.wildcard) { + addr[last - 1].opt.bind = 1; + bind_wildcard = 1; + + } else { + bind_wildcard = 0; + } + + i = 0; + + while (i < last) { + + if (bind_wildcard && !addr[i].opt.bind) { + i++; + continue; + } + + ls = ngx_stream_add_listening(cf, &addr[i]); + if (ls == NULL) { + return NGX_ERROR; + } + + stport = ngx_pcalloc(cf->pool, sizeof(ngx_stream_port_t)); + if (stport == NULL) { + return NGX_ERROR; + } + + ls->servers = stport; + + stport->naddrs = i + 1; + + switch (ls->sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + if (ngx_stream_add_addrs6(cf, stport, addr) != NGX_OK) { + return NGX_ERROR; + } + break; +#endif + default: /* AF_INET */ + if (ngx_stream_add_addrs(cf, stport, addr) != NGX_OK) { + return NGX_ERROR; + } + break; + } + + addr++; + last--; + } + + return NGX_OK; +} + + +static ngx_listening_t * +ngx_stream_add_listening(ngx_conf_t *cf, ngx_stream_conf_addr_t *addr) +{ + ngx_listening_t *ls; + ngx_stream_core_srv_conf_t *cscf; + + ls = ngx_create_listening(cf, addr->opt.sockaddr, addr->opt.socklen); + if (ls == NULL) { + return NULL; + } + + ls->addr_ntop = 1; + + ls->handler = ngx_stream_init_connection; + + ls->pool_size = 256; + + cscf = addr->default_server; + + ls->logp = cscf->error_log; + ls->log.data = &ls->addr_text; + ls->log.handler = ngx_accept_log_error; + + ls->type = addr->opt.type; + ls->protocol = addr->opt.protocol; + ls->backlog = addr->opt.backlog; + ls->rcvbuf = addr->opt.rcvbuf; + ls->sndbuf = addr->opt.sndbuf; + + ls->keepalive = addr->opt.so_keepalive; +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + ls->keepidle = addr->opt.tcp_keepidle; + ls->keepintvl = addr->opt.tcp_keepintvl; + ls->keepcnt = addr->opt.tcp_keepcnt; +#endif + +#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) + ls->accept_filter = addr->opt.accept_filter; +#endif + +#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) + ls->deferred_accept = addr->opt.deferred_accept; +#endif + +#if (NGX_HAVE_INET6) + ls->ipv6only = addr->opt.ipv6only; +#endif + +#if (NGX_HAVE_SETFIB) + ls->setfib = addr->opt.setfib; +#endif + +#if (NGX_HAVE_TCP_FASTOPEN) + ls->fastopen = addr->opt.fastopen; +#endif + +#if (NGX_HAVE_REUSEPORT) + ls->reuseport = addr->opt.reuseport; +#endif + + ls->wildcard = addr->opt.wildcard; + + return ls; +} + + +static ngx_int_t +ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *stport, + ngx_stream_conf_addr_t *addr) +{ + ngx_uint_t i; + struct sockaddr_in *sin; + ngx_stream_in_addr_t *addrs; + ngx_stream_virtual_names_t *vn; + + stport->addrs = ngx_pcalloc(cf->pool, + stport->naddrs * sizeof(ngx_stream_in_addr_t)); + if (stport->addrs == NULL) { + return NGX_ERROR; + } + + addrs = stport->addrs; + + for (i = 0; i < stport->naddrs; i++) { + + sin = (struct sockaddr_in *) addr[i].opt.sockaddr; + addrs[i].addr = sin->sin_addr.s_addr; + addrs[i].conf.default_server = addr[i].default_server; +#if (NGX_STREAM_SSL) + addrs[i].conf.ssl = addr[i].opt.ssl; +#endif + addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; + + if (addr[i].hash.buckets == NULL + && (addr[i].wc_head == NULL + || addr[i].wc_head->hash.buckets == NULL) + && (addr[i].wc_tail == NULL + || addr[i].wc_tail->hash.buckets == NULL) +#if (NGX_PCRE) + && addr[i].nregex == 0 +#endif + ) + { + continue; + } + + vn = ngx_palloc(cf->pool, sizeof(ngx_stream_virtual_names_t)); + if (vn == NULL) { + return NGX_ERROR; + } + + addrs[i].conf.virtual_names = vn; + + vn->names.hash = addr[i].hash; + vn->names.wc_head = addr[i].wc_head; + vn->names.wc_tail = addr[i].wc_tail; +#if (NGX_PCRE) + vn->nregex = addr[i].nregex; + vn->regex = addr[i].regex; +#endif + } + + return NGX_OK; +} + + +#if (NGX_HAVE_INET6) + +static ngx_int_t +ngx_stream_add_addrs6(ngx_conf_t *cf, ngx_stream_port_t *stport, + ngx_stream_conf_addr_t *addr) +{ + ngx_uint_t i; + struct sockaddr_in6 *sin6; + ngx_stream_in6_addr_t *addrs6; + ngx_stream_virtual_names_t *vn; + + stport->addrs = ngx_pcalloc(cf->pool, + stport->naddrs * sizeof(ngx_stream_in6_addr_t)); + if (stport->addrs == NULL) { + return NGX_ERROR; + } + + addrs6 = stport->addrs; + + for (i = 0; i < stport->naddrs; i++) { + + sin6 = (struct sockaddr_in6 *) addr[i].opt.sockaddr; + addrs6[i].addr6 = sin6->sin6_addr; + addrs6[i].conf.default_server = addr[i].default_server; +#if (NGX_STREAM_SSL) + addrs6[i].conf.ssl = addr[i].opt.ssl; +#endif + addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; + + if (addr[i].hash.buckets == NULL + && (addr[i].wc_head == NULL + || addr[i].wc_head->hash.buckets == NULL) + && (addr[i].wc_tail == NULL + || addr[i].wc_tail->hash.buckets == NULL) +#if (NGX_PCRE) + && addr[i].nregex == 0 +#endif + ) + { + continue; + } + + vn = ngx_palloc(cf->pool, sizeof(ngx_stream_virtual_names_t)); + if (vn == NULL) { + return NGX_ERROR; + } + + addrs6[i].conf.virtual_names = vn; + + vn->names.hash = addr[i].hash; + vn->names.wc_head = addr[i].wc_head; + vn->names.wc_tail = addr[i].wc_tail; +#if (NGX_PCRE) + vn->nregex = addr[i].nregex; + vn->regex = addr[i].regex; +#endif + } + + return NGX_OK; +} + +#endif diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream.h b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream.h new file mode 100644 index 000000000..9bc689e99 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream.h @@ -0,0 +1,384 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_STREAM_H_INCLUDED_ +#define _NGX_STREAM_H_INCLUDED_ + + +#include +#include + +#if (NGX_STREAM_SSL) +#include +#endif + + +typedef struct ngx_stream_session_s ngx_stream_session_t; + + +#include +#include +#include +#include + + +#define NGX_STREAM_OK 200 +#define NGX_STREAM_BAD_REQUEST 400 +#define NGX_STREAM_FORBIDDEN 403 +#define NGX_STREAM_INTERNAL_SERVER_ERROR 500 +#define NGX_STREAM_BAD_GATEWAY 502 +#define NGX_STREAM_SERVICE_UNAVAILABLE 503 + + +typedef struct { + void **main_conf; + void **srv_conf; +} ngx_stream_conf_ctx_t; + + +typedef struct { + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_str_t addr_text; + + unsigned set:1; + unsigned default_server:1; + unsigned bind:1; + unsigned wildcard:1; + unsigned ssl:1; +#if (NGX_HAVE_INET6) + unsigned ipv6only:1; +#endif + unsigned deferred_accept:1; + unsigned reuseport:1; + unsigned so_keepalive:2; + unsigned proxy_protocol:1; + + int backlog; + int rcvbuf; + int sndbuf; + int type; + int protocol; +#if (NGX_HAVE_SETFIB) + int setfib; +#endif +#if (NGX_HAVE_TCP_FASTOPEN) + int fastopen; +#endif +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + int tcp_keepidle; + int tcp_keepintvl; + int tcp_keepcnt; +#endif + +#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) + char *accept_filter; +#endif +} ngx_stream_listen_opt_t; + + +typedef enum { + NGX_STREAM_POST_ACCEPT_PHASE = 0, + NGX_STREAM_PREACCESS_PHASE, + NGX_STREAM_ACCESS_PHASE, + NGX_STREAM_SSL_PHASE, + NGX_STREAM_PREREAD_PHASE, + NGX_STREAM_CONTENT_PHASE, + NGX_STREAM_LOG_PHASE +} ngx_stream_phases; + + +typedef struct ngx_stream_phase_handler_s ngx_stream_phase_handler_t; + +typedef ngx_int_t (*ngx_stream_phase_handler_pt)(ngx_stream_session_t *s, + ngx_stream_phase_handler_t *ph); +typedef ngx_int_t (*ngx_stream_handler_pt)(ngx_stream_session_t *s); +typedef void (*ngx_stream_content_handler_pt)(ngx_stream_session_t *s); + + +struct ngx_stream_phase_handler_s { + ngx_stream_phase_handler_pt checker; + ngx_stream_handler_pt handler; + ngx_uint_t next; +}; + + +typedef struct { + ngx_stream_phase_handler_t *handlers; +} ngx_stream_phase_engine_t; + + +typedef struct { + ngx_array_t handlers; +} ngx_stream_phase_t; + + +typedef struct { + ngx_array_t servers; /* ngx_stream_core_srv_conf_t */ + + ngx_stream_phase_engine_t phase_engine; + + ngx_hash_t variables_hash; + + ngx_array_t variables; /* ngx_stream_variable_t */ + ngx_array_t prefix_variables; /* ngx_stream_variable_t */ + ngx_uint_t ncaptures; + + ngx_uint_t server_names_hash_max_size; + ngx_uint_t server_names_hash_bucket_size; + + ngx_uint_t variables_hash_max_size; + ngx_uint_t variables_hash_bucket_size; + + ngx_hash_keys_arrays_t *variables_keys; + + ngx_array_t *ports; + + ngx_stream_phase_t phases[NGX_STREAM_LOG_PHASE + 1]; +} ngx_stream_core_main_conf_t; + + +typedef struct { + /* array of the ngx_stream_server_name_t, "server_name" directive */ + ngx_array_t server_names; + + ngx_stream_content_handler_pt handler; + + ngx_stream_conf_ctx_t *ctx; + + u_char *file_name; + ngx_uint_t line; + + ngx_str_t server_name; + + ngx_flag_t tcp_nodelay; + size_t preread_buffer_size; + ngx_msec_t preread_timeout; + + ngx_log_t *error_log; + + ngx_msec_t resolver_timeout; + ngx_resolver_t *resolver; + + ngx_msec_t proxy_protocol_timeout; + + unsigned listen:1; +#if (NGX_PCRE) + unsigned captures:1; +#endif +} ngx_stream_core_srv_conf_t; + + +/* list of structures to find core_srv_conf quickly at run time */ + + +typedef struct { +#if (NGX_PCRE) + ngx_stream_regex_t *regex; +#endif + ngx_stream_core_srv_conf_t *server; /* virtual name server conf */ + ngx_str_t name; +} ngx_stream_server_name_t; + + +typedef struct { + ngx_hash_combined_t names; + + ngx_uint_t nregex; + ngx_stream_server_name_t *regex; +} ngx_stream_virtual_names_t; + + +typedef struct { + /* the default server configuration for this address:port */ + ngx_stream_core_srv_conf_t *default_server; + + ngx_stream_virtual_names_t *virtual_names; + + unsigned ssl:1; + unsigned proxy_protocol:1; +} ngx_stream_addr_conf_t; + + +typedef struct { + in_addr_t addr; + ngx_stream_addr_conf_t conf; +} ngx_stream_in_addr_t; + + +#if (NGX_HAVE_INET6) + +typedef struct { + struct in6_addr addr6; + ngx_stream_addr_conf_t conf; +} ngx_stream_in6_addr_t; + +#endif + + +typedef struct { + /* ngx_stream_in_addr_t or ngx_stream_in6_addr_t */ + void *addrs; + ngx_uint_t naddrs; +} ngx_stream_port_t; + + +typedef struct { + int family; + int type; + in_port_t port; + ngx_array_t addrs; /* array of ngx_stream_conf_addr_t */ +} ngx_stream_conf_port_t; + + +typedef struct { + ngx_stream_listen_opt_t opt; + + unsigned protocols:3; + unsigned protocols_set:1; + unsigned protocols_changed:1; + + ngx_hash_t hash; + ngx_hash_wildcard_t *wc_head; + ngx_hash_wildcard_t *wc_tail; + +#if (NGX_PCRE) + ngx_uint_t nregex; + ngx_stream_server_name_t *regex; +#endif + + /* the default server configuration for this address:port */ + ngx_stream_core_srv_conf_t *default_server; + ngx_array_t servers; + /* array of ngx_stream_core_srv_conf_t */ +} ngx_stream_conf_addr_t; + + +struct ngx_stream_session_s { + uint32_t signature; /* "STRM" */ + + ngx_connection_t *connection; + + off_t received; + time_t start_sec; + ngx_msec_t start_msec; + + ngx_log_handler_pt log_handler; + + void **ctx; + void **main_conf; + void **srv_conf; + + ngx_stream_virtual_names_t *virtual_names; + + ngx_stream_upstream_t *upstream; + ngx_array_t *upstream_states; + /* of ngx_stream_upstream_state_t */ + ngx_stream_variable_value_t *variables; + +#if (NGX_PCRE) + ngx_uint_t ncaptures; + int *captures; + u_char *captures_data; +#endif + + ngx_int_t phase_handler; + ngx_uint_t status; + + unsigned ssl:1; + + unsigned stat_processing:1; + + unsigned health_check:1; + + unsigned limit_conn_status:2; +}; + + +typedef struct { + ngx_int_t (*preconfiguration)(ngx_conf_t *cf); + ngx_int_t (*postconfiguration)(ngx_conf_t *cf); + + void *(*create_main_conf)(ngx_conf_t *cf); + char *(*init_main_conf)(ngx_conf_t *cf, void *conf); + + void *(*create_srv_conf)(ngx_conf_t *cf); + char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, + void *conf); +} ngx_stream_module_t; + + +#define NGX_STREAM_MODULE 0x4d525453 /* "STRM" */ + +#define NGX_STREAM_MAIN_CONF 0x02000000 +#define NGX_STREAM_SRV_CONF 0x04000000 +#define NGX_STREAM_UPS_CONF 0x08000000 + + +#define NGX_STREAM_MAIN_CONF_OFFSET offsetof(ngx_stream_conf_ctx_t, main_conf) +#define NGX_STREAM_SRV_CONF_OFFSET offsetof(ngx_stream_conf_ctx_t, srv_conf) + + +#define ngx_stream_get_module_ctx(s, module) (s)->ctx[module.ctx_index] +#define ngx_stream_set_ctx(s, c, module) s->ctx[module.ctx_index] = c; +#define ngx_stream_delete_ctx(s, module) s->ctx[module.ctx_index] = NULL; + + +#define ngx_stream_get_module_main_conf(s, module) \ + (s)->main_conf[module.ctx_index] +#define ngx_stream_get_module_srv_conf(s, module) \ + (s)->srv_conf[module.ctx_index] + +#define ngx_stream_conf_get_module_main_conf(cf, module) \ + ((ngx_stream_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index] +#define ngx_stream_conf_get_module_srv_conf(cf, module) \ + ((ngx_stream_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index] + +#define ngx_stream_cycle_get_module_main_conf(cycle, module) \ + (cycle->conf_ctx[ngx_stream_module.index] ? \ + ((ngx_stream_conf_ctx_t *) cycle->conf_ctx[ngx_stream_module.index]) \ + ->main_conf[module.ctx_index]: \ + NULL) + + +#define NGX_STREAM_WRITE_BUFFERED 0x10 + + +ngx_int_t ngx_stream_add_listen(ngx_conf_t *cf, + ngx_stream_core_srv_conf_t *cscf, ngx_stream_listen_opt_t *lsopt); + +void ngx_stream_core_run_phases(ngx_stream_session_t *s); +ngx_int_t ngx_stream_core_generic_phase(ngx_stream_session_t *s, + ngx_stream_phase_handler_t *ph); +ngx_int_t ngx_stream_core_preread_phase(ngx_stream_session_t *s, + ngx_stream_phase_handler_t *ph); +ngx_int_t ngx_stream_core_content_phase(ngx_stream_session_t *s, + ngx_stream_phase_handler_t *ph); + +ngx_int_t ngx_stream_validate_host(ngx_str_t *host, ngx_pool_t *pool, + ngx_uint_t alloc); +ngx_int_t ngx_stream_find_virtual_server(ngx_stream_session_t *s, + ngx_str_t *host, ngx_stream_core_srv_conf_t **cscfp); + +void ngx_stream_init_connection(ngx_connection_t *c); +void ngx_stream_session_handler(ngx_event_t *rev); +void ngx_stream_finalize_session(ngx_stream_session_t *s, ngx_uint_t rc); + + +extern ngx_module_t ngx_stream_module; +extern ngx_uint_t ngx_stream_max_module; +extern ngx_module_t ngx_stream_core_module; + + +typedef ngx_int_t (*ngx_stream_filter_pt)(ngx_stream_session_t *s, + ngx_chain_t *chain, ngx_uint_t from_upstream); + + +extern ngx_stream_filter_pt ngx_stream_top_filter; + + +#endif /* _NGX_STREAM_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_access_module.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_access_module.c new file mode 100644 index 000000000..070c22614 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_access_module.c @@ -0,0 +1,453 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + in_addr_t mask; + in_addr_t addr; + ngx_uint_t deny; /* unsigned deny:1; */ +} ngx_stream_access_rule_t; + +#if (NGX_HAVE_INET6) + +typedef struct { + struct in6_addr addr; + struct in6_addr mask; + ngx_uint_t deny; /* unsigned deny:1; */ +} ngx_stream_access_rule6_t; + +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + +typedef struct { + ngx_uint_t deny; /* unsigned deny:1; */ +} ngx_stream_access_rule_un_t; + +#endif + +typedef struct { + ngx_array_t *rules; /* array of ngx_stream_access_rule_t */ +#if (NGX_HAVE_INET6) + ngx_array_t *rules6; /* array of ngx_stream_access_rule6_t */ +#endif +#if (NGX_HAVE_UNIX_DOMAIN) + ngx_array_t *rules_un; /* array of ngx_stream_access_rule_un_t */ +#endif +} ngx_stream_access_srv_conf_t; + + +static ngx_int_t ngx_stream_access_handler(ngx_stream_session_t *s); +static ngx_int_t ngx_stream_access_inet(ngx_stream_session_t *s, + ngx_stream_access_srv_conf_t *ascf, in_addr_t addr); +#if (NGX_HAVE_INET6) +static ngx_int_t ngx_stream_access_inet6(ngx_stream_session_t *s, + ngx_stream_access_srv_conf_t *ascf, u_char *p); +#endif +#if (NGX_HAVE_UNIX_DOMAIN) +static ngx_int_t ngx_stream_access_unix(ngx_stream_session_t *s, + ngx_stream_access_srv_conf_t *ascf); +#endif +static ngx_int_t ngx_stream_access_found(ngx_stream_session_t *s, + ngx_uint_t deny); +static char *ngx_stream_access_rule(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static void *ngx_stream_access_create_srv_conf(ngx_conf_t *cf); +static char *ngx_stream_access_merge_srv_conf(ngx_conf_t *cf, + void *parent, void *child); +static ngx_int_t ngx_stream_access_init(ngx_conf_t *cf); + + +static ngx_command_t ngx_stream_access_commands[] = { + + { ngx_string("allow"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_access_rule, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("deny"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_access_rule, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + + +static ngx_stream_module_t ngx_stream_access_module_ctx = { + NULL, /* preconfiguration */ + ngx_stream_access_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_stream_access_create_srv_conf, /* create server configuration */ + ngx_stream_access_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_access_module = { + NGX_MODULE_V1, + &ngx_stream_access_module_ctx, /* module context */ + ngx_stream_access_commands, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_int_t +ngx_stream_access_handler(ngx_stream_session_t *s) +{ + struct sockaddr_in *sin; + ngx_stream_access_srv_conf_t *ascf; +#if (NGX_HAVE_INET6) + u_char *p; + in_addr_t addr; + struct sockaddr_in6 *sin6; +#endif + + ascf = ngx_stream_get_module_srv_conf(s, ngx_stream_access_module); + + switch (s->connection->sockaddr->sa_family) { + + case AF_INET: + if (ascf->rules) { + sin = (struct sockaddr_in *) s->connection->sockaddr; + return ngx_stream_access_inet(s, ascf, sin->sin_addr.s_addr); + } + break; + +#if (NGX_HAVE_INET6) + + case AF_INET6: + sin6 = (struct sockaddr_in6 *) s->connection->sockaddr; + p = sin6->sin6_addr.s6_addr; + + if (ascf->rules && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { + addr = (in_addr_t) p[12] << 24; + addr += p[13] << 16; + addr += p[14] << 8; + addr += p[15]; + return ngx_stream_access_inet(s, ascf, htonl(addr)); + } + + if (ascf->rules6) { + return ngx_stream_access_inet6(s, ascf, p); + } + + break; + +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + + case AF_UNIX: + if (ascf->rules_un) { + return ngx_stream_access_unix(s, ascf); + } + + break; + +#endif + } + + return NGX_DECLINED; +} + + +static ngx_int_t +ngx_stream_access_inet(ngx_stream_session_t *s, + ngx_stream_access_srv_conf_t *ascf, in_addr_t addr) +{ + ngx_uint_t i; + ngx_stream_access_rule_t *rule; + + rule = ascf->rules->elts; + for (i = 0; i < ascf->rules->nelts; i++) { + + ngx_log_debug3(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "access: %08XD %08XD %08XD", + addr, rule[i].mask, rule[i].addr); + + if ((addr & rule[i].mask) == rule[i].addr) { + return ngx_stream_access_found(s, rule[i].deny); + } + } + + return NGX_DECLINED; +} + + +#if (NGX_HAVE_INET6) + +static ngx_int_t +ngx_stream_access_inet6(ngx_stream_session_t *s, + ngx_stream_access_srv_conf_t *ascf, u_char *p) +{ + ngx_uint_t n; + ngx_uint_t i; + ngx_stream_access_rule6_t *rule6; + + rule6 = ascf->rules6->elts; + for (i = 0; i < ascf->rules6->nelts; i++) { + +#if (NGX_DEBUG) + { + size_t cl, ml, al; + u_char ct[NGX_INET6_ADDRSTRLEN]; + u_char mt[NGX_INET6_ADDRSTRLEN]; + u_char at[NGX_INET6_ADDRSTRLEN]; + + cl = ngx_inet6_ntop(p, ct, NGX_INET6_ADDRSTRLEN); + ml = ngx_inet6_ntop(rule6[i].mask.s6_addr, mt, NGX_INET6_ADDRSTRLEN); + al = ngx_inet6_ntop(rule6[i].addr.s6_addr, at, NGX_INET6_ADDRSTRLEN); + + ngx_log_debug6(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "access: %*s %*s %*s", cl, ct, ml, mt, al, at); + } +#endif + + for (n = 0; n < 16; n++) { + if ((p[n] & rule6[i].mask.s6_addr[n]) != rule6[i].addr.s6_addr[n]) { + goto next; + } + } + + return ngx_stream_access_found(s, rule6[i].deny); + + next: + continue; + } + + return NGX_DECLINED; +} + +#endif + + +#if (NGX_HAVE_UNIX_DOMAIN) + +static ngx_int_t +ngx_stream_access_unix(ngx_stream_session_t *s, + ngx_stream_access_srv_conf_t *ascf) +{ + ngx_uint_t i; + ngx_stream_access_rule_un_t *rule_un; + + rule_un = ascf->rules_un->elts; + for (i = 0; i < ascf->rules_un->nelts; i++) { + + /* TODO: check path */ + if (1) { + return ngx_stream_access_found(s, rule_un[i].deny); + } + } + + return NGX_DECLINED; +} + +#endif + + +static ngx_int_t +ngx_stream_access_found(ngx_stream_session_t *s, ngx_uint_t deny) +{ + if (deny) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "access forbidden by rule"); + return NGX_STREAM_FORBIDDEN; + } + + return NGX_OK; +} + + +static char * +ngx_stream_access_rule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_access_srv_conf_t *ascf = conf; + + ngx_int_t rc; + ngx_uint_t all; + ngx_str_t *value; + ngx_cidr_t cidr; + ngx_stream_access_rule_t *rule; +#if (NGX_HAVE_INET6) + ngx_stream_access_rule6_t *rule6; +#endif +#if (NGX_HAVE_UNIX_DOMAIN) + ngx_stream_access_rule_un_t *rule_un; +#endif + + all = 0; + ngx_memzero(&cidr, sizeof(ngx_cidr_t)); + + value = cf->args->elts; + + if (value[1].len == 3 && ngx_strcmp(value[1].data, "all") == 0) { + all = 1; + +#if (NGX_HAVE_UNIX_DOMAIN) + } else if (value[1].len == 5 && ngx_strcmp(value[1].data, "unix:") == 0) { + cidr.family = AF_UNIX; +#endif + + } else { + rc = ngx_ptocidr(&value[1], &cidr); + + if (rc == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[1]); + return NGX_CONF_ERROR; + } + + if (rc == NGX_DONE) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "low address bits of %V are meaningless", &value[1]); + } + } + + if (cidr.family == AF_INET || all) { + + if (ascf->rules == NULL) { + ascf->rules = ngx_array_create(cf->pool, 4, + sizeof(ngx_stream_access_rule_t)); + if (ascf->rules == NULL) { + return NGX_CONF_ERROR; + } + } + + rule = ngx_array_push(ascf->rules); + if (rule == NULL) { + return NGX_CONF_ERROR; + } + + rule->mask = cidr.u.in.mask; + rule->addr = cidr.u.in.addr; + rule->deny = (value[0].data[0] == 'd') ? 1 : 0; + } + +#if (NGX_HAVE_INET6) + if (cidr.family == AF_INET6 || all) { + + if (ascf->rules6 == NULL) { + ascf->rules6 = ngx_array_create(cf->pool, 4, + sizeof(ngx_stream_access_rule6_t)); + if (ascf->rules6 == NULL) { + return NGX_CONF_ERROR; + } + } + + rule6 = ngx_array_push(ascf->rules6); + if (rule6 == NULL) { + return NGX_CONF_ERROR; + } + + rule6->mask = cidr.u.in6.mask; + rule6->addr = cidr.u.in6.addr; + rule6->deny = (value[0].data[0] == 'd') ? 1 : 0; + } +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + if (cidr.family == AF_UNIX || all) { + + if (ascf->rules_un == NULL) { + ascf->rules_un = ngx_array_create(cf->pool, 1, + sizeof(ngx_stream_access_rule_un_t)); + if (ascf->rules_un == NULL) { + return NGX_CONF_ERROR; + } + } + + rule_un = ngx_array_push(ascf->rules_un); + if (rule_un == NULL) { + return NGX_CONF_ERROR; + } + + rule_un->deny = (value[0].data[0] == 'd') ? 1 : 0; + } +#endif + + return NGX_CONF_OK; +} + + +static void * +ngx_stream_access_create_srv_conf(ngx_conf_t *cf) +{ + ngx_stream_access_srv_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_access_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + return conf; +} + + +static char * +ngx_stream_access_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_stream_access_srv_conf_t *prev = parent; + ngx_stream_access_srv_conf_t *conf = child; + + if (conf->rules == NULL +#if (NGX_HAVE_INET6) + && conf->rules6 == NULL +#endif +#if (NGX_HAVE_UNIX_DOMAIN) + && conf->rules_un == NULL +#endif + ) { + conf->rules = prev->rules; +#if (NGX_HAVE_INET6) + conf->rules6 = prev->rules6; +#endif +#if (NGX_HAVE_UNIX_DOMAIN) + conf->rules_un = prev->rules_un; +#endif + } + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_stream_access_init(ngx_conf_t *cf) +{ + ngx_stream_handler_pt *h; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + h = ngx_array_push(&cmcf->phases[NGX_STREAM_ACCESS_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_stream_access_handler; + + return NGX_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_core_module.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_core_module.c new file mode 100644 index 000000000..6556c5a73 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_core_module.c @@ -0,0 +1,1523 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +static ngx_uint_t ngx_stream_preread_can_peek(ngx_connection_t *c); +static ngx_int_t ngx_stream_preread_peek(ngx_stream_session_t *s, + ngx_stream_phase_handler_t *ph); +static ngx_int_t ngx_stream_preread(ngx_stream_session_t *s, + ngx_stream_phase_handler_t *ph); +static ngx_int_t ngx_stream_core_preconfiguration(ngx_conf_t *cf); +static void *ngx_stream_core_create_main_conf(ngx_conf_t *cf); +static char *ngx_stream_core_init_main_conf(ngx_conf_t *cf, void *conf); +static void *ngx_stream_core_create_srv_conf(ngx_conf_t *cf); +static char *ngx_stream_core_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); +static char *ngx_stream_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_core_server(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_core_server_name(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + + +static ngx_command_t ngx_stream_core_commands[] = { + + { ngx_string("variables_hash_max_size"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_MAIN_CONF_OFFSET, + offsetof(ngx_stream_core_main_conf_t, variables_hash_max_size), + NULL }, + + { ngx_string("variables_hash_bucket_size"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_MAIN_CONF_OFFSET, + offsetof(ngx_stream_core_main_conf_t, variables_hash_bucket_size), + NULL }, + + { ngx_string("server_names_hash_max_size"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_MAIN_CONF_OFFSET, + offsetof(ngx_stream_core_main_conf_t, server_names_hash_max_size), + NULL }, + + { ngx_string("server_names_hash_bucket_size"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_MAIN_CONF_OFFSET, + offsetof(ngx_stream_core_main_conf_t, server_names_hash_bucket_size), + NULL }, + + { ngx_string("server"), + NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_stream_core_server, + 0, + 0, + NULL }, + + { ngx_string("listen"), + NGX_STREAM_SRV_CONF|NGX_CONF_1MORE, + ngx_stream_core_listen, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("server_name"), + NGX_STREAM_SRV_CONF|NGX_CONF_1MORE, + ngx_stream_core_server_name, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("error_log"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE, + ngx_stream_core_error_log, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("resolver"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE, + ngx_stream_core_resolver, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("resolver_timeout"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_core_srv_conf_t, resolver_timeout), + NULL }, + + { ngx_string("proxy_protocol_timeout"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_core_srv_conf_t, proxy_protocol_timeout), + NULL }, + + { ngx_string("tcp_nodelay"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_core_srv_conf_t, tcp_nodelay), + NULL }, + + { ngx_string("preread_buffer_size"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_core_srv_conf_t, preread_buffer_size), + NULL }, + + { ngx_string("preread_timeout"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_core_srv_conf_t, preread_timeout), + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_core_module_ctx = { + ngx_stream_core_preconfiguration, /* preconfiguration */ + NULL, /* postconfiguration */ + + ngx_stream_core_create_main_conf, /* create main configuration */ + ngx_stream_core_init_main_conf, /* init main configuration */ + + ngx_stream_core_create_srv_conf, /* create server configuration */ + ngx_stream_core_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_core_module = { + NGX_MODULE_V1, + &ngx_stream_core_module_ctx, /* module context */ + ngx_stream_core_commands, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +void +ngx_stream_core_run_phases(ngx_stream_session_t *s) +{ + ngx_int_t rc; + ngx_stream_phase_handler_t *ph; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); + + ph = cmcf->phase_engine.handlers; + + while (ph[s->phase_handler].checker) { + + rc = ph[s->phase_handler].checker(s, &ph[s->phase_handler]); + + if (rc == NGX_OK) { + return; + } + } +} + + +ngx_int_t +ngx_stream_core_generic_phase(ngx_stream_session_t *s, + ngx_stream_phase_handler_t *ph) +{ + ngx_int_t rc; + + /* + * generic phase checker, + * used by all phases, except for preread and content + */ + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "generic phase: %ui", s->phase_handler); + + rc = ph->handler(s); + + if (rc == NGX_OK) { + s->phase_handler = ph->next; + return NGX_AGAIN; + } + + if (rc == NGX_DECLINED) { + s->phase_handler++; + return NGX_AGAIN; + } + + if (rc == NGX_AGAIN || rc == NGX_DONE) { + return NGX_OK; + } + + if (rc == NGX_ERROR) { + rc = NGX_STREAM_INTERNAL_SERVER_ERROR; + } + + ngx_stream_finalize_session(s, rc); + + return NGX_OK; +} + + +ngx_int_t +ngx_stream_core_preread_phase(ngx_stream_session_t *s, + ngx_stream_phase_handler_t *ph) +{ + ngx_int_t rc; + ngx_connection_t *c; + ngx_stream_core_srv_conf_t *cscf; + + c = s->connection; + + c->log->action = "prereading client data"; + + cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); + + if (c->read->timedout) { + rc = NGX_STREAM_OK; + goto done; + } + + if (!c->read->timer_set) { + rc = ph->handler(s); + + if (rc != NGX_AGAIN) { + goto done; + } + } + + if (c->buffer == NULL) { + c->buffer = ngx_create_temp_buf(c->pool, cscf->preread_buffer_size); + if (c->buffer == NULL) { + rc = NGX_ERROR; + goto done; + } + } + + if (ngx_stream_preread_can_peek(c)) { + rc = ngx_stream_preread_peek(s, ph); + + } else { + rc = ngx_stream_preread(s, ph); + } + +done: + + if (rc == NGX_AGAIN) { + if (ngx_handle_read_event(c->read, 0) != NGX_OK) { + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + + if (!c->read->timer_set) { + ngx_add_timer(c->read, cscf->preread_timeout); + } + + c->read->handler = ngx_stream_session_handler; + + return NGX_OK; + } + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (rc == NGX_OK) { + s->phase_handler = ph->next; + return NGX_AGAIN; + } + + if (rc == NGX_DECLINED) { + s->phase_handler++; + return NGX_AGAIN; + } + + if (rc == NGX_DONE) { + return NGX_OK; + } + + if (rc == NGX_ERROR) { + rc = NGX_STREAM_INTERNAL_SERVER_ERROR; + } + + ngx_stream_finalize_session(s, rc); + + return NGX_OK; +} + + +static ngx_uint_t +ngx_stream_preread_can_peek(ngx_connection_t *c) +{ +#if (NGX_STREAM_SSL) + if (c->ssl) { + return 0; + } +#endif + + if ((ngx_event_flags & NGX_USE_CLEAR_EVENT) == 0) { + return 0; + } + +#if (NGX_HAVE_KQUEUE) + if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { + return 1; + } +#endif + +#if (NGX_HAVE_EPOLLRDHUP) + if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) && ngx_use_epoll_rdhup) { + return 1; + } +#endif + + return 0; +} + + +static ngx_int_t +ngx_stream_preread_peek(ngx_stream_session_t *s, ngx_stream_phase_handler_t *ph) +{ + ssize_t n; + ngx_int_t rc; + ngx_err_t err; + ngx_connection_t *c; + + c = s->connection; + + n = recv(c->fd, (char *) c->buffer->last, + c->buffer->end - c->buffer->last, MSG_PEEK); + + err = ngx_socket_errno; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, "stream recv(): %z", n); + + if (n == -1) { + if (err == NGX_EAGAIN) { + c->read->ready = 0; + return NGX_AGAIN; + } + + ngx_connection_error(c, err, "recv() failed"); + return NGX_STREAM_OK; + } + + if (n == 0) { + return NGX_STREAM_OK; + } + + c->buffer->last += n; + + rc = ph->handler(s); + + if (rc != NGX_AGAIN) { + c->buffer->last = c->buffer->pos; + return rc; + } + + if (c->buffer->last == c->buffer->end) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, "preread buffer full"); + return NGX_STREAM_BAD_REQUEST; + } + + if (c->read->pending_eof) { + return NGX_STREAM_OK; + } + + c->buffer->last = c->buffer->pos; + + return NGX_AGAIN; +} + + +static ngx_int_t +ngx_stream_preread(ngx_stream_session_t *s, ngx_stream_phase_handler_t *ph) +{ + ssize_t n; + ngx_int_t rc; + ngx_connection_t *c; + + c = s->connection; + + while (c->read->ready) { + + n = c->recv(c, c->buffer->last, c->buffer->end - c->buffer->last); + + if (n == NGX_AGAIN) { + return NGX_AGAIN; + } + + if (n == NGX_ERROR || n == 0) { + return NGX_STREAM_OK; + } + + c->buffer->last += n; + + rc = ph->handler(s); + + if (rc != NGX_AGAIN) { + return rc; + } + + if (c->buffer->last == c->buffer->end) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, "preread buffer full"); + return NGX_STREAM_BAD_REQUEST; + } + } + + return NGX_AGAIN; +} + + +ngx_int_t +ngx_stream_core_content_phase(ngx_stream_session_t *s, + ngx_stream_phase_handler_t *ph) +{ + ngx_connection_t *c; + ngx_stream_core_srv_conf_t *cscf; + + c = s->connection; + + c->log->action = NULL; + + cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); + + if (c->type == SOCK_STREAM + && cscf->tcp_nodelay + && ngx_tcp_nodelay(c) != NGX_OK) + { + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + + if (cscf->handler == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "no handler for server"); + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + + cscf->handler(s); + + return NGX_OK; +} + + +ngx_int_t +ngx_stream_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc) +{ + u_char *h, ch; + size_t i, dot_pos, host_len; + ngx_int_t port; + + enum { + sw_host_start = 0, + sw_host, + sw_host_ip_literal, + sw_host_end, + sw_port, + } state; + + dot_pos = host->len; + host_len = host->len; + port = 0; + + h = host->data; + + state = sw_host_start; + + for (i = 0; i < host->len; i++) { + ch = h[i]; + + switch (state) { + + case sw_host_start: + + if (ch == '[') { + state = sw_host_ip_literal; + break; + } + + state = sw_host; + + /* fall through */ + + case sw_host: + + if (ch >= 'A' && ch <= 'Z') { + alloc = 1; + break; + } + + if (ch >= 'a' && ch <= 'z') { + break; + } + + if (ch >= '0' && ch <= '9') { + break; + } + + switch (ch) { + case ':': + host_len = i; + state = sw_port; + break; + case '-': + break; + case '.': + if (dot_pos == i - 1) { + return NGX_DECLINED; + } + dot_pos = i; + break; + case '_': + case '~': + /* unreserved */ + break; + case '!': + case '$': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case ';': + case '=': + /* sub-delims */ + break; + case '%': + /* pct-encoded */ + break; + default: + return NGX_DECLINED; + } + break; + + case sw_host_ip_literal: + + if (ch >= 'A' && ch <= 'Z') { + alloc = 1; + break; + } + + if (ch >= 'a' && ch <= 'z') { + break; + } + + if (ch >= '0' && ch <= '9') { + break; + } + + switch (ch) { + case ':': + break; + case ']': + host_len = i + 1; + state = sw_host_end; + break; + case '-': + break; + case '.': + if (dot_pos == i - 1) { + return NGX_DECLINED; + } + dot_pos = i; + break; + case '_': + case '~': + /* unreserved */ + break; + case '!': + case '$': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case ';': + case '=': + /* sub-delims */ + break; + default: + return NGX_DECLINED; + } + break; + + case sw_host_end: + + if (ch == ':') { + state = sw_port; + break; + } + return NGX_DECLINED; + + case sw_port: + + if (ch >= '0' && ch <= '9') { + if (port >= 6553 && (port > 6553 || (ch - '0') > 5)) { + return NGX_DECLINED; + } + + port = port * 10 + (ch - '0'); + break; + } + return NGX_DECLINED; + } + } + + if (state == sw_host_ip_literal) { + return NGX_DECLINED; + } + + if (dot_pos == host_len - 1) { + host_len--; + } + + if (host_len == 0) { + return NGX_DECLINED; + } + + if (alloc) { + host->data = ngx_pnalloc(pool, host_len); + if (host->data == NULL) { + return NGX_ERROR; + } + + ngx_strlow(host->data, h, host_len); + } + + host->len = host_len; + + return NGX_OK; +} + + +ngx_int_t +ngx_stream_find_virtual_server(ngx_stream_session_t *s, + ngx_str_t *host, ngx_stream_core_srv_conf_t **cscfp) +{ + ngx_stream_core_srv_conf_t *cscf; + + if (s->virtual_names == NULL) { + return NGX_DECLINED; + } + + cscf = ngx_hash_find_combined(&s->virtual_names->names, + ngx_hash_key(host->data, host->len), + host->data, host->len); + + if (cscf) { + *cscfp = cscf; + return NGX_OK; + } + +#if (NGX_PCRE) + + if (host->len && s->virtual_names->nregex) { + ngx_int_t n; + ngx_uint_t i; + ngx_stream_server_name_t *sn; + + sn = s->virtual_names->regex; + + for (i = 0; i < s->virtual_names->nregex; i++) { + + n = ngx_stream_regex_exec(s, sn[i].regex, host); + + if (n == NGX_DECLINED) { + continue; + } + + if (n == NGX_OK) { + *cscfp = sn[i].server; + return NGX_OK; + } + + return NGX_ERROR; + } + } + +#endif /* NGX_PCRE */ + + return NGX_DECLINED; +} + + +static ngx_int_t +ngx_stream_core_preconfiguration(ngx_conf_t *cf) +{ + return ngx_stream_variables_add_core_vars(cf); +} + + +static void * +ngx_stream_core_create_main_conf(ngx_conf_t *cf) +{ + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_core_main_conf_t)); + if (cmcf == NULL) { + return NULL; + } + + if (ngx_array_init(&cmcf->servers, cf->pool, 4, + sizeof(ngx_stream_core_srv_conf_t *)) + != NGX_OK) + { + return NULL; + } + + cmcf->server_names_hash_max_size = NGX_CONF_UNSET_UINT; + cmcf->server_names_hash_bucket_size = NGX_CONF_UNSET_UINT; + + cmcf->variables_hash_max_size = NGX_CONF_UNSET_UINT; + cmcf->variables_hash_bucket_size = NGX_CONF_UNSET_UINT; + + return cmcf; +} + + +static char * +ngx_stream_core_init_main_conf(ngx_conf_t *cf, void *conf) +{ + ngx_stream_core_main_conf_t *cmcf = conf; + + ngx_conf_init_uint_value(cmcf->server_names_hash_max_size, 512); + ngx_conf_init_uint_value(cmcf->server_names_hash_bucket_size, + ngx_cacheline_size); + + cmcf->server_names_hash_bucket_size = + ngx_align(cmcf->server_names_hash_bucket_size, ngx_cacheline_size); + + + ngx_conf_init_uint_value(cmcf->variables_hash_max_size, 1024); + ngx_conf_init_uint_value(cmcf->variables_hash_bucket_size, 64); + + cmcf->variables_hash_bucket_size = + ngx_align(cmcf->variables_hash_bucket_size, ngx_cacheline_size); + + if (cmcf->ncaptures) { + cmcf->ncaptures = (cmcf->ncaptures + 1) * 3; + } + + return NGX_CONF_OK; +} + + +static void * +ngx_stream_core_create_srv_conf(ngx_conf_t *cf) +{ + ngx_stream_core_srv_conf_t *cscf; + + cscf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_core_srv_conf_t)); + if (cscf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * cscf->handler = NULL; + * cscf->error_log = NULL; + */ + + if (ngx_array_init(&cscf->server_names, cf->temp_pool, 4, + sizeof(ngx_stream_server_name_t)) + != NGX_OK) + { + return NULL; + } + + cscf->file_name = cf->conf_file->file.name.data; + cscf->line = cf->conf_file->line; + cscf->resolver_timeout = NGX_CONF_UNSET_MSEC; + cscf->proxy_protocol_timeout = NGX_CONF_UNSET_MSEC; + cscf->tcp_nodelay = NGX_CONF_UNSET; + cscf->preread_buffer_size = NGX_CONF_UNSET_SIZE; + cscf->preread_timeout = NGX_CONF_UNSET_MSEC; + + return cscf; +} + + +static char * +ngx_stream_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_stream_core_srv_conf_t *prev = parent; + ngx_stream_core_srv_conf_t *conf = child; + + ngx_str_t name; + ngx_stream_server_name_t *sn; + + ngx_conf_merge_msec_value(conf->resolver_timeout, + prev->resolver_timeout, 30000); + + if (conf->resolver == NULL) { + + if (prev->resolver == NULL) { + + /* + * create dummy resolver in stream {} context + * to inherit it in all servers + */ + + prev->resolver = ngx_resolver_create(cf, NULL, 0); + if (prev->resolver == NULL) { + return NGX_CONF_ERROR; + } + } + + conf->resolver = prev->resolver; + } + + if (conf->error_log == NULL) { + if (prev->error_log) { + conf->error_log = prev->error_log; + } else { + conf->error_log = &cf->cycle->new_log; + } + } + + ngx_conf_merge_msec_value(conf->proxy_protocol_timeout, + prev->proxy_protocol_timeout, 30000); + + ngx_conf_merge_value(conf->tcp_nodelay, prev->tcp_nodelay, 1); + + ngx_conf_merge_size_value(conf->preread_buffer_size, + prev->preread_buffer_size, 16384); + + ngx_conf_merge_msec_value(conf->preread_timeout, + prev->preread_timeout, 30000); + + if (conf->server_names.nelts == 0) { + /* the array has 4 empty preallocated elements, so push cannot fail */ + sn = ngx_array_push(&conf->server_names); +#if (NGX_PCRE) + sn->regex = NULL; +#endif + sn->server = conf; + ngx_str_set(&sn->name, ""); + } + + sn = conf->server_names.elts; + name = sn[0].name; + +#if (NGX_PCRE) + if (sn->regex) { + name.len++; + name.data--; + } else +#endif + + if (name.data[0] == '.') { + name.len--; + name.data++; + } + + conf->server_name.len = name.len; + conf->server_name.data = ngx_pstrdup(cf->pool, &name); + if (conf->server_name.data == NULL) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_stream_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_core_srv_conf_t *cscf = conf; + + return ngx_log_set_log(cf, &cscf->error_log); +} + + +static char * +ngx_stream_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *rv; + void *mconf; + ngx_uint_t m; + ngx_conf_t pcf; + ngx_stream_module_t *module; + ngx_stream_conf_ctx_t *ctx, *stream_ctx; + ngx_stream_core_srv_conf_t *cscf, **cscfp; + ngx_stream_core_main_conf_t *cmcf; + + ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_conf_ctx_t)); + if (ctx == NULL) { + return NGX_CONF_ERROR; + } + + stream_ctx = cf->ctx; + ctx->main_conf = stream_ctx->main_conf; + + /* the server{}'s srv_conf */ + + ctx->srv_conf = ngx_pcalloc(cf->pool, + sizeof(void *) * ngx_stream_max_module); + if (ctx->srv_conf == NULL) { + return NGX_CONF_ERROR; + } + + for (m = 0; cf->cycle->modules[m]; m++) { + if (cf->cycle->modules[m]->type != NGX_STREAM_MODULE) { + continue; + } + + module = cf->cycle->modules[m]->ctx; + + if (module->create_srv_conf) { + mconf = module->create_srv_conf(cf); + if (mconf == NULL) { + return NGX_CONF_ERROR; + } + + ctx->srv_conf[cf->cycle->modules[m]->ctx_index] = mconf; + } + } + + /* the server configuration context */ + + cscf = ctx->srv_conf[ngx_stream_core_module.ctx_index]; + cscf->ctx = ctx; + + cmcf = ctx->main_conf[ngx_stream_core_module.ctx_index]; + + cscfp = ngx_array_push(&cmcf->servers); + if (cscfp == NULL) { + return NGX_CONF_ERROR; + } + + *cscfp = cscf; + + + /* parse inside server{} */ + + pcf = *cf; + cf->ctx = ctx; + cf->cmd_type = NGX_STREAM_SRV_CONF; + + rv = ngx_conf_parse(cf, NULL); + + *cf = pcf; + + if (rv == NGX_CONF_OK && !cscf->listen) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"listen\" is defined for server in %s:%ui", + cscf->file_name, cscf->line); + return NGX_CONF_ERROR; + } + + return rv; +} + + +static char * +ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_core_srv_conf_t *cscf = conf; + + ngx_str_t *value, size; + ngx_url_t u; + ngx_uint_t n, i, backlog; + ngx_stream_listen_opt_t lsopt; + + cscf->listen = 1; + + value = cf->args->elts; + + ngx_memzero(&u, sizeof(ngx_url_t)); + + u.url = value[1]; + u.listen = 1; + + if (ngx_parse_url(cf->pool, &u) != NGX_OK) { + if (u.err) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "%s in \"%V\" of the \"listen\" directive", + u.err, &u.url); + } + + return NGX_CONF_ERROR; + } + + ngx_memzero(&lsopt, sizeof(ngx_stream_listen_opt_t)); + + lsopt.backlog = NGX_LISTEN_BACKLOG; + lsopt.type = SOCK_STREAM; + lsopt.rcvbuf = -1; + lsopt.sndbuf = -1; +#if (NGX_HAVE_SETFIB) + lsopt.setfib = -1; +#endif +#if (NGX_HAVE_TCP_FASTOPEN) + lsopt.fastopen = -1; +#endif +#if (NGX_HAVE_INET6) + lsopt.ipv6only = 1; +#endif + + backlog = 0; + + for (i = 2; i < cf->args->nelts; i++) { + + if (ngx_strcmp(value[i].data, "default_server") == 0) { + lsopt.default_server = 1; + continue; + } + +#if !(NGX_WIN32) + if (ngx_strcmp(value[i].data, "udp") == 0) { + lsopt.type = SOCK_DGRAM; + continue; + } +#endif + + if (ngx_strcmp(value[i].data, "bind") == 0) { + lsopt.set = 1; + lsopt.bind = 1; + continue; + } + +#if (NGX_HAVE_SETFIB) + if (ngx_strncmp(value[i].data, "setfib=", 7) == 0) { + lsopt.setfib = ngx_atoi(value[i].data + 7, value[i].len - 7); + lsopt.set = 1; + lsopt.bind = 1; + + if (lsopt.setfib == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid setfib \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } +#endif + +#if (NGX_HAVE_TCP_FASTOPEN) + if (ngx_strncmp(value[i].data, "fastopen=", 9) == 0) { + lsopt.fastopen = ngx_atoi(value[i].data + 9, value[i].len - 9); + lsopt.set = 1; + lsopt.bind = 1; + + if (lsopt.fastopen == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid fastopen \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } +#endif + + if (ngx_strncmp(value[i].data, "backlog=", 8) == 0) { + lsopt.backlog = ngx_atoi(value[i].data + 8, value[i].len - 8); + lsopt.set = 1; + lsopt.bind = 1; + + if (lsopt.backlog == NGX_ERROR || lsopt.backlog == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid backlog \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + backlog = 1; + + continue; + } + + if (ngx_strncmp(value[i].data, "rcvbuf=", 7) == 0) { + size.len = value[i].len - 7; + size.data = value[i].data + 7; + + lsopt.rcvbuf = ngx_parse_size(&size); + lsopt.set = 1; + lsopt.bind = 1; + + if (lsopt.rcvbuf == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid rcvbuf \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "sndbuf=", 7) == 0) { + size.len = value[i].len - 7; + size.data = value[i].data + 7; + + lsopt.sndbuf = ngx_parse_size(&size); + lsopt.set = 1; + lsopt.bind = 1; + + if (lsopt.sndbuf == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid sndbuf \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "accept_filter=", 14) == 0) { +#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) + lsopt.accept_filter = (char *) &value[i].data[14]; + lsopt.set = 1; + lsopt.bind = 1; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "accept filters \"%V\" are not supported " + "on this platform, ignored", + &value[i]); +#endif + continue; + } + + if (ngx_strcmp(value[i].data, "deferred") == 0) { +#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) + lsopt.deferred_accept = 1; + lsopt.set = 1; + lsopt.bind = 1; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the deferred accept is not supported " + "on this platform, ignored"); +#endif + continue; + } + + if (ngx_strncmp(value[i].data, "ipv6only=o", 10) == 0) { +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + if (ngx_strcmp(&value[i].data[10], "n") == 0) { + lsopt.ipv6only = 1; + + } else if (ngx_strcmp(&value[i].data[10], "ff") == 0) { + lsopt.ipv6only = 0; + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid ipv6only flags \"%s\"", + &value[i].data[9]); + return NGX_CONF_ERROR; + } + + lsopt.set = 1; + lsopt.bind = 1; + + continue; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "ipv6only is not supported " + "on this platform"); + return NGX_CONF_ERROR; +#endif + } + + if (ngx_strcmp(value[i].data, "reuseport") == 0) { +#if (NGX_HAVE_REUSEPORT) + lsopt.reuseport = 1; + lsopt.set = 1; + lsopt.bind = 1; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "reuseport is not supported " + "on this platform, ignored"); +#endif + continue; + } + + if (ngx_strcmp(value[i].data, "multipath") == 0) { +#ifdef IPPROTO_MPTCP + lsopt.protocol = IPPROTO_MPTCP; + lsopt.set = 1; + lsopt.bind = 1; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "multipath is not supported " + "on this platform, ignored"); +#endif + continue; + } + + if (ngx_strcmp(value[i].data, "ssl") == 0) { +#if (NGX_STREAM_SSL) + lsopt.ssl = 1; + continue; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"ssl\" parameter requires " + "ngx_stream_ssl_module"); + return NGX_CONF_ERROR; +#endif + } + + if (ngx_strncmp(value[i].data, "so_keepalive=", 13) == 0) { + + if (ngx_strcmp(&value[i].data[13], "on") == 0) { + lsopt.so_keepalive = 1; + + } else if (ngx_strcmp(&value[i].data[13], "off") == 0) { + lsopt.so_keepalive = 2; + + } else { + +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + u_char *p, *end; + ngx_str_t s; + + end = value[i].data + value[i].len; + s.data = value[i].data + 13; + + p = ngx_strlchr(s.data, end, ':'); + if (p == NULL) { + p = end; + } + + if (p > s.data) { + s.len = p - s.data; + + lsopt.tcp_keepidle = ngx_parse_time(&s, 1); + if (lsopt.tcp_keepidle == (time_t) NGX_ERROR) { + goto invalid_so_keepalive; + } + } + + s.data = (p < end) ? (p + 1) : end; + + p = ngx_strlchr(s.data, end, ':'); + if (p == NULL) { + p = end; + } + + if (p > s.data) { + s.len = p - s.data; + + lsopt.tcp_keepintvl = ngx_parse_time(&s, 1); + if (lsopt.tcp_keepintvl == (time_t) NGX_ERROR) { + goto invalid_so_keepalive; + } + } + + s.data = (p < end) ? (p + 1) : end; + + if (s.data < end) { + s.len = end - s.data; + + lsopt.tcp_keepcnt = ngx_atoi(s.data, s.len); + if (lsopt.tcp_keepcnt == NGX_ERROR) { + goto invalid_so_keepalive; + } + } + + if (lsopt.tcp_keepidle == 0 && lsopt.tcp_keepintvl == 0 + && lsopt.tcp_keepcnt == 0) + { + goto invalid_so_keepalive; + } + + lsopt.so_keepalive = 1; + +#else + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"so_keepalive\" parameter accepts " + "only \"on\" or \"off\" on this platform"); + return NGX_CONF_ERROR; + +#endif + } + + lsopt.set = 1; + lsopt.bind = 1; + + continue; + +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + invalid_so_keepalive: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid so_keepalive value: \"%s\"", + &value[i].data[13]); + return NGX_CONF_ERROR; +#endif + } + + if (ngx_strcmp(value[i].data, "proxy_protocol") == 0) { + lsopt.proxy_protocol = 1; + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (lsopt.type == SOCK_DGRAM) { +#if (NGX_HAVE_TCP_FASTOPEN) + if (lsopt.fastopen != -1) { + return "\"fastopen\" parameter is incompatible with \"udp\""; + } +#endif + + if (backlog) { + return "\"backlog\" parameter is incompatible with \"udp\""; + } + +#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) + if (lsopt.accept_filter) { + return "\"accept_filter\" parameter is incompatible with \"udp\""; + } +#endif + +#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) + if (lsopt.deferred_accept) { + return "\"deferred\" parameter is incompatible with \"udp\""; + } +#endif + +#ifdef IPPROTO_MPTCP + if (lsopt.protocol == IPPROTO_MPTCP) { + return "\"multipath\" parameter is incompatible with \"udp\""; + } +#endif + +#if (NGX_STREAM_SSL) + if (lsopt.ssl) { + return "\"ssl\" parameter is incompatible with \"udp\""; + } +#endif + + if (lsopt.so_keepalive) { + return "\"so_keepalive\" parameter is incompatible with \"udp\""; + } + + if (lsopt.proxy_protocol) { + return "\"proxy_protocol\" parameter is incompatible with \"udp\""; + } + } + + for (n = 0; n < u.naddrs; n++) { + + for (i = 0; i < n; i++) { + if (ngx_cmp_sockaddr(u.addrs[n].sockaddr, u.addrs[n].socklen, + u.addrs[i].sockaddr, u.addrs[i].socklen, 1) + == NGX_OK) + { + goto next; + } + } + + lsopt.sockaddr = u.addrs[n].sockaddr; + lsopt.socklen = u.addrs[n].socklen; + lsopt.addr_text = u.addrs[n].name; + lsopt.wildcard = ngx_inet_wildcard(lsopt.sockaddr); + + if (ngx_stream_add_listen(cf, cscf, &lsopt) != NGX_OK) { + return NGX_CONF_ERROR; + } + + next: + continue; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_stream_core_server_name(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_core_srv_conf_t *cscf = conf; + + u_char ch; + ngx_str_t *value; + ngx_uint_t i; + ngx_stream_server_name_t *sn; + + value = cf->args->elts; + + for (i = 1; i < cf->args->nelts; i++) { + + ch = value[i].data[0]; + + if ((ch == '*' && (value[i].len < 3 || value[i].data[1] != '.')) + || (ch == '.' && value[i].len < 2)) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "server name \"%V\" is invalid", &value[i]); + return NGX_CONF_ERROR; + } + + if (ngx_strchr(value[i].data, '/')) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "server name \"%V\" has suspicious symbols", + &value[i]); + } + + sn = ngx_array_push(&cscf->server_names); + if (sn == NULL) { + return NGX_CONF_ERROR; + } + +#if (NGX_PCRE) + sn->regex = NULL; +#endif + sn->server = cscf; + + if (ngx_strcasecmp(value[i].data, (u_char *) "$hostname") == 0) { + sn->name = cf->cycle->hostname; + + } else { + sn->name = value[i]; + } + + if (value[i].data[0] != '~') { + ngx_strlow(sn->name.data, sn->name.data, sn->name.len); + continue; + } + +#if (NGX_PCRE) + { + u_char *p; + ngx_regex_compile_t rc; + u_char errstr[NGX_MAX_CONF_ERRSTR]; + + if (value[i].len == 1) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "empty regex in server name \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + value[i].len--; + value[i].data++; + + ngx_memzero(&rc, sizeof(ngx_regex_compile_t)); + + rc.pattern = value[i]; + rc.err.len = NGX_MAX_CONF_ERRSTR; + rc.err.data = errstr; + + for (p = value[i].data; p < value[i].data + value[i].len; p++) { + if (*p >= 'A' && *p <= 'Z') { + rc.options = NGX_REGEX_CASELESS; + break; + } + } + + sn->regex = ngx_stream_regex_compile(cf, &rc); + if (sn->regex == NULL) { + return NGX_CONF_ERROR; + } + + sn->name = value[i]; + cscf->captures = (rc.captures > 0); + } +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "using regex \"%V\" " + "requires PCRE library", &value[i]); + + return NGX_CONF_ERROR; +#endif + } + + return NGX_CONF_OK; +} + + +static char * +ngx_stream_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_core_srv_conf_t *cscf = conf; + + ngx_str_t *value; + + if (cscf->resolver) { + return "is duplicate"; + } + + value = cf->args->elts; + + cscf->resolver = ngx_resolver_create(cf, &value[1], cf->args->nelts - 1); + if (cscf->resolver == NULL) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_geo_module.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_geo_module.c new file mode 100644 index 000000000..316de9286 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_geo_module.c @@ -0,0 +1,1624 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_stream_variable_value_t *value; + u_short start; + u_short end; +} ngx_stream_geo_range_t; + + +typedef struct { + ngx_radix_tree_t *tree; +#if (NGX_HAVE_INET6) + ngx_radix_tree_t *tree6; +#endif +} ngx_stream_geo_trees_t; + + +typedef struct { + ngx_stream_geo_range_t **low; + ngx_stream_variable_value_t *default_value; +} ngx_stream_geo_high_ranges_t; + + +typedef struct { + ngx_str_node_t sn; + ngx_stream_variable_value_t *value; + size_t offset; +} ngx_stream_geo_variable_value_node_t; + + +typedef struct { + ngx_stream_variable_value_t *value; + ngx_str_t *net; + ngx_stream_geo_high_ranges_t high; + ngx_radix_tree_t *tree; +#if (NGX_HAVE_INET6) + ngx_radix_tree_t *tree6; +#endif + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; + ngx_pool_t *pool; + ngx_pool_t *temp_pool; + + size_t data_size; + + ngx_str_t include_name; + ngx_uint_t includes; + ngx_uint_t entries; + + unsigned ranges:1; + unsigned outside_entries:1; + unsigned allow_binary_include:1; + unsigned binary_include:1; + unsigned no_cacheable:1; +} ngx_stream_geo_conf_ctx_t; + + +typedef struct { + union { + ngx_stream_geo_trees_t trees; + ngx_stream_geo_high_ranges_t high; + } u; + + ngx_int_t index; +} ngx_stream_geo_ctx_t; + + +static ngx_int_t ngx_stream_geo_addr(ngx_stream_session_t *s, + ngx_stream_geo_ctx_t *ctx, ngx_addr_t *addr); + +static char *ngx_stream_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); +static char *ngx_stream_geo_range(ngx_conf_t *cf, + ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *value); +static char *ngx_stream_geo_add_range(ngx_conf_t *cf, + ngx_stream_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end); +static ngx_uint_t ngx_stream_geo_delete_range(ngx_conf_t *cf, + ngx_stream_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end); +static char *ngx_stream_geo_cidr(ngx_conf_t *cf, + ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *value); +static char *ngx_stream_geo_cidr_add(ngx_conf_t *cf, + ngx_stream_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr, ngx_str_t *value, + ngx_str_t *net); +static ngx_stream_variable_value_t *ngx_stream_geo_value(ngx_conf_t *cf, + ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *value); +static ngx_int_t ngx_stream_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, + ngx_cidr_t *cidr); +static char *ngx_stream_geo_include(ngx_conf_t *cf, + ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *name); +static ngx_int_t ngx_stream_geo_include_binary_base(ngx_conf_t *cf, + ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *name); +static void ngx_stream_geo_create_binary_base(ngx_stream_geo_conf_ctx_t *ctx); +static u_char *ngx_stream_geo_copy_values(u_char *base, u_char *p, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); + + +static ngx_command_t ngx_stream_geo_commands[] = { + + { ngx_string("geo"), + NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12, + ngx_stream_geo_block, + 0, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_geo_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_geo_module = { + NGX_MODULE_V1, + &ngx_stream_geo_module_ctx, /* module context */ + ngx_stream_geo_commands, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +typedef struct { + u_char GEORNG[6]; + u_char version; + u_char ptr_size; + uint32_t endianness; + uint32_t crc32; +} ngx_stream_geo_header_t; + + +static ngx_stream_geo_header_t ngx_stream_geo_header = { + { 'G', 'E', 'O', 'R', 'N', 'G' }, 0, sizeof(void *), 0x12345678, 0 +}; + + +/* geo range is AF_INET only */ + +static ngx_int_t +ngx_stream_geo_cidr_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_stream_geo_ctx_t *ctx = (ngx_stream_geo_ctx_t *) data; + + in_addr_t inaddr; + ngx_addr_t addr; + struct sockaddr_in *sin; + ngx_stream_variable_value_t *vv; +#if (NGX_HAVE_INET6) + u_char *p; + struct in6_addr *inaddr6; +#endif + + if (ngx_stream_geo_addr(s, ctx, &addr) != NGX_OK) { + vv = (ngx_stream_variable_value_t *) + ngx_radix32tree_find(ctx->u.trees.tree, INADDR_NONE); + goto done; + } + + switch (addr.sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; + p = inaddr6->s6_addr; + + if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { + inaddr = (in_addr_t) p[12] << 24; + inaddr += p[13] << 16; + inaddr += p[14] << 8; + inaddr += p[15]; + + vv = (ngx_stream_variable_value_t *) + ngx_radix32tree_find(ctx->u.trees.tree, inaddr); + + } else { + vv = (ngx_stream_variable_value_t *) + ngx_radix128tree_find(ctx->u.trees.tree6, p); + } + + break; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + vv = (ngx_stream_variable_value_t *) + ngx_radix32tree_find(ctx->u.trees.tree, INADDR_NONE); + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) addr.sockaddr; + inaddr = ntohl(sin->sin_addr.s_addr); + + vv = (ngx_stream_variable_value_t *) + ngx_radix32tree_find(ctx->u.trees.tree, inaddr); + + break; + } + +done: + + *v = *vv; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream geo: %v", v); + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_geo_range_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_stream_geo_ctx_t *ctx = (ngx_stream_geo_ctx_t *) data; + + in_addr_t inaddr; + ngx_addr_t addr; + ngx_uint_t n; + struct sockaddr_in *sin; + ngx_stream_geo_range_t *range; +#if (NGX_HAVE_INET6) + u_char *p; + struct in6_addr *inaddr6; +#endif + + *v = *ctx->u.high.default_value; + + if (ngx_stream_geo_addr(s, ctx, &addr) == NGX_OK) { + + switch (addr.sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; + + if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { + p = inaddr6->s6_addr; + + inaddr = (in_addr_t) p[12] << 24; + inaddr += p[13] << 16; + inaddr += p[14] << 8; + inaddr += p[15]; + + } else { + inaddr = INADDR_NONE; + } + + break; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + inaddr = INADDR_NONE; + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) addr.sockaddr; + inaddr = ntohl(sin->sin_addr.s_addr); + break; + } + + } else { + inaddr = INADDR_NONE; + } + + if (ctx->u.high.low) { + range = ctx->u.high.low[inaddr >> 16]; + + if (range) { + n = inaddr & 0xffff; + do { + if (n >= (ngx_uint_t) range->start + && n <= (ngx_uint_t) range->end) + { + *v = *range->value; + break; + } + } while ((++range)->value); + } + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream geo: %v", v); + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_geo_addr(ngx_stream_session_t *s, ngx_stream_geo_ctx_t *ctx, + ngx_addr_t *addr) +{ + ngx_stream_variable_value_t *v; + + if (ctx->index == -1) { + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream geo started: %V", &s->connection->addr_text); + + addr->sockaddr = s->connection->sockaddr; + addr->socklen = s->connection->socklen; + /* addr->name = s->connection->addr_text; */ + + return NGX_OK; + } + + v = ngx_stream_get_flushed_variable(s, ctx->index); + + if (v == NULL || v->not_found) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream geo not found"); + + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream geo started: %v", v); + + if (ngx_parse_addr(s->connection->pool, addr, v->data, v->len) == NGX_OK) { + return NGX_OK; + } + + return NGX_ERROR; +} + + +static char * +ngx_stream_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *rv; + size_t len; + ngx_str_t *value, name; + ngx_uint_t i; + ngx_conf_t save; + ngx_pool_t *pool; + ngx_array_t *a; + ngx_stream_variable_t *var; + ngx_stream_geo_ctx_t *geo; + ngx_stream_geo_conf_ctx_t ctx; +#if (NGX_HAVE_INET6) + static struct in6_addr zero; +#endif + + value = cf->args->elts; + + geo = ngx_palloc(cf->pool, sizeof(ngx_stream_geo_ctx_t)); + if (geo == NULL) { + return NGX_CONF_ERROR; + } + + name = value[1]; + + if (name.data[0] != '$') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"%V\"", &name); + return NGX_CONF_ERROR; + } + + name.len--; + name.data++; + + if (cf->args->nelts == 3) { + + geo->index = ngx_stream_get_variable_index(cf, &name); + if (geo->index == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + name = value[2]; + + if (name.data[0] != '$') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"%V\"", &name); + return NGX_CONF_ERROR; + } + + name.len--; + name.data++; + + } else { + geo->index = -1; + } + + var = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE); + if (var == NULL) { + return NGX_CONF_ERROR; + } + + pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log); + if (pool == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(&ctx, sizeof(ngx_stream_geo_conf_ctx_t)); + + ctx.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log); + if (ctx.temp_pool == NULL) { + ngx_destroy_pool(pool); + return NGX_CONF_ERROR; + } + + ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, ngx_str_rbtree_insert_value); + + ctx.pool = cf->pool; + ctx.data_size = sizeof(ngx_stream_geo_header_t) + + sizeof(ngx_stream_variable_value_t) + + 0x10000 * sizeof(ngx_stream_geo_range_t *); + ctx.allow_binary_include = 1; + ctx.no_cacheable = 0; + + save = *cf; + cf->pool = pool; + cf->ctx = &ctx; + cf->handler = ngx_stream_geo; + cf->handler_conf = conf; + + rv = ngx_conf_parse(cf, NULL); + + *cf = save; + + if (rv != NGX_CONF_OK) { + goto failed; + } + + if (ctx.no_cacheable) { + var->flags |= NGX_STREAM_VAR_NOCACHEABLE; + } + + if (ctx.ranges) { + + if (ctx.high.low && !ctx.binary_include) { + for (i = 0; i < 0x10000; i++) { + a = (ngx_array_t *) ctx.high.low[i]; + + if (a == NULL) { + continue; + } + + if (a->nelts == 0) { + ctx.high.low[i] = NULL; + continue; + } + + len = a->nelts * sizeof(ngx_stream_geo_range_t); + + ctx.high.low[i] = ngx_palloc(cf->pool, len + sizeof(void *)); + if (ctx.high.low[i] == NULL) { + goto failed; + } + + ngx_memcpy(ctx.high.low[i], a->elts, len); + ctx.high.low[i][a->nelts].value = NULL; + ctx.data_size += len + sizeof(void *); + } + + if (ctx.allow_binary_include + && !ctx.outside_entries + && ctx.entries > 100000 + && ctx.includes == 1) + { + ngx_stream_geo_create_binary_base(&ctx); + } + } + + if (ctx.high.default_value == NULL) { + ctx.high.default_value = &ngx_stream_variable_null_value; + } + + geo->u.high = ctx.high; + + var->get_handler = ngx_stream_geo_range_variable; + var->data = (uintptr_t) geo; + + } else { + if (ctx.tree == NULL) { + ctx.tree = ngx_radix_tree_create(cf->pool, -1); + if (ctx.tree == NULL) { + goto failed; + } + } + + geo->u.trees.tree = ctx.tree; + +#if (NGX_HAVE_INET6) + if (ctx.tree6 == NULL) { + ctx.tree6 = ngx_radix_tree_create(cf->pool, -1); + if (ctx.tree6 == NULL) { + goto failed; + } + } + + geo->u.trees.tree6 = ctx.tree6; +#endif + + var->get_handler = ngx_stream_geo_cidr_variable; + var->data = (uintptr_t) geo; + + if (ngx_radix32tree_insert(ctx.tree, 0, 0, + (uintptr_t) &ngx_stream_variable_null_value) + == NGX_ERROR) + { + goto failed; + } + + /* NGX_BUSY is okay (default was set explicitly) */ + +#if (NGX_HAVE_INET6) + if (ngx_radix128tree_insert(ctx.tree6, zero.s6_addr, zero.s6_addr, + (uintptr_t) &ngx_stream_variable_null_value) + == NGX_ERROR) + { + goto failed; + } +#endif + } + + ngx_destroy_pool(ctx.temp_pool); + ngx_destroy_pool(pool); + + return NGX_CONF_OK; + +failed: + + ngx_destroy_pool(ctx.temp_pool); + ngx_destroy_pool(pool); + + return NGX_CONF_ERROR; +} + + +static char * +ngx_stream_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) +{ + char *rv; + ngx_str_t *value; + ngx_stream_geo_conf_ctx_t *ctx; + + ctx = cf->ctx; + + value = cf->args->elts; + + if (cf->args->nelts == 1) { + + if (ngx_strcmp(value[0].data, "ranges") == 0) { + + if (ctx->tree +#if (NGX_HAVE_INET6) + || ctx->tree6 +#endif + ) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"ranges\" directive must be " + "the first directive inside \"geo\" block"); + goto failed; + } + + ctx->ranges = 1; + + rv = NGX_CONF_OK; + + goto done; + } + + else if (ngx_strcmp(value[0].data, "volatile") == 0) { + ctx->no_cacheable = 1; + rv = NGX_CONF_OK; + goto done; + } + } + + if (cf->args->nelts != 2) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid number of the geo parameters"); + goto failed; + } + + if (ngx_strcmp(value[0].data, "include") == 0) { + + if (strpbrk((char *) value[1].data, "*?[") == NULL) { + rv = ngx_stream_geo_include(cf, ctx, &value[1]); + + } else { + rv = ngx_conf_include(cf, dummy, conf); + } + + goto done; + } + + if (ctx->ranges) { + rv = ngx_stream_geo_range(cf, ctx, value); + + } else { + rv = ngx_stream_geo_cidr(cf, ctx, value); + } + +done: + + ngx_reset_pool(cf->pool); + + return rv; + +failed: + + ngx_reset_pool(cf->pool); + + return NGX_CONF_ERROR; +} + + +static char * +ngx_stream_geo_range(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx, + ngx_str_t *value) +{ + u_char *p, *last; + in_addr_t start, end; + ngx_str_t *net; + ngx_uint_t del; + + if (ngx_strcmp(value[0].data, "default") == 0) { + + if (ctx->high.default_value) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "duplicate default geo range value: \"%V\", old value: \"%v\"", + &value[1], ctx->high.default_value); + } + + ctx->high.default_value = ngx_stream_geo_value(cf, ctx, &value[1]); + if (ctx->high.default_value == NULL) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; + } + + if (ctx->binary_include) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "binary geo range base \"%s\" cannot be mixed with usual entries", + ctx->include_name.data); + return NGX_CONF_ERROR; + } + + if (ctx->high.low == NULL) { + ctx->high.low = ngx_pcalloc(ctx->pool, + 0x10000 * sizeof(ngx_stream_geo_range_t *)); + if (ctx->high.low == NULL) { + return NGX_CONF_ERROR; + } + } + + ctx->entries++; + ctx->outside_entries = 1; + + if (ngx_strcmp(value[0].data, "delete") == 0) { + net = &value[1]; + del = 1; + + } else { + net = &value[0]; + del = 0; + } + + last = net->data + net->len; + + p = ngx_strlchr(net->data, last, '-'); + + if (p == NULL) { + goto invalid; + } + + start = ngx_inet_addr(net->data, p - net->data); + + if (start == INADDR_NONE) { + goto invalid; + } + + start = ntohl(start); + + p++; + + end = ngx_inet_addr(p, last - p); + + if (end == INADDR_NONE) { + goto invalid; + } + + end = ntohl(end); + + if (start > end) { + goto invalid; + } + + if (del) { + if (ngx_stream_geo_delete_range(cf, ctx, start, end)) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "no address range \"%V\" to delete", net); + } + + return NGX_CONF_OK; + } + + ctx->value = ngx_stream_geo_value(cf, ctx, &value[1]); + + if (ctx->value == NULL) { + return NGX_CONF_ERROR; + } + + ctx->net = net; + + return ngx_stream_geo_add_range(cf, ctx, start, end); + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid range \"%V\"", net); + + return NGX_CONF_ERROR; +} + + +/* the add procedure is optimized to add a growing up sequence */ + +static char * +ngx_stream_geo_add_range(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx, + in_addr_t start, in_addr_t end) +{ + in_addr_t n; + ngx_uint_t h, i, s, e; + ngx_array_t *a; + ngx_stream_geo_range_t *range; + + for (n = start; n <= end; n = (n + 0x10000) & 0xffff0000) { + + h = n >> 16; + + if (n == start) { + s = n & 0xffff; + } else { + s = 0; + } + + if ((n | 0xffff) > end) { + e = end & 0xffff; + + } else { + e = 0xffff; + } + + a = (ngx_array_t *) ctx->high.low[h]; + + if (a == NULL) { + a = ngx_array_create(ctx->temp_pool, 64, + sizeof(ngx_stream_geo_range_t)); + if (a == NULL) { + return NGX_CONF_ERROR; + } + + ctx->high.low[h] = (ngx_stream_geo_range_t *) a; + } + + i = a->nelts; + range = a->elts; + + while (i) { + + i--; + + if (e < (ngx_uint_t) range[i].start) { + continue; + } + + if (s > (ngx_uint_t) range[i].end) { + + /* add after the range */ + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + range = a->elts; + + ngx_memmove(&range[i + 2], &range[i + 1], + (a->nelts - 2 - i) * sizeof(ngx_stream_geo_range_t)); + + range[i + 1].start = (u_short) s; + range[i + 1].end = (u_short) e; + range[i + 1].value = ctx->value; + + goto next; + } + + if (s == (ngx_uint_t) range[i].start + && e == (ngx_uint_t) range[i].end) + { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "duplicate range \"%V\", value: \"%v\", old value: \"%v\"", + ctx->net, ctx->value, range[i].value); + + range[i].value = ctx->value; + + goto next; + } + + if (s > (ngx_uint_t) range[i].start + && e < (ngx_uint_t) range[i].end) + { + /* split the range and insert the new one */ + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + range = a->elts; + + ngx_memmove(&range[i + 3], &range[i + 1], + (a->nelts - 3 - i) * sizeof(ngx_stream_geo_range_t)); + + range[i + 2].start = (u_short) (e + 1); + range[i + 2].end = range[i].end; + range[i + 2].value = range[i].value; + + range[i + 1].start = (u_short) s; + range[i + 1].end = (u_short) e; + range[i + 1].value = ctx->value; + + range[i].end = (u_short) (s - 1); + + goto next; + } + + if (s == (ngx_uint_t) range[i].start + && e < (ngx_uint_t) range[i].end) + { + /* shift the range start and insert the new range */ + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + range = a->elts; + + ngx_memmove(&range[i + 1], &range[i], + (a->nelts - 1 - i) * sizeof(ngx_stream_geo_range_t)); + + range[i + 1].start = (u_short) (e + 1); + + range[i].start = (u_short) s; + range[i].end = (u_short) e; + range[i].value = ctx->value; + + goto next; + } + + if (s > (ngx_uint_t) range[i].start + && e == (ngx_uint_t) range[i].end) + { + /* shift the range end and insert the new range */ + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + range = a->elts; + + ngx_memmove(&range[i + 2], &range[i + 1], + (a->nelts - 2 - i) * sizeof(ngx_stream_geo_range_t)); + + range[i + 1].start = (u_short) s; + range[i + 1].end = (u_short) e; + range[i + 1].value = ctx->value; + + range[i].end = (u_short) (s - 1); + + goto next; + } + + s = (ngx_uint_t) range[i].start; + e = (ngx_uint_t) range[i].end; + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "range \"%V\" overlaps \"%d.%d.%d.%d-%d.%d.%d.%d\"", + ctx->net, + h >> 8, h & 0xff, s >> 8, s & 0xff, + h >> 8, h & 0xff, e >> 8, e & 0xff); + + return NGX_CONF_ERROR; + } + + /* add the first range */ + + range = ngx_array_push(a); + if (range == NULL) { + return NGX_CONF_ERROR; + } + + range = a->elts; + + ngx_memmove(&range[1], &range[0], + (a->nelts - 1) * sizeof(ngx_stream_geo_range_t)); + + range[0].start = (u_short) s; + range[0].end = (u_short) e; + range[0].value = ctx->value; + + next: + + if (h == 0xffff) { + break; + } + } + + return NGX_CONF_OK; +} + + +static ngx_uint_t +ngx_stream_geo_delete_range(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx, + in_addr_t start, in_addr_t end) +{ + in_addr_t n; + ngx_uint_t h, i, s, e, warn; + ngx_array_t *a; + ngx_stream_geo_range_t *range; + + warn = 0; + + for (n = start; n <= end; n = (n + 0x10000) & 0xffff0000) { + + h = n >> 16; + + if (n == start) { + s = n & 0xffff; + } else { + s = 0; + } + + if ((n | 0xffff) > end) { + e = end & 0xffff; + + } else { + e = 0xffff; + } + + a = (ngx_array_t *) ctx->high.low[h]; + + if (a == NULL || a->nelts == 0) { + warn = 1; + goto next; + } + + range = a->elts; + for (i = 0; i < a->nelts; i++) { + + if (s == (ngx_uint_t) range[i].start + && e == (ngx_uint_t) range[i].end) + { + ngx_memmove(&range[i], &range[i + 1], + (a->nelts - 1 - i) * sizeof(ngx_stream_geo_range_t)); + + a->nelts--; + + break; + } + + if (i == a->nelts - 1) { + warn = 1; + } + } + + next: + + if (h == 0xffff) { + break; + } + } + + return warn; +} + + +static char * +ngx_stream_geo_cidr(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx, + ngx_str_t *value) +{ + char *rv; + ngx_int_t rc, del; + ngx_str_t *net; + ngx_cidr_t cidr; + + if (ctx->tree == NULL) { + ctx->tree = ngx_radix_tree_create(ctx->pool, -1); + if (ctx->tree == NULL) { + return NGX_CONF_ERROR; + } + } + +#if (NGX_HAVE_INET6) + if (ctx->tree6 == NULL) { + ctx->tree6 = ngx_radix_tree_create(ctx->pool, -1); + if (ctx->tree6 == NULL) { + return NGX_CONF_ERROR; + } + } +#endif + + if (ngx_strcmp(value[0].data, "default") == 0) { + cidr.family = AF_INET; + cidr.u.in.addr = 0; + cidr.u.in.mask = 0; + + rv = ngx_stream_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]); + + if (rv != NGX_CONF_OK) { + return rv; + } + +#if (NGX_HAVE_INET6) + cidr.family = AF_INET6; + ngx_memzero(&cidr.u.in6, sizeof(ngx_in6_cidr_t)); + + rv = ngx_stream_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]); + + if (rv != NGX_CONF_OK) { + return rv; + } +#endif + + return NGX_CONF_OK; + } + + if (ngx_strcmp(value[0].data, "delete") == 0) { + net = &value[1]; + del = 1; + + } else { + net = &value[0]; + del = 0; + } + + if (ngx_stream_geo_cidr_value(cf, net, &cidr) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (cidr.family == AF_INET) { + cidr.u.in.addr = ntohl(cidr.u.in.addr); + cidr.u.in.mask = ntohl(cidr.u.in.mask); + } + + if (del) { + switch (cidr.family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + rc = ngx_radix128tree_delete(ctx->tree6, + cidr.u.in6.addr.s6_addr, + cidr.u.in6.mask.s6_addr); + break; +#endif + + default: /* AF_INET */ + rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr, + cidr.u.in.mask); + break; + } + + if (rc != NGX_OK) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "no network \"%V\" to delete", net); + } + + return NGX_CONF_OK; + } + + return ngx_stream_geo_cidr_add(cf, ctx, &cidr, &value[1], net); +} + + +static char * +ngx_stream_geo_cidr_add(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx, + ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net) +{ + ngx_int_t rc; + ngx_stream_variable_value_t *val, *old; + + val = ngx_stream_geo_value(cf, ctx, value); + + if (val == NULL) { + return NGX_CONF_ERROR; + } + + switch (cidr->family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr, + cidr->u.in6.mask.s6_addr, + (uintptr_t) val); + + if (rc == NGX_OK) { + return NGX_CONF_OK; + } + + if (rc == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + /* rc == NGX_BUSY */ + + old = (ngx_stream_variable_value_t *) + ngx_radix128tree_find(ctx->tree6, + cidr->u.in6.addr.s6_addr); + + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "duplicate network \"%V\", value: \"%v\", old value: \"%v\"", + net, val, old); + + rc = ngx_radix128tree_delete(ctx->tree6, + cidr->u.in6.addr.s6_addr, + cidr->u.in6.mask.s6_addr); + + if (rc == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree"); + return NGX_CONF_ERROR; + } + + rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr, + cidr->u.in6.mask.s6_addr, + (uintptr_t) val); + + break; +#endif + + default: /* AF_INET */ + rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr, + cidr->u.in.mask, (uintptr_t) val); + + if (rc == NGX_OK) { + return NGX_CONF_OK; + } + + if (rc == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + /* rc == NGX_BUSY */ + + old = (ngx_stream_variable_value_t *) + ngx_radix32tree_find(ctx->tree, cidr->u.in.addr); + + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "duplicate network \"%V\", value: \"%v\", old value: \"%v\"", + net, val, old); + + rc = ngx_radix32tree_delete(ctx->tree, + cidr->u.in.addr, cidr->u.in.mask); + + if (rc == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree"); + return NGX_CONF_ERROR; + } + + rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr, + cidr->u.in.mask, (uintptr_t) val); + + break; + } + + if (rc == NGX_OK) { + return NGX_CONF_OK; + } + + return NGX_CONF_ERROR; +} + + +static ngx_stream_variable_value_t * +ngx_stream_geo_value(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx, + ngx_str_t *value) +{ + uint32_t hash; + ngx_stream_variable_value_t *val; + ngx_stream_geo_variable_value_node_t *gvvn; + + hash = ngx_crc32_long(value->data, value->len); + + gvvn = (ngx_stream_geo_variable_value_node_t *) + ngx_str_rbtree_lookup(&ctx->rbtree, value, hash); + + if (gvvn) { + return gvvn->value; + } + + val = ngx_pcalloc(ctx->pool, sizeof(ngx_stream_variable_value_t)); + if (val == NULL) { + return NULL; + } + + val->len = value->len; + val->data = ngx_pstrdup(ctx->pool, value); + if (val->data == NULL) { + return NULL; + } + + val->valid = 1; + + gvvn = ngx_palloc(ctx->temp_pool, + sizeof(ngx_stream_geo_variable_value_node_t)); + if (gvvn == NULL) { + return NULL; + } + + gvvn->sn.node.key = hash; + gvvn->sn.str.len = val->len; + gvvn->sn.str.data = val->data; + gvvn->value = val; + gvvn->offset = 0; + + ngx_rbtree_insert(&ctx->rbtree, &gvvn->sn.node); + + ctx->data_size += ngx_align(sizeof(ngx_stream_variable_value_t) + + value->len, sizeof(void *)); + + return val; +} + + +static ngx_int_t +ngx_stream_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr) +{ + ngx_int_t rc; + + if (ngx_strcmp(net->data, "255.255.255.255") == 0) { + cidr->family = AF_INET; + cidr->u.in.addr = 0xffffffff; + cidr->u.in.mask = 0xffffffff; + + return NGX_OK; + } + + rc = ngx_ptocidr(net, cidr); + + if (rc == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net); + return NGX_ERROR; + } + + if (rc == NGX_DONE) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "low address bits of %V are meaningless", net); + } + + return NGX_OK; +} + + +static char * +ngx_stream_geo_include(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx, + ngx_str_t *name) +{ + char *rv; + ngx_str_t file; + + file.len = name->len + 4; + file.data = ngx_pnalloc(ctx->temp_pool, name->len + 5); + if (file.data == NULL) { + return NGX_CONF_ERROR; + } + + ngx_sprintf(file.data, "%V.bin%Z", name); + + if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (ctx->ranges) { + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); + + switch (ngx_stream_geo_include_binary_base(cf, ctx, &file)) { + case NGX_OK: + return NGX_CONF_OK; + case NGX_ERROR: + return NGX_CONF_ERROR; + default: + break; + } + } + + file.len -= 4; + file.data[file.len] = '\0'; + + ctx->include_name = file; + + if (ctx->outside_entries) { + ctx->allow_binary_include = 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); + + rv = ngx_conf_parse(cf, &file); + + ctx->includes++; + ctx->outside_entries = 0; + + return rv; +} + + +static ngx_int_t +ngx_stream_geo_include_binary_base(ngx_conf_t *cf, + ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *name) +{ + u_char *base, ch; + time_t mtime; + size_t size, len; + ssize_t n; + uint32_t crc32; + ngx_err_t err; + ngx_int_t rc; + ngx_uint_t i; + ngx_file_t file; + ngx_file_info_t fi; + ngx_stream_geo_range_t *range, **ranges; + ngx_stream_geo_header_t *header; + ngx_stream_variable_value_t *vv; + + ngx_memzero(&file, sizeof(ngx_file_t)); + file.name = *name; + file.log = cf->log; + + file.fd = ngx_open_file(name->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); + + if (file.fd == NGX_INVALID_FILE) { + err = ngx_errno; + if (err != NGX_ENOENT) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, err, + ngx_open_file_n " \"%s\" failed", name->data); + } + return NGX_DECLINED; + } + + if (ctx->outside_entries) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "binary geo range base \"%s\" cannot be mixed with usual entries", + name->data); + rc = NGX_ERROR; + goto done; + } + + if (ctx->binary_include) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "second binary geo range base \"%s\" cannot be mixed with \"%s\"", + name->data, ctx->include_name.data); + rc = NGX_ERROR; + goto done; + } + + if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, + ngx_fd_info_n " \"%s\" failed", name->data); + goto failed; + } + + size = (size_t) ngx_file_size(&fi); + mtime = ngx_file_mtime(&fi); + + ch = name->data[name->len - 4]; + name->data[name->len - 4] = '\0'; + + if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, + ngx_file_info_n " \"%s\" failed", name->data); + goto failed; + } + + name->data[name->len - 4] = ch; + + if (mtime < ngx_file_mtime(&fi)) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "stale binary geo range base \"%s\"", name->data); + goto failed; + } + + base = ngx_palloc(ctx->pool, size); + if (base == NULL) { + goto failed; + } + + n = ngx_read_file(&file, base, size, 0); + + if (n == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno, + ngx_read_file_n " \"%s\" failed", name->data); + goto failed; + } + + if ((size_t) n != size) { + ngx_conf_log_error(NGX_LOG_CRIT, cf, 0, + ngx_read_file_n " \"%s\" returned only %z bytes instead of %z", + name->data, n, size); + goto failed; + } + + header = (ngx_stream_geo_header_t *) base; + + if (size < 16 || ngx_memcmp(&ngx_stream_geo_header, header, 12) != 0) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "incompatible binary geo range base \"%s\"", name->data); + goto failed; + } + + ngx_crc32_init(crc32); + + vv = (ngx_stream_variable_value_t *) + (base + sizeof(ngx_stream_geo_header_t)); + + while (vv->data) { + len = ngx_align(sizeof(ngx_stream_variable_value_t) + vv->len, + sizeof(void *)); + ngx_crc32_update(&crc32, (u_char *) vv, len); + vv->data += (size_t) base; + vv = (ngx_stream_variable_value_t *) ((u_char *) vv + len); + } + ngx_crc32_update(&crc32, (u_char *) vv, + sizeof(ngx_stream_variable_value_t)); + vv++; + + ranges = (ngx_stream_geo_range_t **) vv; + + for (i = 0; i < 0x10000; i++) { + ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *)); + if (ranges[i]) { + ranges[i] = (ngx_stream_geo_range_t *) + ((u_char *) ranges[i] + (size_t) base); + } + } + + range = (ngx_stream_geo_range_t *) &ranges[0x10000]; + + while ((u_char *) range < base + size) { + while (range->value) { + ngx_crc32_update(&crc32, (u_char *) range, + sizeof(ngx_stream_geo_range_t)); + range->value = (ngx_stream_variable_value_t *) + ((u_char *) range->value + (size_t) base); + range++; + } + ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *)); + range = (ngx_stream_geo_range_t *) ((u_char *) range + sizeof(void *)); + } + + ngx_crc32_final(crc32); + + if (crc32 != header->crc32) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "CRC32 mismatch in binary geo range base \"%s\"", name->data); + goto failed; + } + + ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0, + "using binary geo range base \"%s\"", name->data); + + ctx->include_name = *name; + ctx->binary_include = 1; + ctx->high.low = ranges; + rc = NGX_OK; + + goto done; + +failed: + + rc = NGX_DECLINED; + +done: + + if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", name->data); + } + + return rc; +} + + +static void +ngx_stream_geo_create_binary_base(ngx_stream_geo_conf_ctx_t *ctx) +{ + u_char *p; + uint32_t hash; + ngx_str_t s; + ngx_uint_t i; + ngx_file_mapping_t fm; + ngx_stream_geo_range_t *r, *range, **ranges; + ngx_stream_geo_header_t *header; + ngx_stream_geo_variable_value_node_t *gvvn; + + fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5); + if (fm.name == NULL) { + return; + } + + ngx_sprintf(fm.name, "%V.bin%Z", &ctx->include_name); + + fm.size = ctx->data_size; + fm.log = ctx->pool->log; + + ngx_log_error(NGX_LOG_NOTICE, fm.log, 0, + "creating binary geo range base \"%s\"", fm.name); + + if (ngx_create_file_mapping(&fm) != NGX_OK) { + return; + } + + p = ngx_cpymem(fm.addr, &ngx_stream_geo_header, + sizeof(ngx_stream_geo_header_t)); + + p = ngx_stream_geo_copy_values(fm.addr, p, ctx->rbtree.root, + ctx->rbtree.sentinel); + + p += sizeof(ngx_stream_variable_value_t); + + ranges = (ngx_stream_geo_range_t **) p; + + p += 0x10000 * sizeof(ngx_stream_geo_range_t *); + + for (i = 0; i < 0x10000; i++) { + r = ctx->high.low[i]; + if (r == NULL) { + continue; + } + + range = (ngx_stream_geo_range_t *) p; + ranges[i] = (ngx_stream_geo_range_t *) (p - (u_char *) fm.addr); + + do { + s.len = r->value->len; + s.data = r->value->data; + hash = ngx_crc32_long(s.data, s.len); + gvvn = (ngx_stream_geo_variable_value_node_t *) + ngx_str_rbtree_lookup(&ctx->rbtree, &s, hash); + + range->value = (ngx_stream_variable_value_t *) gvvn->offset; + range->start = r->start; + range->end = r->end; + range++; + + } while ((++r)->value); + + range->value = NULL; + + p = (u_char *) range + sizeof(void *); + } + + header = fm.addr; + header->crc32 = ngx_crc32_long((u_char *) fm.addr + + sizeof(ngx_stream_geo_header_t), + fm.size - sizeof(ngx_stream_geo_header_t)); + + ngx_close_file_mapping(&fm); +} + + +static u_char * +ngx_stream_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node, + ngx_rbtree_node_t *sentinel) +{ + ngx_stream_variable_value_t *vv; + ngx_stream_geo_variable_value_node_t *gvvn; + + if (node == sentinel) { + return p; + } + + gvvn = (ngx_stream_geo_variable_value_node_t *) node; + gvvn->offset = p - base; + + vv = (ngx_stream_variable_value_t *) p; + *vv = *gvvn->value; + p += sizeof(ngx_stream_variable_value_t); + vv->data = (u_char *) (p - base); + + p = ngx_cpymem(p, gvvn->sn.str.data, gvvn->sn.str.len); + + p = ngx_align_ptr(p, sizeof(void *)); + + p = ngx_stream_geo_copy_values(base, p, node->left, sentinel); + + return ngx_stream_geo_copy_values(base, p, node->right, sentinel); +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_geoip_module.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_geoip_module.c new file mode 100644 index 000000000..3ee8f0e33 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_geoip_module.c @@ -0,0 +1,814 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + +#include +#include + + +#define NGX_GEOIP_COUNTRY_CODE 0 +#define NGX_GEOIP_COUNTRY_CODE3 1 +#define NGX_GEOIP_COUNTRY_NAME 2 + + +typedef struct { + GeoIP *country; + GeoIP *org; + GeoIP *city; +#if (NGX_HAVE_GEOIP_V6) + unsigned country_v6:1; + unsigned org_v6:1; + unsigned city_v6:1; +#endif +} ngx_stream_geoip_conf_t; + + +typedef struct { + ngx_str_t *name; + uintptr_t data; +} ngx_stream_geoip_var_t; + + +typedef const char *(*ngx_stream_geoip_variable_handler_pt)(GeoIP *, + u_long addr); + + +ngx_stream_geoip_variable_handler_pt ngx_stream_geoip_country_functions[] = { + GeoIP_country_code_by_ipnum, + GeoIP_country_code3_by_ipnum, + GeoIP_country_name_by_ipnum, +}; + + +#if (NGX_HAVE_GEOIP_V6) + +typedef const char *(*ngx_stream_geoip_variable_handler_v6_pt)(GeoIP *, + geoipv6_t addr); + + +ngx_stream_geoip_variable_handler_v6_pt + ngx_stream_geoip_country_v6_functions[] = +{ + GeoIP_country_code_by_ipnum_v6, + GeoIP_country_code3_by_ipnum_v6, + GeoIP_country_name_by_ipnum_v6, +}; + +#endif + + +static ngx_int_t ngx_stream_geoip_country_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_geoip_org_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_geoip_city_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_geoip_region_name_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_geoip_city_float_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_geoip_city_int_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static GeoIPRecord *ngx_stream_geoip_get_city_record(ngx_stream_session_t *s); + +static ngx_int_t ngx_stream_geoip_add_variables(ngx_conf_t *cf); +static void *ngx_stream_geoip_create_conf(ngx_conf_t *cf); +static char *ngx_stream_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static void ngx_stream_geoip_cleanup(void *data); + + +static ngx_command_t ngx_stream_geoip_commands[] = { + + { ngx_string("geoip_country"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE12, + ngx_stream_geoip_country, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("geoip_org"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE12, + ngx_stream_geoip_org, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("geoip_city"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE12, + ngx_stream_geoip_city, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_geoip_module_ctx = { + ngx_stream_geoip_add_variables, /* preconfiguration */ + NULL, /* postconfiguration */ + + ngx_stream_geoip_create_conf, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_geoip_module = { + NGX_MODULE_V1, + &ngx_stream_geoip_module_ctx, /* module context */ + ngx_stream_geoip_commands, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_stream_variable_t ngx_stream_geoip_vars[] = { + + { ngx_string("geoip_country_code"), NULL, + ngx_stream_geoip_country_variable, + NGX_GEOIP_COUNTRY_CODE, 0, 0 }, + + { ngx_string("geoip_country_code3"), NULL, + ngx_stream_geoip_country_variable, + NGX_GEOIP_COUNTRY_CODE3, 0, 0 }, + + { ngx_string("geoip_country_name"), NULL, + ngx_stream_geoip_country_variable, + NGX_GEOIP_COUNTRY_NAME, 0, 0 }, + + { ngx_string("geoip_org"), NULL, + ngx_stream_geoip_org_variable, + 0, 0, 0 }, + + { ngx_string("geoip_city_continent_code"), NULL, + ngx_stream_geoip_city_variable, + offsetof(GeoIPRecord, continent_code), 0, 0 }, + + { ngx_string("geoip_city_country_code"), NULL, + ngx_stream_geoip_city_variable, + offsetof(GeoIPRecord, country_code), 0, 0 }, + + { ngx_string("geoip_city_country_code3"), NULL, + ngx_stream_geoip_city_variable, + offsetof(GeoIPRecord, country_code3), 0, 0 }, + + { ngx_string("geoip_city_country_name"), NULL, + ngx_stream_geoip_city_variable, + offsetof(GeoIPRecord, country_name), 0, 0 }, + + { ngx_string("geoip_region"), NULL, + ngx_stream_geoip_city_variable, + offsetof(GeoIPRecord, region), 0, 0 }, + + { ngx_string("geoip_region_name"), NULL, + ngx_stream_geoip_region_name_variable, + 0, 0, 0 }, + + { ngx_string("geoip_city"), NULL, + ngx_stream_geoip_city_variable, + offsetof(GeoIPRecord, city), 0, 0 }, + + { ngx_string("geoip_postal_code"), NULL, + ngx_stream_geoip_city_variable, + offsetof(GeoIPRecord, postal_code), 0, 0 }, + + { ngx_string("geoip_latitude"), NULL, + ngx_stream_geoip_city_float_variable, + offsetof(GeoIPRecord, latitude), 0, 0 }, + + { ngx_string("geoip_longitude"), NULL, + ngx_stream_geoip_city_float_variable, + offsetof(GeoIPRecord, longitude), 0, 0 }, + + { ngx_string("geoip_dma_code"), NULL, + ngx_stream_geoip_city_int_variable, + offsetof(GeoIPRecord, dma_code), 0, 0 }, + + { ngx_string("geoip_area_code"), NULL, + ngx_stream_geoip_city_int_variable, + offsetof(GeoIPRecord, area_code), 0, 0 }, + + ngx_stream_null_variable +}; + + +static u_long +ngx_stream_geoip_addr(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf) +{ + ngx_addr_t addr; + struct sockaddr_in *sin; + + addr.sockaddr = s->connection->sockaddr; + addr.socklen = s->connection->socklen; + /* addr.name = s->connection->addr_text; */ + +#if (NGX_HAVE_INET6) + + if (addr.sockaddr->sa_family == AF_INET6) { + u_char *p; + in_addr_t inaddr; + struct in6_addr *inaddr6; + + inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr; + + if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { + p = inaddr6->s6_addr; + + inaddr = (in_addr_t) p[12] << 24; + inaddr += p[13] << 16; + inaddr += p[14] << 8; + inaddr += p[15]; + + return inaddr; + } + } + +#endif + + if (addr.sockaddr->sa_family != AF_INET) { + return INADDR_NONE; + } + + sin = (struct sockaddr_in *) addr.sockaddr; + return ntohl(sin->sin_addr.s_addr); +} + + +#if (NGX_HAVE_GEOIP_V6) + +static geoipv6_t +ngx_stream_geoip_addr_v6(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf) +{ + ngx_addr_t addr; + in_addr_t addr4; + struct in6_addr addr6; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + addr.sockaddr = s->connection->sockaddr; + addr.socklen = s->connection->socklen; + /* addr.name = s->connection->addr_text; */ + + switch (addr.sockaddr->sa_family) { + + case AF_INET: + /* Produce IPv4-mapped IPv6 address. */ + sin = (struct sockaddr_in *) addr.sockaddr; + addr4 = ntohl(sin->sin_addr.s_addr); + + ngx_memzero(&addr6, sizeof(struct in6_addr)); + addr6.s6_addr[10] = 0xff; + addr6.s6_addr[11] = 0xff; + addr6.s6_addr[12] = addr4 >> 24; + addr6.s6_addr[13] = addr4 >> 16; + addr6.s6_addr[14] = addr4 >> 8; + addr6.s6_addr[15] = addr4; + return addr6; + + case AF_INET6: + sin6 = (struct sockaddr_in6 *) addr.sockaddr; + return sin6->sin6_addr; + + default: + return in6addr_any; + } +} + +#endif + + +static ngx_int_t +ngx_stream_geoip_country_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_stream_geoip_variable_handler_pt handler = + ngx_stream_geoip_country_functions[data]; +#if (NGX_HAVE_GEOIP_V6) + ngx_stream_geoip_variable_handler_v6_pt handler_v6 = + ngx_stream_geoip_country_v6_functions[data]; +#endif + + const char *val; + ngx_stream_geoip_conf_t *gcf; + + gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module); + + if (gcf->country == NULL) { + goto not_found; + } + +#if (NGX_HAVE_GEOIP_V6) + val = gcf->country_v6 + ? handler_v6(gcf->country, ngx_stream_geoip_addr_v6(s, gcf)) + : handler(gcf->country, ngx_stream_geoip_addr(s, gcf)); +#else + val = handler(gcf->country, ngx_stream_geoip_addr(s, gcf)); +#endif + + if (val == NULL) { + goto not_found; + } + + v->len = ngx_strlen(val); + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) val; + + return NGX_OK; + +not_found: + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_geoip_org_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + size_t len; + char *val; + ngx_stream_geoip_conf_t *gcf; + + gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module); + + if (gcf->org == NULL) { + goto not_found; + } + +#if (NGX_HAVE_GEOIP_V6) + val = gcf->org_v6 + ? GeoIP_name_by_ipnum_v6(gcf->org, + ngx_stream_geoip_addr_v6(s, gcf)) + : GeoIP_name_by_ipnum(gcf->org, + ngx_stream_geoip_addr(s, gcf)); +#else + val = GeoIP_name_by_ipnum(gcf->org, ngx_stream_geoip_addr(s, gcf)); +#endif + + if (val == NULL) { + goto not_found; + } + + len = ngx_strlen(val); + v->data = ngx_pnalloc(s->connection->pool, len); + if (v->data == NULL) { + ngx_free(val); + return NGX_ERROR; + } + + ngx_memcpy(v->data, val, len); + + v->len = len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + ngx_free(val); + + return NGX_OK; + +not_found: + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_geoip_city_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + char *val; + size_t len; + GeoIPRecord *gr; + + gr = ngx_stream_geoip_get_city_record(s); + if (gr == NULL) { + goto not_found; + } + + val = *(char **) ((char *) gr + data); + if (val == NULL) { + goto no_value; + } + + len = ngx_strlen(val); + v->data = ngx_pnalloc(s->connection->pool, len); + if (v->data == NULL) { + GeoIPRecord_delete(gr); + return NGX_ERROR; + } + + ngx_memcpy(v->data, val, len); + + v->len = len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + GeoIPRecord_delete(gr); + + return NGX_OK; + +no_value: + + GeoIPRecord_delete(gr); + +not_found: + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_geoip_region_name_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + size_t len; + const char *val; + GeoIPRecord *gr; + + gr = ngx_stream_geoip_get_city_record(s); + if (gr == NULL) { + goto not_found; + } + + val = GeoIP_region_name_by_code(gr->country_code, gr->region); + + GeoIPRecord_delete(gr); + + if (val == NULL) { + goto not_found; + } + + len = ngx_strlen(val); + v->data = ngx_pnalloc(s->connection->pool, len); + if (v->data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(v->data, val, len); + + v->len = len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; + +not_found: + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_geoip_city_float_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + float val; + GeoIPRecord *gr; + + gr = ngx_stream_geoip_get_city_record(s); + if (gr == NULL) { + v->not_found = 1; + return NGX_OK; + } + + v->data = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN + 5); + if (v->data == NULL) { + GeoIPRecord_delete(gr); + return NGX_ERROR; + } + + val = *(float *) ((char *) gr + data); + + v->len = ngx_sprintf(v->data, "%.4f", val) - v->data; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + GeoIPRecord_delete(gr); + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_geoip_city_int_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + int val; + GeoIPRecord *gr; + + gr = ngx_stream_geoip_get_city_record(s); + if (gr == NULL) { + v->not_found = 1; + return NGX_OK; + } + + v->data = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN); + if (v->data == NULL) { + GeoIPRecord_delete(gr); + return NGX_ERROR; + } + + val = *(int *) ((char *) gr + data); + + v->len = ngx_sprintf(v->data, "%d", val) - v->data; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + GeoIPRecord_delete(gr); + + return NGX_OK; +} + + +static GeoIPRecord * +ngx_stream_geoip_get_city_record(ngx_stream_session_t *s) +{ + ngx_stream_geoip_conf_t *gcf; + + gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module); + + if (gcf->city) { +#if (NGX_HAVE_GEOIP_V6) + return gcf->city_v6 + ? GeoIP_record_by_ipnum_v6(gcf->city, + ngx_stream_geoip_addr_v6(s, gcf)) + : GeoIP_record_by_ipnum(gcf->city, + ngx_stream_geoip_addr(s, gcf)); +#else + return GeoIP_record_by_ipnum(gcf->city, ngx_stream_geoip_addr(s, gcf)); +#endif + } + + return NULL; +} + + +static ngx_int_t +ngx_stream_geoip_add_variables(ngx_conf_t *cf) +{ + ngx_stream_variable_t *var, *v; + + for (v = ngx_stream_geoip_vars; v->name.len; v++) { + var = ngx_stream_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + +static void * +ngx_stream_geoip_create_conf(ngx_conf_t *cf) +{ + ngx_pool_cleanup_t *cln; + ngx_stream_geoip_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_geoip_conf_t)); + if (conf == NULL) { + return NULL; + } + + cln = ngx_pool_cleanup_add(cf->pool, 0); + if (cln == NULL) { + return NULL; + } + + cln->handler = ngx_stream_geoip_cleanup; + cln->data = conf; + + return conf; +} + + +static char * +ngx_stream_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_geoip_conf_t *gcf = conf; + + ngx_str_t *value; + + if (gcf->country) { + return "is duplicate"; + } + + value = cf->args->elts; + + gcf->country = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE); + + if (gcf->country == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "GeoIP_open(\"%V\") failed", &value[1]); + + return NGX_CONF_ERROR; + } + + if (cf->args->nelts == 3) { + if (ngx_strcmp(value[2].data, "utf8") == 0) { + GeoIP_set_charset(gcf->country, GEOIP_CHARSET_UTF8); + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + } + + switch (gcf->country->databaseType) { + + case GEOIP_COUNTRY_EDITION: + + return NGX_CONF_OK; + +#if (NGX_HAVE_GEOIP_V6) + case GEOIP_COUNTRY_EDITION_V6: + + gcf->country_v6 = 1; + return NGX_CONF_OK; +#endif + + default: + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid GeoIP database \"%V\" type:%d", + &value[1], gcf->country->databaseType); + return NGX_CONF_ERROR; + } +} + + +static char * +ngx_stream_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_geoip_conf_t *gcf = conf; + + ngx_str_t *value; + + if (gcf->org) { + return "is duplicate"; + } + + value = cf->args->elts; + + gcf->org = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE); + + if (gcf->org == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "GeoIP_open(\"%V\") failed", &value[1]); + + return NGX_CONF_ERROR; + } + + if (cf->args->nelts == 3) { + if (ngx_strcmp(value[2].data, "utf8") == 0) { + GeoIP_set_charset(gcf->org, GEOIP_CHARSET_UTF8); + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + } + + switch (gcf->org->databaseType) { + + case GEOIP_ISP_EDITION: + case GEOIP_ORG_EDITION: + case GEOIP_DOMAIN_EDITION: + case GEOIP_ASNUM_EDITION: + + return NGX_CONF_OK; + +#if (NGX_HAVE_GEOIP_V6) + case GEOIP_ISP_EDITION_V6: + case GEOIP_ORG_EDITION_V6: + case GEOIP_DOMAIN_EDITION_V6: + case GEOIP_ASNUM_EDITION_V6: + + gcf->org_v6 = 1; + return NGX_CONF_OK; +#endif + + default: + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid GeoIP database \"%V\" type:%d", + &value[1], gcf->org->databaseType); + return NGX_CONF_ERROR; + } +} + + +static char * +ngx_stream_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_geoip_conf_t *gcf = conf; + + ngx_str_t *value; + + if (gcf->city) { + return "is duplicate"; + } + + value = cf->args->elts; + + gcf->city = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE); + + if (gcf->city == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "GeoIP_open(\"%V\") failed", &value[1]); + + return NGX_CONF_ERROR; + } + + if (cf->args->nelts == 3) { + if (ngx_strcmp(value[2].data, "utf8") == 0) { + GeoIP_set_charset(gcf->city, GEOIP_CHARSET_UTF8); + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + } + + switch (gcf->city->databaseType) { + + case GEOIP_CITY_EDITION_REV0: + case GEOIP_CITY_EDITION_REV1: + + return NGX_CONF_OK; + +#if (NGX_HAVE_GEOIP_V6) + case GEOIP_CITY_EDITION_REV0_V6: + case GEOIP_CITY_EDITION_REV1_V6: + + gcf->city_v6 = 1; + return NGX_CONF_OK; +#endif + + default: + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid GeoIP City database \"%V\" type:%d", + &value[1], gcf->city->databaseType); + return NGX_CONF_ERROR; + } +} + + +static void +ngx_stream_geoip_cleanup(void *data) +{ + ngx_stream_geoip_conf_t *gcf = data; + + if (gcf->country) { + GeoIP_delete(gcf->country); + } + + if (gcf->org) { + GeoIP_delete(gcf->org); + } + + if (gcf->city) { + GeoIP_delete(gcf->city); + } +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_handler.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_handler.c new file mode 100644 index 000000000..a7ffc6e61 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_handler.c @@ -0,0 +1,389 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include + + +static void ngx_stream_log_session(ngx_stream_session_t *s); +static void ngx_stream_close_connection(ngx_connection_t *c); +static u_char *ngx_stream_log_error(ngx_log_t *log, u_char *buf, size_t len); +static void ngx_stream_proxy_protocol_handler(ngx_event_t *rev); + + +void +ngx_stream_init_connection(ngx_connection_t *c) +{ + u_char text[NGX_SOCKADDR_STRLEN]; + size_t len; + ngx_uint_t i; + ngx_time_t *tp; + ngx_event_t *rev; + struct sockaddr *sa; + ngx_stream_port_t *port; + struct sockaddr_in *sin; + ngx_stream_in_addr_t *addr; + ngx_stream_session_t *s; + ngx_stream_conf_ctx_t *ctx; + ngx_stream_addr_conf_t *addr_conf; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; + ngx_stream_in6_addr_t *addr6; +#endif + ngx_stream_core_srv_conf_t *cscf; + ngx_stream_core_main_conf_t *cmcf; + + /* find the server configuration for the address:port */ + + port = c->listening->servers; + + if (port->naddrs > 1) { + + /* + * There are several addresses on this port and one of them + * is the "*:port" wildcard so getsockname() is needed to determine + * the server address. + * + * AcceptEx() and recvmsg() already gave this address. + */ + + if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { + ngx_stream_close_connection(c); + return; + } + + sa = c->local_sockaddr; + + switch (sa->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) sa; + + addr6 = port->addrs; + + /* the last address is "*" */ + + for (i = 0; i < port->naddrs - 1; i++) { + if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) { + break; + } + } + + addr_conf = &addr6[i].conf; + + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) sa; + + addr = port->addrs; + + /* the last address is "*" */ + + for (i = 0; i < port->naddrs - 1; i++) { + if (addr[i].addr == sin->sin_addr.s_addr) { + break; + } + } + + addr_conf = &addr[i].conf; + + break; + } + + } else { + switch (c->local_sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + addr6 = port->addrs; + addr_conf = &addr6[0].conf; + break; +#endif + + default: /* AF_INET */ + addr = port->addrs; + addr_conf = &addr[0].conf; + break; + } + } + + s = ngx_pcalloc(c->pool, sizeof(ngx_stream_session_t)); + if (s == NULL) { + ngx_stream_close_connection(c); + return; + } + + ctx = addr_conf->default_server->ctx; + + s->signature = NGX_STREAM_MODULE; + s->main_conf = ctx->main_conf; + s->srv_conf = ctx->srv_conf; + s->virtual_names = addr_conf->virtual_names; + +#if (NGX_STREAM_SSL) + s->ssl = addr_conf->ssl; +#endif + + if (c->buffer) { + s->received += c->buffer->last - c->buffer->pos; + } + + s->connection = c; + c->data = s; + + cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); + + ngx_set_connection_log(c, cscf->error_log); + + len = ngx_sock_ntop(c->sockaddr, c->socklen, text, NGX_SOCKADDR_STRLEN, 1); + + ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%uA %sclient %*s connected to %V", + c->number, c->type == SOCK_DGRAM ? "udp " : "", + len, text, &c->listening->addr_text); + + c->log->connection = c->number; + c->log->handler = ngx_stream_log_error; + c->log->data = s; + c->log->action = "initializing session"; + c->log_error = NGX_ERROR_INFO; + + s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_stream_max_module); + if (s->ctx == NULL) { + ngx_stream_close_connection(c); + return; + } + + cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); + + s->variables = ngx_pcalloc(s->connection->pool, + cmcf->variables.nelts + * sizeof(ngx_stream_variable_value_t)); + + if (s->variables == NULL) { + ngx_stream_close_connection(c); + return; + } + + tp = ngx_timeofday(); + s->start_sec = tp->sec; + s->start_msec = tp->msec; + + rev = c->read; + rev->handler = ngx_stream_session_handler; + + if (addr_conf->proxy_protocol) { + c->log->action = "reading PROXY protocol"; + + rev->handler = ngx_stream_proxy_protocol_handler; + + if (!rev->ready) { + ngx_add_timer(rev, cscf->proxy_protocol_timeout); + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_stream_finalize_session(s, + NGX_STREAM_INTERNAL_SERVER_ERROR); + } + + return; + } + } + + if (ngx_use_accept_mutex) { + ngx_post_event(rev, &ngx_posted_events); + return; + } + + rev->handler(rev); +} + + +static void +ngx_stream_proxy_protocol_handler(ngx_event_t *rev) +{ + u_char *p, buf[NGX_PROXY_PROTOCOL_MAX_HEADER]; + size_t size; + ssize_t n; + ngx_err_t err; + ngx_connection_t *c; + ngx_stream_session_t *s; + ngx_stream_core_srv_conf_t *cscf; + + c = rev->data; + s = c->data; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream PROXY protocol handler"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + ngx_stream_finalize_session(s, NGX_STREAM_OK); + return; + } + + n = recv(c->fd, (char *) buf, sizeof(buf), MSG_PEEK); + + err = ngx_socket_errno; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, "recv(): %z", n); + + if (n == -1) { + if (err == NGX_EAGAIN) { + rev->ready = 0; + + if (!rev->timer_set) { + cscf = ngx_stream_get_module_srv_conf(s, + ngx_stream_core_module); + + ngx_add_timer(rev, cscf->proxy_protocol_timeout); + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_stream_finalize_session(s, + NGX_STREAM_INTERNAL_SERVER_ERROR); + } + + return; + } + + ngx_connection_error(c, err, "recv() failed"); + + ngx_stream_finalize_session(s, NGX_STREAM_OK); + return; + } + + if (rev->timer_set) { + ngx_del_timer(rev); + } + + p = ngx_proxy_protocol_read(c, buf, buf + n); + + if (p == NULL) { + ngx_stream_finalize_session(s, NGX_STREAM_BAD_REQUEST); + return; + } + + size = p - buf; + + if (c->recv(c, buf, size) != (ssize_t) size) { + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + c->log->action = "initializing session"; + + ngx_stream_session_handler(rev); +} + + +void +ngx_stream_session_handler(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_stream_session_t *s; + + c = rev->data; + s = c->data; + + ngx_stream_core_run_phases(s); +} + + +void +ngx_stream_finalize_session(ngx_stream_session_t *s, ngx_uint_t rc) +{ + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "finalize stream session: %i", rc); + + s->status = rc; + + ngx_stream_log_session(s); + + ngx_stream_close_connection(s->connection); +} + + +static void +ngx_stream_log_session(ngx_stream_session_t *s) +{ + ngx_uint_t i, n; + ngx_stream_handler_pt *log_handler; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); + + log_handler = cmcf->phases[NGX_STREAM_LOG_PHASE].handlers.elts; + n = cmcf->phases[NGX_STREAM_LOG_PHASE].handlers.nelts; + + for (i = 0; i < n; i++) { + log_handler[i](s); + } +} + + +static void +ngx_stream_close_connection(ngx_connection_t *c) +{ + ngx_pool_t *pool; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "close stream connection: %d", c->fd); + +#if (NGX_STREAM_SSL) + + if (c->ssl) { + if (ngx_ssl_shutdown(c) == NGX_AGAIN) { + c->ssl->handler = ngx_stream_close_connection; + return; + } + } + +#endif + +#if (NGX_STAT_STUB) + (void) ngx_atomic_fetch_add(ngx_stat_active, -1); +#endif + + pool = c->pool; + + ngx_close_connection(c); + + ngx_destroy_pool(pool); +} + + +static u_char * +ngx_stream_log_error(ngx_log_t *log, u_char *buf, size_t len) +{ + u_char *p; + ngx_stream_session_t *s; + + if (log->action) { + p = ngx_snprintf(buf, len, " while %s", log->action); + len -= p - buf; + buf = p; + } + + s = log->data; + + p = ngx_snprintf(buf, len, ", %sclient: %V, server: %V", + s->connection->type == SOCK_DGRAM ? "udp " : "", + &s->connection->addr_text, + &s->connection->listening->addr_text); + len -= p - buf; + buf = p; + + if (s->log_handler) { + p = s->log_handler(log, buf, len); + } + + return p; +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_limit_conn_module.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_limit_conn_module.c new file mode 100644 index 000000000..bae034ad3 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_limit_conn_module.c @@ -0,0 +1,737 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +#define NGX_STREAM_LIMIT_CONN_PASSED 1 +#define NGX_STREAM_LIMIT_CONN_REJECTED 2 +#define NGX_STREAM_LIMIT_CONN_REJECTED_DRY_RUN 3 + + +typedef struct { + u_char color; + u_char len; + u_short conn; + u_char data[1]; +} ngx_stream_limit_conn_node_t; + + +typedef struct { + ngx_shm_zone_t *shm_zone; + ngx_rbtree_node_t *node; +} ngx_stream_limit_conn_cleanup_t; + + +typedef struct { + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; +} ngx_stream_limit_conn_shctx_t; + + +typedef struct { + ngx_stream_limit_conn_shctx_t *sh; + ngx_slab_pool_t *shpool; + ngx_stream_complex_value_t key; +} ngx_stream_limit_conn_ctx_t; + + +typedef struct { + ngx_shm_zone_t *shm_zone; + ngx_uint_t conn; +} ngx_stream_limit_conn_limit_t; + + +typedef struct { + ngx_array_t limits; + ngx_uint_t log_level; + ngx_flag_t dry_run; +} ngx_stream_limit_conn_conf_t; + + +static ngx_rbtree_node_t *ngx_stream_limit_conn_lookup(ngx_rbtree_t *rbtree, + ngx_str_t *key, uint32_t hash); +static void ngx_stream_limit_conn_cleanup(void *data); +static ngx_inline void ngx_stream_limit_conn_cleanup_all(ngx_pool_t *pool); + +static ngx_int_t ngx_stream_limit_conn_status_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static void *ngx_stream_limit_conn_create_conf(ngx_conf_t *cf); +static char *ngx_stream_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, + void *child); +static char *ngx_stream_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static ngx_int_t ngx_stream_limit_conn_add_variables(ngx_conf_t *cf); +static ngx_int_t ngx_stream_limit_conn_init(ngx_conf_t *cf); + + +static ngx_conf_enum_t ngx_stream_limit_conn_log_levels[] = { + { ngx_string("info"), NGX_LOG_INFO }, + { ngx_string("notice"), NGX_LOG_NOTICE }, + { ngx_string("warn"), NGX_LOG_WARN }, + { ngx_string("error"), NGX_LOG_ERR }, + { ngx_null_string, 0 } +}; + + +static ngx_command_t ngx_stream_limit_conn_commands[] = { + + { ngx_string("limit_conn_zone"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE2, + ngx_stream_limit_conn_zone, + 0, + 0, + NULL }, + + { ngx_string("limit_conn"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2, + ngx_stream_limit_conn, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("limit_conn_log_level"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_enum_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_limit_conn_conf_t, log_level), + &ngx_stream_limit_conn_log_levels }, + + { ngx_string("limit_conn_dry_run"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_limit_conn_conf_t, dry_run), + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_limit_conn_module_ctx = { + ngx_stream_limit_conn_add_variables, /* preconfiguration */ + ngx_stream_limit_conn_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_stream_limit_conn_create_conf, /* create server configuration */ + ngx_stream_limit_conn_merge_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_limit_conn_module = { + NGX_MODULE_V1, + &ngx_stream_limit_conn_module_ctx, /* module context */ + ngx_stream_limit_conn_commands, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_stream_variable_t ngx_stream_limit_conn_vars[] = { + + { ngx_string("limit_conn_status"), NULL, + ngx_stream_limit_conn_status_variable, 0, NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + ngx_stream_null_variable +}; + + +static ngx_str_t ngx_stream_limit_conn_status[] = { + ngx_string("PASSED"), + ngx_string("REJECTED"), + ngx_string("REJECTED_DRY_RUN") +}; + + +static ngx_int_t +ngx_stream_limit_conn_handler(ngx_stream_session_t *s) +{ + size_t n; + uint32_t hash; + ngx_str_t key; + ngx_uint_t i; + ngx_rbtree_node_t *node; + ngx_pool_cleanup_t *cln; + ngx_stream_limit_conn_ctx_t *ctx; + ngx_stream_limit_conn_node_t *lc; + ngx_stream_limit_conn_conf_t *lccf; + ngx_stream_limit_conn_limit_t *limits; + ngx_stream_limit_conn_cleanup_t *lccln; + + lccf = ngx_stream_get_module_srv_conf(s, ngx_stream_limit_conn_module); + limits = lccf->limits.elts; + + for (i = 0; i < lccf->limits.nelts; i++) { + ctx = limits[i].shm_zone->data; + + if (ngx_stream_complex_value(s, &ctx->key, &key) != NGX_OK) { + return NGX_ERROR; + } + + if (key.len == 0) { + continue; + } + + if (key.len > 255) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "the value of the \"%V\" key " + "is more than 255 bytes: \"%V\"", + &ctx->key.value, &key); + continue; + } + + s->limit_conn_status = NGX_STREAM_LIMIT_CONN_PASSED; + + hash = ngx_crc32_short(key.data, key.len); + + ngx_shmtx_lock(&ctx->shpool->mutex); + + node = ngx_stream_limit_conn_lookup(&ctx->sh->rbtree, &key, hash); + + if (node == NULL) { + + n = offsetof(ngx_rbtree_node_t, color) + + offsetof(ngx_stream_limit_conn_node_t, data) + + key.len; + + node = ngx_slab_alloc_locked(ctx->shpool, n); + + if (node == NULL) { + ngx_shmtx_unlock(&ctx->shpool->mutex); + ngx_stream_limit_conn_cleanup_all(s->connection->pool); + + if (lccf->dry_run) { + s->limit_conn_status = + NGX_STREAM_LIMIT_CONN_REJECTED_DRY_RUN; + return NGX_DECLINED; + } + + s->limit_conn_status = NGX_STREAM_LIMIT_CONN_REJECTED; + + return NGX_STREAM_SERVICE_UNAVAILABLE; + } + + lc = (ngx_stream_limit_conn_node_t *) &node->color; + + node->key = hash; + lc->len = (u_char) key.len; + lc->conn = 1; + ngx_memcpy(lc->data, key.data, key.len); + + ngx_rbtree_insert(&ctx->sh->rbtree, node); + + } else { + + lc = (ngx_stream_limit_conn_node_t *) &node->color; + + if ((ngx_uint_t) lc->conn >= limits[i].conn) { + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + ngx_log_error(lccf->log_level, s->connection->log, 0, + "limiting connections%s by zone \"%V\"", + lccf->dry_run ? ", dry run," : "", + &limits[i].shm_zone->shm.name); + + ngx_stream_limit_conn_cleanup_all(s->connection->pool); + + if (lccf->dry_run) { + s->limit_conn_status = + NGX_STREAM_LIMIT_CONN_REJECTED_DRY_RUN; + return NGX_DECLINED; + } + + s->limit_conn_status = NGX_STREAM_LIMIT_CONN_REJECTED; + + return NGX_STREAM_SERVICE_UNAVAILABLE; + } + + lc->conn++; + } + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "limit conn: %08Xi %d", node->key, lc->conn); + + ngx_shmtx_unlock(&ctx->shpool->mutex); + + cln = ngx_pool_cleanup_add(s->connection->pool, + sizeof(ngx_stream_limit_conn_cleanup_t)); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->handler = ngx_stream_limit_conn_cleanup; + lccln = cln->data; + + lccln->shm_zone = limits[i].shm_zone; + lccln->node = node; + } + + return NGX_DECLINED; +} + + +static void +ngx_stream_limit_conn_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + ngx_rbtree_node_t **p; + ngx_stream_limit_conn_node_t *lcn, *lcnt; + + for ( ;; ) { + + if (node->key < temp->key) { + + p = &temp->left; + + } else if (node->key > temp->key) { + + p = &temp->right; + + } else { /* node->key == temp->key */ + + lcn = (ngx_stream_limit_conn_node_t *) &node->color; + lcnt = (ngx_stream_limit_conn_node_t *) &temp->color; + + p = (ngx_memn2cmp(lcn->data, lcnt->data, lcn->len, lcnt->len) < 0) + ? &temp->left : &temp->right; + } + + if (*p == sentinel) { + break; + } + + temp = *p; + } + + *p = node; + node->parent = temp; + node->left = sentinel; + node->right = sentinel; + ngx_rbt_red(node); +} + + +static ngx_rbtree_node_t * +ngx_stream_limit_conn_lookup(ngx_rbtree_t *rbtree, ngx_str_t *key, + uint32_t hash) +{ + ngx_int_t rc; + ngx_rbtree_node_t *node, *sentinel; + ngx_stream_limit_conn_node_t *lcn; + + node = rbtree->root; + sentinel = rbtree->sentinel; + + while (node != sentinel) { + + if (hash < node->key) { + node = node->left; + continue; + } + + if (hash > node->key) { + node = node->right; + continue; + } + + /* hash == node->key */ + + lcn = (ngx_stream_limit_conn_node_t *) &node->color; + + rc = ngx_memn2cmp(key->data, lcn->data, key->len, (size_t) lcn->len); + + if (rc == 0) { + return node; + } + + node = (rc < 0) ? node->left : node->right; + } + + return NULL; +} + + +static void +ngx_stream_limit_conn_cleanup(void *data) +{ + ngx_stream_limit_conn_cleanup_t *lccln = data; + + ngx_rbtree_node_t *node; + ngx_stream_limit_conn_ctx_t *ctx; + ngx_stream_limit_conn_node_t *lc; + + ctx = lccln->shm_zone->data; + node = lccln->node; + lc = (ngx_stream_limit_conn_node_t *) &node->color; + + ngx_shmtx_lock(&ctx->shpool->mutex); + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, lccln->shm_zone->shm.log, 0, + "limit conn cleanup: %08Xi %d", node->key, lc->conn); + + lc->conn--; + + if (lc->conn == 0) { + ngx_rbtree_delete(&ctx->sh->rbtree, node); + ngx_slab_free_locked(ctx->shpool, node); + } + + ngx_shmtx_unlock(&ctx->shpool->mutex); +} + + +static ngx_inline void +ngx_stream_limit_conn_cleanup_all(ngx_pool_t *pool) +{ + ngx_pool_cleanup_t *cln; + + cln = pool->cleanup; + + while (cln && cln->handler == ngx_stream_limit_conn_cleanup) { + ngx_stream_limit_conn_cleanup(cln->data); + cln = cln->next; + } + + pool->cleanup = cln; +} + + +static ngx_int_t +ngx_stream_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data) +{ + ngx_stream_limit_conn_ctx_t *octx = data; + + size_t len; + ngx_stream_limit_conn_ctx_t *ctx; + + ctx = shm_zone->data; + + if (octx) { + if (ctx->key.value.len != octx->key.value.len + || ngx_strncmp(ctx->key.value.data, octx->key.value.data, + ctx->key.value.len) + != 0) + { + ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, + "limit_conn_zone \"%V\" uses the \"%V\" key " + "while previously it used the \"%V\" key", + &shm_zone->shm.name, &ctx->key.value, + &octx->key.value); + return NGX_ERROR; + } + + ctx->sh = octx->sh; + ctx->shpool = octx->shpool; + + return NGX_OK; + } + + ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + + if (shm_zone->shm.exists) { + ctx->sh = ctx->shpool->data; + + return NGX_OK; + } + + ctx->sh = ngx_slab_alloc(ctx->shpool, + sizeof(ngx_stream_limit_conn_shctx_t)); + if (ctx->sh == NULL) { + return NGX_ERROR; + } + + ctx->shpool->data = ctx->sh; + + ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel, + ngx_stream_limit_conn_rbtree_insert_value); + + len = sizeof(" in limit_conn_zone \"\"") + shm_zone->shm.name.len; + + ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len); + if (ctx->shpool->log_ctx == NULL) { + return NGX_ERROR; + } + + ngx_sprintf(ctx->shpool->log_ctx, " in limit_conn_zone \"%V\"%Z", + &shm_zone->shm.name); + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_limit_conn_status_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + if (s->limit_conn_status == 0) { + v->not_found = 1; + return NGX_OK; + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->len = ngx_stream_limit_conn_status[s->limit_conn_status - 1].len; + v->data = ngx_stream_limit_conn_status[s->limit_conn_status - 1].data; + + return NGX_OK; +} + + +static void * +ngx_stream_limit_conn_create_conf(ngx_conf_t *cf) +{ + ngx_stream_limit_conn_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_limit_conn_conf_t)); + if (conf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * conf->limits.elts = NULL; + */ + + conf->log_level = NGX_CONF_UNSET_UINT; + conf->dry_run = NGX_CONF_UNSET; + + return conf; +} + + +static char * +ngx_stream_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_stream_limit_conn_conf_t *prev = parent; + ngx_stream_limit_conn_conf_t *conf = child; + + if (conf->limits.elts == NULL) { + conf->limits = prev->limits; + } + + ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR); + + ngx_conf_merge_value(conf->dry_run, prev->dry_run, 0); + + return NGX_CONF_OK; +} + + +static char * +ngx_stream_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + u_char *p; + ssize_t size; + ngx_str_t *value, name, s; + ngx_uint_t i; + ngx_shm_zone_t *shm_zone; + ngx_stream_limit_conn_ctx_t *ctx; + ngx_stream_compile_complex_value_t ccv; + + value = cf->args->elts; + + ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_limit_conn_ctx_t)); + if (ctx == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &ctx->key; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + size = 0; + name.len = 0; + + for (i = 2; i < cf->args->nelts; i++) { + + if (ngx_strncmp(value[i].data, "zone=", 5) == 0) { + + name.data = value[i].data + 5; + + p = (u_char *) ngx_strchr(name.data, ':'); + + if (p == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid zone size \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + name.len = p - name.data; + + s.data = p + 1; + s.len = value[i].data + value[i].len - s.data; + + size = ngx_parse_size(&s); + + if (size == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid zone size \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (size < (ssize_t) (8 * ngx_pagesize)) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "zone \"%V\" is too small", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (name.len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%V\" must have \"zone\" parameter", + &cmd->name); + return NGX_CONF_ERROR; + } + + shm_zone = ngx_shared_memory_add(cf, &name, size, + &ngx_stream_limit_conn_module); + if (shm_zone == NULL) { + return NGX_CONF_ERROR; + } + + if (shm_zone->data) { + ctx = shm_zone->data; + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "%V \"%V\" is already bound to key \"%V\"", + &cmd->name, &name, &ctx->key.value); + return NGX_CONF_ERROR; + } + + shm_zone->init = ngx_stream_limit_conn_init_zone; + shm_zone->data = ctx; + + return NGX_CONF_OK; +} + + +static char * +ngx_stream_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_shm_zone_t *shm_zone; + ngx_stream_limit_conn_conf_t *lccf = conf; + ngx_stream_limit_conn_limit_t *limit, *limits; + + ngx_str_t *value; + ngx_int_t n; + ngx_uint_t i; + + value = cf->args->elts; + + shm_zone = ngx_shared_memory_add(cf, &value[1], 0, + &ngx_stream_limit_conn_module); + if (shm_zone == NULL) { + return NGX_CONF_ERROR; + } + + limits = lccf->limits.elts; + + if (limits == NULL) { + if (ngx_array_init(&lccf->limits, cf->pool, 1, + sizeof(ngx_stream_limit_conn_limit_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + } + + for (i = 0; i < lccf->limits.nelts; i++) { + if (shm_zone == limits[i].shm_zone) { + return "is duplicate"; + } + } + + n = ngx_atoi(value[2].data, value[2].len); + if (n <= 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid number of connections \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + + if (n > 65535) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "connection limit must be less 65536"); + return NGX_CONF_ERROR; + } + + limit = ngx_array_push(&lccf->limits); + if (limit == NULL) { + return NGX_CONF_ERROR; + } + + limit->conn = n; + limit->shm_zone = shm_zone; + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_stream_limit_conn_add_variables(ngx_conf_t *cf) +{ + ngx_stream_variable_t *var, *v; + + for (v = ngx_stream_limit_conn_vars; v->name.len; v++) { + var = ngx_stream_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_limit_conn_init(ngx_conf_t *cf) +{ + ngx_stream_handler_pt *h; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + h = ngx_array_push(&cmcf->phases[NGX_STREAM_PREACCESS_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_stream_limit_conn_handler; + + return NGX_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_log_module.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_log_module.c new file mode 100644 index 000000000..0ff7f4244 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_log_module.c @@ -0,0 +1,1596 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + +#if (NGX_ZLIB) +#include +#endif + + +typedef struct ngx_stream_log_op_s ngx_stream_log_op_t; + +typedef u_char *(*ngx_stream_log_op_run_pt) (ngx_stream_session_t *s, + u_char *buf, ngx_stream_log_op_t *op); + +typedef size_t (*ngx_stream_log_op_getlen_pt) (ngx_stream_session_t *s, + uintptr_t data); + + +struct ngx_stream_log_op_s { + size_t len; + ngx_stream_log_op_getlen_pt getlen; + ngx_stream_log_op_run_pt run; + uintptr_t data; +}; + + +typedef struct { + ngx_str_t name; + ngx_array_t *flushes; + ngx_array_t *ops; /* array of ngx_stream_log_op_t */ +} ngx_stream_log_fmt_t; + + +typedef struct { + ngx_array_t formats; /* array of ngx_stream_log_fmt_t */ +} ngx_stream_log_main_conf_t; + + +typedef struct { + u_char *start; + u_char *pos; + u_char *last; + + ngx_event_t *event; + ngx_msec_t flush; + ngx_int_t gzip; +} ngx_stream_log_buf_t; + + +typedef struct { + ngx_array_t *lengths; + ngx_array_t *values; +} ngx_stream_log_script_t; + + +typedef struct { + ngx_open_file_t *file; + ngx_stream_log_script_t *script; + time_t disk_full_time; + time_t error_log_time; + ngx_syslog_peer_t *syslog_peer; + ngx_stream_log_fmt_t *format; + ngx_stream_complex_value_t *filter; +} ngx_stream_log_t; + + +typedef struct { + ngx_array_t *logs; /* array of ngx_stream_log_t */ + + ngx_open_file_cache_t *open_file_cache; + time_t open_file_cache_valid; + ngx_uint_t open_file_cache_min_uses; + + ngx_uint_t off; /* unsigned off:1 */ +} ngx_stream_log_srv_conf_t; + + +typedef struct { + ngx_str_t name; + size_t len; + ngx_stream_log_op_run_pt run; +} ngx_stream_log_var_t; + + +#define NGX_STREAM_LOG_ESCAPE_DEFAULT 0 +#define NGX_STREAM_LOG_ESCAPE_JSON 1 +#define NGX_STREAM_LOG_ESCAPE_NONE 2 + + +static void ngx_stream_log_write(ngx_stream_session_t *s, ngx_stream_log_t *log, + u_char *buf, size_t len); +static ssize_t ngx_stream_log_script_write(ngx_stream_session_t *s, + ngx_stream_log_script_t *script, u_char **name, u_char *buf, size_t len); + +#if (NGX_ZLIB) +static ssize_t ngx_stream_log_gzip(ngx_fd_t fd, u_char *buf, size_t len, + ngx_int_t level, ngx_log_t *log); + +static void *ngx_stream_log_gzip_alloc(void *opaque, u_int items, u_int size); +static void ngx_stream_log_gzip_free(void *opaque, void *address); +#endif + +static void ngx_stream_log_flush(ngx_open_file_t *file, ngx_log_t *log); +static void ngx_stream_log_flush_handler(ngx_event_t *ev); + +static ngx_int_t ngx_stream_log_variable_compile(ngx_conf_t *cf, + ngx_stream_log_op_t *op, ngx_str_t *value, ngx_uint_t escape); +static size_t ngx_stream_log_variable_getlen(ngx_stream_session_t *s, + uintptr_t data); +static u_char *ngx_stream_log_variable(ngx_stream_session_t *s, u_char *buf, + ngx_stream_log_op_t *op); +static uintptr_t ngx_stream_log_escape(u_char *dst, u_char *src, size_t size); +static size_t ngx_stream_log_json_variable_getlen(ngx_stream_session_t *s, + uintptr_t data); +static u_char *ngx_stream_log_json_variable(ngx_stream_session_t *s, + u_char *buf, ngx_stream_log_op_t *op); +static size_t ngx_stream_log_unescaped_variable_getlen(ngx_stream_session_t *s, + uintptr_t data); +static u_char *ngx_stream_log_unescaped_variable(ngx_stream_session_t *s, + u_char *buf, ngx_stream_log_op_t *op); + + +static void *ngx_stream_log_create_main_conf(ngx_conf_t *cf); +static void *ngx_stream_log_create_srv_conf(ngx_conf_t *cf); +static char *ngx_stream_log_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); +static char *ngx_stream_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_log_compile_format(ngx_conf_t *cf, + ngx_array_t *flushes, ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s); +static char *ngx_stream_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static ngx_int_t ngx_stream_log_init(ngx_conf_t *cf); + + +static ngx_command_t ngx_stream_log_commands[] = { + + { ngx_string("log_format"), + NGX_STREAM_MAIN_CONF|NGX_CONF_2MORE, + ngx_stream_log_set_format, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("access_log"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE, + ngx_stream_log_set_log, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("open_log_file_cache"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1234, + ngx_stream_log_open_file_cache, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_log_module_ctx = { + NULL, /* preconfiguration */ + ngx_stream_log_init, /* postconfiguration */ + + ngx_stream_log_create_main_conf, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_stream_log_create_srv_conf, /* create server configuration */ + ngx_stream_log_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_log_module = { + NGX_MODULE_V1, + &ngx_stream_log_module_ctx, /* module context */ + ngx_stream_log_commands, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_int_t +ngx_stream_log_handler(ngx_stream_session_t *s) +{ + u_char *line, *p; + size_t len, size; + ssize_t n; + ngx_str_t val; + ngx_uint_t i, l; + ngx_stream_log_t *log; + ngx_stream_log_op_t *op; + ngx_stream_log_buf_t *buffer; + ngx_stream_log_srv_conf_t *lscf; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream log handler"); + + lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_log_module); + + if (lscf->off || lscf->logs == NULL) { + return NGX_OK; + } + + log = lscf->logs->elts; + for (l = 0; l < lscf->logs->nelts; l++) { + + if (log[l].filter) { + if (ngx_stream_complex_value(s, log[l].filter, &val) != NGX_OK) { + return NGX_ERROR; + } + + if (val.len == 0 || (val.len == 1 && val.data[0] == '0')) { + continue; + } + } + + if (ngx_time() == log[l].disk_full_time) { + + /* + * on FreeBSD writing to a full filesystem with enabled softupdates + * may block process for much longer time than writing to non-full + * filesystem, so we skip writing to a log for one second + */ + + continue; + } + + ngx_stream_script_flush_no_cacheable_variables(s, + log[l].format->flushes); + + len = 0; + op = log[l].format->ops->elts; + for (i = 0; i < log[l].format->ops->nelts; i++) { + if (op[i].len == 0) { + len += op[i].getlen(s, op[i].data); + + } else { + len += op[i].len; + } + } + + if (log[l].syslog_peer) { + + /* length of syslog's PRI and HEADER message parts */ + len += sizeof("<255>Jan 01 00:00:00 ") - 1 + + ngx_cycle->hostname.len + 1 + + log[l].syslog_peer->tag.len + 2; + + goto alloc_line; + } + + len += NGX_LINEFEED_SIZE; + + buffer = log[l].file ? log[l].file->data : NULL; + + if (buffer) { + + if (len > (size_t) (buffer->last - buffer->pos)) { + + ngx_stream_log_write(s, &log[l], buffer->start, + buffer->pos - buffer->start); + + buffer->pos = buffer->start; + } + + if (len <= (size_t) (buffer->last - buffer->pos)) { + + p = buffer->pos; + + if (buffer->event && p == buffer->start) { + ngx_add_timer(buffer->event, buffer->flush); + } + + for (i = 0; i < log[l].format->ops->nelts; i++) { + p = op[i].run(s, p, &op[i]); + } + + ngx_linefeed(p); + + buffer->pos = p; + + continue; + } + + if (buffer->event && buffer->event->timer_set) { + ngx_del_timer(buffer->event); + } + } + + alloc_line: + + line = ngx_pnalloc(s->connection->pool, len); + if (line == NULL) { + return NGX_ERROR; + } + + p = line; + + if (log[l].syslog_peer) { + p = ngx_syslog_add_header(log[l].syslog_peer, line); + } + + for (i = 0; i < log[l].format->ops->nelts; i++) { + p = op[i].run(s, p, &op[i]); + } + + if (log[l].syslog_peer) { + + size = p - line; + + n = ngx_syslog_send(log[l].syslog_peer, line, size); + + if (n < 0) { + ngx_log_error(NGX_LOG_WARN, s->connection->log, 0, + "send() to syslog failed"); + + } else if ((size_t) n != size) { + ngx_log_error(NGX_LOG_WARN, s->connection->log, 0, + "send() to syslog has written only %z of %uz", + n, size); + } + + continue; + } + + ngx_linefeed(p); + + ngx_stream_log_write(s, &log[l], line, p - line); + } + + return NGX_OK; +} + + +static void +ngx_stream_log_write(ngx_stream_session_t *s, ngx_stream_log_t *log, + u_char *buf, size_t len) +{ + u_char *name; + time_t now; + ssize_t n; + ngx_err_t err; +#if (NGX_ZLIB) + ngx_stream_log_buf_t *buffer; +#endif + + if (log->script == NULL) { + name = log->file->name.data; + +#if (NGX_ZLIB) + buffer = log->file->data; + + if (buffer && buffer->gzip) { + n = ngx_stream_log_gzip(log->file->fd, buf, len, buffer->gzip, + s->connection->log); + } else { + n = ngx_write_fd(log->file->fd, buf, len); + } +#else + n = ngx_write_fd(log->file->fd, buf, len); +#endif + + } else { + name = NULL; + n = ngx_stream_log_script_write(s, log->script, &name, buf, len); + } + + if (n == (ssize_t) len) { + return; + } + + now = ngx_time(); + + if (n == -1) { + err = ngx_errno; + + if (err == NGX_ENOSPC) { + log->disk_full_time = now; + } + + if (now - log->error_log_time > 59) { + ngx_log_error(NGX_LOG_ALERT, s->connection->log, err, + ngx_write_fd_n " to \"%s\" failed", name); + + log->error_log_time = now; + } + + return; + } + + if (now - log->error_log_time > 59) { + ngx_log_error(NGX_LOG_ALERT, s->connection->log, 0, + ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz", + name, n, len); + + log->error_log_time = now; + } +} + + +static ssize_t +ngx_stream_log_script_write(ngx_stream_session_t *s, + ngx_stream_log_script_t *script, u_char **name, u_char *buf, size_t len) +{ + ssize_t n; + ngx_str_t log; + ngx_open_file_info_t of; + ngx_stream_log_srv_conf_t *lscf; + + if (ngx_stream_script_run(s, &log, script->lengths->elts, 1, + script->values->elts) + == NULL) + { + /* simulate successful logging */ + return len; + } + + log.data[log.len - 1] = '\0'; + *name = log.data; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream log \"%s\"", log.data); + + lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_log_module); + + ngx_memzero(&of, sizeof(ngx_open_file_info_t)); + + of.log = 1; + of.valid = lscf->open_file_cache_valid; + of.min_uses = lscf->open_file_cache_min_uses; + of.directio = NGX_OPEN_FILE_DIRECTIO_OFF; + + if (ngx_open_cached_file(lscf->open_file_cache, &log, &of, + s->connection->pool) + != NGX_OK) + { + if (of.err == 0) { + /* simulate successful logging */ + return len; + } + + ngx_log_error(NGX_LOG_CRIT, s->connection->log, ngx_errno, + "%s \"%s\" failed", of.failed, log.data); + /* simulate successful logging */ + return len; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream log #%d", of.fd); + + n = ngx_write_fd(of.fd, buf, len); + + return n; +} + + +#if (NGX_ZLIB) + +static ssize_t +ngx_stream_log_gzip(ngx_fd_t fd, u_char *buf, size_t len, ngx_int_t level, + ngx_log_t *log) +{ + int rc, wbits, memlevel; + u_char *out; + size_t size; + ssize_t n; + z_stream zstream; + ngx_err_t err; + ngx_pool_t *pool; + + wbits = MAX_WBITS; + memlevel = MAX_MEM_LEVEL - 1; + + while ((ssize_t) len < ((1 << (wbits - 1)) - 262)) { + wbits--; + memlevel--; + } + + /* + * This is a formula from deflateBound() for conservative upper bound of + * compressed data plus 18 bytes of gzip wrapper. + */ + + size = len + ((len + 7) >> 3) + ((len + 63) >> 6) + 5 + 18; + + ngx_memzero(&zstream, sizeof(z_stream)); + + pool = ngx_create_pool(256, log); + if (pool == NULL) { + /* simulate successful logging */ + return len; + } + + pool->log = log; + + zstream.zalloc = ngx_stream_log_gzip_alloc; + zstream.zfree = ngx_stream_log_gzip_free; + zstream.opaque = pool; + + out = ngx_pnalloc(pool, size); + if (out == NULL) { + goto done; + } + + zstream.next_in = buf; + zstream.avail_in = len; + zstream.next_out = out; + zstream.avail_out = size; + + rc = deflateInit2(&zstream, (int) level, Z_DEFLATED, wbits + 16, memlevel, + Z_DEFAULT_STRATEGY); + + if (rc != Z_OK) { + ngx_log_error(NGX_LOG_ALERT, log, 0, "deflateInit2() failed: %d", rc); + goto done; + } + + ngx_log_debug4(NGX_LOG_DEBUG_STREAM, log, 0, + "deflate in: ni:%p no:%p ai:%ud ao:%ud", + zstream.next_in, zstream.next_out, + zstream.avail_in, zstream.avail_out); + + rc = deflate(&zstream, Z_FINISH); + + if (rc != Z_STREAM_END) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "deflate(Z_FINISH) failed: %d", rc); + goto done; + } + + ngx_log_debug5(NGX_LOG_DEBUG_STREAM, log, 0, + "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", + zstream.next_in, zstream.next_out, + zstream.avail_in, zstream.avail_out, + rc); + + size -= zstream.avail_out; + + rc = deflateEnd(&zstream); + + if (rc != Z_OK) { + ngx_log_error(NGX_LOG_ALERT, log, 0, "deflateEnd() failed: %d", rc); + goto done; + } + + n = ngx_write_fd(fd, out, size); + + if (n != (ssize_t) size) { + err = (n == -1) ? ngx_errno : 0; + + ngx_destroy_pool(pool); + + ngx_set_errno(err); + return -1; + } + +done: + + ngx_destroy_pool(pool); + + /* simulate successful logging */ + return len; +} + + +static void * +ngx_stream_log_gzip_alloc(void *opaque, u_int items, u_int size) +{ + ngx_pool_t *pool = opaque; + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pool->log, 0, + "gzip alloc: n:%ud s:%ud", items, size); + + return ngx_palloc(pool, items * size); +} + + +static void +ngx_stream_log_gzip_free(void *opaque, void *address) +{ +#if 0 + ngx_pool_t *pool = opaque; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pool->log, 0, + "gzip free: %p", address); +#endif +} + +#endif + + +static void +ngx_stream_log_flush(ngx_open_file_t *file, ngx_log_t *log) +{ + size_t len; + ssize_t n; + ngx_stream_log_buf_t *buffer; + + buffer = file->data; + + len = buffer->pos - buffer->start; + + if (len == 0) { + return; + } + +#if (NGX_ZLIB) + if (buffer->gzip) { + n = ngx_stream_log_gzip(file->fd, buffer->start, len, buffer->gzip, + log); + } else { + n = ngx_write_fd(file->fd, buffer->start, len); + } +#else + n = ngx_write_fd(file->fd, buffer->start, len); +#endif + + if (n == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_write_fd_n " to \"%s\" failed", + file->name.data); + + } else if ((size_t) n != len) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz", + file->name.data, n, len); + } + + buffer->pos = buffer->start; + + if (buffer->event && buffer->event->timer_set) { + ngx_del_timer(buffer->event); + } +} + + +static void +ngx_stream_log_flush_handler(ngx_event_t *ev) +{ + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, + "stream log buffer flush handler"); + + ngx_stream_log_flush(ev->data, ev->log); +} + + +static u_char * +ngx_stream_log_copy_short(ngx_stream_session_t *s, u_char *buf, + ngx_stream_log_op_t *op) +{ + size_t len; + uintptr_t data; + + len = op->len; + data = op->data; + + while (len--) { + *buf++ = (u_char) (data & 0xff); + data >>= 8; + } + + return buf; +} + + +static u_char * +ngx_stream_log_copy_long(ngx_stream_session_t *s, u_char *buf, + ngx_stream_log_op_t *op) +{ + return ngx_cpymem(buf, (u_char *) op->data, op->len); +} + + +static ngx_int_t +ngx_stream_log_variable_compile(ngx_conf_t *cf, ngx_stream_log_op_t *op, + ngx_str_t *value, ngx_uint_t escape) +{ + ngx_int_t index; + + index = ngx_stream_get_variable_index(cf, value); + if (index == NGX_ERROR) { + return NGX_ERROR; + } + + op->len = 0; + + switch (escape) { + case NGX_STREAM_LOG_ESCAPE_JSON: + op->getlen = ngx_stream_log_json_variable_getlen; + op->run = ngx_stream_log_json_variable; + break; + + case NGX_STREAM_LOG_ESCAPE_NONE: + op->getlen = ngx_stream_log_unescaped_variable_getlen; + op->run = ngx_stream_log_unescaped_variable; + break; + + default: /* NGX_STREAM_LOG_ESCAPE_DEFAULT */ + op->getlen = ngx_stream_log_variable_getlen; + op->run = ngx_stream_log_variable; + } + + op->data = index; + + return NGX_OK; +} + + +static size_t +ngx_stream_log_variable_getlen(ngx_stream_session_t *s, uintptr_t data) +{ + uintptr_t len; + ngx_stream_variable_value_t *value; + + value = ngx_stream_get_indexed_variable(s, data); + + if (value == NULL || value->not_found) { + return 1; + } + + len = ngx_stream_log_escape(NULL, value->data, value->len); + + value->escape = len ? 1 : 0; + + return value->len + len * 3; +} + + +static u_char * +ngx_stream_log_variable(ngx_stream_session_t *s, u_char *buf, + ngx_stream_log_op_t *op) +{ + ngx_stream_variable_value_t *value; + + value = ngx_stream_get_indexed_variable(s, op->data); + + if (value == NULL || value->not_found) { + *buf = '-'; + return buf + 1; + } + + if (value->escape == 0) { + return ngx_cpymem(buf, value->data, value->len); + + } else { + return (u_char *) ngx_stream_log_escape(buf, value->data, value->len); + } +} + + +static uintptr_t +ngx_stream_log_escape(u_char *dst, u_char *src, size_t size) +{ + ngx_uint_t n; + static u_char hex[] = "0123456789ABCDEF"; + + static uint32_t escape[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x00000004, /* 0000 0000 0000 0000 0000 0000 0000 0100 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x10000000, /* 0001 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + + if (dst == NULL) { + + /* find the number of the characters to be escaped */ + + n = 0; + + while (size) { + if (escape[*src >> 5] & (1U << (*src & 0x1f))) { + n++; + } + src++; + size--; + } + + return (uintptr_t) n; + } + + while (size) { + if (escape[*src >> 5] & (1U << (*src & 0x1f))) { + *dst++ = '\\'; + *dst++ = 'x'; + *dst++ = hex[*src >> 4]; + *dst++ = hex[*src & 0xf]; + src++; + + } else { + *dst++ = *src++; + } + size--; + } + + return (uintptr_t) dst; +} + + +static size_t +ngx_stream_log_json_variable_getlen(ngx_stream_session_t *s, uintptr_t data) +{ + uintptr_t len; + ngx_stream_variable_value_t *value; + + value = ngx_stream_get_indexed_variable(s, data); + + if (value == NULL || value->not_found) { + return 0; + } + + len = ngx_escape_json(NULL, value->data, value->len); + + value->escape = len ? 1 : 0; + + return value->len + len; +} + + +static u_char * +ngx_stream_log_json_variable(ngx_stream_session_t *s, u_char *buf, + ngx_stream_log_op_t *op) +{ + ngx_stream_variable_value_t *value; + + value = ngx_stream_get_indexed_variable(s, op->data); + + if (value == NULL || value->not_found) { + return buf; + } + + if (value->escape == 0) { + return ngx_cpymem(buf, value->data, value->len); + + } else { + return (u_char *) ngx_escape_json(buf, value->data, value->len); + } +} + + +static size_t +ngx_stream_log_unescaped_variable_getlen(ngx_stream_session_t *s, + uintptr_t data) +{ + ngx_stream_variable_value_t *value; + + value = ngx_stream_get_indexed_variable(s, data); + + if (value == NULL || value->not_found) { + return 0; + } + + value->escape = 0; + + return value->len; +} + + +static u_char * +ngx_stream_log_unescaped_variable(ngx_stream_session_t *s, u_char *buf, + ngx_stream_log_op_t *op) +{ + ngx_stream_variable_value_t *value; + + value = ngx_stream_get_indexed_variable(s, op->data); + + if (value == NULL || value->not_found) { + return buf; + } + + return ngx_cpymem(buf, value->data, value->len); +} + + +static void * +ngx_stream_log_create_main_conf(ngx_conf_t *cf) +{ + ngx_stream_log_main_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_main_conf_t)); + if (conf == NULL) { + return NULL; + } + + if (ngx_array_init(&conf->formats, cf->pool, 4, + sizeof(ngx_stream_log_fmt_t)) + != NGX_OK) + { + return NULL; + } + + return conf; +} + + +static void * +ngx_stream_log_create_srv_conf(ngx_conf_t *cf) +{ + ngx_stream_log_srv_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + conf->open_file_cache = NGX_CONF_UNSET_PTR; + + return conf; +} + + +static char * +ngx_stream_log_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_stream_log_srv_conf_t *prev = parent; + ngx_stream_log_srv_conf_t *conf = child; + + if (conf->open_file_cache == NGX_CONF_UNSET_PTR) { + + conf->open_file_cache = prev->open_file_cache; + conf->open_file_cache_valid = prev->open_file_cache_valid; + conf->open_file_cache_min_uses = prev->open_file_cache_min_uses; + + if (conf->open_file_cache == NGX_CONF_UNSET_PTR) { + conf->open_file_cache = NULL; + } + } + + if (conf->logs || conf->off) { + return NGX_CONF_OK; + } + + conf->logs = prev->logs; + conf->off = prev->off; + + return NGX_CONF_OK; +} + + +static char * +ngx_stream_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_log_srv_conf_t *lscf = conf; + + ssize_t size; + ngx_int_t gzip; + ngx_uint_t i, n; + ngx_msec_t flush; + ngx_str_t *value, name, s; + ngx_stream_log_t *log; + ngx_syslog_peer_t *peer; + ngx_stream_log_buf_t *buffer; + ngx_stream_log_fmt_t *fmt; + ngx_stream_script_compile_t sc; + ngx_stream_log_main_conf_t *lmcf; + ngx_stream_compile_complex_value_t ccv; + + value = cf->args->elts; + + if (ngx_strcmp(value[1].data, "off") == 0) { + lscf->off = 1; + if (cf->args->nelts == 2) { + return NGX_CONF_OK; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + + if (lscf->logs == NULL) { + lscf->logs = ngx_array_create(cf->pool, 2, sizeof(ngx_stream_log_t)); + if (lscf->logs == NULL) { + return NGX_CONF_ERROR; + } + } + + lmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_log_module); + + log = ngx_array_push(lscf->logs); + if (log == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(log, sizeof(ngx_stream_log_t)); + + + if (ngx_strncmp(value[1].data, "syslog:", 7) == 0) { + + peer = ngx_pcalloc(cf->pool, sizeof(ngx_syslog_peer_t)); + if (peer == NULL) { + return NGX_CONF_ERROR; + } + + if (ngx_syslog_process_conf(cf, peer) != NGX_CONF_OK) { + return NGX_CONF_ERROR; + } + + log->syslog_peer = peer; + + goto process_formats; + } + + n = ngx_stream_script_variables_count(&value[1]); + + if (n == 0) { + log->file = ngx_conf_open_file(cf->cycle, &value[1]); + if (log->file == NULL) { + return NGX_CONF_ERROR; + } + + } else { + if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + + log->script = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_script_t)); + if (log->script == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(&sc, sizeof(ngx_stream_script_compile_t)); + + sc.cf = cf; + sc.source = &value[1]; + sc.lengths = &log->script->lengths; + sc.values = &log->script->values; + sc.variables = n; + sc.complete_lengths = 1; + sc.complete_values = 1; + + if (ngx_stream_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + +process_formats: + + if (cf->args->nelts >= 3) { + name = value[2]; + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "log format is not specified"); + return NGX_CONF_ERROR; + } + + fmt = lmcf->formats.elts; + for (i = 0; i < lmcf->formats.nelts; i++) { + if (fmt[i].name.len == name.len + && ngx_strcasecmp(fmt[i].name.data, name.data) == 0) + { + log->format = &fmt[i]; + break; + } + } + + if (log->format == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unknown log format \"%V\"", &name); + return NGX_CONF_ERROR; + } + + size = 0; + flush = 0; + gzip = 0; + + for (i = 3; i < cf->args->nelts; i++) { + + if (ngx_strncmp(value[i].data, "buffer=", 7) == 0) { + s.len = value[i].len - 7; + s.data = value[i].data + 7; + + size = ngx_parse_size(&s); + + if (size == NGX_ERROR || size == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid buffer size \"%V\"", &s); + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "flush=", 6) == 0) { + s.len = value[i].len - 6; + s.data = value[i].data + 6; + + flush = ngx_parse_time(&s, 0); + + if (flush == (ngx_msec_t) NGX_ERROR || flush == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid flush time \"%V\"", &s); + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "gzip", 4) == 0 + && (value[i].len == 4 || value[i].data[4] == '=')) + { +#if (NGX_ZLIB) + if (size == 0) { + size = 64 * 1024; + } + + if (value[i].len == 4) { + gzip = Z_BEST_SPEED; + continue; + } + + s.len = value[i].len - 5; + s.data = value[i].data + 5; + + gzip = ngx_atoi(s.data, s.len); + + if (gzip < 1 || gzip > 9) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid compression level \"%V\"", &s); + return NGX_CONF_ERROR; + } + + continue; + +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "nginx was built without zlib support"); + return NGX_CONF_ERROR; +#endif + } + + if (ngx_strncmp(value[i].data, "if=", 3) == 0) { + s.len = value[i].len - 3; + s.data = value[i].data + 3; + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &s; + ccv.complex_value = ngx_palloc(cf->pool, + sizeof(ngx_stream_complex_value_t)); + if (ccv.complex_value == NULL) { + return NGX_CONF_ERROR; + } + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + log->filter = ccv.complex_value; + + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (flush && size == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "no buffer is defined for access_log \"%V\"", + &value[1]); + return NGX_CONF_ERROR; + } + + if (size) { + + if (log->script) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "buffered logs cannot have variables in name"); + return NGX_CONF_ERROR; + } + + if (log->syslog_peer) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "logs to syslog cannot be buffered"); + return NGX_CONF_ERROR; + } + + if (log->file->data) { + buffer = log->file->data; + + if (buffer->last - buffer->start != size + || buffer->flush != flush + || buffer->gzip != gzip) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "access_log \"%V\" already defined " + "with conflicting parameters", + &value[1]); + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; + } + + buffer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_buf_t)); + if (buffer == NULL) { + return NGX_CONF_ERROR; + } + + buffer->start = ngx_pnalloc(cf->pool, size); + if (buffer->start == NULL) { + return NGX_CONF_ERROR; + } + + buffer->pos = buffer->start; + buffer->last = buffer->start + size; + + if (flush) { + buffer->event = ngx_pcalloc(cf->pool, sizeof(ngx_event_t)); + if (buffer->event == NULL) { + return NGX_CONF_ERROR; + } + + buffer->event->data = log->file; + buffer->event->handler = ngx_stream_log_flush_handler; + buffer->event->log = &cf->cycle->new_log; + buffer->event->cancelable = 1; + + buffer->flush = flush; + } + + buffer->gzip = gzip; + + log->file->flush = ngx_stream_log_flush; + log->file->data = buffer; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_stream_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_log_main_conf_t *lmcf = conf; + + ngx_str_t *value; + ngx_uint_t i; + ngx_stream_log_fmt_t *fmt; + + value = cf->args->elts; + + fmt = lmcf->formats.elts; + for (i = 0; i < lmcf->formats.nelts; i++) { + if (fmt[i].name.len == value[1].len + && ngx_strcmp(fmt[i].name.data, value[1].data) == 0) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate \"log_format\" name \"%V\"", + &value[1]); + return NGX_CONF_ERROR; + } + } + + fmt = ngx_array_push(&lmcf->formats); + if (fmt == NULL) { + return NGX_CONF_ERROR; + } + + fmt->name = value[1]; + + fmt->flushes = ngx_array_create(cf->pool, 4, sizeof(ngx_int_t)); + if (fmt->flushes == NULL) { + return NGX_CONF_ERROR; + } + + fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_stream_log_op_t)); + if (fmt->ops == NULL) { + return NGX_CONF_ERROR; + } + + return ngx_stream_log_compile_format(cf, fmt->flushes, fmt->ops, + cf->args, 2); +} + + +static char * +ngx_stream_log_compile_format(ngx_conf_t *cf, ngx_array_t *flushes, + ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s) +{ + u_char *data, *p, ch; + size_t i, len; + ngx_str_t *value, var; + ngx_int_t *flush; + ngx_uint_t bracket, escape; + ngx_stream_log_op_t *op; + + escape = NGX_STREAM_LOG_ESCAPE_DEFAULT; + value = args->elts; + + if (s < args->nelts && ngx_strncmp(value[s].data, "escape=", 7) == 0) { + data = value[s].data + 7; + + if (ngx_strcmp(data, "json") == 0) { + escape = NGX_STREAM_LOG_ESCAPE_JSON; + + } else if (ngx_strcmp(data, "none") == 0) { + escape = NGX_STREAM_LOG_ESCAPE_NONE; + + } else if (ngx_strcmp(data, "default") != 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unknown log format escaping \"%s\"", data); + return NGX_CONF_ERROR; + } + + s++; + } + + for ( /* void */ ; s < args->nelts; s++) { + + i = 0; + + while (i < value[s].len) { + + op = ngx_array_push(ops); + if (op == NULL) { + return NGX_CONF_ERROR; + } + + data = &value[s].data[i]; + + if (value[s].data[i] == '$') { + + if (++i == value[s].len) { + goto invalid; + } + + if (value[s].data[i] == '{') { + bracket = 1; + + if (++i == value[s].len) { + goto invalid; + } + + var.data = &value[s].data[i]; + + } else { + bracket = 0; + var.data = &value[s].data[i]; + } + + for (var.len = 0; i < value[s].len; i++, var.len++) { + ch = value[s].data[i]; + + if (ch == '}' && bracket) { + i++; + bracket = 0; + break; + } + + if ((ch >= 'A' && ch <= 'Z') + || (ch >= 'a' && ch <= 'z') + || (ch >= '0' && ch <= '9') + || ch == '_') + { + continue; + } + + break; + } + + if (bracket) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the closing bracket in \"%V\" " + "variable is missing", &var); + return NGX_CONF_ERROR; + } + + if (var.len == 0) { + goto invalid; + } + + if (ngx_stream_log_variable_compile(cf, op, &var, escape) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + if (flushes) { + + flush = ngx_array_push(flushes); + if (flush == NULL) { + return NGX_CONF_ERROR; + } + + *flush = op->data; /* variable index */ + } + + continue; + } + + i++; + + while (i < value[s].len && value[s].data[i] != '$') { + i++; + } + + len = &value[s].data[i] - data; + + if (len) { + + op->len = len; + op->getlen = NULL; + + if (len <= sizeof(uintptr_t)) { + op->run = ngx_stream_log_copy_short; + op->data = 0; + + while (len--) { + op->data <<= 8; + op->data |= data[len]; + } + + } else { + op->run = ngx_stream_log_copy_long; + + p = ngx_pnalloc(cf->pool, len); + if (p == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memcpy(p, data, len); + op->data = (uintptr_t) p; + } + } + } + } + + return NGX_CONF_OK; + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%s\"", data); + + return NGX_CONF_ERROR; +} + + +static char * +ngx_stream_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_log_srv_conf_t *lscf = conf; + + time_t inactive, valid; + ngx_str_t *value, s; + ngx_int_t max, min_uses; + ngx_uint_t i; + + if (lscf->open_file_cache != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + value = cf->args->elts; + + max = 0; + inactive = 10; + valid = 60; + min_uses = 1; + + for (i = 1; i < cf->args->nelts; i++) { + + if (ngx_strncmp(value[i].data, "max=", 4) == 0) { + + max = ngx_atoi(value[i].data + 4, value[i].len - 4); + if (max == NGX_ERROR) { + goto failed; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) { + + s.len = value[i].len - 9; + s.data = value[i].data + 9; + + inactive = ngx_parse_time(&s, 1); + if (inactive == (time_t) NGX_ERROR) { + goto failed; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "min_uses=", 9) == 0) { + + min_uses = ngx_atoi(value[i].data + 9, value[i].len - 9); + if (min_uses == NGX_ERROR) { + goto failed; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "valid=", 6) == 0) { + + s.len = value[i].len - 6; + s.data = value[i].data + 6; + + valid = ngx_parse_time(&s, 1); + if (valid == (time_t) NGX_ERROR) { + goto failed; + } + + continue; + } + + if (ngx_strcmp(value[i].data, "off") == 0) { + + lscf->open_file_cache = NULL; + + continue; + } + + failed: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid \"open_log_file_cache\" parameter \"%V\"", + &value[i]); + return NGX_CONF_ERROR; + } + + if (lscf->open_file_cache == NULL) { + return NGX_CONF_OK; + } + + if (max == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"open_log_file_cache\" must have \"max\" parameter"); + return NGX_CONF_ERROR; + } + + lscf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive); + + if (lscf->open_file_cache) { + + lscf->open_file_cache_valid = valid; + lscf->open_file_cache_min_uses = min_uses; + + return NGX_CONF_OK; + } + + return NGX_CONF_ERROR; +} + + +static ngx_int_t +ngx_stream_log_init(ngx_conf_t *cf) +{ + ngx_stream_handler_pt *h; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + h = ngx_array_push(&cmcf->phases[NGX_STREAM_LOG_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_stream_log_handler; + + return NGX_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_map_module.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_map_module.c new file mode 100644 index 000000000..ef06b2de8 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_map_module.c @@ -0,0 +1,588 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_uint_t hash_max_size; + ngx_uint_t hash_bucket_size; +} ngx_stream_map_conf_t; + + +typedef struct { + ngx_hash_keys_arrays_t keys; + + ngx_array_t *values_hash; +#if (NGX_PCRE) + ngx_array_t regexes; +#endif + + ngx_stream_variable_value_t *default_value; + ngx_conf_t *cf; + unsigned hostnames:1; + unsigned no_cacheable:1; +} ngx_stream_map_conf_ctx_t; + + +typedef struct { + ngx_stream_map_t map; + ngx_stream_complex_value_t value; + ngx_stream_variable_value_t *default_value; + ngx_uint_t hostnames; /* unsigned hostnames:1 */ +} ngx_stream_map_ctx_t; + + +static int ngx_libc_cdecl ngx_stream_map_cmp_dns_wildcards(const void *one, + const void *two); +static void *ngx_stream_map_create_conf(ngx_conf_t *cf); +static char *ngx_stream_map_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); + + +static ngx_command_t ngx_stream_map_commands[] = { + + { ngx_string("map"), + NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2, + ngx_stream_map_block, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("map_hash_max_size"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_MAIN_CONF_OFFSET, + offsetof(ngx_stream_map_conf_t, hash_max_size), + NULL }, + + { ngx_string("map_hash_bucket_size"), + NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_MAIN_CONF_OFFSET, + offsetof(ngx_stream_map_conf_t, hash_bucket_size), + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_map_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + ngx_stream_map_create_conf, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_map_module = { + NGX_MODULE_V1, + &ngx_stream_map_module_ctx, /* module context */ + ngx_stream_map_commands, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_int_t +ngx_stream_map_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, + uintptr_t data) +{ + ngx_stream_map_ctx_t *map = (ngx_stream_map_ctx_t *) data; + + ngx_str_t val, str; + ngx_stream_complex_value_t *cv; + ngx_stream_variable_value_t *value; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream map started"); + + if (ngx_stream_complex_value(s, &map->value, &val) != NGX_OK) { + return NGX_ERROR; + } + + if (map->hostnames && val.len > 0 && val.data[val.len - 1] == '.') { + val.len--; + } + + value = ngx_stream_map_find(s, &map->map, &val); + + if (value == NULL) { + value = map->default_value; + } + + if (!value->valid) { + cv = (ngx_stream_complex_value_t *) value->data; + + if (ngx_stream_complex_value(s, cv, &str) != NGX_OK) { + return NGX_ERROR; + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->len = str.len; + v->data = str.data; + + } else { + *v = *value; + } + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream map: \"%V\" \"%v\"", &val, v); + + return NGX_OK; +} + + +static void * +ngx_stream_map_create_conf(ngx_conf_t *cf) +{ + ngx_stream_map_conf_t *mcf; + + mcf = ngx_palloc(cf->pool, sizeof(ngx_stream_map_conf_t)); + if (mcf == NULL) { + return NULL; + } + + mcf->hash_max_size = NGX_CONF_UNSET_UINT; + mcf->hash_bucket_size = NGX_CONF_UNSET_UINT; + + return mcf; +} + + +static char * +ngx_stream_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_map_conf_t *mcf = conf; + + char *rv; + ngx_str_t *value, name; + ngx_conf_t save; + ngx_pool_t *pool; + ngx_hash_init_t hash; + ngx_stream_map_ctx_t *map; + ngx_stream_variable_t *var; + ngx_stream_map_conf_ctx_t ctx; + ngx_stream_compile_complex_value_t ccv; + + if (mcf->hash_max_size == NGX_CONF_UNSET_UINT) { + mcf->hash_max_size = 2048; + } + + if (mcf->hash_bucket_size == NGX_CONF_UNSET_UINT) { + mcf->hash_bucket_size = ngx_cacheline_size; + + } else { + mcf->hash_bucket_size = ngx_align(mcf->hash_bucket_size, + ngx_cacheline_size); + } + + map = ngx_pcalloc(cf->pool, sizeof(ngx_stream_map_ctx_t)); + if (map == NULL) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &map->value; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + name = value[2]; + + if (name.data[0] != '$') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"%V\"", &name); + return NGX_CONF_ERROR; + } + + name.len--; + name.data++; + + var = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE); + if (var == NULL) { + return NGX_CONF_ERROR; + } + + var->get_handler = ngx_stream_map_variable; + var->data = (uintptr_t) map; + + pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log); + if (pool == NULL) { + return NGX_CONF_ERROR; + } + + ctx.keys.pool = cf->pool; + ctx.keys.temp_pool = pool; + + if (ngx_hash_keys_array_init(&ctx.keys, NGX_HASH_LARGE) != NGX_OK) { + ngx_destroy_pool(pool); + return NGX_CONF_ERROR; + } + + ctx.values_hash = ngx_pcalloc(pool, sizeof(ngx_array_t) * ctx.keys.hsize); + if (ctx.values_hash == NULL) { + ngx_destroy_pool(pool); + return NGX_CONF_ERROR; + } + +#if (NGX_PCRE) + if (ngx_array_init(&ctx.regexes, cf->pool, 2, + sizeof(ngx_stream_map_regex_t)) + != NGX_OK) + { + ngx_destroy_pool(pool); + return NGX_CONF_ERROR; + } +#endif + + ctx.default_value = NULL; + ctx.cf = &save; + ctx.hostnames = 0; + ctx.no_cacheable = 0; + + save = *cf; + cf->pool = pool; + cf->ctx = &ctx; + cf->handler = ngx_stream_map; + cf->handler_conf = conf; + + rv = ngx_conf_parse(cf, NULL); + + *cf = save; + + if (rv != NGX_CONF_OK) { + ngx_destroy_pool(pool); + return rv; + } + + if (ctx.no_cacheable) { + var->flags |= NGX_STREAM_VAR_NOCACHEABLE; + } + + map->default_value = ctx.default_value ? ctx.default_value: + &ngx_stream_variable_null_value; + + map->hostnames = ctx.hostnames; + + hash.key = ngx_hash_key_lc; + hash.max_size = mcf->hash_max_size; + hash.bucket_size = mcf->hash_bucket_size; + hash.name = "map_hash"; + hash.pool = cf->pool; + + if (ctx.keys.keys.nelts) { + hash.hash = &map->map.hash.hash; + hash.temp_pool = NULL; + + if (ngx_hash_init(&hash, ctx.keys.keys.elts, ctx.keys.keys.nelts) + != NGX_OK) + { + ngx_destroy_pool(pool); + return NGX_CONF_ERROR; + } + } + + if (ctx.keys.dns_wc_head.nelts) { + + ngx_qsort(ctx.keys.dns_wc_head.elts, + (size_t) ctx.keys.dns_wc_head.nelts, + sizeof(ngx_hash_key_t), ngx_stream_map_cmp_dns_wildcards); + + hash.hash = NULL; + hash.temp_pool = pool; + + if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_head.elts, + ctx.keys.dns_wc_head.nelts) + != NGX_OK) + { + ngx_destroy_pool(pool); + return NGX_CONF_ERROR; + } + + map->map.hash.wc_head = (ngx_hash_wildcard_t *) hash.hash; + } + + if (ctx.keys.dns_wc_tail.nelts) { + + ngx_qsort(ctx.keys.dns_wc_tail.elts, + (size_t) ctx.keys.dns_wc_tail.nelts, + sizeof(ngx_hash_key_t), ngx_stream_map_cmp_dns_wildcards); + + hash.hash = NULL; + hash.temp_pool = pool; + + if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_tail.elts, + ctx.keys.dns_wc_tail.nelts) + != NGX_OK) + { + ngx_destroy_pool(pool); + return NGX_CONF_ERROR; + } + + map->map.hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash; + } + +#if (NGX_PCRE) + + if (ctx.regexes.nelts) { + map->map.regex = ctx.regexes.elts; + map->map.nregex = ctx.regexes.nelts; + } + +#endif + + ngx_destroy_pool(pool); + + return rv; +} + + +static int ngx_libc_cdecl +ngx_stream_map_cmp_dns_wildcards(const void *one, const void *two) +{ + ngx_hash_key_t *first, *second; + + first = (ngx_hash_key_t *) one; + second = (ngx_hash_key_t *) two; + + return ngx_dns_strcmp(first->key.data, second->key.data); +} + + +static char * +ngx_stream_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) +{ + u_char *data; + size_t len; + ngx_int_t rv; + ngx_str_t *value, v; + ngx_uint_t i, key; + ngx_stream_map_conf_ctx_t *ctx; + ngx_stream_complex_value_t cv, *cvp; + ngx_stream_variable_value_t *var, **vp; + ngx_stream_compile_complex_value_t ccv; + + ctx = cf->ctx; + + value = cf->args->elts; + + if (cf->args->nelts == 1 + && ngx_strcmp(value[0].data, "hostnames") == 0) + { + ctx->hostnames = 1; + return NGX_CONF_OK; + } + + if (cf->args->nelts == 1 + && ngx_strcmp(value[0].data, "volatile") == 0) + { + ctx->no_cacheable = 1; + return NGX_CONF_OK; + } + + if (cf->args->nelts != 2) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid number of the map parameters"); + return NGX_CONF_ERROR; + } + + if (ngx_strcmp(value[0].data, "include") == 0) { + return ngx_conf_include(cf, dummy, conf); + } + + key = 0; + + for (i = 0; i < value[1].len; i++) { + key = ngx_hash(key, value[1].data[i]); + } + + key %= ctx->keys.hsize; + + vp = ctx->values_hash[key].elts; + + if (vp) { + for (i = 0; i < ctx->values_hash[key].nelts; i++) { + + if (vp[i]->valid) { + data = vp[i]->data; + len = vp[i]->len; + + } else { + cvp = (ngx_stream_complex_value_t *) vp[i]->data; + data = cvp->value.data; + len = cvp->value.len; + } + + if (value[1].len != len) { + continue; + } + + if (ngx_strncmp(value[1].data, data, len) == 0) { + var = vp[i]; + goto found; + } + } + + } else { + if (ngx_array_init(&ctx->values_hash[key], cf->pool, 4, + sizeof(ngx_stream_variable_value_t *)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + } + + var = ngx_palloc(ctx->keys.pool, sizeof(ngx_stream_variable_value_t)); + if (var == NULL) { + return NGX_CONF_ERROR; + } + + v.len = value[1].len; + v.data = ngx_pstrdup(ctx->keys.pool, &value[1]); + if (v.data == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = ctx->cf; + ccv.value = &v; + ccv.complex_value = &cv; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (cv.lengths != NULL) { + cvp = ngx_palloc(ctx->keys.pool, sizeof(ngx_stream_complex_value_t)); + if (cvp == NULL) { + return NGX_CONF_ERROR; + } + + *cvp = cv; + + var->len = 0; + var->data = (u_char *) cvp; + var->valid = 0; + + } else { + var->len = v.len; + var->data = v.data; + var->valid = 1; + } + + var->no_cacheable = 0; + var->not_found = 0; + + vp = ngx_array_push(&ctx->values_hash[key]); + if (vp == NULL) { + return NGX_CONF_ERROR; + } + + *vp = var; + +found: + + if (ngx_strcmp(value[0].data, "default") == 0) { + + if (ctx->default_value) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate default map parameter"); + return NGX_CONF_ERROR; + } + + ctx->default_value = var; + + return NGX_CONF_OK; + } + +#if (NGX_PCRE) + + if (value[0].len && value[0].data[0] == '~') { + ngx_regex_compile_t rc; + ngx_stream_map_regex_t *regex; + u_char errstr[NGX_MAX_CONF_ERRSTR]; + + regex = ngx_array_push(&ctx->regexes); + if (regex == NULL) { + return NGX_CONF_ERROR; + } + + value[0].len--; + value[0].data++; + + ngx_memzero(&rc, sizeof(ngx_regex_compile_t)); + + if (value[0].data[0] == '*') { + value[0].len--; + value[0].data++; + rc.options = NGX_REGEX_CASELESS; + } + + rc.pattern = value[0]; + rc.err.len = NGX_MAX_CONF_ERRSTR; + rc.err.data = errstr; + + regex->regex = ngx_stream_regex_compile(ctx->cf, &rc); + if (regex->regex == NULL) { + return NGX_CONF_ERROR; + } + + regex->value = var; + + return NGX_CONF_OK; + } + +#endif + + if (value[0].len && value[0].data[0] == '\\') { + value[0].len--; + value[0].data++; + } + + rv = ngx_hash_add_key(&ctx->keys, &value[0], var, + (ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0); + + if (rv == NGX_OK) { + return NGX_CONF_OK; + } + + if (rv == NGX_DECLINED) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid hostname or wildcard \"%V\"", &value[0]); + } + + if (rv == NGX_BUSY) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "conflicting parameter \"%V\"", &value[0]); + } + + return NGX_CONF_ERROR; +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_pass_module.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_pass_module.c new file mode 100644 index 000000000..1d671087c --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_pass_module.c @@ -0,0 +1,336 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +#define NGX_STREAM_PASS_MAX_PASSES 10 + + +typedef struct { + ngx_addr_t *addr; + ngx_stream_complex_value_t *addr_value; +} ngx_stream_pass_srv_conf_t; + + +static void ngx_stream_pass_handler(ngx_stream_session_t *s); +static ngx_int_t ngx_stream_pass_check_cycle(ngx_connection_t *c); +static void ngx_stream_pass_cleanup(void *data); +static ngx_int_t ngx_stream_pass_match(ngx_listening_t *ls, ngx_addr_t *addr); +static void *ngx_stream_pass_create_srv_conf(ngx_conf_t *cf); +static char *ngx_stream_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + + +static ngx_command_t ngx_stream_pass_commands[] = { + + { ngx_string("pass"), + NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_pass, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_pass_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_stream_pass_create_srv_conf, /* create server configuration */ + NULL /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_pass_module = { + NGX_MODULE_V1, + &ngx_stream_pass_module_ctx, /* module context */ + ngx_stream_pass_commands, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static void +ngx_stream_pass_handler(ngx_stream_session_t *s) +{ + ngx_url_t u; + ngx_str_t url; + ngx_addr_t *addr; + ngx_uint_t i; + ngx_listening_t *ls; + ngx_connection_t *c; + ngx_stream_pass_srv_conf_t *pscf; + + c = s->connection; + + c->log->action = "passing connection to port"; + + if (c->type == SOCK_DGRAM) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, "cannot pass udp connection"); + goto failed; + } + + if (c->buffer && c->buffer->pos != c->buffer->last) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "cannot pass connection with preread data"); + goto failed; + } + + pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_pass_module); + + addr = pscf->addr; + + if (addr == NULL) { + if (ngx_stream_complex_value(s, pscf->addr_value, &url) != NGX_OK) { + goto failed; + } + + ngx_memzero(&u, sizeof(ngx_url_t)); + + u.url = url; + u.no_resolve = 1; + + if (ngx_parse_url(c->pool, &u) != NGX_OK) { + if (u.err) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "%s in pass \"%V\"", u.err, &u.url); + } + + goto failed; + } + + if (u.naddrs == 0) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "no addresses in pass \"%V\"", &u.url); + goto failed; + } + + if (u.no_port) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "no port in pass \"%V\"", &u.url); + goto failed; + } + + addr = &u.addrs[0]; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream pass addr: \"%V\"", &addr->name); + + if (ngx_stream_pass_check_cycle(c) != NGX_OK) { + goto failed; + } + + ls = ngx_cycle->listening.elts; + + for (i = 0; i < ngx_cycle->listening.nelts; i++) { + + if (ngx_stream_pass_match(&ls[i], addr) != NGX_OK) { + continue; + } + + c->listening = &ls[i]; + + c->data = NULL; + c->buffer = NULL; + + *c->log = c->listening->log; + c->log->handler = NULL; + c->log->data = NULL; + + c->local_sockaddr = addr->sockaddr; + c->local_socklen = addr->socklen; + + c->listening->handler(c); + + return; + } + + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "port not found for \"%V\"", &addr->name); + + ngx_stream_finalize_session(s, NGX_STREAM_OK); + + return; + +failed: + + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); +} + + +static ngx_int_t +ngx_stream_pass_check_cycle(ngx_connection_t *c) +{ + ngx_uint_t *num; + ngx_pool_cleanup_t *cln; + + for (cln = c->pool->cleanup; cln; cln = cln->next) { + if (cln->handler != ngx_stream_pass_cleanup) { + continue; + } + + num = cln->data; + + if (++(*num) > NGX_STREAM_PASS_MAX_PASSES) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, "stream pass cycle"); + return NGX_ERROR; + } + + return NGX_OK; + } + + cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_uint_t)); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->handler = ngx_stream_pass_cleanup; + + num = cln->data; + *num = 1; + + return NGX_OK; +} + + +static void +ngx_stream_pass_cleanup(void *data) +{ + return; +} + + +static ngx_int_t +ngx_stream_pass_match(ngx_listening_t *ls, ngx_addr_t *addr) +{ + if (ls->type == SOCK_DGRAM) { + return NGX_DECLINED; + } + + if (!ls->wildcard) { + return ngx_cmp_sockaddr(ls->sockaddr, ls->socklen, + addr->sockaddr, addr->socklen, 1); + } + + if (ls->sockaddr->sa_family == addr->sockaddr->sa_family + && ngx_inet_get_port(ls->sockaddr) == ngx_inet_get_port(addr->sockaddr)) + { + return NGX_OK; + } + + return NGX_DECLINED; +} + + +static void * +ngx_stream_pass_create_srv_conf(ngx_conf_t *cf) +{ + ngx_stream_pass_srv_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_pass_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * conf->addr = NULL; + * conf->addr_value = NULL; + */ + + return conf; +} + + +static char * +ngx_stream_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_pass_srv_conf_t *pscf = conf; + + ngx_url_t u; + ngx_str_t *value, *url; + ngx_stream_complex_value_t cv; + ngx_stream_core_srv_conf_t *cscf; + ngx_stream_compile_complex_value_t ccv; + + if (pscf->addr || pscf->addr_value) { + return "is duplicate"; + } + + cscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_core_module); + + cscf->handler = ngx_stream_pass_handler; + + value = cf->args->elts; + + url = &value[1]; + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = url; + ccv.complex_value = &cv; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (cv.lengths) { + pscf->addr_value = ngx_palloc(cf->pool, + sizeof(ngx_stream_complex_value_t)); + if (pscf->addr_value == NULL) { + return NGX_CONF_ERROR; + } + + *pscf->addr_value = cv; + + return NGX_CONF_OK; + } + + ngx_memzero(&u, sizeof(ngx_url_t)); + + u.url = *url; + u.no_resolve = 1; + + if (ngx_parse_url(cf->pool, &u) != NGX_OK) { + if (u.err) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "%s in \"%V\" of the \"pass\" directive", + u.err, &u.url); + } + + return NGX_CONF_ERROR; + } + + if (u.naddrs == 0) { + return "has no addresses"; + } + + if (u.no_port) { + return "has no port"; + } + + pscf->addr = &u.addrs[0]; + + return NGX_CONF_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_proxy_module.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_proxy_module.c new file mode 100644 index 000000000..300bdf681 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_proxy_module.c @@ -0,0 +1,2683 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_addr_t *addr; + ngx_stream_complex_value_t *value; +#if (NGX_HAVE_TRANSPARENT_PROXY) + ngx_uint_t transparent; /* unsigned transparent:1; */ +#endif +} ngx_stream_upstream_local_t; + + +typedef struct { + ngx_msec_t connect_timeout; + ngx_msec_t timeout; + ngx_msec_t next_upstream_timeout; + size_t buffer_size; + ngx_stream_complex_value_t *upload_rate; + ngx_stream_complex_value_t *download_rate; + ngx_uint_t requests; + ngx_uint_t responses; + ngx_uint_t next_upstream_tries; + ngx_flag_t next_upstream; + ngx_flag_t proxy_protocol; + ngx_flag_t half_close; + ngx_stream_upstream_local_t *local; + ngx_flag_t socket_keepalive; + +#if (NGX_STREAM_SSL) + ngx_flag_t ssl_enable; + ngx_flag_t ssl_session_reuse; + ngx_uint_t ssl_protocols; + ngx_str_t ssl_ciphers; + ngx_stream_complex_value_t *ssl_name; + ngx_flag_t ssl_server_name; + + ngx_flag_t ssl_verify; + ngx_uint_t ssl_verify_depth; + ngx_str_t ssl_trusted_certificate; + ngx_str_t ssl_crl; + ngx_stream_complex_value_t *ssl_certificate; + ngx_stream_complex_value_t *ssl_certificate_key; + ngx_ssl_cache_t *ssl_certificate_cache; + ngx_array_t *ssl_passwords; + ngx_array_t *ssl_conf_commands; + + ngx_ssl_t *ssl; +#endif + + ngx_stream_upstream_srv_conf_t *upstream; + ngx_stream_complex_value_t *upstream_value; +} ngx_stream_proxy_srv_conf_t; + + +static void ngx_stream_proxy_handler(ngx_stream_session_t *s); +static ngx_int_t ngx_stream_proxy_eval(ngx_stream_session_t *s, + ngx_stream_proxy_srv_conf_t *pscf); +static ngx_int_t ngx_stream_proxy_set_local(ngx_stream_session_t *s, + ngx_stream_upstream_t *u, ngx_stream_upstream_local_t *local); +static void ngx_stream_proxy_connect(ngx_stream_session_t *s); +static void ngx_stream_proxy_init_upstream(ngx_stream_session_t *s); +static void ngx_stream_proxy_resolve_handler(ngx_resolver_ctx_t *ctx); +static void ngx_stream_proxy_upstream_handler(ngx_event_t *ev); +static void ngx_stream_proxy_downstream_handler(ngx_event_t *ev); +static void ngx_stream_proxy_process_connection(ngx_event_t *ev, + ngx_uint_t from_upstream); +static void ngx_stream_proxy_connect_handler(ngx_event_t *ev); +static ngx_int_t ngx_stream_proxy_test_connect(ngx_connection_t *c); +static void ngx_stream_proxy_process(ngx_stream_session_t *s, + ngx_uint_t from_upstream, ngx_uint_t do_write); +static ngx_int_t ngx_stream_proxy_test_finalize(ngx_stream_session_t *s, + ngx_uint_t from_upstream); +static void ngx_stream_proxy_next_upstream(ngx_stream_session_t *s); +static void ngx_stream_proxy_finalize(ngx_stream_session_t *s, ngx_uint_t rc); +static u_char *ngx_stream_proxy_log_error(ngx_log_t *log, u_char *buf, + size_t len); + +static void *ngx_stream_proxy_create_srv_conf(ngx_conf_t *cf); +static char *ngx_stream_proxy_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); +static char *ngx_stream_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_proxy_bind(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +#if (NGX_STREAM_SSL) + +static ngx_int_t ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s); +static char *ngx_stream_proxy_ssl_certificate_cache(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); +static char *ngx_stream_proxy_ssl_password_file(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); +static char *ngx_stream_proxy_ssl_conf_command_check(ngx_conf_t *cf, void *post, + void *data); +static void ngx_stream_proxy_ssl_init_connection(ngx_stream_session_t *s); +static void ngx_stream_proxy_ssl_handshake(ngx_connection_t *pc); +static void ngx_stream_proxy_ssl_save_session(ngx_connection_t *c); +static ngx_int_t ngx_stream_proxy_ssl_name(ngx_stream_session_t *s); +static ngx_int_t ngx_stream_proxy_ssl_certificate(ngx_stream_session_t *s); +static ngx_int_t ngx_stream_proxy_merge_ssl(ngx_conf_t *cf, + ngx_stream_proxy_srv_conf_t *conf, ngx_stream_proxy_srv_conf_t *prev); +static ngx_int_t ngx_stream_proxy_merge_ssl_passwords(ngx_conf_t *cf, + ngx_stream_proxy_srv_conf_t *conf, ngx_stream_proxy_srv_conf_t *prev); +static ngx_int_t ngx_stream_proxy_set_ssl(ngx_conf_t *cf, + ngx_stream_proxy_srv_conf_t *pscf); + + +static ngx_conf_bitmask_t ngx_stream_proxy_ssl_protocols[] = { + { ngx_string("SSLv2"), NGX_SSL_SSLv2 }, + { ngx_string("SSLv3"), NGX_SSL_SSLv3 }, + { ngx_string("TLSv1"), NGX_SSL_TLSv1 }, + { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 }, + { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 }, + { ngx_string("TLSv1.3"), NGX_SSL_TLSv1_3 }, + { ngx_null_string, 0 } +}; + +static ngx_conf_post_t ngx_stream_proxy_ssl_conf_command_post = + { ngx_stream_proxy_ssl_conf_command_check }; + +#endif + + +static ngx_conf_deprecated_t ngx_conf_deprecated_proxy_downstream_buffer = { + ngx_conf_deprecated, "proxy_downstream_buffer", "proxy_buffer_size" +}; + +static ngx_conf_deprecated_t ngx_conf_deprecated_proxy_upstream_buffer = { + ngx_conf_deprecated, "proxy_upstream_buffer", "proxy_buffer_size" +}; + + +static ngx_command_t ngx_stream_proxy_commands[] = { + + { ngx_string("proxy_pass"), + NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_proxy_pass, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("proxy_bind"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE12, + ngx_stream_proxy_bind, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("proxy_socket_keepalive"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, socket_keepalive), + NULL }, + + { ngx_string("proxy_connect_timeout"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, connect_timeout), + NULL }, + + { ngx_string("proxy_timeout"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, timeout), + NULL }, + + { ngx_string("proxy_buffer_size"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, buffer_size), + NULL }, + + { ngx_string("proxy_downstream_buffer"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, buffer_size), + &ngx_conf_deprecated_proxy_downstream_buffer }, + + { ngx_string("proxy_upstream_buffer"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_size_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, buffer_size), + &ngx_conf_deprecated_proxy_upstream_buffer }, + + { ngx_string("proxy_upload_rate"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_set_complex_value_size_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, upload_rate), + NULL }, + + { ngx_string("proxy_download_rate"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_set_complex_value_size_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, download_rate), + NULL }, + + { ngx_string("proxy_requests"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, requests), + NULL }, + + { ngx_string("proxy_responses"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, responses), + NULL }, + + { ngx_string("proxy_next_upstream"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, next_upstream), + NULL }, + + { ngx_string("proxy_next_upstream_tries"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, next_upstream_tries), + NULL }, + + { ngx_string("proxy_next_upstream_timeout"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, next_upstream_timeout), + NULL }, + + { ngx_string("proxy_protocol"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, proxy_protocol), + NULL }, + + { ngx_string("proxy_half_close"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, half_close), + NULL }, + +#if (NGX_STREAM_SSL) + + { ngx_string("proxy_ssl"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, ssl_enable), + NULL }, + + { ngx_string("proxy_ssl_session_reuse"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, ssl_session_reuse), + NULL }, + + { ngx_string("proxy_ssl_protocols"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, ssl_protocols), + &ngx_stream_proxy_ssl_protocols }, + + { ngx_string("proxy_ssl_ciphers"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, ssl_ciphers), + NULL }, + + { ngx_string("proxy_ssl_name"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_set_complex_value_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, ssl_name), + NULL }, + + { ngx_string("proxy_ssl_server_name"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, ssl_server_name), + NULL }, + + { ngx_string("proxy_ssl_verify"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, ssl_verify), + NULL }, + + { ngx_string("proxy_ssl_verify_depth"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, ssl_verify_depth), + NULL }, + + { ngx_string("proxy_ssl_trusted_certificate"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, ssl_trusted_certificate), + NULL }, + + { ngx_string("proxy_ssl_crl"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, ssl_crl), + NULL }, + + { ngx_string("proxy_ssl_certificate"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_set_complex_value_zero_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, ssl_certificate), + NULL }, + + { ngx_string("proxy_ssl_certificate_key"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_set_complex_value_zero_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, ssl_certificate_key), + NULL }, + + { ngx_string("proxy_ssl_certificate_cache"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE123, + ngx_stream_proxy_ssl_certificate_cache, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("proxy_ssl_password_file"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_proxy_ssl_password_file, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("proxy_ssl_conf_command"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2, + ngx_conf_set_keyval_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_proxy_srv_conf_t, ssl_conf_commands), + &ngx_stream_proxy_ssl_conf_command_post }, + +#endif + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_proxy_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_stream_proxy_create_srv_conf, /* create server configuration */ + ngx_stream_proxy_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_proxy_module = { + NGX_MODULE_V1, + &ngx_stream_proxy_module_ctx, /* module context */ + ngx_stream_proxy_commands, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static void +ngx_stream_proxy_handler(ngx_stream_session_t *s) +{ + u_char *p; + ngx_str_t *host; + ngx_uint_t i; + ngx_connection_t *c; + ngx_resolver_ctx_t *ctx, temp; + ngx_stream_upstream_t *u; + ngx_stream_core_srv_conf_t *cscf; + ngx_stream_proxy_srv_conf_t *pscf; + ngx_stream_upstream_srv_conf_t *uscf, **uscfp; + ngx_stream_upstream_main_conf_t *umcf; + + c = s->connection; + + pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "proxy connection handler"); + + u = ngx_pcalloc(c->pool, sizeof(ngx_stream_upstream_t)); + if (u == NULL) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + s->upstream = u; + + s->log_handler = ngx_stream_proxy_log_error; + + u->requests = 1; + + u->peer.log = c->log; + u->peer.log_error = NGX_ERROR_ERR; + + if (ngx_stream_proxy_set_local(s, u, pscf->local) != NGX_OK) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + if (pscf->socket_keepalive) { + u->peer.so_keepalive = 1; + } + + u->peer.type = c->type; + u->start_sec = ngx_time(); + + c->write->handler = ngx_stream_proxy_downstream_handler; + c->read->handler = ngx_stream_proxy_downstream_handler; + + s->upstream_states = ngx_array_create(c->pool, 1, + sizeof(ngx_stream_upstream_state_t)); + if (s->upstream_states == NULL) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + p = ngx_pnalloc(c->pool, pscf->buffer_size); + if (p == NULL) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + u->downstream_buf.start = p; + u->downstream_buf.end = p + pscf->buffer_size; + u->downstream_buf.pos = p; + u->downstream_buf.last = p; + + if (c->read->ready) { + ngx_post_event(c->read, &ngx_posted_events); + } + + if (pscf->upstream_value) { + if (ngx_stream_proxy_eval(s, pscf) != NGX_OK) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + } + + if (u->resolved == NULL) { + + uscf = pscf->upstream; + + } else { + +#if (NGX_STREAM_SSL) + u->ssl_name = u->resolved->host; +#endif + + host = &u->resolved->host; + + umcf = ngx_stream_get_module_main_conf(s, ngx_stream_upstream_module); + + uscfp = umcf->upstreams.elts; + + for (i = 0; i < umcf->upstreams.nelts; i++) { + + uscf = uscfp[i]; + + if (uscf->host.len == host->len + && ((uscf->port == 0 && u->resolved->no_port) + || uscf->port == u->resolved->port) + && ngx_strncasecmp(uscf->host.data, host->data, host->len) == 0) + { + goto found; + } + } + + if (u->resolved->sockaddr) { + + if (u->resolved->port == 0 + && u->resolved->sockaddr->sa_family != AF_UNIX) + { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "no port in upstream \"%V\"", host); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + if (ngx_stream_upstream_create_round_robin_peer(s, u->resolved) + != NGX_OK) + { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + ngx_stream_proxy_connect(s); + + return; + } + + if (u->resolved->port == 0) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "no port in upstream \"%V\"", host); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + temp.name = *host; + + cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); + + ctx = ngx_resolve_start(cscf->resolver, &temp); + if (ctx == NULL) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + if (ctx == NGX_NO_RESOLVER) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "no resolver defined to resolve %V", host); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + ctx->name = *host; + ctx->handler = ngx_stream_proxy_resolve_handler; + ctx->data = s; + ctx->timeout = cscf->resolver_timeout; + + u->resolved->ctx = ctx; + + if (ngx_resolve_name(ctx) != NGX_OK) { + u->resolved->ctx = NULL; + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + return; + } + +found: + + if (uscf == NULL) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, "no upstream configuration"); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + u->upstream = uscf; + +#if (NGX_STREAM_SSL) + u->ssl_name = uscf->host; +#endif + + if (uscf->peer.init(s, uscf) != NGX_OK) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + u->peer.start_time = ngx_current_msec; + + if (pscf->next_upstream_tries + && u->peer.tries > pscf->next_upstream_tries) + { + u->peer.tries = pscf->next_upstream_tries; + } + + ngx_stream_proxy_connect(s); +} + + +static ngx_int_t +ngx_stream_proxy_eval(ngx_stream_session_t *s, + ngx_stream_proxy_srv_conf_t *pscf) +{ + ngx_str_t host; + ngx_url_t url; + ngx_stream_upstream_t *u; + + if (ngx_stream_complex_value(s, pscf->upstream_value, &host) != NGX_OK) { + return NGX_ERROR; + } + + ngx_memzero(&url, sizeof(ngx_url_t)); + + url.url = host; + url.no_resolve = 1; + + if (ngx_parse_url(s->connection->pool, &url) != NGX_OK) { + if (url.err) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "%s in upstream \"%V\"", url.err, &url.url); + } + + return NGX_ERROR; + } + + u = s->upstream; + + u->resolved = ngx_pcalloc(s->connection->pool, + sizeof(ngx_stream_upstream_resolved_t)); + if (u->resolved == NULL) { + return NGX_ERROR; + } + + if (url.addrs) { + u->resolved->sockaddr = url.addrs[0].sockaddr; + u->resolved->socklen = url.addrs[0].socklen; + u->resolved->name = url.addrs[0].name; + u->resolved->naddrs = 1; + } + + u->resolved->host = url.host; + u->resolved->port = url.port; + u->resolved->no_port = url.no_port; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_proxy_set_local(ngx_stream_session_t *s, ngx_stream_upstream_t *u, + ngx_stream_upstream_local_t *local) +{ + ngx_int_t rc; + ngx_str_t val; + ngx_addr_t *addr; + + if (local == NULL) { + u->peer.local = NULL; + return NGX_OK; + } + +#if (NGX_HAVE_TRANSPARENT_PROXY) + u->peer.transparent = local->transparent; +#endif + + if (local->value == NULL) { + u->peer.local = local->addr; + return NGX_OK; + } + + if (ngx_stream_complex_value(s, local->value, &val) != NGX_OK) { + return NGX_ERROR; + } + + if (val.len == 0) { + u->peer.local = NULL; + return NGX_OK; + } + + addr = ngx_palloc(s->connection->pool, sizeof(ngx_addr_t)); + if (addr == NULL) { + return NGX_ERROR; + } + + rc = ngx_parse_addr_port(s->connection->pool, addr, val.data, val.len); + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc != NGX_OK) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "invalid local address \"%V\"", &val); + u->peer.local = NULL; + return NGX_OK; + } + + addr->name = val; + u->peer.local = addr; + + return NGX_OK; +} + + +static void +ngx_stream_proxy_connect(ngx_stream_session_t *s) +{ + ngx_int_t rc; + ngx_connection_t *c, *pc; + ngx_stream_upstream_t *u; + ngx_stream_proxy_srv_conf_t *pscf; + + c = s->connection; + + c->log->action = "connecting to upstream"; + + pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); + + u = s->upstream; + + u->connected = 0; + u->proxy_protocol = pscf->proxy_protocol; + + if (u->state) { + u->state->response_time = ngx_current_msec - u->start_time; + } + + u->state = ngx_array_push(s->upstream_states); + if (u->state == NULL) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + ngx_memzero(u->state, sizeof(ngx_stream_upstream_state_t)); + + u->start_time = ngx_current_msec; + + u->state->connect_time = (ngx_msec_t) -1; + u->state->first_byte_time = (ngx_msec_t) -1; + u->state->response_time = (ngx_msec_t) -1; + + rc = ngx_event_connect_peer(&u->peer); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, "proxy connect: %i", rc); + + if (rc == NGX_ERROR) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + u->state->peer = u->peer.name; + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (u->upstream && u->upstream->shm_zone + && (u->upstream->flags & NGX_STREAM_UPSTREAM_MODIFY)) + { + u->state->peer = ngx_palloc(s->connection->pool, + sizeof(ngx_str_t) + u->peer.name->len); + if (u->state->peer == NULL) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + u->state->peer->len = u->peer.name->len; + u->state->peer->data = (u_char *) (u->state->peer + 1); + ngx_memcpy(u->state->peer->data, u->peer.name->data, u->peer.name->len); + + u->peer.name = u->state->peer; + } +#endif + + if (rc == NGX_BUSY) { + ngx_log_error(NGX_LOG_ERR, c->log, 0, "no live upstreams"); + ngx_stream_proxy_finalize(s, NGX_STREAM_BAD_GATEWAY); + return; + } + + if (rc == NGX_DECLINED) { + ngx_stream_proxy_next_upstream(s); + return; + } + + /* rc == NGX_OK || rc == NGX_AGAIN || rc == NGX_DONE */ + + pc = u->peer.connection; + + pc->data = s; + pc->log = c->log; + pc->pool = c->pool; + pc->read->log = c->log; + pc->write->log = c->log; + + if (rc != NGX_AGAIN) { + ngx_stream_proxy_init_upstream(s); + return; + } + + pc->read->handler = ngx_stream_proxy_connect_handler; + pc->write->handler = ngx_stream_proxy_connect_handler; + + ngx_add_timer(pc->write, pscf->connect_timeout); +} + + +static void +ngx_stream_proxy_init_upstream(ngx_stream_session_t *s) +{ + u_char *p; + ngx_chain_t *cl; + ngx_connection_t *c, *pc; + ngx_log_handler_pt handler; + ngx_stream_upstream_t *u; + ngx_stream_core_srv_conf_t *cscf; + ngx_stream_proxy_srv_conf_t *pscf; + + u = s->upstream; + pc = u->peer.connection; + + cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); + + if (pc->type == SOCK_STREAM + && cscf->tcp_nodelay + && ngx_tcp_nodelay(pc) != NGX_OK) + { + ngx_stream_proxy_next_upstream(s); + return; + } + + pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); + +#if (NGX_STREAM_SSL) + + if (pc->type == SOCK_STREAM && pscf->ssl_enable) { + + if (u->proxy_protocol) { + if (ngx_stream_proxy_send_proxy_protocol(s) != NGX_OK) { + return; + } + + u->proxy_protocol = 0; + } + + if (pc->ssl == NULL) { + ngx_stream_proxy_ssl_init_connection(s); + return; + } + } + +#endif + + c = s->connection; + + if (c->log->log_level >= NGX_LOG_INFO) { + ngx_str_t str; + u_char addr[NGX_SOCKADDR_STRLEN]; + + str.len = NGX_SOCKADDR_STRLEN; + str.data = addr; + + if (ngx_connection_local_sockaddr(pc, &str, 1) == NGX_OK) { + handler = c->log->handler; + c->log->handler = NULL; + + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "%sproxy %V connected to %V", + pc->type == SOCK_DGRAM ? "udp " : "", + &str, u->peer.name); + + c->log->handler = handler; + } + } + + u->state->connect_time = ngx_current_msec - u->start_time; + + if (u->peer.notify) { + u->peer.notify(&u->peer, u->peer.data, + NGX_STREAM_UPSTREAM_NOTIFY_CONNECT); + } + + if (u->upstream_buf.start == NULL) { + p = ngx_pnalloc(c->pool, pscf->buffer_size); + if (p == NULL) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + u->upstream_buf.start = p; + u->upstream_buf.end = p + pscf->buffer_size; + u->upstream_buf.pos = p; + u->upstream_buf.last = p; + } + + if (c->buffer && c->buffer->pos <= c->buffer->last) { + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream proxy add preread buffer: %uz", + c->buffer->last - c->buffer->pos); + + cl = ngx_chain_get_free_buf(c->pool, &u->free); + if (cl == NULL) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + *cl->buf = *c->buffer; + + cl->buf->tag = (ngx_buf_tag_t) &ngx_stream_proxy_module; + cl->buf->temporary = (cl->buf->pos == cl->buf->last) ? 0 : 1; + cl->buf->flush = 1; + + cl->next = u->upstream_out; + u->upstream_out = cl; + } + + if (u->proxy_protocol) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream proxy add PROXY protocol header"); + + cl = ngx_chain_get_free_buf(c->pool, &u->free); + if (cl == NULL) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + p = ngx_pnalloc(c->pool, NGX_PROXY_PROTOCOL_V1_MAX_HEADER); + if (p == NULL) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + cl->buf->pos = p; + + p = ngx_proxy_protocol_write(c, p, + p + NGX_PROXY_PROTOCOL_V1_MAX_HEADER); + if (p == NULL) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + cl->buf->last = p; + cl->buf->temporary = 1; + cl->buf->flush = 0; + cl->buf->last_buf = 0; + cl->buf->tag = (ngx_buf_tag_t) &ngx_stream_proxy_module; + + cl->next = u->upstream_out; + u->upstream_out = cl; + + u->proxy_protocol = 0; + } + + u->upload_rate = ngx_stream_complex_value_size(s, pscf->upload_rate, 0); + u->download_rate = ngx_stream_complex_value_size(s, pscf->download_rate, 0); + + u->connected = 1; + + pc->read->handler = ngx_stream_proxy_upstream_handler; + pc->write->handler = ngx_stream_proxy_upstream_handler; + + if (pc->read->ready) { + ngx_post_event(pc->read, &ngx_posted_events); + } + + ngx_stream_proxy_process(s, 0, 1); +} + + +#if (NGX_STREAM_SSL) + +static ngx_int_t +ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s) +{ + u_char *p; + ssize_t n, size; + ngx_connection_t *c, *pc; + ngx_stream_upstream_t *u; + ngx_stream_proxy_srv_conf_t *pscf; + u_char buf[NGX_PROXY_PROTOCOL_V1_MAX_HEADER]; + + c = s->connection; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream proxy send PROXY protocol header"); + + p = ngx_proxy_protocol_write(c, buf, + buf + NGX_PROXY_PROTOCOL_V1_MAX_HEADER); + if (p == NULL) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + u = s->upstream; + + pc = u->peer.connection; + + size = p - buf; + + n = pc->send(pc, buf, size); + + if (n == NGX_AGAIN) { + if (ngx_handle_write_event(pc->write, 0) != NGX_OK) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return NGX_ERROR; + } + + pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); + + ngx_add_timer(pc->write, pscf->timeout); + + pc->write->handler = ngx_stream_proxy_connect_handler; + + return NGX_AGAIN; + } + + if (n == NGX_ERROR) { + ngx_stream_proxy_finalize(s, NGX_STREAM_OK); + return NGX_ERROR; + } + + if (n != size) { + + /* + * PROXY protocol specification: + * The sender must always ensure that the header + * is sent at once, so that the transport layer + * maintains atomicity along the path to the receiver. + */ + + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "could not send PROXY protocol header at once"); + + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + + return NGX_ERROR; + } + + return NGX_OK; +} + + +static char * +ngx_stream_proxy_ssl_certificate_cache(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + ngx_stream_proxy_srv_conf_t *pscf = conf; + + time_t inactive, valid; + ngx_str_t *value, s; + ngx_int_t max; + ngx_uint_t i; + + if (pscf->ssl_certificate_cache != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + value = cf->args->elts; + + max = 0; + inactive = 10; + valid = 60; + + for (i = 1; i < cf->args->nelts; i++) { + + if (ngx_strncmp(value[i].data, "max=", 4) == 0) { + + max = ngx_atoi(value[i].data + 4, value[i].len - 4); + if (max <= 0) { + goto failed; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) { + + s.len = value[i].len - 9; + s.data = value[i].data + 9; + + inactive = ngx_parse_time(&s, 1); + if (inactive == (time_t) NGX_ERROR) { + goto failed; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "valid=", 6) == 0) { + + s.len = value[i].len - 6; + s.data = value[i].data + 6; + + valid = ngx_parse_time(&s, 1); + if (valid == (time_t) NGX_ERROR) { + goto failed; + } + + continue; + } + + if (ngx_strcmp(value[i].data, "off") == 0) { + + pscf->ssl_certificate_cache = NULL; + + continue; + } + + failed: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (pscf->ssl_certificate_cache == NULL) { + return NGX_CONF_OK; + } + + if (max == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"proxy_ssl_certificate_cache\" must have " + "the \"max\" parameter"); + return NGX_CONF_ERROR; + } + + pscf->ssl_certificate_cache = ngx_ssl_cache_init(cf->pool, max, valid, + inactive); + if (pscf->ssl_certificate_cache == NULL) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_stream_proxy_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + ngx_stream_proxy_srv_conf_t *pscf = conf; + + ngx_str_t *value; + + if (pscf->ssl_passwords != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + value = cf->args->elts; + + pscf->ssl_passwords = ngx_ssl_read_password_file(cf, &value[1]); + + if (pscf->ssl_passwords == NULL) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_stream_proxy_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data) +{ +#ifndef SSL_CONF_FLAG_FILE + return "is not supported on this platform"; +#else + return NGX_CONF_OK; +#endif +} + + +static void +ngx_stream_proxy_ssl_init_connection(ngx_stream_session_t *s) +{ + ngx_int_t rc; + ngx_connection_t *pc; + ngx_stream_upstream_t *u; + ngx_stream_proxy_srv_conf_t *pscf; + + u = s->upstream; + + pc = u->peer.connection; + + pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); + + if (ngx_ssl_create_connection(pscf->ssl, pc, NGX_SSL_BUFFER|NGX_SSL_CLIENT) + != NGX_OK) + { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + if (pscf->ssl_server_name || pscf->ssl_verify) { + if (ngx_stream_proxy_ssl_name(s) != NGX_OK) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + } + + if (pscf->ssl_certificate + && pscf->ssl_certificate->value.len + && (pscf->ssl_certificate->lengths + || pscf->ssl_certificate_key->lengths)) + { + if (ngx_stream_proxy_ssl_certificate(s) != NGX_OK) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + } + + if (pscf->ssl_session_reuse) { + pc->ssl->save_session = ngx_stream_proxy_ssl_save_session; + + if (u->peer.set_session(&u->peer, u->peer.data) != NGX_OK) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + } + + s->connection->log->action = "SSL handshaking to upstream"; + + rc = ngx_ssl_handshake(pc); + + if (rc == NGX_AGAIN) { + + if (!pc->write->timer_set) { + ngx_add_timer(pc->write, pscf->connect_timeout); + } + + pc->ssl->handler = ngx_stream_proxy_ssl_handshake; + return; + } + + ngx_stream_proxy_ssl_handshake(pc); +} + + +static void +ngx_stream_proxy_ssl_handshake(ngx_connection_t *pc) +{ + long rc; + ngx_stream_session_t *s; + ngx_stream_upstream_t *u; + ngx_stream_proxy_srv_conf_t *pscf; + + s = pc->data; + + pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); + + if (pc->ssl->handshaked) { + + if (pscf->ssl_verify) { + rc = SSL_get_verify_result(pc->ssl->connection); + + if (rc != X509_V_OK) { + ngx_log_error(NGX_LOG_ERR, pc->log, 0, + "upstream SSL certificate verify error: (%l:%s)", + rc, X509_verify_cert_error_string(rc)); + goto failed; + } + + u = s->upstream; + + if (ngx_ssl_check_host(pc, &u->ssl_name) != NGX_OK) { + ngx_log_error(NGX_LOG_ERR, pc->log, 0, + "upstream SSL certificate does not match \"%V\"", + &u->ssl_name); + goto failed; + } + } + + if (pc->write->timer_set) { + ngx_del_timer(pc->write); + } + + ngx_stream_proxy_init_upstream(s); + + return; + } + +failed: + + ngx_stream_proxy_next_upstream(s); +} + + +static void +ngx_stream_proxy_ssl_save_session(ngx_connection_t *c) +{ + ngx_stream_session_t *s; + ngx_stream_upstream_t *u; + + s = c->data; + u = s->upstream; + + u->peer.save_session(&u->peer, u->peer.data); +} + + +static ngx_int_t +ngx_stream_proxy_ssl_name(ngx_stream_session_t *s) +{ + u_char *p, *last; + ngx_str_t name; + ngx_stream_upstream_t *u; + ngx_stream_proxy_srv_conf_t *pscf; + + pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); + + u = s->upstream; + + if (pscf->ssl_name) { + if (ngx_stream_complex_value(s, pscf->ssl_name, &name) != NGX_OK) { + return NGX_ERROR; + } + + } else { + name = u->ssl_name; + } + + if (name.len == 0) { + goto done; + } + + /* + * ssl name here may contain port, strip it for compatibility + * with the http module + */ + + p = name.data; + last = name.data + name.len; + + if (*p == '[') { + p = ngx_strlchr(p, last, ']'); + + if (p == NULL) { + p = name.data; + } + } + + p = ngx_strlchr(p, last, ':'); + + if (p != NULL) { + name.len = p - name.data; + } + + if (!pscf->ssl_server_name) { + goto done; + } + +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + + /* as per RFC 6066, literal IPv4 and IPv6 addresses are not permitted */ + + if (name.len == 0 || *name.data == '[') { + goto done; + } + + if (ngx_inet_addr(name.data, name.len) != INADDR_NONE) { + goto done; + } + + /* + * SSL_set_tlsext_host_name() needs a null-terminated string, + * hence we explicitly null-terminate name here + */ + + p = ngx_pnalloc(s->connection->pool, name.len + 1); + if (p == NULL) { + return NGX_ERROR; + } + + (void) ngx_cpystrn(p, name.data, name.len + 1); + + name.data = p; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "upstream SSL server name: \"%s\"", name.data); + + if (SSL_set_tlsext_host_name(u->peer.connection->ssl->connection, + (char *) name.data) + == 0) + { + ngx_ssl_error(NGX_LOG_ERR, s->connection->log, 0, + "SSL_set_tlsext_host_name(\"%s\") failed", name.data); + return NGX_ERROR; + } + +#endif + +done: + + u->ssl_name = name; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_proxy_ssl_certificate(ngx_stream_session_t *s) +{ + ngx_str_t cert, key; + ngx_connection_t *c; + ngx_stream_proxy_srv_conf_t *pscf; + + c = s->upstream->peer.connection; + + pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); + + if (ngx_stream_complex_value(s, pscf->ssl_certificate, &cert) + != NGX_OK) + { + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream upstream ssl cert: \"%s\"", cert.data); + + if (*cert.data == '\0') { + return NGX_OK; + } + + if (ngx_stream_complex_value(s, pscf->ssl_certificate_key, &key) + != NGX_OK) + { + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream upstream ssl key: \"%s\"", key.data); + + if (ngx_ssl_connection_certificate(c, c->pool, &cert, &key, + pscf->ssl_certificate_cache, + pscf->ssl_passwords) + != NGX_OK) + { + return NGX_ERROR; + } + + return NGX_OK; +} + +#endif + + +static void +ngx_stream_proxy_downstream_handler(ngx_event_t *ev) +{ + ngx_stream_proxy_process_connection(ev, ev->write); +} + + +static void +ngx_stream_proxy_resolve_handler(ngx_resolver_ctx_t *ctx) +{ + ngx_stream_session_t *s; + ngx_stream_upstream_t *u; + ngx_stream_proxy_srv_conf_t *pscf; + ngx_stream_upstream_resolved_t *ur; + + s = ctx->data; + + u = s->upstream; + ur = u->resolved; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream upstream resolve"); + + if (ctx->state) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "%V could not be resolved (%i: %s)", + &ctx->name, ctx->state, + ngx_resolver_strerror(ctx->state)); + + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + ur->naddrs = ctx->naddrs; + ur->addrs = ctx->addrs; + +#if (NGX_DEBUG) + { + u_char text[NGX_SOCKADDR_STRLEN]; + ngx_str_t addr; + ngx_uint_t i; + + addr.data = text; + + for (i = 0; i < ctx->naddrs; i++) { + addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen, + text, NGX_SOCKADDR_STRLEN, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "name was resolved to %V", &addr); + } + } +#endif + + if (ngx_stream_upstream_create_round_robin_peer(s, ur) != NGX_OK) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + ngx_resolve_name_done(ctx); + ur->ctx = NULL; + + u->peer.start_time = ngx_current_msec; + + pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); + + if (pscf->next_upstream_tries + && u->peer.tries > pscf->next_upstream_tries) + { + u->peer.tries = pscf->next_upstream_tries; + } + + ngx_stream_proxy_connect(s); +} + + +static void +ngx_stream_proxy_upstream_handler(ngx_event_t *ev) +{ + ngx_stream_proxy_process_connection(ev, !ev->write); +} + + +static void +ngx_stream_proxy_process_connection(ngx_event_t *ev, ngx_uint_t from_upstream) +{ + ngx_connection_t *c, *pc; + ngx_log_handler_pt handler; + ngx_stream_session_t *s; + ngx_stream_upstream_t *u; + ngx_stream_proxy_srv_conf_t *pscf; + + c = ev->data; + s = c->data; + u = s->upstream; + + if (c->close) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, "shutdown timeout"); + ngx_stream_proxy_finalize(s, NGX_STREAM_OK); + return; + } + + c = s->connection; + pc = u->peer.connection; + + pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); + + if (ev->timedout) { + ev->timedout = 0; + + if (ev->delayed) { + ev->delayed = 0; + + if (!ev->ready) { + if (ngx_handle_read_event(ev, 0) != NGX_OK) { + ngx_stream_proxy_finalize(s, + NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + if (u->connected && !c->read->delayed && !pc->read->delayed) { + ngx_add_timer(c->write, pscf->timeout); + } + + return; + } + + } else { + if (s->connection->type == SOCK_DGRAM) { + + if (pscf->responses == NGX_MAX_INT32_VALUE + || (u->responses >= pscf->responses * u->requests)) + { + + /* + * successfully terminate timed out UDP session + * if expected number of responses was received + */ + + handler = c->log->handler; + c->log->handler = NULL; + + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "udp timed out" + ", packets from/to client:%ui/%ui" + ", bytes from/to client:%O/%O" + ", bytes from/to upstream:%O/%O", + u->requests, u->responses, + s->received, c->sent, u->received, + pc ? pc->sent : 0); + + c->log->handler = handler; + + ngx_stream_proxy_finalize(s, NGX_STREAM_OK); + return; + } + + ngx_connection_error(pc, NGX_ETIMEDOUT, "upstream timed out"); + + pc->read->error = 1; + + ngx_stream_proxy_finalize(s, NGX_STREAM_BAD_GATEWAY); + + return; + } + + ngx_connection_error(c, NGX_ETIMEDOUT, "connection timed out"); + + ngx_stream_proxy_finalize(s, NGX_STREAM_OK); + + return; + } + + } else if (ev->delayed) { + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream connection delayed"); + + if (ngx_handle_read_event(ev, 0) != NGX_OK) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + } + + return; + } + + if (from_upstream && !u->connected) { + return; + } + + ngx_stream_proxy_process(s, from_upstream, ev->write); +} + + +static void +ngx_stream_proxy_connect_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; + ngx_stream_session_t *s; + + c = ev->data; + s = c->data; + + if (ev->timedout) { + ngx_log_error(NGX_LOG_ERR, c->log, NGX_ETIMEDOUT, "upstream timed out"); + ngx_stream_proxy_next_upstream(s); + return; + } + + ngx_del_timer(c->write); + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream proxy connect upstream"); + + if (ngx_stream_proxy_test_connect(c) != NGX_OK) { + ngx_stream_proxy_next_upstream(s); + return; + } + + ngx_stream_proxy_init_upstream(s); +} + + +static ngx_int_t +ngx_stream_proxy_test_connect(ngx_connection_t *c) +{ + int err; + socklen_t len; + +#if (NGX_HAVE_KQUEUE) + + if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { + err = c->write->kq_errno ? c->write->kq_errno : c->read->kq_errno; + + if (err) { + (void) ngx_connection_error(c, err, + "kevent() reported that connect() failed"); + return NGX_ERROR; + } + + } else +#endif + { + err = 0; + len = sizeof(int); + + /* + * BSDs and Linux return 0 and set a pending error in err + * Solaris returns -1 and sets errno + */ + + if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len) + == -1) + { + err = ngx_socket_errno; + } + + if (err) { + (void) ngx_connection_error(c, err, "connect() failed"); + return NGX_ERROR; + } + } + + return NGX_OK; +} + + +static void +ngx_stream_proxy_process(ngx_stream_session_t *s, ngx_uint_t from_upstream, + ngx_uint_t do_write) +{ + char *recv_action, *send_action; + off_t *received, limit; + size_t size, limit_rate; + ssize_t n; + ngx_buf_t *b; + ngx_int_t rc; + ngx_uint_t flags, *packets; + ngx_msec_t delay; + ngx_chain_t *cl, **ll, **out, **busy; + ngx_connection_t *c, *pc, *src, *dst; + ngx_log_handler_pt handler; + ngx_stream_upstream_t *u; + ngx_stream_proxy_srv_conf_t *pscf; + + u = s->upstream; + + c = s->connection; + pc = u->connected ? u->peer.connection : NULL; + + if (c->type == SOCK_DGRAM && (ngx_terminate || ngx_exiting)) { + + /* socket is already closed on worker shutdown */ + + handler = c->log->handler; + c->log->handler = NULL; + + ngx_log_error(NGX_LOG_INFO, c->log, 0, "disconnected on shutdown"); + + c->log->handler = handler; + + ngx_stream_proxy_finalize(s, NGX_STREAM_OK); + return; + } + + pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); + + if (from_upstream) { + src = pc; + dst = c; + b = &u->upstream_buf; + limit_rate = u->download_rate; + received = &u->received; + packets = &u->responses; + out = &u->downstream_out; + busy = &u->downstream_busy; + recv_action = "proxying and reading from upstream"; + send_action = "proxying and sending to client"; + + } else { + src = c; + dst = pc; + b = &u->downstream_buf; + limit_rate = u->upload_rate; + received = &s->received; + packets = &u->requests; + out = &u->upstream_out; + busy = &u->upstream_busy; + recv_action = "proxying and reading from client"; + send_action = "proxying and sending to upstream"; + } + + for ( ;; ) { + + if (do_write && dst) { + + if (*out || *busy || dst->buffered) { + c->log->action = send_action; + + rc = ngx_stream_top_filter(s, *out, from_upstream); + + if (rc == NGX_ERROR) { + ngx_stream_proxy_finalize(s, NGX_STREAM_OK); + return; + } + + ngx_chain_update_chains(c->pool, &u->free, busy, out, + (ngx_buf_tag_t) &ngx_stream_proxy_module); + + if (*busy == NULL) { + b->pos = b->start; + b->last = b->start; + } + } + } + + size = b->end - b->last; + + if (size && src->read->ready && !src->read->delayed) { + + if (limit_rate) { + limit = (off_t) limit_rate * (ngx_time() - u->start_sec + 1) + - *received; + + if (limit <= 0) { + src->read->delayed = 1; + delay = (ngx_msec_t) (- limit * 1000 / limit_rate + 1); + ngx_add_timer(src->read, delay); + break; + } + + if (c->type == SOCK_STREAM && (off_t) size > limit) { + size = (size_t) limit; + } + } + + c->log->action = recv_action; + + n = src->recv(src, b->last, size); + + if (n == NGX_AGAIN) { + break; + } + + if (n == NGX_ERROR) { + src->read->eof = 1; + n = 0; + } + + if (n >= 0) { + if (limit_rate) { + delay = (ngx_msec_t) (n * 1000 / limit_rate); + + if (delay > 0) { + src->read->delayed = 1; + ngx_add_timer(src->read, delay); + } + } + + if (from_upstream) { + if (u->state->first_byte_time == (ngx_msec_t) -1) { + u->state->first_byte_time = ngx_current_msec + - u->start_time; + } + } + + for (ll = out; *ll; ll = &(*ll)->next) { /* void */ } + + cl = ngx_chain_get_free_buf(c->pool, &u->free); + if (cl == NULL) { + ngx_stream_proxy_finalize(s, + NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + *ll = cl; + + cl->buf->pos = b->last; + cl->buf->last = b->last + n; + cl->buf->tag = (ngx_buf_tag_t) &ngx_stream_proxy_module; + + cl->buf->temporary = (n ? 1 : 0); + cl->buf->last_buf = src->read->eof; + cl->buf->flush = !src->read->eof; + + (*packets)++; + *received += n; + b->last += n; + do_write = 1; + + continue; + } + } + + break; + } + + c->log->action = "proxying connection"; + + if (ngx_stream_proxy_test_finalize(s, from_upstream) == NGX_OK) { + return; + } + + flags = src->read->eof ? NGX_CLOSE_EVENT : 0; + + if (ngx_handle_read_event(src->read, flags) != NGX_OK) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + if (dst) { + + if (dst->type == SOCK_STREAM && pscf->half_close + && src->read->eof && !u->half_closed && !dst->buffered) + { + if (ngx_shutdown_socket(dst->fd, NGX_WRITE_SHUTDOWN) == -1) { + ngx_connection_error(c, ngx_socket_errno, + ngx_shutdown_socket_n " failed"); + + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + u->half_closed = 1; + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream proxy %s socket shutdown", + from_upstream ? "client" : "upstream"); + } + + if (ngx_handle_write_event(dst->write, 0) != NGX_OK) { + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + if (!c->read->delayed && !pc->read->delayed) { + ngx_add_timer(c->write, pscf->timeout); + + } else if (c->write->timer_set) { + ngx_del_timer(c->write); + } + } +} + + +static ngx_int_t +ngx_stream_proxy_test_finalize(ngx_stream_session_t *s, + ngx_uint_t from_upstream) +{ + ngx_connection_t *c, *pc; + ngx_log_handler_pt handler; + ngx_stream_upstream_t *u; + ngx_stream_proxy_srv_conf_t *pscf; + + pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); + + c = s->connection; + u = s->upstream; + pc = u->connected ? u->peer.connection : NULL; + + if (c->type == SOCK_DGRAM) { + + if (pscf->requests && u->requests < pscf->requests) { + return NGX_DECLINED; + } + + if (pscf->requests) { + ngx_delete_udp_connection(c); + } + + if (pscf->responses == NGX_MAX_INT32_VALUE + || u->responses < pscf->responses * u->requests) + { + return NGX_DECLINED; + } + + if (pc == NULL || c->buffered || pc->buffered) { + return NGX_DECLINED; + } + + handler = c->log->handler; + c->log->handler = NULL; + + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "udp done" + ", packets from/to client:%ui/%ui" + ", bytes from/to client:%O/%O" + ", bytes from/to upstream:%O/%O", + u->requests, u->responses, + s->received, c->sent, u->received, pc ? pc->sent : 0); + + c->log->handler = handler; + + ngx_stream_proxy_finalize(s, NGX_STREAM_OK); + + return NGX_OK; + } + + /* c->type == SOCK_STREAM */ + + if (pc == NULL + || (!c->read->eof && !pc->read->eof) + || (!c->read->eof && c->buffered) + || (!pc->read->eof && pc->buffered)) + { + return NGX_DECLINED; + } + + if (pscf->half_close) { + /* avoid closing live connections until both read ends get EOF */ + if (!(c->read->eof && pc->read->eof && !c->buffered && !pc->buffered)) { + return NGX_DECLINED; + } + } + + handler = c->log->handler; + c->log->handler = NULL; + + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "%s disconnected" + ", bytes from/to client:%O/%O" + ", bytes from/to upstream:%O/%O", + from_upstream ? "upstream" : "client", + s->received, c->sent, u->received, pc ? pc->sent : 0); + + c->log->handler = handler; + + ngx_stream_proxy_finalize(s, NGX_STREAM_OK); + + return NGX_OK; +} + + +static void +ngx_stream_proxy_next_upstream(ngx_stream_session_t *s) +{ + ngx_msec_t timeout; + ngx_connection_t *pc; + ngx_stream_upstream_t *u; + ngx_stream_proxy_srv_conf_t *pscf; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream proxy next upstream"); + + u = s->upstream; + pc = u->peer.connection; + + if (pc && pc->buffered) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "buffered data on next upstream"); + ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + if (s->connection->type == SOCK_DGRAM) { + u->upstream_out = NULL; + } + + if (u->peer.sockaddr) { + u->peer.free(&u->peer, u->peer.data, NGX_PEER_FAILED); + u->peer.sockaddr = NULL; + } + + pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module); + + timeout = pscf->next_upstream_timeout; + + if (u->peer.tries == 0 + || !pscf->next_upstream + || (timeout && ngx_current_msec - u->peer.start_time >= timeout)) + { + ngx_stream_proxy_finalize(s, NGX_STREAM_BAD_GATEWAY); + return; + } + + if (pc) { + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "close proxy upstream connection: %d", pc->fd); + +#if (NGX_STREAM_SSL) + if (pc->ssl) { + pc->ssl->no_wait_shutdown = 1; + pc->ssl->no_send_shutdown = 1; + + (void) ngx_ssl_shutdown(pc); + } +#endif + + u->state->bytes_received = u->received; + u->state->bytes_sent = pc->sent; + + ngx_close_connection(pc); + u->peer.connection = NULL; + } + + ngx_stream_proxy_connect(s); +} + + +static void +ngx_stream_proxy_finalize(ngx_stream_session_t *s, ngx_uint_t rc) +{ + ngx_uint_t state; + ngx_connection_t *pc; + ngx_stream_upstream_t *u; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "finalize stream proxy: %i", rc); + + u = s->upstream; + + if (u == NULL) { + goto noupstream; + } + + if (u->resolved && u->resolved->ctx) { + ngx_resolve_name_done(u->resolved->ctx); + u->resolved->ctx = NULL; + } + + pc = u->peer.connection; + + if (u->state) { + if (u->state->response_time == (ngx_msec_t) -1) { + u->state->response_time = ngx_current_msec - u->start_time; + } + + if (pc) { + u->state->bytes_received = u->received; + u->state->bytes_sent = pc->sent; + } + } + + if (u->peer.free && u->peer.sockaddr) { + state = 0; + + if (pc && pc->type == SOCK_DGRAM + && (pc->read->error || pc->write->error)) + { + state = NGX_PEER_FAILED; + } + + u->peer.free(&u->peer, u->peer.data, state); + u->peer.sockaddr = NULL; + } + + if (pc) { + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "close stream proxy upstream connection: %d", pc->fd); + +#if (NGX_STREAM_SSL) + if (pc->ssl) { + pc->ssl->no_wait_shutdown = 1; + (void) ngx_ssl_shutdown(pc); + } +#endif + + ngx_close_connection(pc); + u->peer.connection = NULL; + } + +noupstream: + + ngx_stream_finalize_session(s, rc); +} + + +static u_char * +ngx_stream_proxy_log_error(ngx_log_t *log, u_char *buf, size_t len) +{ + u_char *p; + ngx_connection_t *pc; + ngx_stream_session_t *s; + ngx_stream_upstream_t *u; + + s = log->data; + + u = s->upstream; + + p = buf; + + if (u->peer.name) { + p = ngx_snprintf(p, len, ", upstream: \"%V\"", u->peer.name); + len -= p - buf; + } + + pc = u->peer.connection; + + p = ngx_snprintf(p, len, + ", bytes from/to client:%O/%O" + ", bytes from/to upstream:%O/%O", + s->received, s->connection->sent, + u->received, pc ? pc->sent : 0); + + return p; +} + + +static void * +ngx_stream_proxy_create_srv_conf(ngx_conf_t *cf) +{ + ngx_stream_proxy_srv_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_proxy_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * conf->ssl_protocols = 0; + * conf->ssl_ciphers = { 0, NULL }; + * conf->ssl_trusted_certificate = { 0, NULL }; + * conf->ssl_crl = { 0, NULL }; + * + * conf->ssl = NULL; + * conf->upstream = NULL; + * conf->upstream_value = NULL; + */ + + conf->connect_timeout = NGX_CONF_UNSET_MSEC; + conf->timeout = NGX_CONF_UNSET_MSEC; + conf->next_upstream_timeout = NGX_CONF_UNSET_MSEC; + conf->buffer_size = NGX_CONF_UNSET_SIZE; + conf->upload_rate = NGX_CONF_UNSET_PTR; + conf->download_rate = NGX_CONF_UNSET_PTR; + conf->requests = NGX_CONF_UNSET_UINT; + conf->responses = NGX_CONF_UNSET_UINT; + conf->next_upstream_tries = NGX_CONF_UNSET_UINT; + conf->next_upstream = NGX_CONF_UNSET; + conf->proxy_protocol = NGX_CONF_UNSET; + conf->local = NGX_CONF_UNSET_PTR; + conf->socket_keepalive = NGX_CONF_UNSET; + conf->half_close = NGX_CONF_UNSET; + +#if (NGX_STREAM_SSL) + conf->ssl_enable = NGX_CONF_UNSET; + conf->ssl_session_reuse = NGX_CONF_UNSET; + conf->ssl_name = NGX_CONF_UNSET_PTR; + conf->ssl_server_name = NGX_CONF_UNSET; + conf->ssl_verify = NGX_CONF_UNSET; + conf->ssl_verify_depth = NGX_CONF_UNSET_UINT; + conf->ssl_certificate = NGX_CONF_UNSET_PTR; + conf->ssl_certificate_key = NGX_CONF_UNSET_PTR; + conf->ssl_certificate_cache = NGX_CONF_UNSET_PTR; + conf->ssl_passwords = NGX_CONF_UNSET_PTR; + conf->ssl_conf_commands = NGX_CONF_UNSET_PTR; +#endif + + return conf; +} + + +static char * +ngx_stream_proxy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_stream_proxy_srv_conf_t *prev = parent; + ngx_stream_proxy_srv_conf_t *conf = child; + + ngx_conf_merge_msec_value(conf->connect_timeout, + prev->connect_timeout, 60000); + + ngx_conf_merge_msec_value(conf->timeout, + prev->timeout, 10 * 60000); + + ngx_conf_merge_msec_value(conf->next_upstream_timeout, + prev->next_upstream_timeout, 0); + + ngx_conf_merge_size_value(conf->buffer_size, + prev->buffer_size, 16384); + + ngx_conf_merge_ptr_value(conf->upload_rate, prev->upload_rate, NULL); + + ngx_conf_merge_ptr_value(conf->download_rate, prev->download_rate, NULL); + + ngx_conf_merge_uint_value(conf->requests, + prev->requests, 0); + + ngx_conf_merge_uint_value(conf->responses, + prev->responses, NGX_MAX_INT32_VALUE); + + ngx_conf_merge_uint_value(conf->next_upstream_tries, + prev->next_upstream_tries, 0); + + ngx_conf_merge_value(conf->next_upstream, prev->next_upstream, 1); + + ngx_conf_merge_value(conf->proxy_protocol, prev->proxy_protocol, 0); + + ngx_conf_merge_ptr_value(conf->local, prev->local, NULL); + + ngx_conf_merge_value(conf->socket_keepalive, + prev->socket_keepalive, 0); + + ngx_conf_merge_value(conf->half_close, prev->half_close, 0); + +#if (NGX_STREAM_SSL) + + if (ngx_stream_proxy_merge_ssl(cf, conf, prev) != NGX_OK) { + return NGX_CONF_ERROR; + } + + ngx_conf_merge_value(conf->ssl_enable, prev->ssl_enable, 0); + + ngx_conf_merge_value(conf->ssl_session_reuse, + prev->ssl_session_reuse, 1); + + ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, + (NGX_CONF_BITMASK_SET|NGX_SSL_DEFAULT_PROTOCOLS)); + + ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); + + ngx_conf_merge_ptr_value(conf->ssl_name, prev->ssl_name, NULL); + + ngx_conf_merge_value(conf->ssl_server_name, prev->ssl_server_name, 0); + + ngx_conf_merge_value(conf->ssl_verify, prev->ssl_verify, 0); + + ngx_conf_merge_uint_value(conf->ssl_verify_depth, + prev->ssl_verify_depth, 1); + + ngx_conf_merge_str_value(conf->ssl_trusted_certificate, + prev->ssl_trusted_certificate, ""); + + ngx_conf_merge_str_value(conf->ssl_crl, prev->ssl_crl, ""); + + ngx_conf_merge_ptr_value(conf->ssl_certificate, + prev->ssl_certificate, NULL); + + ngx_conf_merge_ptr_value(conf->ssl_certificate_key, + prev->ssl_certificate_key, NULL); + + ngx_conf_merge_ptr_value(conf->ssl_certificate_cache, + prev->ssl_certificate_cache, NULL); + + if (ngx_stream_proxy_merge_ssl_passwords(cf, conf, prev) != NGX_OK) { + return NGX_CONF_ERROR; + } + + ngx_conf_merge_ptr_value(conf->ssl_conf_commands, + prev->ssl_conf_commands, NULL); + + if (conf->ssl_enable && ngx_stream_proxy_set_ssl(cf, conf) != NGX_OK) { + return NGX_CONF_ERROR; + } + +#endif + + return NGX_CONF_OK; +} + + +#if (NGX_STREAM_SSL) + +static ngx_int_t +ngx_stream_proxy_merge_ssl(ngx_conf_t *cf, ngx_stream_proxy_srv_conf_t *conf, + ngx_stream_proxy_srv_conf_t *prev) +{ + ngx_uint_t preserve; + + if (conf->ssl_protocols == 0 + && conf->ssl_ciphers.data == NULL + && conf->ssl_certificate == NGX_CONF_UNSET_PTR + && conf->ssl_certificate_key == NGX_CONF_UNSET_PTR + && conf->ssl_passwords == NGX_CONF_UNSET_PTR + && conf->ssl_verify == NGX_CONF_UNSET + && conf->ssl_verify_depth == NGX_CONF_UNSET_UINT + && conf->ssl_trusted_certificate.data == NULL + && conf->ssl_crl.data == NULL + && conf->ssl_session_reuse == NGX_CONF_UNSET + && conf->ssl_conf_commands == NGX_CONF_UNSET_PTR) + { + if (prev->ssl) { + conf->ssl = prev->ssl; + return NGX_OK; + } + + preserve = 1; + + } else { + preserve = 0; + } + + conf->ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t)); + if (conf->ssl == NULL) { + return NGX_ERROR; + } + + conf->ssl->log = cf->log; + + /* + * special handling to preserve conf->ssl + * in the "stream" section to inherit it to all servers + */ + + if (preserve) { + prev->ssl = conf->ssl; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_proxy_merge_ssl_passwords(ngx_conf_t *cf, + ngx_stream_proxy_srv_conf_t *conf, ngx_stream_proxy_srv_conf_t *prev) +{ + ngx_uint_t preserve; + + ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL); + + if (conf->ssl_certificate == NULL + || conf->ssl_certificate->value.len == 0 + || conf->ssl_certificate_key == NULL) + { + return NGX_OK; + } + + if (conf->ssl_certificate->lengths == NULL + && conf->ssl_certificate_key->lengths == NULL) + { + if (conf->ssl_passwords && conf->ssl_passwords->pool == NULL) { + /* un-preserve empty password list */ + conf->ssl_passwords = NULL; + } + + return NGX_OK; + } + + if (conf->ssl_passwords && conf->ssl_passwords->pool != cf->temp_pool) { + /* already preserved */ + return NGX_OK; + } + + preserve = (conf->ssl_passwords == prev->ssl_passwords) ? 1 : 0; + + conf->ssl_passwords = ngx_ssl_preserve_passwords(cf, conf->ssl_passwords); + if (conf->ssl_passwords == NULL) { + return NGX_ERROR; + } + + /* + * special handling to keep a preserved ssl_passwords copy + * in the previous configuration to inherit it to all children + */ + + if (preserve) { + prev->ssl_passwords = conf->ssl_passwords; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_proxy_set_ssl(ngx_conf_t *cf, ngx_stream_proxy_srv_conf_t *pscf) +{ + ngx_pool_cleanup_t *cln; + + if (pscf->ssl->ctx) { + return NGX_OK; + } + + if (ngx_ssl_create(pscf->ssl, pscf->ssl_protocols, NULL) != NGX_OK) { + return NGX_ERROR; + } + + cln = ngx_pool_cleanup_add(cf->pool, 0); + if (cln == NULL) { + ngx_ssl_cleanup_ctx(pscf->ssl); + return NGX_ERROR; + } + + cln->handler = ngx_ssl_cleanup_ctx; + cln->data = pscf->ssl; + + if (ngx_ssl_ciphers(cf, pscf->ssl, &pscf->ssl_ciphers, 0) != NGX_OK) { + return NGX_ERROR; + } + + if (pscf->ssl_certificate + && pscf->ssl_certificate->value.len) + { + if (pscf->ssl_certificate_key == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"proxy_ssl_certificate_key\" is defined " + "for certificate \"%V\"", + &pscf->ssl_certificate->value); + return NGX_ERROR; + } + + if (pscf->ssl_certificate->lengths == NULL + && pscf->ssl_certificate_key->lengths == NULL) + { + if (ngx_ssl_certificate(cf, pscf->ssl, + &pscf->ssl_certificate->value, + &pscf->ssl_certificate_key->value, + pscf->ssl_passwords) + != NGX_OK) + { + return NGX_ERROR; + } + } + } + + if (pscf->ssl_verify) { + if (pscf->ssl_trusted_certificate.len == 0) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no proxy_ssl_trusted_certificate for proxy_ssl_verify"); + return NGX_ERROR; + } + + if (ngx_ssl_trusted_certificate(cf, pscf->ssl, + &pscf->ssl_trusted_certificate, + pscf->ssl_verify_depth) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_ssl_crl(cf, pscf->ssl, &pscf->ssl_crl) != NGX_OK) { + return NGX_ERROR; + } + } + + if (ngx_ssl_client_session_cache(cf, pscf->ssl, pscf->ssl_session_reuse) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_ssl_conf_commands(cf, pscf->ssl, pscf->ssl_conf_commands) + != NGX_OK) + { + return NGX_ERROR; + } + + return NGX_OK; +} + +#endif + + +static char * +ngx_stream_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_proxy_srv_conf_t *pscf = conf; + + ngx_url_t u; + ngx_str_t *value, *url; + ngx_stream_complex_value_t cv; + ngx_stream_core_srv_conf_t *cscf; + ngx_stream_compile_complex_value_t ccv; + + if (pscf->upstream || pscf->upstream_value) { + return "is duplicate"; + } + + cscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_core_module); + + cscf->handler = ngx_stream_proxy_handler; + + value = cf->args->elts; + + url = &value[1]; + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = url; + ccv.complex_value = &cv; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (cv.lengths) { + pscf->upstream_value = ngx_palloc(cf->pool, + sizeof(ngx_stream_complex_value_t)); + if (pscf->upstream_value == NULL) { + return NGX_CONF_ERROR; + } + + *pscf->upstream_value = cv; + + return NGX_CONF_OK; + } + + ngx_memzero(&u, sizeof(ngx_url_t)); + + u.url = *url; + u.no_resolve = 1; + + pscf->upstream = ngx_stream_upstream_add(cf, &u, 0); + if (pscf->upstream == NULL) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_stream_proxy_bind(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_proxy_srv_conf_t *pscf = conf; + + ngx_int_t rc; + ngx_str_t *value; + ngx_stream_complex_value_t cv; + ngx_stream_upstream_local_t *local; + ngx_stream_compile_complex_value_t ccv; + + if (pscf->local != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + value = cf->args->elts; + + if (cf->args->nelts == 2 && ngx_strcmp(value[1].data, "off") == 0) { + pscf->local = NULL; + return NGX_CONF_OK; + } + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &cv; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + local = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_local_t)); + if (local == NULL) { + return NGX_CONF_ERROR; + } + + pscf->local = local; + + if (cv.lengths) { + local->value = ngx_palloc(cf->pool, sizeof(ngx_stream_complex_value_t)); + if (local->value == NULL) { + return NGX_CONF_ERROR; + } + + *local->value = cv; + + } else { + local->addr = ngx_palloc(cf->pool, sizeof(ngx_addr_t)); + if (local->addr == NULL) { + return NGX_CONF_ERROR; + } + + rc = ngx_parse_addr_port(cf->pool, local->addr, value[1].data, + value[1].len); + + switch (rc) { + case NGX_OK: + local->addr->name = value[1]; + break; + + case NGX_DECLINED: + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid address \"%V\"", &value[1]); + /* fall through */ + + default: + return NGX_CONF_ERROR; + } + } + + if (cf->args->nelts > 2) { + if (ngx_strcmp(value[2].data, "transparent") == 0) { +#if (NGX_HAVE_TRANSPARENT_PROXY) + ngx_core_conf_t *ccf; + + ccf = (ngx_core_conf_t *) ngx_get_conf(cf->cycle->conf_ctx, + ngx_core_module); + + ccf->transparent = 1; + local->transparent = 1; +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "transparent proxying is not supported " + "on this platform, ignored"); +#endif + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + } + + return NGX_CONF_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_realip_module.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_realip_module.c new file mode 100644 index 000000000..603f5973b --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_realip_module.c @@ -0,0 +1,401 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_array_t *from; /* array of ngx_cidr_t */ +} ngx_stream_realip_srv_conf_t; + + +typedef struct { + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_str_t addr_text; +} ngx_stream_realip_ctx_t; + + +static ngx_int_t ngx_stream_realip_handler(ngx_stream_session_t *s); +static ngx_int_t ngx_stream_realip_set_addr(ngx_stream_session_t *s, + ngx_addr_t *addr); +static char *ngx_stream_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static void *ngx_stream_realip_create_srv_conf(ngx_conf_t *cf); +static char *ngx_stream_realip_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); +static ngx_int_t ngx_stream_realip_add_variables(ngx_conf_t *cf); +static ngx_int_t ngx_stream_realip_init(ngx_conf_t *cf); + + +static ngx_int_t ngx_stream_realip_remote_addr_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_realip_remote_port_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); + + +static ngx_command_t ngx_stream_realip_commands[] = { + + { ngx_string("set_real_ip_from"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_realip_from, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_realip_module_ctx = { + ngx_stream_realip_add_variables, /* preconfiguration */ + ngx_stream_realip_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_stream_realip_create_srv_conf, /* create server configuration */ + ngx_stream_realip_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_realip_module = { + NGX_MODULE_V1, + &ngx_stream_realip_module_ctx, /* module context */ + ngx_stream_realip_commands, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_stream_variable_t ngx_stream_realip_vars[] = { + + { ngx_string("realip_remote_addr"), NULL, + ngx_stream_realip_remote_addr_variable, 0, 0, 0 }, + + { ngx_string("realip_remote_port"), NULL, + ngx_stream_realip_remote_port_variable, 0, 0, 0 }, + + ngx_stream_null_variable +}; + + +static ngx_int_t +ngx_stream_realip_handler(ngx_stream_session_t *s) +{ + ngx_addr_t addr; + ngx_connection_t *c; + ngx_stream_realip_srv_conf_t *rscf; + + rscf = ngx_stream_get_module_srv_conf(s, ngx_stream_realip_module); + + if (rscf->from == NULL) { + return NGX_DECLINED; + } + + c = s->connection; + + if (c->proxy_protocol == NULL) { + return NGX_DECLINED; + } + + if (ngx_cidr_match(c->sockaddr, rscf->from) != NGX_OK) { + return NGX_DECLINED; + } + + if (ngx_parse_addr(c->pool, &addr, c->proxy_protocol->src_addr.data, + c->proxy_protocol->src_addr.len) + != NGX_OK) + { + return NGX_DECLINED; + } + + ngx_inet_set_port(addr.sockaddr, c->proxy_protocol->src_port); + + return ngx_stream_realip_set_addr(s, &addr); +} + + +static ngx_int_t +ngx_stream_realip_set_addr(ngx_stream_session_t *s, ngx_addr_t *addr) +{ + size_t len; + u_char *p; + u_char text[NGX_SOCKADDR_STRLEN]; + ngx_connection_t *c; + ngx_stream_realip_ctx_t *ctx; + + c = s->connection; + + ctx = ngx_palloc(c->pool, sizeof(ngx_stream_realip_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + len = ngx_sock_ntop(addr->sockaddr, addr->socklen, text, + NGX_SOCKADDR_STRLEN, 0); + if (len == 0) { + return NGX_ERROR; + } + + p = ngx_pnalloc(c->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, text, len); + + ngx_stream_set_ctx(s, ctx, ngx_stream_realip_module); + + ctx->sockaddr = c->sockaddr; + ctx->socklen = c->socklen; + ctx->addr_text = c->addr_text; + + c->sockaddr = addr->sockaddr; + c->socklen = addr->socklen; + c->addr_text.len = len; + c->addr_text.data = p; + + return NGX_DECLINED; +} + + +static char * +ngx_stream_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_realip_srv_conf_t *rscf = conf; + + ngx_int_t rc; + ngx_str_t *value; + ngx_url_t u; + ngx_cidr_t c, *cidr; + ngx_uint_t i; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + value = cf->args->elts; + + if (rscf->from == NULL) { + rscf->from = ngx_array_create(cf->pool, 2, + sizeof(ngx_cidr_t)); + if (rscf->from == NULL) { + return NGX_CONF_ERROR; + } + } + +#if (NGX_HAVE_UNIX_DOMAIN) + + if (ngx_strcmp(value[1].data, "unix:") == 0) { + cidr = ngx_array_push(rscf->from); + if (cidr == NULL) { + return NGX_CONF_ERROR; + } + + cidr->family = AF_UNIX; + return NGX_CONF_OK; + } + +#endif + + rc = ngx_ptocidr(&value[1], &c); + + if (rc != NGX_ERROR) { + if (rc == NGX_DONE) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "low address bits of %V are meaningless", + &value[1]); + } + + cidr = ngx_array_push(rscf->from); + if (cidr == NULL) { + return NGX_CONF_ERROR; + } + + *cidr = c; + + return NGX_CONF_OK; + } + + ngx_memzero(&u, sizeof(ngx_url_t)); + u.host = value[1]; + + if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) { + if (u.err) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "%s in set_real_ip_from \"%V\"", + u.err, &u.host); + } + + return NGX_CONF_ERROR; + } + + cidr = ngx_array_push_n(rscf->from, u.naddrs); + if (cidr == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(cidr, u.naddrs * sizeof(ngx_cidr_t)); + + for (i = 0; i < u.naddrs; i++) { + cidr[i].family = u.addrs[i].sockaddr->sa_family; + + switch (cidr[i].family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) u.addrs[i].sockaddr; + cidr[i].u.in6.addr = sin6->sin6_addr; + ngx_memset(cidr[i].u.in6.mask.s6_addr, 0xff, 16); + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) u.addrs[i].sockaddr; + cidr[i].u.in.addr = sin->sin_addr.s_addr; + cidr[i].u.in.mask = 0xffffffff; + break; + } + } + + return NGX_CONF_OK; +} + + +static void * +ngx_stream_realip_create_srv_conf(ngx_conf_t *cf) +{ + ngx_stream_realip_srv_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_realip_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * conf->from = NULL; + */ + + return conf; +} + + +static char * +ngx_stream_realip_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_stream_realip_srv_conf_t *prev = parent; + ngx_stream_realip_srv_conf_t *conf = child; + + if (conf->from == NULL) { + conf->from = prev->from; + } + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_stream_realip_add_variables(ngx_conf_t *cf) +{ + ngx_stream_variable_t *var, *v; + + for (v = ngx_stream_realip_vars; v->name.len; v++) { + var = ngx_stream_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_realip_init(ngx_conf_t *cf) +{ + ngx_stream_handler_pt *h; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + h = ngx_array_push(&cmcf->phases[NGX_STREAM_POST_ACCEPT_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_stream_realip_handler; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_realip_remote_addr_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_str_t *addr_text; + ngx_stream_realip_ctx_t *ctx; + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_realip_module); + + addr_text = ctx ? &ctx->addr_text : &s->connection->addr_text; + + v->len = addr_text->len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = addr_text->data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_realip_remote_port_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_uint_t port; + struct sockaddr *sa; + ngx_stream_realip_ctx_t *ctx; + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_realip_module); + + sa = ctx ? ctx->sockaddr : s->connection->sockaddr; + + v->len = 0; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + v->data = ngx_pnalloc(s->connection->pool, sizeof("65535") - 1); + if (v->data == NULL) { + return NGX_ERROR; + } + + port = ngx_inet_get_port(sa); + + if (port > 0 && port < 65536) { + v->len = ngx_sprintf(v->data, "%ui", port) - v->data; + } + + return NGX_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_return_module.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_return_module.c new file mode 100644 index 000000000..9301b025a --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_return_module.c @@ -0,0 +1,218 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_stream_complex_value_t text; +} ngx_stream_return_srv_conf_t; + + +typedef struct { + ngx_chain_t *out; +} ngx_stream_return_ctx_t; + + +static void ngx_stream_return_handler(ngx_stream_session_t *s); +static void ngx_stream_return_write_handler(ngx_event_t *ev); + +static void *ngx_stream_return_create_srv_conf(ngx_conf_t *cf); +static char *ngx_stream_return(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + + +static ngx_command_t ngx_stream_return_commands[] = { + + { ngx_string("return"), + NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_return, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_return_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_stream_return_create_srv_conf, /* create server configuration */ + NULL /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_return_module = { + NGX_MODULE_V1, + &ngx_stream_return_module_ctx, /* module context */ + ngx_stream_return_commands, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static void +ngx_stream_return_handler(ngx_stream_session_t *s) +{ + ngx_str_t text; + ngx_buf_t *b; + ngx_connection_t *c; + ngx_stream_return_ctx_t *ctx; + ngx_stream_return_srv_conf_t *rscf; + + c = s->connection; + + c->log->action = "returning text"; + + rscf = ngx_stream_get_module_srv_conf(s, ngx_stream_return_module); + + if (ngx_stream_complex_value(s, &rscf->text, &text) != NGX_OK) { + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream return text: \"%V\"", &text); + + if (text.len == 0) { + ngx_stream_finalize_session(s, NGX_STREAM_OK); + return; + } + + ctx = ngx_pcalloc(c->pool, sizeof(ngx_stream_return_ctx_t)); + if (ctx == NULL) { + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + ngx_stream_set_ctx(s, ctx, ngx_stream_return_module); + + b = ngx_calloc_buf(c->pool); + if (b == NULL) { + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + b->memory = 1; + b->pos = text.data; + b->last = text.data + text.len; + b->last_buf = 1; + + ctx->out = ngx_alloc_chain_link(c->pool); + if (ctx->out == NULL) { + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + ctx->out->buf = b; + ctx->out->next = NULL; + + c->write->handler = ngx_stream_return_write_handler; + + ngx_stream_return_write_handler(c->write); +} + + +static void +ngx_stream_return_write_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; + ngx_stream_session_t *s; + ngx_stream_return_ctx_t *ctx; + + c = ev->data; + s = c->data; + + if (ev->timedout) { + ngx_connection_error(c, NGX_ETIMEDOUT, "connection timed out"); + ngx_stream_finalize_session(s, NGX_STREAM_OK); + return; + } + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_return_module); + + if (ngx_stream_top_filter(s, ctx->out, 1) == NGX_ERROR) { + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + ctx->out = NULL; + + if (!c->buffered) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream return done sending"); + ngx_stream_finalize_session(s, NGX_STREAM_OK); + return; + } + + if (ngx_handle_write_event(ev, 0) != NGX_OK) { + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + ngx_add_timer(ev, 5000); +} + + +static void * +ngx_stream_return_create_srv_conf(ngx_conf_t *cf) +{ + ngx_stream_return_srv_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_return_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + return conf; +} + + +static char * +ngx_stream_return(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_return_srv_conf_t *rscf = conf; + + ngx_str_t *value; + ngx_stream_core_srv_conf_t *cscf; + ngx_stream_compile_complex_value_t ccv; + + if (rscf->text.value.data) { + return "is duplicate"; + } + + value = cf->args->elts; + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &rscf->text; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + cscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_core_module); + + cscf->handler = ngx_stream_return_handler; + + return NGX_CONF_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_script.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_script.c new file mode 100644 index 000000000..c447e152f --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_script.c @@ -0,0 +1,1021 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +static ngx_int_t ngx_stream_script_init_arrays( + ngx_stream_script_compile_t *sc); +static ngx_int_t ngx_stream_script_done(ngx_stream_script_compile_t *sc); +static ngx_int_t ngx_stream_script_add_copy_code( + ngx_stream_script_compile_t *sc, ngx_str_t *value, ngx_uint_t last); +static ngx_int_t ngx_stream_script_add_var_code( + ngx_stream_script_compile_t *sc, ngx_str_t *name); +#if (NGX_PCRE) +static ngx_int_t ngx_stream_script_add_capture_code( + ngx_stream_script_compile_t *sc, ngx_uint_t n); +#endif +static ngx_int_t ngx_stream_script_add_full_name_code( + ngx_stream_script_compile_t *sc); +static size_t ngx_stream_script_full_name_len_code( + ngx_stream_script_engine_t *e); +static void ngx_stream_script_full_name_code(ngx_stream_script_engine_t *e); + + +#define ngx_stream_script_exit (u_char *) &ngx_stream_script_exit_code + +static uintptr_t ngx_stream_script_exit_code = (uintptr_t) NULL; + + +void +ngx_stream_script_flush_complex_value(ngx_stream_session_t *s, + ngx_stream_complex_value_t *val) +{ + ngx_uint_t *index; + + index = val->flushes; + + if (index) { + while (*index != (ngx_uint_t) -1) { + + if (s->variables[*index].no_cacheable) { + s->variables[*index].valid = 0; + s->variables[*index].not_found = 0; + } + + index++; + } + } +} + + +ngx_int_t +ngx_stream_complex_value(ngx_stream_session_t *s, + ngx_stream_complex_value_t *val, ngx_str_t *value) +{ + size_t len; + ngx_stream_script_code_pt code; + ngx_stream_script_engine_t e; + ngx_stream_script_len_code_pt lcode; + + if (val->lengths == NULL) { + *value = val->value; + return NGX_OK; + } + + ngx_stream_script_flush_complex_value(s, val); + + ngx_memzero(&e, sizeof(ngx_stream_script_engine_t)); + + e.ip = val->lengths; + e.session = s; + e.flushed = 1; + + len = 0; + + while (*(uintptr_t *) e.ip) { + lcode = *(ngx_stream_script_len_code_pt *) e.ip; + len += lcode(&e); + } + + value->len = len; + value->data = ngx_pnalloc(s->connection->pool, len); + if (value->data == NULL) { + return NGX_ERROR; + } + + e.ip = val->values; + e.pos = value->data; + e.buf = *value; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_stream_script_code_pt *) e.ip; + code((ngx_stream_script_engine_t *) &e); + } + + *value = e.buf; + + return NGX_OK; +} + + +size_t +ngx_stream_complex_value_size(ngx_stream_session_t *s, + ngx_stream_complex_value_t *val, size_t default_value) +{ + size_t size; + ngx_str_t value; + + if (val == NULL) { + return default_value; + } + + if (val->lengths == NULL) { + return val->u.size; + } + + if (ngx_stream_complex_value(s, val, &value) != NGX_OK) { + return default_value; + } + + size = ngx_parse_size(&value); + + if (size == (size_t) NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "invalid size \"%V\"", &value); + return default_value; + } + + return size; +} + + +ngx_int_t +ngx_stream_compile_complex_value(ngx_stream_compile_complex_value_t *ccv) +{ + ngx_str_t *v; + ngx_uint_t i, n, nv, nc; + ngx_array_t flushes, lengths, values, *pf, *pl, *pv; + ngx_stream_script_compile_t sc; + + v = ccv->value; + + nv = 0; + nc = 0; + + for (i = 0; i < v->len; i++) { + if (v->data[i] == '$') { + if (v->data[i + 1] >= '1' && v->data[i + 1] <= '9') { + nc++; + + } else { + nv++; + } + } + } + + if ((v->len == 0 || v->data[0] != '$') + && (ccv->conf_prefix || ccv->root_prefix)) + { + if (ngx_conf_full_name(ccv->cf->cycle, v, ccv->conf_prefix) != NGX_OK) { + return NGX_ERROR; + } + + ccv->conf_prefix = 0; + ccv->root_prefix = 0; + } + + ccv->complex_value->value = *v; + ccv->complex_value->flushes = NULL; + ccv->complex_value->lengths = NULL; + ccv->complex_value->values = NULL; + + if (nv == 0 && nc == 0) { + return NGX_OK; + } + + n = nv + 1; + + if (ngx_array_init(&flushes, ccv->cf->pool, n, sizeof(ngx_uint_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + n = nv * (2 * sizeof(ngx_stream_script_copy_code_t) + + sizeof(ngx_stream_script_var_code_t)) + + sizeof(uintptr_t); + + if (ngx_array_init(&lengths, ccv->cf->pool, n, 1) != NGX_OK) { + return NGX_ERROR; + } + + n = (nv * (2 * sizeof(ngx_stream_script_copy_code_t) + + sizeof(ngx_stream_script_var_code_t)) + + sizeof(uintptr_t) + + v->len + + sizeof(uintptr_t) - 1) + & ~(sizeof(uintptr_t) - 1); + + if (ngx_array_init(&values, ccv->cf->pool, n, 1) != NGX_OK) { + return NGX_ERROR; + } + + pf = &flushes; + pl = &lengths; + pv = &values; + + ngx_memzero(&sc, sizeof(ngx_stream_script_compile_t)); + + sc.cf = ccv->cf; + sc.source = v; + sc.flushes = &pf; + sc.lengths = &pl; + sc.values = &pv; + sc.complete_lengths = 1; + sc.complete_values = 1; + sc.zero = ccv->zero; + sc.conf_prefix = ccv->conf_prefix; + sc.root_prefix = ccv->root_prefix; + + if (ngx_stream_script_compile(&sc) != NGX_OK) { + return NGX_ERROR; + } + + if (flushes.nelts) { + ccv->complex_value->flushes = flushes.elts; + ccv->complex_value->flushes[flushes.nelts] = (ngx_uint_t) -1; + } + + ccv->complex_value->lengths = lengths.elts; + ccv->complex_value->values = values.elts; + + return NGX_OK; +} + + +char * +ngx_stream_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *p = conf; + + ngx_str_t *value; + ngx_stream_complex_value_t **cv; + ngx_stream_compile_complex_value_t ccv; + + cv = (ngx_stream_complex_value_t **) (p + cmd->offset); + + if (*cv != NGX_CONF_UNSET_PTR && *cv != NULL) { + return "is duplicate"; + } + + *cv = ngx_palloc(cf->pool, sizeof(ngx_stream_complex_value_t)); + if (*cv == NULL) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = *cv; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +char * +ngx_stream_set_complex_value_zero_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *p = conf; + + ngx_str_t *value; + ngx_stream_complex_value_t **cv; + ngx_stream_compile_complex_value_t ccv; + + cv = (ngx_stream_complex_value_t **) (p + cmd->offset); + + if (*cv != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + *cv = ngx_palloc(cf->pool, sizeof(ngx_stream_complex_value_t)); + if (*cv == NULL) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = *cv; + ccv.zero = 1; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +char * +ngx_stream_set_complex_value_size_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *p = conf; + + char *rv; + ngx_stream_complex_value_t *cv; + + rv = ngx_stream_set_complex_value_slot(cf, cmd, conf); + + if (rv != NGX_CONF_OK) { + return rv; + } + + cv = *(ngx_stream_complex_value_t **) (p + cmd->offset); + + if (cv->lengths) { + return NGX_CONF_OK; + } + + cv->u.size = ngx_parse_size(&cv->value); + if (cv->u.size == (size_t) NGX_ERROR) { + return "invalid value"; + } + + return NGX_CONF_OK; +} + + +ngx_uint_t +ngx_stream_script_variables_count(ngx_str_t *value) +{ + ngx_uint_t i, n; + + for (n = 0, i = 0; i < value->len; i++) { + if (value->data[i] == '$') { + n++; + } + } + + return n; +} + + +ngx_int_t +ngx_stream_script_compile(ngx_stream_script_compile_t *sc) +{ + u_char ch; + ngx_str_t name; + ngx_uint_t i, bracket; + + if (ngx_stream_script_init_arrays(sc) != NGX_OK) { + return NGX_ERROR; + } + + for (i = 0; i < sc->source->len; /* void */ ) { + + name.len = 0; + + if (sc->source->data[i] == '$') { + + if (++i == sc->source->len) { + goto invalid_variable; + } + + if (sc->source->data[i] >= '1' && sc->source->data[i] <= '9') { +#if (NGX_PCRE) + ngx_uint_t n; + + n = sc->source->data[i] - '0'; + + if (ngx_stream_script_add_capture_code(sc, n) != NGX_OK) { + return NGX_ERROR; + } + + i++; + + continue; +#else + ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0, + "using variable \"$%c\" requires " + "PCRE library", sc->source->data[i]); + return NGX_ERROR; +#endif + } + + if (sc->source->data[i] == '{') { + bracket = 1; + + if (++i == sc->source->len) { + goto invalid_variable; + } + + name.data = &sc->source->data[i]; + + } else { + bracket = 0; + name.data = &sc->source->data[i]; + } + + for ( /* void */ ; i < sc->source->len; i++, name.len++) { + ch = sc->source->data[i]; + + if (ch == '}' && bracket) { + i++; + bracket = 0; + break; + } + + if ((ch >= 'A' && ch <= 'Z') + || (ch >= 'a' && ch <= 'z') + || (ch >= '0' && ch <= '9') + || ch == '_') + { + continue; + } + + break; + } + + if (bracket) { + ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0, + "the closing bracket in \"%V\" " + "variable is missing", &name); + return NGX_ERROR; + } + + if (name.len == 0) { + goto invalid_variable; + } + + sc->variables++; + + if (ngx_stream_script_add_var_code(sc, &name) != NGX_OK) { + return NGX_ERROR; + } + + continue; + } + + name.data = &sc->source->data[i]; + + while (i < sc->source->len) { + + if (sc->source->data[i] == '$') { + break; + } + + i++; + name.len++; + } + + sc->size += name.len; + + if (ngx_stream_script_add_copy_code(sc, &name, (i == sc->source->len)) + != NGX_OK) + { + return NGX_ERROR; + } + } + + return ngx_stream_script_done(sc); + +invalid_variable: + + ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0, "invalid variable name"); + + return NGX_ERROR; +} + + +u_char * +ngx_stream_script_run(ngx_stream_session_t *s, ngx_str_t *value, + void *code_lengths, size_t len, void *code_values) +{ + ngx_uint_t i; + ngx_stream_script_code_pt code; + ngx_stream_script_engine_t e; + ngx_stream_core_main_conf_t *cmcf; + ngx_stream_script_len_code_pt lcode; + + cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); + + for (i = 0; i < cmcf->variables.nelts; i++) { + if (s->variables[i].no_cacheable) { + s->variables[i].valid = 0; + s->variables[i].not_found = 0; + } + } + + ngx_memzero(&e, sizeof(ngx_stream_script_engine_t)); + + e.ip = code_lengths; + e.session = s; + e.flushed = 1; + + while (*(uintptr_t *) e.ip) { + lcode = *(ngx_stream_script_len_code_pt *) e.ip; + len += lcode(&e); + } + + + value->len = len; + value->data = ngx_pnalloc(s->connection->pool, len); + if (value->data == NULL) { + return NULL; + } + + e.ip = code_values; + e.pos = value->data; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_stream_script_code_pt *) e.ip; + code((ngx_stream_script_engine_t *) &e); + } + + return e.pos; +} + + +void +ngx_stream_script_flush_no_cacheable_variables(ngx_stream_session_t *s, + ngx_array_t *indices) +{ + ngx_uint_t n, *index; + + if (indices) { + index = indices->elts; + for (n = 0; n < indices->nelts; n++) { + if (s->variables[index[n]].no_cacheable) { + s->variables[index[n]].valid = 0; + s->variables[index[n]].not_found = 0; + } + } + } +} + + +static ngx_int_t +ngx_stream_script_init_arrays(ngx_stream_script_compile_t *sc) +{ + ngx_uint_t n; + + if (sc->flushes && *sc->flushes == NULL) { + n = sc->variables ? sc->variables : 1; + *sc->flushes = ngx_array_create(sc->cf->pool, n, sizeof(ngx_uint_t)); + if (*sc->flushes == NULL) { + return NGX_ERROR; + } + } + + if (*sc->lengths == NULL) { + n = sc->variables * (2 * sizeof(ngx_stream_script_copy_code_t) + + sizeof(ngx_stream_script_var_code_t)) + + sizeof(uintptr_t); + + *sc->lengths = ngx_array_create(sc->cf->pool, n, 1); + if (*sc->lengths == NULL) { + return NGX_ERROR; + } + } + + if (*sc->values == NULL) { + n = (sc->variables * (2 * sizeof(ngx_stream_script_copy_code_t) + + sizeof(ngx_stream_script_var_code_t)) + + sizeof(uintptr_t) + + sc->source->len + + sizeof(uintptr_t) - 1) + & ~(sizeof(uintptr_t) - 1); + + *sc->values = ngx_array_create(sc->cf->pool, n, 1); + if (*sc->values == NULL) { + return NGX_ERROR; + } + } + + sc->variables = 0; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_script_done(ngx_stream_script_compile_t *sc) +{ + ngx_str_t zero; + uintptr_t *code; + + if (sc->zero) { + + zero.len = 1; + zero.data = (u_char *) "\0"; + + if (ngx_stream_script_add_copy_code(sc, &zero, 0) != NGX_OK) { + return NGX_ERROR; + } + } + + if (sc->conf_prefix || sc->root_prefix) { + if (ngx_stream_script_add_full_name_code(sc) != NGX_OK) { + return NGX_ERROR; + } + } + + if (sc->complete_lengths) { + code = ngx_stream_script_add_code(*sc->lengths, sizeof(uintptr_t), + NULL); + if (code == NULL) { + return NGX_ERROR; + } + + *code = (uintptr_t) NULL; + } + + if (sc->complete_values) { + code = ngx_stream_script_add_code(*sc->values, sizeof(uintptr_t), + &sc->main); + if (code == NULL) { + return NGX_ERROR; + } + + *code = (uintptr_t) NULL; + } + + return NGX_OK; +} + + +void * +ngx_stream_script_add_code(ngx_array_t *codes, size_t size, void *code) +{ + u_char *elts, **p; + void *new; + + elts = codes->elts; + + new = ngx_array_push_n(codes, size); + if (new == NULL) { + return NULL; + } + + if (code) { + if (elts != codes->elts) { + p = code; + *p += (u_char *) codes->elts - elts; + } + } + + return new; +} + + +static ngx_int_t +ngx_stream_script_add_copy_code(ngx_stream_script_compile_t *sc, + ngx_str_t *value, ngx_uint_t last) +{ + u_char *p; + size_t size, len, zero; + ngx_stream_script_copy_code_t *code; + + zero = (sc->zero && last); + len = value->len + zero; + + code = ngx_stream_script_add_code(*sc->lengths, + sizeof(ngx_stream_script_copy_code_t), + NULL); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = (ngx_stream_script_code_pt) (void *) + ngx_stream_script_copy_len_code; + code->len = len; + + size = (sizeof(ngx_stream_script_copy_code_t) + len + sizeof(uintptr_t) - 1) + & ~(sizeof(uintptr_t) - 1); + + code = ngx_stream_script_add_code(*sc->values, size, &sc->main); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = ngx_stream_script_copy_code; + code->len = len; + + p = ngx_cpymem((u_char *) code + sizeof(ngx_stream_script_copy_code_t), + value->data, value->len); + + if (zero) { + *p = '\0'; + sc->zero = 0; + } + + return NGX_OK; +} + + +size_t +ngx_stream_script_copy_len_code(ngx_stream_script_engine_t *e) +{ + ngx_stream_script_copy_code_t *code; + + code = (ngx_stream_script_copy_code_t *) e->ip; + + e->ip += sizeof(ngx_stream_script_copy_code_t); + + return code->len; +} + + +void +ngx_stream_script_copy_code(ngx_stream_script_engine_t *e) +{ + u_char *p; + ngx_stream_script_copy_code_t *code; + + code = (ngx_stream_script_copy_code_t *) e->ip; + + p = e->pos; + + if (!e->skip) { + e->pos = ngx_copy(p, e->ip + sizeof(ngx_stream_script_copy_code_t), + code->len); + } + + e->ip += sizeof(ngx_stream_script_copy_code_t) + + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1)); + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, e->session->connection->log, 0, + "stream script copy: \"%*s\"", e->pos - p, p); +} + + +static ngx_int_t +ngx_stream_script_add_var_code(ngx_stream_script_compile_t *sc, ngx_str_t *name) +{ + ngx_int_t index, *p; + ngx_stream_script_var_code_t *code; + + index = ngx_stream_get_variable_index(sc->cf, name); + + if (index == NGX_ERROR) { + return NGX_ERROR; + } + + if (sc->flushes) { + p = ngx_array_push(*sc->flushes); + if (p == NULL) { + return NGX_ERROR; + } + + *p = index; + } + + code = ngx_stream_script_add_code(*sc->lengths, + sizeof(ngx_stream_script_var_code_t), + NULL); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = (ngx_stream_script_code_pt) (void *) + ngx_stream_script_copy_var_len_code; + code->index = (uintptr_t) index; + + code = ngx_stream_script_add_code(*sc->values, + sizeof(ngx_stream_script_var_code_t), + &sc->main); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = ngx_stream_script_copy_var_code; + code->index = (uintptr_t) index; + + return NGX_OK; +} + + +size_t +ngx_stream_script_copy_var_len_code(ngx_stream_script_engine_t *e) +{ + ngx_stream_variable_value_t *value; + ngx_stream_script_var_code_t *code; + + code = (ngx_stream_script_var_code_t *) e->ip; + + e->ip += sizeof(ngx_stream_script_var_code_t); + + if (e->flushed) { + value = ngx_stream_get_indexed_variable(e->session, code->index); + + } else { + value = ngx_stream_get_flushed_variable(e->session, code->index); + } + + if (value && !value->not_found) { + return value->len; + } + + return 0; +} + + +void +ngx_stream_script_copy_var_code(ngx_stream_script_engine_t *e) +{ + u_char *p; + ngx_stream_variable_value_t *value; + ngx_stream_script_var_code_t *code; + + code = (ngx_stream_script_var_code_t *) e->ip; + + e->ip += sizeof(ngx_stream_script_var_code_t); + + if (!e->skip) { + + if (e->flushed) { + value = ngx_stream_get_indexed_variable(e->session, code->index); + + } else { + value = ngx_stream_get_flushed_variable(e->session, code->index); + } + + if (value && !value->not_found) { + p = e->pos; + e->pos = ngx_copy(p, value->data, value->len); + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, + e->session->connection->log, 0, + "stream script var: \"%*s\"", e->pos - p, p); + } + } +} + + +#if (NGX_PCRE) + +static ngx_int_t +ngx_stream_script_add_capture_code(ngx_stream_script_compile_t *sc, + ngx_uint_t n) +{ + ngx_stream_script_copy_capture_code_t *code; + + code = ngx_stream_script_add_code(*sc->lengths, + sizeof(ngx_stream_script_copy_capture_code_t), + NULL); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = (ngx_stream_script_code_pt) (void *) + ngx_stream_script_copy_capture_len_code; + code->n = 2 * n; + + + code = ngx_stream_script_add_code(*sc->values, + sizeof(ngx_stream_script_copy_capture_code_t), + &sc->main); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = ngx_stream_script_copy_capture_code; + code->n = 2 * n; + + if (sc->ncaptures < n) { + sc->ncaptures = n; + } + + return NGX_OK; +} + + +size_t +ngx_stream_script_copy_capture_len_code(ngx_stream_script_engine_t *e) +{ + int *cap; + ngx_uint_t n; + ngx_stream_session_t *s; + ngx_stream_script_copy_capture_code_t *code; + + s = e->session; + + code = (ngx_stream_script_copy_capture_code_t *) e->ip; + + e->ip += sizeof(ngx_stream_script_copy_capture_code_t); + + n = code->n; + + if (n < s->ncaptures) { + cap = s->captures; + return cap[n + 1] - cap[n]; + } + + return 0; +} + + +void +ngx_stream_script_copy_capture_code(ngx_stream_script_engine_t *e) +{ + int *cap; + u_char *p, *pos; + ngx_uint_t n; + ngx_stream_session_t *s; + ngx_stream_script_copy_capture_code_t *code; + + s = e->session; + + code = (ngx_stream_script_copy_capture_code_t *) e->ip; + + e->ip += sizeof(ngx_stream_script_copy_capture_code_t); + + n = code->n; + + pos = e->pos; + + if (n < s->ncaptures) { + cap = s->captures; + p = s->captures_data; + e->pos = ngx_copy(pos, &p[cap[n]], cap[n + 1] - cap[n]); + } + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, e->session->connection->log, 0, + "stream script capture: \"%*s\"", e->pos - pos, pos); +} + +#endif + + +static ngx_int_t +ngx_stream_script_add_full_name_code(ngx_stream_script_compile_t *sc) +{ + ngx_stream_script_full_name_code_t *code; + + code = ngx_stream_script_add_code(*sc->lengths, + sizeof(ngx_stream_script_full_name_code_t), + NULL); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = (ngx_stream_script_code_pt) (void *) + ngx_stream_script_full_name_len_code; + code->conf_prefix = sc->conf_prefix; + + code = ngx_stream_script_add_code(*sc->values, + sizeof(ngx_stream_script_full_name_code_t), &sc->main); + if (code == NULL) { + return NGX_ERROR; + } + + code->code = ngx_stream_script_full_name_code; + code->conf_prefix = sc->conf_prefix; + + return NGX_OK; +} + + +static size_t +ngx_stream_script_full_name_len_code(ngx_stream_script_engine_t *e) +{ + ngx_stream_script_full_name_code_t *code; + + code = (ngx_stream_script_full_name_code_t *) e->ip; + + e->ip += sizeof(ngx_stream_script_full_name_code_t); + + return code->conf_prefix ? ngx_cycle->conf_prefix.len: + ngx_cycle->prefix.len; +} + + +static void +ngx_stream_script_full_name_code(ngx_stream_script_engine_t *e) +{ + ngx_stream_script_full_name_code_t *code; + + ngx_str_t value, *prefix; + + code = (ngx_stream_script_full_name_code_t *) e->ip; + + value.data = e->buf.data; + value.len = e->pos - e->buf.data; + + prefix = code->conf_prefix ? (ngx_str_t *) &ngx_cycle->conf_prefix: + (ngx_str_t *) &ngx_cycle->prefix; + + if (ngx_get_full_name(e->session->connection->pool, prefix, &value) + != NGX_OK) + { + e->ip = ngx_stream_script_exit; + return; + } + + e->buf = value; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, e->session->connection->log, 0, + "stream script fullname: \"%V\"", &value); + + e->ip += sizeof(ngx_stream_script_full_name_code_t); +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_script.h b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_script.h new file mode 100644 index 000000000..d8f374047 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_script.h @@ -0,0 +1,137 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_STREAM_SCRIPT_H_INCLUDED_ +#define _NGX_STREAM_SCRIPT_H_INCLUDED_ + + +#include +#include +#include + + +typedef struct { + u_char *ip; + u_char *pos; + ngx_stream_variable_value_t *sp; + + ngx_str_t buf; + ngx_str_t line; + + unsigned flushed:1; + unsigned skip:1; + + ngx_stream_session_t *session; +} ngx_stream_script_engine_t; + + +typedef struct { + ngx_conf_t *cf; + ngx_str_t *source; + + ngx_array_t **flushes; + ngx_array_t **lengths; + ngx_array_t **values; + + ngx_uint_t variables; + ngx_uint_t ncaptures; + ngx_uint_t size; + + void *main; + + unsigned complete_lengths:1; + unsigned complete_values:1; + unsigned zero:1; + unsigned conf_prefix:1; + unsigned root_prefix:1; +} ngx_stream_script_compile_t; + + +typedef struct { + ngx_str_t value; + ngx_uint_t *flushes; + void *lengths; + void *values; + + union { + size_t size; + } u; +} ngx_stream_complex_value_t; + + +typedef struct { + ngx_conf_t *cf; + ngx_str_t *value; + ngx_stream_complex_value_t *complex_value; + + unsigned zero:1; + unsigned conf_prefix:1; + unsigned root_prefix:1; +} ngx_stream_compile_complex_value_t; + + +typedef void (*ngx_stream_script_code_pt) (ngx_stream_script_engine_t *e); +typedef size_t (*ngx_stream_script_len_code_pt) (ngx_stream_script_engine_t *e); + + +typedef struct { + ngx_stream_script_code_pt code; + uintptr_t len; +} ngx_stream_script_copy_code_t; + + +typedef struct { + ngx_stream_script_code_pt code; + uintptr_t index; +} ngx_stream_script_var_code_t; + + +typedef struct { + ngx_stream_script_code_pt code; + uintptr_t n; +} ngx_stream_script_copy_capture_code_t; + + +typedef struct { + ngx_stream_script_code_pt code; + uintptr_t conf_prefix; +} ngx_stream_script_full_name_code_t; + + +void ngx_stream_script_flush_complex_value(ngx_stream_session_t *s, + ngx_stream_complex_value_t *val); +ngx_int_t ngx_stream_complex_value(ngx_stream_session_t *s, + ngx_stream_complex_value_t *val, ngx_str_t *value); +size_t ngx_stream_complex_value_size(ngx_stream_session_t *s, + ngx_stream_complex_value_t *val, size_t default_value); +ngx_int_t ngx_stream_compile_complex_value( + ngx_stream_compile_complex_value_t *ccv); +char *ngx_stream_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +char *ngx_stream_set_complex_value_zero_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +char *ngx_stream_set_complex_value_size_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + + +ngx_uint_t ngx_stream_script_variables_count(ngx_str_t *value); +ngx_int_t ngx_stream_script_compile(ngx_stream_script_compile_t *sc); +u_char *ngx_stream_script_run(ngx_stream_session_t *s, ngx_str_t *value, + void *code_lengths, size_t reserved, void *code_values); +void ngx_stream_script_flush_no_cacheable_variables(ngx_stream_session_t *s, + ngx_array_t *indices); + +void *ngx_stream_script_add_code(ngx_array_t *codes, size_t size, void *code); + +size_t ngx_stream_script_copy_len_code(ngx_stream_script_engine_t *e); +void ngx_stream_script_copy_code(ngx_stream_script_engine_t *e); +size_t ngx_stream_script_copy_var_len_code(ngx_stream_script_engine_t *e); +void ngx_stream_script_copy_var_code(ngx_stream_script_engine_t *e); +size_t ngx_stream_script_copy_capture_len_code(ngx_stream_script_engine_t *e); +void ngx_stream_script_copy_capture_code(ngx_stream_script_engine_t *e); + +#endif /* _NGX_STREAM_SCRIPT_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_set_module.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_set_module.c new file mode 100644 index 000000000..9b34a6099 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_set_module.c @@ -0,0 +1,226 @@ + +/* + * Copyright (C) Pavel Pautov + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_int_t index; + ngx_stream_set_variable_pt set_handler; + uintptr_t data; + ngx_stream_complex_value_t value; +} ngx_stream_set_cmd_t; + + +typedef struct { + ngx_array_t commands; +} ngx_stream_set_srv_conf_t; + + +static ngx_int_t ngx_stream_set_handler(ngx_stream_session_t *s); +static ngx_int_t ngx_stream_set_var(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_set_init(ngx_conf_t *cf); +static void *ngx_stream_set_create_srv_conf(ngx_conf_t *cf); +static char *ngx_stream_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + + +static ngx_command_t ngx_stream_set_commands[] = { + + { ngx_string("set"), + NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2, + ngx_stream_set, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_set_module_ctx = { + NULL, /* preconfiguration */ + ngx_stream_set_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_stream_set_create_srv_conf, /* create server configuration */ + NULL /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_set_module = { + NGX_MODULE_V1, + &ngx_stream_set_module_ctx, /* module context */ + ngx_stream_set_commands, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_int_t +ngx_stream_set_handler(ngx_stream_session_t *s) +{ + ngx_str_t str; + ngx_uint_t i; + ngx_stream_set_cmd_t *cmds; + ngx_stream_set_srv_conf_t *scf; + ngx_stream_variable_value_t vv; + + scf = ngx_stream_get_module_srv_conf(s, ngx_stream_set_module); + cmds = scf->commands.elts; + vv = ngx_stream_variable_null_value; + + for (i = 0; i < scf->commands.nelts; i++) { + if (ngx_stream_complex_value(s, &cmds[i].value, &str) != NGX_OK) { + return NGX_ERROR; + } + + if (cmds[i].set_handler != NULL) { + vv.len = str.len; + vv.data = str.data; + cmds[i].set_handler(s, &vv, cmds[i].data); + + } else { + s->variables[cmds[i].index].len = str.len; + s->variables[cmds[i].index].valid = 1; + s->variables[cmds[i].index].no_cacheable = 0; + s->variables[cmds[i].index].not_found = 0; + s->variables[cmds[i].index].data = str.data; + } + } + + return NGX_DECLINED; +} + + +static ngx_int_t +ngx_stream_set_var(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, + uintptr_t data) +{ + *v = ngx_stream_variable_null_value; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_set_init(ngx_conf_t *cf) +{ + ngx_stream_handler_pt *h; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + h = ngx_array_push(&cmcf->phases[NGX_STREAM_PREACCESS_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_stream_set_handler; + + return NGX_OK; +} + + +static void * +ngx_stream_set_create_srv_conf(ngx_conf_t *cf) +{ + ngx_stream_set_srv_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_set_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * conf->commands = { NULL }; + */ + + return conf; +} + + +static char * +ngx_stream_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_set_srv_conf_t *scf = conf; + + ngx_str_t *args; + ngx_int_t index; + ngx_stream_set_cmd_t *set_cmd; + ngx_stream_variable_t *v; + ngx_stream_compile_complex_value_t ccv; + + args = cf->args->elts; + + if (args[1].data[0] != '$') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"%V\"", &args[1]); + return NGX_CONF_ERROR; + } + + args[1].len--; + args[1].data++; + + v = ngx_stream_add_variable(cf, &args[1], + NGX_STREAM_VAR_CHANGEABLE|NGX_STREAM_VAR_WEAK); + if (v == NULL) { + return NGX_CONF_ERROR; + } + + index = ngx_stream_get_variable_index(cf, &args[1]); + if (index == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + if (v->get_handler == NULL) { + v->get_handler = ngx_stream_set_var; + } + + if (scf->commands.elts == NULL) { + if (ngx_array_init(&scf->commands, cf->pool, 1, + sizeof(ngx_stream_set_cmd_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + } + + set_cmd = ngx_array_push(&scf->commands); + if (set_cmd == NULL) { + return NGX_CONF_ERROR; + } + + set_cmd->index = index; + set_cmd->set_handler = v->set_handler; + set_cmd->data = v->data; + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &args[2]; + ccv.complex_value = &set_cmd->value; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_split_clients_module.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_split_clients_module.c new file mode 100644 index 000000000..af6c8a1b4 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_split_clients_module.c @@ -0,0 +1,244 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + uint32_t percent; + ngx_stream_variable_value_t value; +} ngx_stream_split_clients_part_t; + + +typedef struct { + ngx_stream_complex_value_t value; + ngx_array_t parts; +} ngx_stream_split_clients_ctx_t; + + +static char *ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_split_clients(ngx_conf_t *cf, ngx_command_t *dummy, + void *conf); + +static ngx_command_t ngx_stream_split_clients_commands[] = { + + { ngx_string("split_clients"), + NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2, + ngx_conf_split_clients_block, + NGX_STREAM_MAIN_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_split_clients_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_split_clients_module = { + NGX_MODULE_V1, + &ngx_stream_split_clients_module_ctx, /* module context */ + ngx_stream_split_clients_commands, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_int_t +ngx_stream_split_clients_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_stream_split_clients_ctx_t *ctx = + (ngx_stream_split_clients_ctx_t *) data; + + uint32_t hash; + ngx_str_t val; + ngx_uint_t i; + ngx_stream_split_clients_part_t *part; + + *v = ngx_stream_variable_null_value; + + if (ngx_stream_complex_value(s, &ctx->value, &val) != NGX_OK) { + return NGX_OK; + } + + hash = ngx_murmur_hash2(val.data, val.len); + + part = ctx->parts.elts; + + for (i = 0; i < ctx->parts.nelts; i++) { + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream split: %uD %uD", hash, part[i].percent); + + if (hash < part[i].percent || part[i].percent == 0) { + *v = part[i].value; + return NGX_OK; + } + } + + return NGX_OK; +} + + +static char * +ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *rv; + uint32_t sum, last; + ngx_str_t *value, name; + ngx_uint_t i; + ngx_conf_t save; + ngx_stream_variable_t *var; + ngx_stream_split_clients_ctx_t *ctx; + ngx_stream_split_clients_part_t *part; + ngx_stream_compile_complex_value_t ccv; + + ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_split_clients_ctx_t)); + if (ctx == NULL) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &ctx->value; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + name = value[2]; + + if (name.data[0] != '$') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"%V\"", &name); + return NGX_CONF_ERROR; + } + + name.len--; + name.data++; + + var = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE); + if (var == NULL) { + return NGX_CONF_ERROR; + } + + var->get_handler = ngx_stream_split_clients_variable; + var->data = (uintptr_t) ctx; + + if (ngx_array_init(&ctx->parts, cf->pool, 2, + sizeof(ngx_stream_split_clients_part_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + save = *cf; + cf->ctx = ctx; + cf->handler = ngx_stream_split_clients; + cf->handler_conf = conf; + + rv = ngx_conf_parse(cf, NULL); + + *cf = save; + + if (rv != NGX_CONF_OK) { + return rv; + } + + sum = 0; + last = 0; + part = ctx->parts.elts; + + for (i = 0; i < ctx->parts.nelts; i++) { + sum = part[i].percent ? sum + part[i].percent : 10000; + if (sum > 10000) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "percent total is greater than 100%%"); + return NGX_CONF_ERROR; + } + + if (part[i].percent) { + last += part[i].percent * (uint64_t) 0xffffffff / 10000; + part[i].percent = last; + } + } + + return rv; +} + + +static char * +ngx_stream_split_clients(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) +{ + ngx_int_t n; + ngx_str_t *value; + ngx_stream_split_clients_ctx_t *ctx; + ngx_stream_split_clients_part_t *part; + + ctx = cf->ctx; + value = cf->args->elts; + + part = ngx_array_push(&ctx->parts); + if (part == NULL) { + return NGX_CONF_ERROR; + } + + if (value[0].len == 1 && value[0].data[0] == '*') { + part->percent = 0; + + } else { + if (value[0].len == 0 || value[0].data[value[0].len - 1] != '%') { + goto invalid; + } + + n = ngx_atofp(value[0].data, value[0].len - 1, 2); + if (n == NGX_ERROR || n == 0) { + goto invalid; + } + + part->percent = (uint32_t) n; + } + + part->value.len = value[1].len; + part->value.valid = 1; + part->value.no_cacheable = 0; + part->value.not_found = 0; + part->value.data = value[1].data; + + return NGX_CONF_OK; + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid percent value \"%V\"", &value[0]); + return NGX_CONF_ERROR; +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_ssl_module.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_ssl_module.c new file mode 100644 index 000000000..0e17cff4d --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_ssl_module.c @@ -0,0 +1,1784 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c, + ngx_pool_t *pool, ngx_str_t *s); + + +#define NGX_DEFAULT_CIPHERS "HIGH:!aNULL:!MD5" +#define NGX_DEFAULT_ECDH_CURVE "auto" + + +static ngx_int_t ngx_stream_ssl_handler(ngx_stream_session_t *s); +static ngx_int_t ngx_stream_ssl_init_connection(ngx_ssl_t *ssl, + ngx_connection_t *c); +static void ngx_stream_ssl_handshake_handler(ngx_connection_t *c); +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME +static int ngx_stream_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, + void *arg); +#endif +#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation +static int ngx_stream_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, + const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, void *arg); +#endif +#ifdef SSL_R_CERT_CB_ERROR +static int ngx_stream_ssl_certificate(ngx_ssl_conn_t *ssl_conn, void *arg); +#endif +static ngx_int_t ngx_stream_ssl_static_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_ssl_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); + +static ngx_int_t ngx_stream_ssl_add_variables(ngx_conf_t *cf); +static void *ngx_stream_ssl_create_srv_conf(ngx_conf_t *cf); +static char *ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); + +static ngx_int_t ngx_stream_ssl_compile_certificates(ngx_conf_t *cf, + ngx_stream_ssl_srv_conf_t *conf); + +static char *ngx_stream_ssl_certificate_cache(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); +static char *ngx_stream_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_ssl_ocsp_cache(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_stream_ssl_alpn(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + +static char *ngx_stream_ssl_conf_command_check(ngx_conf_t *cf, void *post, + void *data); + +static ngx_int_t ngx_stream_ssl_init(ngx_conf_t *cf); + + +static ngx_conf_bitmask_t ngx_stream_ssl_protocols[] = { + { ngx_string("SSLv2"), NGX_SSL_SSLv2 }, + { ngx_string("SSLv3"), NGX_SSL_SSLv3 }, + { ngx_string("TLSv1"), NGX_SSL_TLSv1 }, + { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 }, + { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 }, + { ngx_string("TLSv1.3"), NGX_SSL_TLSv1_3 }, + { ngx_null_string, 0 } +}; + + +static ngx_conf_enum_t ngx_stream_ssl_verify[] = { + { ngx_string("off"), 0 }, + { ngx_string("on"), 1 }, + { ngx_string("optional"), 2 }, + { ngx_string("optional_no_ca"), 3 }, + { ngx_null_string, 0 } +}; + + +static ngx_conf_enum_t ngx_stream_ssl_ocsp[] = { + { ngx_string("off"), 0 }, + { ngx_string("on"), 1 }, + { ngx_string("leaf"), 2 }, + { ngx_null_string, 0 } +}; + + +static ngx_conf_post_t ngx_stream_ssl_conf_command_post = + { ngx_stream_ssl_conf_command_check }; + + +static ngx_command_t ngx_stream_ssl_commands[] = { + + { ngx_string("ssl_handshake_timeout"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, handshake_timeout), + NULL }, + + { ngx_string("ssl_certificate"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_array_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, certificates), + NULL }, + + { ngx_string("ssl_certificate_key"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_array_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, certificate_keys), + NULL }, + + { ngx_string("ssl_certificate_cache"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE123, + ngx_stream_ssl_certificate_cache, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("ssl_ech_file"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_array_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, ech_files), + NULL }, + + { ngx_string("ssl_password_file"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_ssl_password_file, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("ssl_certificate_compression"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, certificate_compression), + NULL }, + + { ngx_string("ssl_dhparam"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, dhparam), + NULL }, + + { ngx_string("ssl_ecdh_curve"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, ecdh_curve), + NULL }, + + { ngx_string("ssl_protocols"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE, + ngx_conf_set_bitmask_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, protocols), + &ngx_stream_ssl_protocols }, + + { ngx_string("ssl_ciphers"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, ciphers), + NULL }, + + { ngx_string("ssl_verify_client"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_enum_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, verify), + &ngx_stream_ssl_verify }, + + { ngx_string("ssl_verify_depth"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, verify_depth), + NULL }, + + { ngx_string("ssl_client_certificate"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, client_certificate), + NULL }, + + { ngx_string("ssl_trusted_certificate"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, trusted_certificate), + NULL }, + + { ngx_string("ssl_prefer_server_ciphers"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, prefer_server_ciphers), + NULL }, + + { ngx_string("ssl_session_cache"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE12, + ngx_stream_ssl_session_cache, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("ssl_session_tickets"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, session_tickets), + NULL }, + + { ngx_string("ssl_session_ticket_key"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_array_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, session_ticket_keys), + NULL }, + + { ngx_string("ssl_session_timeout"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_sec_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, session_timeout), + NULL }, + + { ngx_string("ssl_crl"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, crl), + NULL }, + + { ngx_string("ssl_ocsp"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_enum_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, ocsp), + &ngx_stream_ssl_ocsp }, + + { ngx_string("ssl_ocsp_responder"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, ocsp_responder), + NULL }, + + { ngx_string("ssl_ocsp_cache"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_stream_ssl_ocsp_cache, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("ssl_stapling"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, stapling), + NULL }, + + { ngx_string("ssl_stapling_file"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, stapling_file), + NULL }, + + { ngx_string("ssl_stapling_responder"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, stapling_responder), + NULL }, + + { ngx_string("ssl_stapling_verify"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, stapling_verify), + NULL }, + + { ngx_string("ssl_conf_command"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2, + ngx_conf_set_keyval_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, conf_commands), + &ngx_stream_ssl_conf_command_post }, + + { ngx_string("ssl_reject_handshake"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_srv_conf_t, reject_handshake), + NULL }, + + { ngx_string("ssl_alpn"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE, + ngx_stream_ssl_alpn, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_ssl_module_ctx = { + ngx_stream_ssl_add_variables, /* preconfiguration */ + ngx_stream_ssl_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_stream_ssl_create_srv_conf, /* create server configuration */ + ngx_stream_ssl_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_ssl_module = { + NGX_MODULE_V1, + &ngx_stream_ssl_module_ctx, /* module context */ + ngx_stream_ssl_commands, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_stream_variable_t ngx_stream_ssl_vars[] = { + + { ngx_string("ssl_protocol"), NULL, ngx_stream_ssl_static_variable, + (uintptr_t) ngx_ssl_get_protocol, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_cipher"), NULL, ngx_stream_ssl_static_variable, + (uintptr_t) ngx_ssl_get_cipher_name, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_ciphers"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_ciphers, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_curve"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_curve, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_curves"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_curves, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_sigalg"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_sigalg, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_session_id"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_session_id, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_session_reused"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_session_reused, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_server_name"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_server_name, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_alpn_protocol"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_alpn_protocol, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_ech_status"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_ech_status, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_ech_outer_server_name"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_ech_outer_server_name, + NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_client_cert"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_certificate, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_client_raw_cert"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_raw_certificate, + NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_client_escaped_cert"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_escaped_certificate, + NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_client_s_dn"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_subject_dn, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_client_i_dn"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_issuer_dn, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_client_serial"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_serial_number, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_client_fingerprint"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_fingerprint, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_client_verify"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_client_verify, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_client_v_start"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_client_v_start, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_client_v_end"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_client_v_end, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_client_v_remain"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_client_v_remain, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + { ngx_string("ssl_client_sigalg"), NULL, ngx_stream_ssl_variable, + (uintptr_t) ngx_ssl_get_client_sigalg, NGX_STREAM_VAR_CHANGEABLE, 0 }, + + ngx_stream_null_variable +}; + + +static ngx_str_t ngx_stream_ssl_sess_id_ctx = ngx_string("STREAM"); + + +static ngx_int_t +ngx_stream_ssl_handler(ngx_stream_session_t *s) +{ + long rc; + X509 *cert; + ngx_int_t rv; + const char *str; + ngx_connection_t *c; + ngx_stream_ssl_srv_conf_t *sscf; + + if (!s->ssl) { + return NGX_OK; + } + + c = s->connection; + + sscf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module); + + if (c->ssl == NULL) { + c->log->action = "SSL handshaking"; + + rv = ngx_stream_ssl_init_connection(&sscf->ssl, c); + + if (rv != NGX_OK) { + return rv; + } + } + + if (sscf->verify) { + rc = SSL_get_verify_result(c->ssl->connection); + + if (rc != X509_V_OK + && (sscf->verify != 3 || !ngx_ssl_verify_error_optional(rc))) + { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client SSL certificate verify error: (%l:%s)", + rc, X509_verify_cert_error_string(rc)); + + ngx_ssl_remove_cached_session(c->ssl->session_ctx, + (SSL_get0_session(c->ssl->connection))); + return NGX_ERROR; + } + + if (sscf->verify == 1) { + cert = SSL_get_peer_certificate(c->ssl->connection); + + if (cert == NULL) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client sent no required SSL certificate"); + + ngx_ssl_remove_cached_session(c->ssl->session_ctx, + (SSL_get0_session(c->ssl->connection))); + return NGX_ERROR; + } + + X509_free(cert); + } + + if (ngx_ssl_ocsp_get_status(c, &str) != NGX_OK) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client SSL certificate verify error: %s", str); + + ngx_ssl_remove_cached_session(c->ssl->session_ctx, + (SSL_get0_session(c->ssl->connection))); + return NGX_ERROR; + } + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c) +{ + ngx_int_t rc; + ngx_stream_session_t *s; + ngx_stream_ssl_srv_conf_t *sscf; + ngx_stream_core_srv_conf_t *cscf; + + s = c->data; + + cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); + + if (cscf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) { + return NGX_ERROR; + } + + if (ngx_ssl_create_connection(ssl, c, 0) != NGX_OK) { + return NGX_ERROR; + } + + rc = ngx_ssl_handshake(c); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_AGAIN) { + sscf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module); + + ngx_add_timer(c->read, sscf->handshake_timeout); + + c->ssl->handler = ngx_stream_ssl_handshake_handler; + + return NGX_AGAIN; + } + + /* rc == NGX_OK */ + + return NGX_OK; +} + + +static void +ngx_stream_ssl_handshake_handler(ngx_connection_t *c) +{ + ngx_stream_session_t *s; + + s = c->data; + + if (!c->ssl->handshaked) { + ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); + return; + } + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + ngx_stream_core_run_phases(s); +} + + +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + +static int +ngx_stream_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg) +{ + ngx_int_t rc; + ngx_str_t host; + const char *servername; + ngx_connection_t *c; + ngx_stream_session_t *s; + ngx_stream_ssl_srv_conf_t *sscf; + ngx_stream_core_srv_conf_t *cscf; + + c = ngx_ssl_get_connection(ssl_conn); + + if (c->ssl->handshaked) { + *ad = SSL_AD_NO_RENEGOTIATION; + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + if (c->ssl->sni_accepted) { + return SSL_TLSEXT_ERR_OK; + } + + if (c->ssl->handshake_rejected) { + *ad = SSL_AD_UNRECOGNIZED_NAME; + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + s = c->data; + + if (arg) { + host = *(ngx_str_t *) arg; + + if (host.data == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "SSL server name: null"); + goto done; + } + + } else { + servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name); + + if (servername == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, + "SSL server name: null"); + goto done; + } + + host.len = ngx_strlen(servername); + host.data = (u_char *) servername; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "SSL server name: \"%V\"", &host); + + if (host.len == 0) { + goto done; + } + + rc = ngx_stream_validate_host(&host, c->pool, 1); + + if (rc == NGX_ERROR) { + goto error; + } + + if (rc == NGX_DECLINED) { + goto done; + } + + rc = ngx_stream_find_virtual_server(s, &host, &cscf); + + if (rc == NGX_ERROR) { + goto error; + } + + if (rc == NGX_DECLINED) { + goto done; + } + + s->srv_conf = cscf->ctx->srv_conf; + + ngx_set_connection_log(c, cscf->error_log); + + sscf = ngx_stream_get_module_srv_conf(cscf->ctx, ngx_stream_ssl_module); + + if (sscf->ssl.ctx) { + if (SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx) == NULL) { + goto error; + } + + /* + * SSL_set_SSL_CTX() only changes certs as of 1.0.0d + * adjust other things we care about + */ + + SSL_set_verify(ssl_conn, SSL_CTX_get_verify_mode(sscf->ssl.ctx), + SSL_CTX_get_verify_callback(sscf->ssl.ctx)); + + SSL_set_verify_depth(ssl_conn, SSL_CTX_get_verify_depth(sscf->ssl.ctx)); + +#if OPENSSL_VERSION_NUMBER >= 0x009080dfL + /* only in 0.9.8m+ */ + SSL_clear_options(ssl_conn, SSL_get_options(ssl_conn) & + ~SSL_CTX_get_options(sscf->ssl.ctx)); +#endif + + SSL_set_options(ssl_conn, SSL_CTX_get_options(sscf->ssl.ctx)); + +#ifdef SSL_OP_NO_RENEGOTIATION + SSL_set_options(ssl_conn, SSL_OP_NO_RENEGOTIATION); +#endif + } + +done: + + sscf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module); + + if (sscf->reject_handshake) { + c->ssl->handshake_rejected = 1; + *ad = SSL_AD_UNRECOGNIZED_NAME; + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + c->ssl->sni_accepted = 1; + return SSL_TLSEXT_ERR_OK; + +error: + + *ad = SSL_AD_INTERNAL_ERROR; + return SSL_TLSEXT_ERR_ALERT_FATAL; +} + +#endif + + +#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation + +static int +ngx_stream_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out, + unsigned char *outlen, const unsigned char *in, unsigned int inlen, + void *arg) +{ + ngx_str_t *alpn; +#if (NGX_DEBUG) + unsigned int i; + ngx_connection_t *c; + + c = ngx_ssl_get_connection(ssl_conn); + + for (i = 0; i < inlen; i += in[i] + 1) { + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, c->log, 0, + "SSL ALPN supported by client: %*s", + (size_t) in[i], &in[i + 1]); + } + +#endif + + alpn = arg; + + if (SSL_select_next_proto((unsigned char **) out, outlen, alpn->data, + alpn->len, in, inlen) + != OPENSSL_NPN_NEGOTIATED) + { + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, c->log, 0, + "SSL ALPN selected: %*s", (size_t) *outlen, *out); + + return SSL_TLSEXT_ERR_OK; +} + +#endif + + +#ifdef SSL_R_CERT_CB_ERROR + +static int +ngx_stream_ssl_certificate(ngx_ssl_conn_t *ssl_conn, void *arg) +{ + ngx_str_t cert, key; + ngx_uint_t i, nelts; + ngx_connection_t *c; + ngx_stream_session_t *s; + ngx_stream_ssl_srv_conf_t *sscf; + ngx_stream_complex_value_t *certs, *keys; + + c = ngx_ssl_get_connection(ssl_conn); + + if (c->ssl->handshaked) { + return 0; + } + + s = c->data; + + sscf = arg; + + nelts = sscf->certificate_values->nelts; + certs = sscf->certificate_values->elts; + keys = sscf->certificate_key_values->elts; + + for (i = 0; i < nelts; i++) { + + if (ngx_stream_complex_value(s, &certs[i], &cert) != NGX_OK) { + return 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "ssl cert: \"%s\"", cert.data); + + if (ngx_stream_complex_value(s, &keys[i], &key) != NGX_OK) { + return 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "ssl key: \"%s\"", key.data); + + if (ngx_ssl_connection_certificate(c, c->pool, &cert, &key, + sscf->certificate_cache, + sscf->passwords) + != NGX_OK) + { + return 0; + } + } + + return 1; +} + +#endif + + +static ngx_int_t +ngx_stream_ssl_static_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_ssl_variable_handler_pt handler = (ngx_ssl_variable_handler_pt) data; + + size_t len; + ngx_str_t str; + + if (s->connection->ssl) { + + (void) handler(s->connection, NULL, &str); + + v->data = str.data; + + for (len = 0; v->data[len]; len++) { /* void */ } + + v->len = len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; + } + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_ssl_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_ssl_variable_handler_pt handler = (ngx_ssl_variable_handler_pt) data; + + ngx_str_t str; + + if (s->connection->ssl) { + + if (handler(s->connection, s->connection->pool, &str) != NGX_OK) { + return NGX_ERROR; + } + + v->len = str.len; + v->data = str.data; + + if (v->len) { + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; + } + } + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_ssl_add_variables(ngx_conf_t *cf) +{ + ngx_stream_variable_t *var, *v; + + for (v = ngx_stream_ssl_vars; v->name.len; v++) { + var = ngx_stream_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + +static void * +ngx_stream_ssl_create_srv_conf(ngx_conf_t *cf) +{ + ngx_stream_ssl_srv_conf_t *sscf; + + sscf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_ssl_srv_conf_t)); + if (sscf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * sscf->protocols = 0; + * sscf->certificate_values = NULL; + * sscf->dhparam = { 0, NULL }; + * sscf->ecdh_curve = { 0, NULL }; + * sscf->client_certificate = { 0, NULL }; + * sscf->trusted_certificate = { 0, NULL }; + * sscf->crl = { 0, NULL }; + * sscf->alpn = { 0, NULL }; + * sscf->ciphers = { 0, NULL }; + * sscf->shm_zone = NULL; + * sscf->ocsp_responder = { 0, NULL }; + * sscf->stapling_file = { 0, NULL }; + * sscf->stapling_responder = { 0, NULL }; + */ + + sscf->handshake_timeout = NGX_CONF_UNSET_MSEC; + sscf->certificates = NGX_CONF_UNSET_PTR; + sscf->certificate_keys = NGX_CONF_UNSET_PTR; + sscf->certificate_cache = NGX_CONF_UNSET_PTR; + sscf->ech_files = NGX_CONF_UNSET_PTR; + sscf->passwords = NGX_CONF_UNSET_PTR; + sscf->conf_commands = NGX_CONF_UNSET_PTR; + sscf->prefer_server_ciphers = NGX_CONF_UNSET; + sscf->certificate_compression = NGX_CONF_UNSET; + sscf->reject_handshake = NGX_CONF_UNSET; + sscf->verify = NGX_CONF_UNSET_UINT; + sscf->verify_depth = NGX_CONF_UNSET_UINT; + sscf->builtin_session_cache = NGX_CONF_UNSET; + sscf->session_timeout = NGX_CONF_UNSET; + sscf->session_tickets = NGX_CONF_UNSET; + sscf->session_ticket_keys = NGX_CONF_UNSET_PTR; + sscf->ocsp = NGX_CONF_UNSET_UINT; + sscf->ocsp_cache_zone = NGX_CONF_UNSET_PTR; + sscf->stapling = NGX_CONF_UNSET; + sscf->stapling_verify = NGX_CONF_UNSET; + + return sscf; +} + + +static char * +ngx_stream_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_stream_ssl_srv_conf_t *prev = parent; + ngx_stream_ssl_srv_conf_t *conf = child; + + ngx_pool_cleanup_t *cln; + + ngx_conf_merge_msec_value(conf->handshake_timeout, + prev->handshake_timeout, 60000); + + ngx_conf_merge_value(conf->session_timeout, + prev->session_timeout, 300); + + ngx_conf_merge_value(conf->prefer_server_ciphers, + prev->prefer_server_ciphers, 0); + + ngx_conf_merge_value(conf->certificate_compression, + prev->certificate_compression, 0); + + ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0); + + ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols, + (NGX_CONF_BITMASK_SET|NGX_SSL_DEFAULT_PROTOCOLS)); + + ngx_conf_merge_uint_value(conf->verify, prev->verify, 0); + ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1); + + ngx_conf_merge_ptr_value(conf->certificates, prev->certificates, NULL); + ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys, + NULL); + + ngx_conf_merge_ptr_value(conf->certificate_cache, prev->certificate_cache, + NULL); + + ngx_conf_merge_ptr_value(conf->ech_files, prev->ech_files, NULL); + + ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL); + + ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, ""); + + ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate, + ""); + ngx_conf_merge_str_value(conf->trusted_certificate, + prev->trusted_certificate, ""); + ngx_conf_merge_str_value(conf->crl, prev->crl, ""); + ngx_conf_merge_str_value(conf->alpn, prev->alpn, ""); + + ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve, + NGX_DEFAULT_ECDH_CURVE); + + ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS); + + ngx_conf_merge_ptr_value(conf->conf_commands, prev->conf_commands, NULL); + + ngx_conf_merge_uint_value(conf->ocsp, prev->ocsp, 0); + ngx_conf_merge_str_value(conf->ocsp_responder, prev->ocsp_responder, ""); + ngx_conf_merge_ptr_value(conf->ocsp_cache_zone, + prev->ocsp_cache_zone, NULL); + + ngx_conf_merge_value(conf->stapling, prev->stapling, 0); + ngx_conf_merge_value(conf->stapling_verify, prev->stapling_verify, 0); + ngx_conf_merge_str_value(conf->stapling_file, prev->stapling_file, ""); + ngx_conf_merge_str_value(conf->stapling_responder, + prev->stapling_responder, ""); + + conf->ssl.log = cf->log; + + if (conf->certificates) { + + if (conf->certificate_keys == NULL + || conf->certificate_keys->nelts < conf->certificates->nelts) + { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"ssl_certificate_key\" is defined " + "for certificate \"%V\"", + ((ngx_str_t *) conf->certificates->elts) + + conf->certificates->nelts - 1); + return NGX_CONF_ERROR; + } + + } else if (!conf->reject_handshake) { + return NGX_CONF_OK; + } + + if (ngx_ssl_create(&conf->ssl, conf->protocols, NULL) != NGX_OK) { + return NGX_CONF_ERROR; + } + + cln = ngx_pool_cleanup_add(cf->pool, 0); + if (cln == NULL) { + ngx_ssl_cleanup_ctx(&conf->ssl); + return NGX_CONF_ERROR; + } + + cln->handler = ngx_ssl_cleanup_ctx; + cln->data = &conf->ssl; + +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + { + static ngx_ssl_client_hello_arg cb = { ngx_stream_ssl_servername }; + + if (ngx_ssl_set_client_hello_callback(&conf->ssl, &cb) != NGX_OK) { + return NGX_CONF_ERROR; + } + + SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx, + ngx_stream_ssl_servername); + } +#endif + +#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation + if (conf->alpn.len) { + SSL_CTX_set_alpn_select_cb(conf->ssl.ctx, ngx_stream_ssl_alpn_select, + &conf->alpn); + } +#endif + + if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers, + conf->prefer_server_ciphers) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + if (ngx_stream_ssl_compile_certificates(cf, conf) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (conf->certificate_values) { + +#ifdef SSL_R_CERT_CB_ERROR + + /* install callback to lookup certificates */ + + SSL_CTX_set_cert_cb(conf->ssl.ctx, ngx_stream_ssl_certificate, conf); + +#else + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "variables in " + "\"ssl_certificate\" and \"ssl_certificate_key\" " + "directives are not supported on this platform"); + return NGX_CONF_ERROR; +#endif + + } else if (conf->certificates) { + + /* configure certificates */ + + if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates, + conf->certificate_keys, conf->passwords) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + if (ngx_ssl_certificate_compression(cf, &conf->ssl, + conf->certificate_compression) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + } + + if (conf->verify) { + + if (conf->verify != 3 + && conf->client_certificate.len == 0 + && conf->trusted_certificate.len == 0) + { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no ssl_client_certificate or " + "ssl_trusted_certificate for ssl_verify_client"); + return NGX_CONF_ERROR; + } + + if (ngx_ssl_client_certificate(cf, &conf->ssl, + &conf->client_certificate, + conf->verify_depth) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + } + + if (ngx_ssl_trusted_certificate(cf, &conf->ssl, + &conf->trusted_certificate, + conf->verify_depth) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (conf->ocsp) { + + if (conf->verify == 3) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "\"ssl_ocsp\" is incompatible with " + "\"ssl_verify_client optional_no_ca\""); + return NGX_CONF_ERROR; + } + + if (ngx_ssl_ocsp(cf, &conf->ssl, &conf->ocsp_responder, conf->ocsp, + conf->ocsp_cache_zone) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + } + + if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (ngx_ssl_ech_files(cf, &conf->ssl, conf->ech_files) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) { + return NGX_CONF_ERROR; + } + + ngx_conf_merge_value(conf->builtin_session_cache, + prev->builtin_session_cache, NGX_SSL_NONE_SCACHE); + + if (conf->shm_zone == NULL) { + conf->shm_zone = prev->shm_zone; + } + + if (ngx_ssl_session_cache(&conf->ssl, &ngx_stream_ssl_sess_id_ctx, + conf->certificates, conf->builtin_session_cache, + conf->shm_zone, conf->session_timeout) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + ngx_conf_merge_value(conf->session_tickets, + prev->session_tickets, 1); + +#ifdef SSL_OP_NO_TICKET + if (!conf->session_tickets) { + SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_NO_TICKET); + } +#endif + + ngx_conf_merge_ptr_value(conf->session_ticket_keys, + prev->session_ticket_keys, NULL); + + if (ngx_ssl_session_ticket_keys(cf, &conf->ssl, conf->session_ticket_keys) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + if (conf->stapling) { + + if (conf->certificate_compression) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "\"ssl_stapling\" is incompatible with " + "\"ssl_certificate_compression\""); + return NGX_CONF_ERROR; + } + + if (ngx_ssl_stapling(cf, &conf->ssl, &conf->stapling_file, + &conf->stapling_responder, conf->stapling_verify) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + } + + if (ngx_ssl_conf_commands(cf, &conf->ssl, conf->conf_commands) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_stream_ssl_compile_certificates(ngx_conf_t *cf, + ngx_stream_ssl_srv_conf_t *conf) +{ + ngx_str_t *cert, *key; + ngx_uint_t i, nelts; + ngx_stream_complex_value_t *cv; + ngx_stream_compile_complex_value_t ccv; + + if (conf->certificates == NULL) { + return NGX_OK; + } + + cert = conf->certificates->elts; + key = conf->certificate_keys->elts; + nelts = conf->certificates->nelts; + + for (i = 0; i < nelts; i++) { + + if (ngx_stream_script_variables_count(&cert[i])) { + goto found; + } + + if (ngx_stream_script_variables_count(&key[i])) { + goto found; + } + } + + return NGX_OK; + +found: + + conf->certificate_values = ngx_array_create(cf->pool, nelts, + sizeof(ngx_stream_complex_value_t)); + if (conf->certificate_values == NULL) { + return NGX_ERROR; + } + + conf->certificate_key_values = ngx_array_create(cf->pool, nelts, + sizeof(ngx_stream_complex_value_t)); + if (conf->certificate_key_values == NULL) { + return NGX_ERROR; + } + + for (i = 0; i < nelts; i++) { + + cv = ngx_array_push(conf->certificate_values); + if (cv == NULL) { + return NGX_ERROR; + } + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &cert[i]; + ccv.complex_value = cv; + ccv.zero = 1; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_ERROR; + } + + cv = ngx_array_push(conf->certificate_key_values); + if (cv == NULL) { + return NGX_ERROR; + } + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &key[i]; + ccv.complex_value = cv; + ccv.zero = 1; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_ERROR; + } + } + + conf->passwords = ngx_ssl_preserve_passwords(cf, conf->passwords); + if (conf->passwords == NULL) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +static char * +ngx_stream_ssl_certificate_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_ssl_srv_conf_t *sscf = conf; + + time_t inactive, valid; + ngx_str_t *value, s; + ngx_int_t max; + ngx_uint_t i; + + if (sscf->certificate_cache != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + value = cf->args->elts; + + max = 0; + inactive = 10; + valid = 60; + + for (i = 1; i < cf->args->nelts; i++) { + + if (ngx_strncmp(value[i].data, "max=", 4) == 0) { + + max = ngx_atoi(value[i].data + 4, value[i].len - 4); + if (max <= 0) { + goto failed; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) { + + s.len = value[i].len - 9; + s.data = value[i].data + 9; + + inactive = ngx_parse_time(&s, 1); + if (inactive == (time_t) NGX_ERROR) { + goto failed; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "valid=", 6) == 0) { + + s.len = value[i].len - 6; + s.data = value[i].data + 6; + + valid = ngx_parse_time(&s, 1); + if (valid == (time_t) NGX_ERROR) { + goto failed; + } + + continue; + } + + if (ngx_strcmp(value[i].data, "off") == 0) { + + sscf->certificate_cache = NULL; + + continue; + } + + failed: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + if (sscf->certificate_cache == NULL) { + return NGX_CONF_OK; + } + + if (max == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"ssl_certificate_cache\" must have " + "the \"max\" parameter"); + return NGX_CONF_ERROR; + } + + sscf->certificate_cache = ngx_ssl_cache_init(cf->pool, max, valid, + inactive); + if (sscf->certificate_cache == NULL) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_stream_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_ssl_srv_conf_t *sscf = conf; + + ngx_str_t *value; + + if (sscf->passwords != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + value = cf->args->elts; + + sscf->passwords = ngx_ssl_read_password_file(cf, &value[1]); + + if (sscf->passwords == NULL) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_stream_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_ssl_srv_conf_t *sscf = conf; + + size_t len; + ngx_str_t *value, name, size; + ngx_int_t n; + ngx_uint_t i, j; + + value = cf->args->elts; + + for (i = 1; i < cf->args->nelts; i++) { + + if (ngx_strcmp(value[i].data, "off") == 0) { + sscf->builtin_session_cache = NGX_SSL_NO_SCACHE; + continue; + } + + if (ngx_strcmp(value[i].data, "none") == 0) { + sscf->builtin_session_cache = NGX_SSL_NONE_SCACHE; + continue; + } + + if (ngx_strcmp(value[i].data, "builtin") == 0) { + sscf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE; + continue; + } + + if (value[i].len > sizeof("builtin:") - 1 + && ngx_strncmp(value[i].data, "builtin:", sizeof("builtin:") - 1) + == 0) + { + n = ngx_atoi(value[i].data + sizeof("builtin:") - 1, + value[i].len - (sizeof("builtin:") - 1)); + + if (n == NGX_ERROR) { + goto invalid; + } + + sscf->builtin_session_cache = n; + + continue; + } + + if (value[i].len > sizeof("shared:") - 1 + && ngx_strncmp(value[i].data, "shared:", sizeof("shared:") - 1) + == 0) + { + len = 0; + + for (j = sizeof("shared:") - 1; j < value[i].len; j++) { + if (value[i].data[j] == ':') { + break; + } + + len++; + } + + if (len == 0 || j == value[i].len) { + goto invalid; + } + + name.len = len; + name.data = value[i].data + sizeof("shared:") - 1; + + size.len = value[i].len - j - 1; + size.data = name.data + len + 1; + + n = ngx_parse_size(&size); + + if (n == NGX_ERROR) { + goto invalid; + } + + if (n < (ngx_int_t) (8 * ngx_pagesize)) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "session cache \"%V\" is too small", + &value[i]); + + return NGX_CONF_ERROR; + } + + sscf->shm_zone = ngx_shared_memory_add(cf, &name, n, + &ngx_stream_ssl_module); + if (sscf->shm_zone == NULL) { + return NGX_CONF_ERROR; + } + + sscf->shm_zone->init = ngx_ssl_session_cache_init; + + continue; + } + + goto invalid; + } + + if (sscf->shm_zone && sscf->builtin_session_cache == NGX_CONF_UNSET) { + sscf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE; + } + + return NGX_CONF_OK; + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid session cache \"%V\"", &value[i]); + + return NGX_CONF_ERROR; +} + + +static char * +ngx_stream_ssl_ocsp_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_ssl_srv_conf_t *sscf = conf; + + size_t len; + ngx_int_t n; + ngx_str_t *value, name, size; + ngx_uint_t j; + + if (sscf->ocsp_cache_zone != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + value = cf->args->elts; + + if (ngx_strcmp(value[1].data, "off") == 0) { + sscf->ocsp_cache_zone = NULL; + return NGX_CONF_OK; + } + + if (value[1].len <= sizeof("shared:") - 1 + || ngx_strncmp(value[1].data, "shared:", sizeof("shared:") - 1) != 0) + { + goto invalid; + } + + len = 0; + + for (j = sizeof("shared:") - 1; j < value[1].len; j++) { + if (value[1].data[j] == ':') { + break; + } + + len++; + } + + if (len == 0 || j == value[1].len) { + goto invalid; + } + + name.len = len; + name.data = value[1].data + sizeof("shared:") - 1; + + size.len = value[1].len - j - 1; + size.data = name.data + len + 1; + + n = ngx_parse_size(&size); + + if (n == NGX_ERROR) { + goto invalid; + } + + if (n < (ngx_int_t) (8 * ngx_pagesize)) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "OCSP cache \"%V\" is too small", &value[1]); + + return NGX_CONF_ERROR; + } + + sscf->ocsp_cache_zone = ngx_shared_memory_add(cf, &name, n, + &ngx_stream_ssl_module_ctx); + if (sscf->ocsp_cache_zone == NULL) { + return NGX_CONF_ERROR; + } + + sscf->ocsp_cache_zone->init = ngx_ssl_ocsp_cache_init; + + return NGX_CONF_OK; + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid OCSP cache \"%V\"", &value[1]); + + return NGX_CONF_ERROR; +} + + +static char * +ngx_stream_ssl_alpn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ +#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation + + ngx_stream_ssl_srv_conf_t *sscf = conf; + + u_char *p; + size_t len; + ngx_str_t *value; + ngx_uint_t i; + + if (sscf->alpn.len) { + return "is duplicate"; + } + + value = cf->args->elts; + + len = 0; + + for (i = 1; i < cf->args->nelts; i++) { + + if (value[i].len > 255) { + return "protocol too long"; + } + + len += value[i].len + 1; + } + + sscf->alpn.data = ngx_pnalloc(cf->pool, len); + if (sscf->alpn.data == NULL) { + return NGX_CONF_ERROR; + } + + p = sscf->alpn.data; + + for (i = 1; i < cf->args->nelts; i++) { + *p++ = value[i].len; + p = ngx_cpymem(p, value[i].data, value[i].len); + } + + sscf->alpn.len = len; + + return NGX_CONF_OK; + +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the \"ssl_alpn\" directive requires OpenSSL " + "with ALPN support"); + return NGX_CONF_ERROR; +#endif +} + + +static char * +ngx_stream_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data) +{ +#ifndef SSL_CONF_FLAG_FILE + return "is not supported on this platform"; +#else + return NGX_CONF_OK; +#endif +} + + +static ngx_int_t +ngx_stream_ssl_init(ngx_conf_t *cf) +{ + ngx_uint_t a, p, s; + ngx_stream_handler_pt *h; + ngx_stream_conf_addr_t *addr; + ngx_stream_conf_port_t *port; + ngx_stream_ssl_srv_conf_t *sscf; + ngx_stream_core_srv_conf_t **cscfp, *cscf; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + cscfp = cmcf->servers.elts; + + for (s = 0; s < cmcf->servers.nelts; s++) { + + sscf = cscfp[s]->ctx->srv_conf[ngx_stream_ssl_module.ctx_index]; + + if (sscf->ssl.ctx == NULL) { + continue; + } + + cscf = cscfp[s]->ctx->srv_conf[ngx_stream_core_module.ctx_index]; + + if (sscf->stapling) { + if (ngx_ssl_stapling_resolver(cf, &sscf->ssl, cscf->resolver, + cscf->resolver_timeout) + != NGX_OK) + { + return NGX_ERROR; + } + } + + if (sscf->ocsp) { + if (ngx_ssl_ocsp_resolver(cf, &sscf->ssl, cscf->resolver, + cscf->resolver_timeout) + != NGX_OK) + { + return NGX_ERROR; + } + } + } + + h = ngx_array_push(&cmcf->phases[NGX_STREAM_SSL_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_stream_ssl_handler; + + if (cmcf->ports == NULL) { + return NGX_OK; + } + + port = cmcf->ports->elts; + for (p = 0; p < cmcf->ports->nelts; p++) { + + addr = port[p].addrs.elts; + for (a = 0; a < port[p].addrs.nelts; a++) { + + if (!addr[a].opt.ssl) { + continue; + } + + cscf = addr[a].default_server; + sscf = cscf->ctx->srv_conf[ngx_stream_ssl_module.ctx_index]; + + if (sscf->certificates) { + continue; + } + + if (!sscf->reject_handshake) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"ssl_certificate\" is defined for " + "the \"listen ... ssl\" directive in %s:%ui", + cscf->file_name, cscf->line); + return NGX_ERROR; + } + + /* + * if no certificates are defined in the default server, + * check all non-default server blocks + */ + + cscfp = addr[a].servers.elts; + for (s = 0; s < addr[a].servers.nelts; s++) { + + cscf = cscfp[s]; + sscf = cscf->ctx->srv_conf[ngx_stream_ssl_module.ctx_index]; + + if (sscf->certificates || sscf->reject_handshake) { + continue; + } + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"ssl_certificate\" is defined for " + "the \"listen ... ssl\" directive in %s:%ui", + cscf->file_name, cscf->line); + return NGX_ERROR; + } + } + } + + return NGX_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_ssl_module.h b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_ssl_module.h new file mode 100644 index 000000000..6fdd8f88c --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_ssl_module.h @@ -0,0 +1,75 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_STREAM_SSL_H_INCLUDED_ +#define _NGX_STREAM_SSL_H_INCLUDED_ + + +#include +#include +#include + + +typedef struct { + ngx_msec_t handshake_timeout; + + ngx_flag_t prefer_server_ciphers; + ngx_flag_t certificate_compression; + ngx_flag_t reject_handshake; + + ngx_ssl_t ssl; + + ngx_uint_t protocols; + + ngx_uint_t verify; + ngx_uint_t verify_depth; + + ssize_t builtin_session_cache; + + time_t session_timeout; + + ngx_array_t *certificates; + ngx_array_t *certificate_keys; + + ngx_array_t *certificate_values; + ngx_array_t *certificate_key_values; + + ngx_ssl_cache_t *certificate_cache; + + ngx_str_t dhparam; + ngx_str_t ecdh_curve; + ngx_str_t client_certificate; + ngx_str_t trusted_certificate; + ngx_str_t crl; + ngx_str_t alpn; + + ngx_str_t ciphers; + + ngx_array_t *ech_files; + ngx_array_t *passwords; + ngx_array_t *conf_commands; + + ngx_shm_zone_t *shm_zone; + + ngx_flag_t session_tickets; + ngx_array_t *session_ticket_keys; + + ngx_uint_t ocsp; + ngx_str_t ocsp_responder; + ngx_shm_zone_t *ocsp_cache_zone; + + ngx_flag_t stapling; + ngx_flag_t stapling_verify; + ngx_str_t stapling_file; + ngx_str_t stapling_responder; +} ngx_stream_ssl_srv_conf_t; + + +extern ngx_module_t ngx_stream_ssl_module; + + +#endif /* _NGX_STREAM_SSL_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_ssl_preread_module.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_ssl_preread_module.c new file mode 100644 index 000000000..3fc83ff2f --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_ssl_preread_module.c @@ -0,0 +1,715 @@ + +/* + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_flag_t enabled; +} ngx_stream_ssl_preread_srv_conf_t; + + +typedef struct { + size_t left; + size_t size; + size_t ext; + u_char *pos; + u_char *dst; + u_char buf[4]; + u_char version[2]; + ngx_str_t host; + ngx_str_t alpn; + ngx_log_t *log; + ngx_pool_t *pool; + ngx_uint_t state; +} ngx_stream_ssl_preread_ctx_t; + + +static ngx_int_t ngx_stream_ssl_preread_handler(ngx_stream_session_t *s); +static ngx_int_t ngx_stream_ssl_preread_parse_record( + ngx_stream_ssl_preread_ctx_t *ctx, u_char *pos, u_char *last); +static ngx_int_t ngx_stream_ssl_preread_servername(ngx_stream_session_t *s, + ngx_str_t *servername); +static ngx_int_t ngx_stream_ssl_preread_protocol_variable( + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_ssl_preread_server_name_variable( + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_ssl_preread_alpn_protocols_variable( + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_ssl_preread_add_variables(ngx_conf_t *cf); +static void *ngx_stream_ssl_preread_create_srv_conf(ngx_conf_t *cf); +static char *ngx_stream_ssl_preread_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); +static ngx_int_t ngx_stream_ssl_preread_init(ngx_conf_t *cf); + + +static ngx_command_t ngx_stream_ssl_preread_commands[] = { + + { ngx_string("ssl_preread"), + NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_ssl_preread_srv_conf_t, enabled), + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_ssl_preread_module_ctx = { + ngx_stream_ssl_preread_add_variables, /* preconfiguration */ + ngx_stream_ssl_preread_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_stream_ssl_preread_create_srv_conf, /* create server configuration */ + ngx_stream_ssl_preread_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_ssl_preread_module = { + NGX_MODULE_V1, + &ngx_stream_ssl_preread_module_ctx, /* module context */ + ngx_stream_ssl_preread_commands, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_stream_variable_t ngx_stream_ssl_preread_vars[] = { + + { ngx_string("ssl_preread_protocol"), NULL, + ngx_stream_ssl_preread_protocol_variable, 0, 0, 0 }, + + { ngx_string("ssl_preread_server_name"), NULL, + ngx_stream_ssl_preread_server_name_variable, 0, 0, 0 }, + + { ngx_string("ssl_preread_alpn_protocols"), NULL, + ngx_stream_ssl_preread_alpn_protocols_variable, 0, 0, 0 }, + + ngx_stream_null_variable +}; + + +static ngx_int_t +ngx_stream_ssl_preread_handler(ngx_stream_session_t *s) +{ + u_char *last, *p; + size_t len; + ngx_int_t rc; + ngx_connection_t *c; + ngx_stream_ssl_preread_ctx_t *ctx; + ngx_stream_ssl_preread_srv_conf_t *sscf; + + c = s->connection; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, "ssl preread handler"); + + sscf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_preread_module); + + if (!sscf->enabled) { + return NGX_DECLINED; + } + + if (c->type != SOCK_STREAM) { + return NGX_DECLINED; + } + + if (c->buffer == NULL) { + return NGX_AGAIN; + } + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_ssl_preread_module); + if (ctx == NULL) { + ctx = ngx_pcalloc(c->pool, sizeof(ngx_stream_ssl_preread_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_stream_set_ctx(s, ctx, ngx_stream_ssl_preread_module); + + ctx->pool = c->pool; + ctx->log = c->log; + ctx->pos = c->buffer->pos; + } + + p = ctx->pos; + last = c->buffer->last; + + while (last - p >= 5) { + + if ((p[0] & 0x80) && p[2] == 1 && (p[3] == 0 || p[3] == 3)) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: version 2 ClientHello"); + ctx->version[0] = p[3]; + ctx->version[1] = p[4]; + return NGX_OK; + } + + if (p[0] != 0x16) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: not a handshake"); + ngx_stream_set_ctx(s, NULL, ngx_stream_ssl_preread_module); + return NGX_DECLINED; + } + + if (p[1] != 3) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: unsupported SSL version"); + ngx_stream_set_ctx(s, NULL, ngx_stream_ssl_preread_module); + return NGX_DECLINED; + } + + len = (p[3] << 8) + p[4]; + + /* read the whole record before parsing */ + if ((size_t) (last - p) < len + 5) { + break; + } + + p += 5; + + rc = ngx_stream_ssl_preread_parse_record(ctx, p, p + len); + + if (rc == NGX_DECLINED) { + ngx_stream_set_ctx(s, NULL, ngx_stream_ssl_preread_module); + return NGX_DECLINED; + } + + if (rc == NGX_OK) { + return ngx_stream_ssl_preread_servername(s, &ctx->host); + } + + if (rc != NGX_AGAIN) { + return rc; + } + + p += len; + } + + ctx->pos = p; + + return NGX_AGAIN; +} + + +static ngx_int_t +ngx_stream_ssl_preread_parse_record(ngx_stream_ssl_preread_ctx_t *ctx, + u_char *pos, u_char *last) +{ + size_t left, n, size, ext; + u_char *dst, *p; + + enum { + sw_start = 0, + sw_header, /* handshake msg_type, length */ + sw_version, /* client_version */ + sw_random, /* random */ + sw_sid_len, /* session_id length */ + sw_sid, /* session_id */ + sw_cs_len, /* cipher_suites length */ + sw_cs, /* cipher_suites */ + sw_cm_len, /* compression_methods length */ + sw_cm, /* compression_methods */ + sw_ext, /* extension */ + sw_ext_header, /* extension_type, extension_data length */ + sw_sni_len, /* SNI length */ + sw_sni_host_head, /* SNI name_type, host_name length */ + sw_sni_host, /* SNI host_name */ + sw_alpn_len, /* ALPN length */ + sw_alpn_proto_len, /* ALPN protocol_name length */ + sw_alpn_proto_data, /* ALPN protocol_name */ + sw_supver_len /* supported_versions length */ + } state; + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: state %ui left %z", ctx->state, ctx->left); + + state = ctx->state; + size = ctx->size; + left = ctx->left; + ext = ctx->ext; + dst = ctx->dst; + p = ctx->buf; + + for ( ;; ) { + n = ngx_min((size_t) (last - pos), size); + + if (dst) { + dst = ngx_cpymem(dst, pos, n); + } + + pos += n; + size -= n; + left -= n; + + if (size != 0) { + break; + } + + switch (state) { + + case sw_start: + state = sw_header; + dst = p; + size = 4; + left = size; + break; + + case sw_header: + if (p[0] != 1) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: not a client hello"); + return NGX_DECLINED; + } + + state = sw_version; + dst = ctx->version; + size = 2; + left = (p[1] << 16) + (p[2] << 8) + p[3]; + break; + + case sw_version: + state = sw_random; + dst = NULL; + size = 32; + break; + + case sw_random: + state = sw_sid_len; + dst = p; + size = 1; + break; + + case sw_sid_len: + state = sw_sid; + dst = NULL; + size = p[0]; + break; + + case sw_sid: + state = sw_cs_len; + dst = p; + size = 2; + break; + + case sw_cs_len: + state = sw_cs; + dst = NULL; + size = (p[0] << 8) + p[1]; + break; + + case sw_cs: + state = sw_cm_len; + dst = p; + size = 1; + break; + + case sw_cm_len: + state = sw_cm; + dst = NULL; + size = p[0]; + break; + + case sw_cm: + if (left == 0) { + /* no extensions */ + return NGX_OK; + } + + state = sw_ext; + dst = p; + size = 2; + break; + + case sw_ext: + if (left == 0) { + return NGX_OK; + } + + state = sw_ext_header; + dst = p; + size = 4; + break; + + case sw_ext_header: + if (p[0] == 0 && p[1] == 0 && ctx->host.data == NULL) { + /* SNI extension */ + state = sw_sni_len; + dst = p; + size = 2; + break; + } + + if (p[0] == 0 && p[1] == 16 && ctx->alpn.data == NULL) { + /* ALPN extension */ + state = sw_alpn_len; + dst = p; + size = 2; + break; + } + + if (p[0] == 0 && p[1] == 43) { + /* supported_versions extension */ + state = sw_supver_len; + dst = p; + size = 1; + break; + } + + state = sw_ext; + dst = NULL; + size = (p[2] << 8) + p[3]; + break; + + case sw_sni_len: + ext = (p[0] << 8) + p[1]; + state = sw_sni_host_head; + dst = p; + size = 3; + break; + + case sw_sni_host_head: + if (p[0] != 0) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: SNI hostname type is not DNS"); + return NGX_DECLINED; + } + + size = (p[1] << 8) + p[2]; + + if (ext < 3 + size) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: SNI format error"); + return NGX_DECLINED; + } + ext -= 3 + size; + + ctx->host.data = ngx_pnalloc(ctx->pool, size); + if (ctx->host.data == NULL) { + return NGX_ERROR; + } + + state = sw_sni_host; + dst = ctx->host.data; + break; + + case sw_sni_host: + ctx->host.len = (p[1] << 8) + p[2]; + + state = sw_ext; + dst = NULL; + size = ext; + break; + + case sw_alpn_len: + ext = (p[0] << 8) + p[1]; + + ctx->alpn.data = ngx_pnalloc(ctx->pool, ext); + if (ctx->alpn.data == NULL) { + return NGX_ERROR; + } + + state = sw_alpn_proto_len; + dst = p; + size = 1; + break; + + case sw_alpn_proto_len: + size = p[0]; + + if (size == 0) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: ALPN empty protocol"); + return NGX_DECLINED; + } + + if (ext < 1 + size) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: ALPN format error"); + return NGX_DECLINED; + } + ext -= 1 + size; + + state = sw_alpn_proto_data; + dst = ctx->alpn.data + ctx->alpn.len; + break; + + case sw_alpn_proto_data: + ctx->alpn.len += p[0]; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: ALPN protocols \"%V\"", &ctx->alpn); + + if (ext) { + ctx->alpn.data[ctx->alpn.len++] = ','; + + state = sw_alpn_proto_len; + dst = p; + size = 1; + break; + } + + state = sw_ext; + dst = NULL; + size = 0; + break; + + case sw_supver_len: + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: supported_versions"); + + /* set TLSv1.3 */ + ctx->version[0] = 3; + ctx->version[1] = 4; + + state = sw_ext; + dst = NULL; + size = p[0]; + break; + } + + if (left < size) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0, + "ssl preread: failed to parse handshake"); + return NGX_DECLINED; + } + } + + ctx->state = state; + ctx->size = size; + ctx->left = left; + ctx->ext = ext; + ctx->dst = dst; + + return NGX_AGAIN; +} + + +static ngx_int_t +ngx_stream_ssl_preread_servername(ngx_stream_session_t *s, + ngx_str_t *servername) +{ + ngx_int_t rc; + ngx_str_t host; + ngx_connection_t *c; + ngx_stream_core_srv_conf_t *cscf; + + c = s->connection; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "SSL preread server name: \"%V\"", servername); + + if (servername->len == 0) { + return NGX_OK; + } + + host = *servername; + + rc = ngx_stream_validate_host(&host, c->pool, 0); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_DECLINED) { + return NGX_OK; + } + + rc = ngx_stream_find_virtual_server(s, &host, &cscf); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_DECLINED) { + return NGX_OK; + } + + s->srv_conf = cscf->ctx->srv_conf; + + ngx_set_connection_log(c, cscf->error_log); + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_ssl_preread_protocol_variable(ngx_stream_session_t *s, + ngx_variable_value_t *v, uintptr_t data) +{ + ngx_str_t version; + ngx_stream_ssl_preread_ctx_t *ctx; + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_ssl_preread_module); + + if (ctx == NULL) { + v->not_found = 1; + return NGX_OK; + } + + /* SSL_get_version() format */ + + ngx_str_null(&version); + + switch (ctx->version[0]) { + case 0: + switch (ctx->version[1]) { + case 2: + ngx_str_set(&version, "SSLv2"); + break; + } + break; + case 3: + switch (ctx->version[1]) { + case 0: + ngx_str_set(&version, "SSLv3"); + break; + case 1: + ngx_str_set(&version, "TLSv1"); + break; + case 2: + ngx_str_set(&version, "TLSv1.1"); + break; + case 3: + ngx_str_set(&version, "TLSv1.2"); + break; + case 4: + ngx_str_set(&version, "TLSv1.3"); + break; + } + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->len = version.len; + v->data = version.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_ssl_preread_server_name_variable(ngx_stream_session_t *s, + ngx_variable_value_t *v, uintptr_t data) +{ + ngx_stream_ssl_preread_ctx_t *ctx; + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_ssl_preread_module); + + if (ctx == NULL) { + v->not_found = 1; + return NGX_OK; + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->len = ctx->host.len; + v->data = ctx->host.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_ssl_preread_alpn_protocols_variable(ngx_stream_session_t *s, + ngx_variable_value_t *v, uintptr_t data) +{ + ngx_stream_ssl_preread_ctx_t *ctx; + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_ssl_preread_module); + + if (ctx == NULL) { + v->not_found = 1; + return NGX_OK; + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->len = ctx->alpn.len; + v->data = ctx->alpn.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_ssl_preread_add_variables(ngx_conf_t *cf) +{ + ngx_stream_variable_t *var, *v; + + for (v = ngx_stream_ssl_preread_vars; v->name.len; v++) { + var = ngx_stream_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + +static void * +ngx_stream_ssl_preread_create_srv_conf(ngx_conf_t *cf) +{ + ngx_stream_ssl_preread_srv_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_ssl_preread_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + conf->enabled = NGX_CONF_UNSET; + + return conf; +} + + +static char * +ngx_stream_ssl_preread_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_stream_ssl_preread_srv_conf_t *prev = parent; + ngx_stream_ssl_preread_srv_conf_t *conf = child; + + ngx_conf_merge_value(conf->enabled, prev->enabled, 0); + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_stream_ssl_preread_init(ngx_conf_t *cf) +{ + ngx_stream_handler_pt *h; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + h = ngx_array_push(&cmcf->phases[NGX_STREAM_PREREAD_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_stream_ssl_preread_handler; + + return NGX_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream.c new file mode 100644 index 000000000..006672cdf --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream.c @@ -0,0 +1,868 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +static ngx_int_t ngx_stream_upstream_add_variables(ngx_conf_t *cf); +static ngx_int_t ngx_stream_upstream_addr_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_upstream_response_time_variable( + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_upstream_bytes_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); + +static char *ngx_stream_upstream(ngx_conf_t *cf, ngx_command_t *cmd, + void *dummy); +static char *ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +#if (NGX_STREAM_UPSTREAM_ZONE) +static char *ngx_stream_upstream_resolver(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +#endif + +static void *ngx_stream_upstream_create_main_conf(ngx_conf_t *cf); +static char *ngx_stream_upstream_init_main_conf(ngx_conf_t *cf, void *conf); + + +static ngx_command_t ngx_stream_upstream_commands[] = { + + { ngx_string("upstream"), + NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1, + ngx_stream_upstream, + 0, + 0, + NULL }, + + { ngx_string("server"), + NGX_STREAM_UPS_CONF|NGX_CONF_1MORE, + ngx_stream_upstream_server, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + +#if (NGX_STREAM_UPSTREAM_ZONE) + + { ngx_string("resolver"), + NGX_STREAM_UPS_CONF|NGX_CONF_1MORE, + ngx_stream_upstream_resolver, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("resolver_timeout"), + NGX_STREAM_UPS_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_STREAM_SRV_CONF_OFFSET, + offsetof(ngx_stream_upstream_srv_conf_t, resolver_timeout), + NULL }, + +#endif + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_upstream_module_ctx = { + ngx_stream_upstream_add_variables, /* preconfiguration */ + NULL, /* postconfiguration */ + + ngx_stream_upstream_create_main_conf, /* create main configuration */ + ngx_stream_upstream_init_main_conf, /* init main configuration */ + + NULL, /* create server configuration */ + NULL /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_upstream_module = { + NGX_MODULE_V1, + &ngx_stream_upstream_module_ctx, /* module context */ + ngx_stream_upstream_commands, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_stream_variable_t ngx_stream_upstream_vars[] = { + + { ngx_string("upstream_addr"), NULL, + ngx_stream_upstream_addr_variable, 0, + NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + { ngx_string("upstream_bytes_sent"), NULL, + ngx_stream_upstream_bytes_variable, 0, + NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + { ngx_string("upstream_connect_time"), NULL, + ngx_stream_upstream_response_time_variable, 2, + NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + { ngx_string("upstream_first_byte_time"), NULL, + ngx_stream_upstream_response_time_variable, 1, + NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + { ngx_string("upstream_session_time"), NULL, + ngx_stream_upstream_response_time_variable, 0, + NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + { ngx_string("upstream_bytes_received"), NULL, + ngx_stream_upstream_bytes_variable, 1, + NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + ngx_stream_null_variable +}; + + +static ngx_int_t +ngx_stream_upstream_add_variables(ngx_conf_t *cf) +{ + ngx_stream_variable_t *var, *v; + + for (v = ngx_stream_upstream_vars; v->name.len; v++) { + var = ngx_stream_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_upstream_addr_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + u_char *p; + size_t len; + ngx_uint_t i; + ngx_stream_upstream_state_t *state; + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + if (s->upstream_states == NULL || s->upstream_states->nelts == 0) { + v->not_found = 1; + return NGX_OK; + } + + len = 0; + state = s->upstream_states->elts; + + for (i = 0; i < s->upstream_states->nelts; i++) { + if (state[i].peer) { + len += state[i].peer->len; + } + + len += 2; + } + + p = ngx_pnalloc(s->connection->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + v->data = p; + + i = 0; + + for ( ;; ) { + if (state[i].peer) { + p = ngx_cpymem(p, state[i].peer->data, state[i].peer->len); + } + + if (++i == s->upstream_states->nelts) { + break; + } + + *p++ = ','; + *p++ = ' '; + } + + v->len = p - v->data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_upstream_bytes_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + u_char *p; + size_t len; + ngx_uint_t i; + ngx_stream_upstream_state_t *state; + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + if (s->upstream_states == NULL || s->upstream_states->nelts == 0) { + v->not_found = 1; + return NGX_OK; + } + + len = s->upstream_states->nelts * (NGX_OFF_T_LEN + 2); + + p = ngx_pnalloc(s->connection->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + v->data = p; + + i = 0; + state = s->upstream_states->elts; + + for ( ;; ) { + + if (data == 1) { + p = ngx_sprintf(p, "%O", state[i].bytes_received); + + } else { + p = ngx_sprintf(p, "%O", state[i].bytes_sent); + } + + if (++i == s->upstream_states->nelts) { + break; + } + + *p++ = ','; + *p++ = ' '; + } + + v->len = p - v->data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_upstream_response_time_variable(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + u_char *p; + size_t len; + ngx_uint_t i; + ngx_msec_int_t ms; + ngx_stream_upstream_state_t *state; + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + if (s->upstream_states == NULL || s->upstream_states->nelts == 0) { + v->not_found = 1; + return NGX_OK; + } + + len = s->upstream_states->nelts * (NGX_TIME_T_LEN + 4 + 2); + + p = ngx_pnalloc(s->connection->pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + v->data = p; + + i = 0; + state = s->upstream_states->elts; + + for ( ;; ) { + + if (data == 1) { + ms = state[i].first_byte_time; + + } else if (data == 2) { + ms = state[i].connect_time; + + } else { + ms = state[i].response_time; + } + + if (ms != -1) { + ms = ngx_max(ms, 0); + p = ngx_sprintf(p, "%T.%03M", (time_t) ms / 1000, ms % 1000); + + } else { + *p++ = '-'; + } + + if (++i == s->upstream_states->nelts) { + break; + } + + *p++ = ','; + *p++ = ' '; + } + + v->len = p - v->data; + + return NGX_OK; +} + + +static char * +ngx_stream_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) +{ + char *rv; + void *mconf; + ngx_str_t *value; + ngx_url_t u; + ngx_uint_t m; + ngx_conf_t pcf; + ngx_stream_module_t *module; + ngx_stream_conf_ctx_t *ctx, *stream_ctx; + ngx_stream_upstream_srv_conf_t *uscf; + + ngx_memzero(&u, sizeof(ngx_url_t)); + + value = cf->args->elts; + u.host = value[1]; + u.no_resolve = 1; + u.no_port = 1; + + uscf = ngx_stream_upstream_add(cf, &u, NGX_STREAM_UPSTREAM_CREATE + |NGX_STREAM_UPSTREAM_MODIFY + |NGX_STREAM_UPSTREAM_WEIGHT + |NGX_STREAM_UPSTREAM_MAX_CONNS + |NGX_STREAM_UPSTREAM_MAX_FAILS + |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT + |NGX_STREAM_UPSTREAM_DOWN + |NGX_STREAM_UPSTREAM_BACKUP); + if (uscf == NULL) { + return NGX_CONF_ERROR; + } + + + ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_conf_ctx_t)); + if (ctx == NULL) { + return NGX_CONF_ERROR; + } + + stream_ctx = cf->ctx; + ctx->main_conf = stream_ctx->main_conf; + + /* the upstream{}'s srv_conf */ + + ctx->srv_conf = ngx_pcalloc(cf->pool, + sizeof(void *) * ngx_stream_max_module); + if (ctx->srv_conf == NULL) { + return NGX_CONF_ERROR; + } + + ctx->srv_conf[ngx_stream_upstream_module.ctx_index] = uscf; + + uscf->srv_conf = ctx->srv_conf; + + for (m = 0; cf->cycle->modules[m]; m++) { + if (cf->cycle->modules[m]->type != NGX_STREAM_MODULE) { + continue; + } + + module = cf->cycle->modules[m]->ctx; + + if (module->create_srv_conf) { + mconf = module->create_srv_conf(cf); + if (mconf == NULL) { + return NGX_CONF_ERROR; + } + + ctx->srv_conf[cf->cycle->modules[m]->ctx_index] = mconf; + } + } + + uscf->servers = ngx_array_create(cf->pool, 4, + sizeof(ngx_stream_upstream_server_t)); + if (uscf->servers == NULL) { + return NGX_CONF_ERROR; + } + + + /* parse inside upstream{} */ + + pcf = *cf; + cf->ctx = ctx; + cf->cmd_type = NGX_STREAM_UPS_CONF; + + rv = ngx_conf_parse(cf, NULL); + + *cf = pcf; + + if (rv != NGX_CONF_OK) { + return rv; + } + + if (uscf->servers->nelts == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "no servers are inside upstream"); + return NGX_CONF_ERROR; + } + + return rv; +} + + +static char * +ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_upstream_srv_conf_t *uscf = conf; + + time_t fail_timeout; + ngx_str_t *value, s; + ngx_url_t u; + ngx_int_t weight, max_conns, max_fails; + ngx_uint_t i; +#if (NGX_STREAM_UPSTREAM_ZONE) + ngx_uint_t resolve; +#endif + ngx_stream_upstream_server_t *us; + + us = ngx_array_push(uscf->servers); + if (us == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(us, sizeof(ngx_stream_upstream_server_t)); + + value = cf->args->elts; + + weight = 1; + max_conns = 0; + max_fails = 1; + fail_timeout = 10; +#if (NGX_STREAM_UPSTREAM_ZONE) + resolve = 0; +#endif + + for (i = 2; i < cf->args->nelts; i++) { + + if (ngx_strncmp(value[i].data, "weight=", 7) == 0) { + + if (!(uscf->flags & NGX_STREAM_UPSTREAM_WEIGHT)) { + goto not_supported; + } + + weight = ngx_atoi(&value[i].data[7], value[i].len - 7); + + if (weight == NGX_ERROR || weight == 0) { + goto invalid; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "max_conns=", 10) == 0) { + + if (!(uscf->flags & NGX_STREAM_UPSTREAM_MAX_CONNS)) { + goto not_supported; + } + + max_conns = ngx_atoi(&value[i].data[10], value[i].len - 10); + + if (max_conns == NGX_ERROR) { + goto invalid; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "max_fails=", 10) == 0) { + + if (!(uscf->flags & NGX_STREAM_UPSTREAM_MAX_FAILS)) { + goto not_supported; + } + + max_fails = ngx_atoi(&value[i].data[10], value[i].len - 10); + + if (max_fails == NGX_ERROR) { + goto invalid; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "fail_timeout=", 13) == 0) { + + if (!(uscf->flags & NGX_STREAM_UPSTREAM_FAIL_TIMEOUT)) { + goto not_supported; + } + + s.len = value[i].len - 13; + s.data = &value[i].data[13]; + + fail_timeout = ngx_parse_time(&s, 1); + + if (fail_timeout == (time_t) NGX_ERROR) { + goto invalid; + } + + continue; + } + + if (ngx_strcmp(value[i].data, "backup") == 0) { + + if (!(uscf->flags & NGX_STREAM_UPSTREAM_BACKUP)) { + goto not_supported; + } + + us->backup = 1; + + continue; + } + + if (ngx_strcmp(value[i].data, "down") == 0) { + + if (!(uscf->flags & NGX_STREAM_UPSTREAM_DOWN)) { + goto not_supported; + } + + us->down = NGX_STREAM_UPSTREAM_FAILED; + + continue; + } + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (ngx_strcmp(value[i].data, "resolve") == 0) { + resolve = 1; + continue; + } + + if (ngx_strncmp(value[i].data, "service=", 8) == 0) { + + us->service.len = value[i].len - 8; + us->service.data = &value[i].data[8]; + + if (us->service.len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "service is empty"); + return NGX_CONF_ERROR; + } + + continue; + } +#endif + + goto invalid; + } + + ngx_memzero(&u, sizeof(ngx_url_t)); + + u.url = value[1]; + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (resolve) { + /* resolve at run time */ + u.no_resolve = 1; + } + + if (us->service.len && !resolve) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "service upstream \"%V\" requires " + "\"resolve\" parameter", + &u.url); + return NGX_CONF_ERROR; + } + +#endif + + if (ngx_parse_url(cf->pool, &u) != NGX_OK) { + if (u.err) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "%s in upstream \"%V\"", u.err, &u.url); + } + + return NGX_CONF_ERROR; + } + + if (u.no_port +#if (NGX_STREAM_UPSTREAM_ZONE) + && us->service.len == 0 +#endif + ) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "no port in upstream \"%V\"", &u.url); + return NGX_CONF_ERROR; + } + + us->name = u.url; + +#if (NGX_STREAM_UPSTREAM_ZONE) + + if (us->service.len && !u.no_port) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "service upstream \"%V\" may not have port", + &us->name); + + return NGX_CONF_ERROR; + } + + if (us->service.len && u.naddrs) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "service upstream \"%V\" requires domain name", + &us->name); + + return NGX_CONF_ERROR; + } + + if (resolve && u.naddrs == 0) { + ngx_addr_t *addr; + + /* save port */ + + addr = ngx_pcalloc(cf->pool, sizeof(ngx_addr_t)); + if (addr == NULL) { + return NGX_CONF_ERROR; + } + + addr->sockaddr = ngx_palloc(cf->pool, u.socklen); + if (addr->sockaddr == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memcpy(addr->sockaddr, &u.sockaddr, u.socklen); + + addr->socklen = u.socklen; + + us->addrs = addr; + us->naddrs = 1; + + us->host = u.host; + + } else { + us->addrs = u.addrs; + us->naddrs = u.naddrs; + } + +#else + + us->addrs = u.addrs; + us->naddrs = u.naddrs; + +#endif + + us->weight = weight; + us->max_conns = max_conns; + us->max_fails = max_fails; + us->fail_timeout = fail_timeout; + + return NGX_CONF_OK; + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[i]); + + return NGX_CONF_ERROR; + +not_supported: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "balancing method does not support parameter \"%V\"", + &value[i]); + + return NGX_CONF_ERROR; +} + + +#if (NGX_STREAM_UPSTREAM_ZONE) + +static char * +ngx_stream_upstream_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_upstream_srv_conf_t *uscf = conf; + + ngx_str_t *value; + + if (uscf->resolver) { + return "is duplicate"; + } + + value = cf->args->elts; + + uscf->resolver = ngx_resolver_create(cf, &value[1], cf->args->nelts - 1); + if (uscf->resolver == NULL) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + +#endif + + +ngx_stream_upstream_srv_conf_t * +ngx_stream_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags) +{ + ngx_uint_t i; + ngx_stream_upstream_server_t *us; + ngx_stream_upstream_srv_conf_t *uscf, **uscfp; + ngx_stream_upstream_main_conf_t *umcf; + + if (!(flags & NGX_STREAM_UPSTREAM_CREATE)) { + + if (ngx_parse_url(cf->pool, u) != NGX_OK) { + if (u->err) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "%s in upstream \"%V\"", u->err, &u->url); + } + + return NULL; + } + } + + umcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_upstream_module); + + uscfp = umcf->upstreams.elts; + + for (i = 0; i < umcf->upstreams.nelts; i++) { + + if (uscfp[i]->host.len != u->host.len + || ngx_strncasecmp(uscfp[i]->host.data, u->host.data, u->host.len) + != 0) + { + continue; + } + + if ((flags & NGX_STREAM_UPSTREAM_CREATE) + && (uscfp[i]->flags & NGX_STREAM_UPSTREAM_CREATE)) + { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate upstream \"%V\"", &u->host); + return NULL; + } + + if ((uscfp[i]->flags & NGX_STREAM_UPSTREAM_CREATE) && !u->no_port) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "upstream \"%V\" may not have port %d", + &u->host, u->port); + return NULL; + } + + if ((flags & NGX_STREAM_UPSTREAM_CREATE) && !uscfp[i]->no_port) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "upstream \"%V\" may not have port %d in %s:%ui", + &u->host, uscfp[i]->port, + uscfp[i]->file_name, uscfp[i]->line); + return NULL; + } + + if (uscfp[i]->port != u->port) { + continue; + } + + if (flags & NGX_STREAM_UPSTREAM_CREATE) { + uscfp[i]->flags = flags; + } + + return uscfp[i]; + } + + uscf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_srv_conf_t)); + if (uscf == NULL) { + return NULL; + } + + uscf->flags = flags; + uscf->host = u->host; + uscf->file_name = cf->conf_file->file.name.data; + uscf->line = cf->conf_file->line; + uscf->port = u->port; + uscf->no_port = u->no_port; +#if (NGX_STREAM_UPSTREAM_ZONE) + uscf->resolver_timeout = NGX_CONF_UNSET_MSEC; +#endif + + if (u->naddrs == 1 && (u->port || u->family == AF_UNIX)) { + uscf->servers = ngx_array_create(cf->pool, 1, + sizeof(ngx_stream_upstream_server_t)); + if (uscf->servers == NULL) { + return NULL; + } + + us = ngx_array_push(uscf->servers); + if (us == NULL) { + return NULL; + } + + ngx_memzero(us, sizeof(ngx_stream_upstream_server_t)); + + us->addrs = u->addrs; + us->naddrs = 1; + } + + uscfp = ngx_array_push(&umcf->upstreams); + if (uscfp == NULL) { + return NULL; + } + + *uscfp = uscf; + + return uscf; +} + + +static void * +ngx_stream_upstream_create_main_conf(ngx_conf_t *cf) +{ + ngx_stream_upstream_main_conf_t *umcf; + + umcf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_main_conf_t)); + if (umcf == NULL) { + return NULL; + } + + if (ngx_array_init(&umcf->upstreams, cf->pool, 4, + sizeof(ngx_stream_upstream_srv_conf_t *)) + != NGX_OK) + { + return NULL; + } + + return umcf; +} + + +static char * +ngx_stream_upstream_init_main_conf(ngx_conf_t *cf, void *conf) +{ + ngx_stream_upstream_main_conf_t *umcf = conf; + + ngx_uint_t i; + ngx_stream_upstream_init_pt init; + ngx_stream_upstream_srv_conf_t **uscfp; + + uscfp = umcf->upstreams.elts; + + for (i = 0; i < umcf->upstreams.nelts; i++) { + + init = uscfp[i]->peer.init_upstream + ? uscfp[i]->peer.init_upstream + : ngx_stream_upstream_init_round_robin; + + if (init(cf, uscfp[i]) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + return NGX_CONF_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream.h b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream.h new file mode 100644 index 000000000..c581aa0be --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream.h @@ -0,0 +1,165 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_STREAM_UPSTREAM_H_INCLUDED_ +#define _NGX_STREAM_UPSTREAM_H_INCLUDED_ + + +#include +#include +#include +#include + + +#define NGX_STREAM_UPSTREAM_CREATE 0x0001 +#define NGX_STREAM_UPSTREAM_WEIGHT 0x0002 +#define NGX_STREAM_UPSTREAM_MAX_FAILS 0x0004 +#define NGX_STREAM_UPSTREAM_FAIL_TIMEOUT 0x0008 +#define NGX_STREAM_UPSTREAM_DOWN 0x0010 +#define NGX_STREAM_UPSTREAM_BACKUP 0x0020 +#define NGX_STREAM_UPSTREAM_MODIFY 0x0040 +#define NGX_STREAM_UPSTREAM_MAX_CONNS 0x0100 + + +#define NGX_STREAM_UPSTREAM_NOTIFY_CONNECT 0x1 + + +typedef struct { + ngx_array_t upstreams; + /* ngx_stream_upstream_srv_conf_t */ +} ngx_stream_upstream_main_conf_t; + + +typedef struct ngx_stream_upstream_srv_conf_s ngx_stream_upstream_srv_conf_t; + + +typedef ngx_int_t (*ngx_stream_upstream_init_pt)(ngx_conf_t *cf, + ngx_stream_upstream_srv_conf_t *us); +typedef ngx_int_t (*ngx_stream_upstream_init_peer_pt)(ngx_stream_session_t *s, + ngx_stream_upstream_srv_conf_t *us); + + +typedef struct { + ngx_stream_upstream_init_pt init_upstream; + ngx_stream_upstream_init_peer_pt init; + void *data; +} ngx_stream_upstream_peer_t; + + +typedef struct { + ngx_str_t name; + ngx_addr_t *addrs; + ngx_uint_t naddrs; + ngx_uint_t weight; + ngx_uint_t max_conns; + ngx_uint_t max_fails; + time_t fail_timeout; + ngx_msec_t slow_start; + ngx_uint_t down; + + unsigned backup:1; + +#if (NGX_STREAM_UPSTREAM_ZONE) + ngx_str_t host; + ngx_str_t service; +#endif +} ngx_stream_upstream_server_t; + + +struct ngx_stream_upstream_srv_conf_s { + ngx_stream_upstream_peer_t peer; + void **srv_conf; + + ngx_array_t *servers; + /* ngx_stream_upstream_server_t */ + + ngx_uint_t flags; + ngx_str_t host; + u_char *file_name; + ngx_uint_t line; + in_port_t port; + ngx_uint_t no_port; /* unsigned no_port:1 */ + +#if (NGX_STREAM_UPSTREAM_ZONE) + ngx_shm_zone_t *shm_zone; + ngx_resolver_t *resolver; + ngx_msec_t resolver_timeout; +#endif +}; + + +typedef struct { + ngx_msec_t response_time; + ngx_msec_t connect_time; + ngx_msec_t first_byte_time; + off_t bytes_sent; + off_t bytes_received; + + ngx_str_t *peer; +} ngx_stream_upstream_state_t; + + +typedef struct { + ngx_str_t host; + in_port_t port; + ngx_uint_t no_port; /* unsigned no_port:1 */ + + ngx_uint_t naddrs; + ngx_resolver_addr_t *addrs; + + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_str_t name; + + ngx_resolver_ctx_t *ctx; +} ngx_stream_upstream_resolved_t; + + +typedef struct { + ngx_peer_connection_t peer; + + ngx_buf_t downstream_buf; + ngx_buf_t upstream_buf; + + ngx_chain_t *free; + ngx_chain_t *upstream_out; + ngx_chain_t *upstream_busy; + ngx_chain_t *downstream_out; + ngx_chain_t *downstream_busy; + + off_t received; + time_t start_sec; + ngx_uint_t requests; + ngx_uint_t responses; + ngx_msec_t start_time; + + size_t upload_rate; + size_t download_rate; + + ngx_str_t ssl_name; + + ngx_stream_upstream_srv_conf_t *upstream; + ngx_stream_upstream_resolved_t *resolved; + ngx_stream_upstream_state_t *state; + unsigned connected:1; + unsigned proxy_protocol:1; + unsigned half_closed:1; +} ngx_stream_upstream_t; + + +ngx_stream_upstream_srv_conf_t *ngx_stream_upstream_add(ngx_conf_t *cf, + ngx_url_t *u, ngx_uint_t flags); + + +#define ngx_stream_conf_upstream_srv_conf(uscf, module) \ + uscf->srv_conf[module.ctx_index] + + +extern ngx_module_t ngx_stream_upstream_module; + + +#endif /* _NGX_STREAM_UPSTREAM_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream_hash_module.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream_hash_module.c new file mode 100644 index 000000000..20fca6c0d --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream_hash_module.c @@ -0,0 +1,755 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + uint32_t hash; + ngx_str_t *server; +} ngx_stream_upstream_chash_point_t; + + +typedef struct { + ngx_uint_t number; + ngx_stream_upstream_chash_point_t point[1]; +} ngx_stream_upstream_chash_points_t; + + +typedef struct { +#if (NGX_STREAM_UPSTREAM_ZONE) + ngx_uint_t config; +#endif + ngx_stream_complex_value_t key; + ngx_stream_upstream_chash_points_t *points; +} ngx_stream_upstream_hash_srv_conf_t; + + +typedef struct { + /* the round robin data must be first */ + ngx_stream_upstream_rr_peer_data_t rrp; + ngx_stream_upstream_hash_srv_conf_t *conf; + ngx_str_t key; + ngx_uint_t tries; + ngx_uint_t rehash; + uint32_t hash; + ngx_event_get_peer_pt get_rr_peer; +} ngx_stream_upstream_hash_peer_data_t; + + +static ngx_int_t ngx_stream_upstream_init_hash(ngx_conf_t *cf, + ngx_stream_upstream_srv_conf_t *us); +static ngx_int_t ngx_stream_upstream_init_hash_peer(ngx_stream_session_t *s, + ngx_stream_upstream_srv_conf_t *us); +static ngx_int_t ngx_stream_upstream_get_hash_peer(ngx_peer_connection_t *pc, + void *data); + +static ngx_int_t ngx_stream_upstream_init_chash(ngx_conf_t *cf, + ngx_stream_upstream_srv_conf_t *us); +static ngx_int_t ngx_stream_upstream_update_chash(ngx_pool_t *pool, + ngx_stream_upstream_srv_conf_t *us); +static int ngx_libc_cdecl + ngx_stream_upstream_chash_cmp_points(const void *one, const void *two); +static ngx_uint_t ngx_stream_upstream_find_chash_point( + ngx_stream_upstream_chash_points_t *points, uint32_t hash); +static ngx_int_t ngx_stream_upstream_init_chash_peer(ngx_stream_session_t *s, + ngx_stream_upstream_srv_conf_t *us); +static ngx_int_t ngx_stream_upstream_get_chash_peer(ngx_peer_connection_t *pc, + void *data); + +static void *ngx_stream_upstream_hash_create_conf(ngx_conf_t *cf); +static char *ngx_stream_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + + +static ngx_command_t ngx_stream_upstream_hash_commands[] = { + + { ngx_string("hash"), + NGX_STREAM_UPS_CONF|NGX_CONF_TAKE12, + ngx_stream_upstream_hash, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_upstream_hash_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_stream_upstream_hash_create_conf, /* create server configuration */ + NULL /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_upstream_hash_module = { + NGX_MODULE_V1, + &ngx_stream_upstream_hash_module_ctx, /* module context */ + ngx_stream_upstream_hash_commands, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_int_t +ngx_stream_upstream_init_hash(ngx_conf_t *cf, + ngx_stream_upstream_srv_conf_t *us) +{ + if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) { + return NGX_ERROR; + } + + us->peer.init = ngx_stream_upstream_init_hash_peer; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_upstream_init_hash_peer(ngx_stream_session_t *s, + ngx_stream_upstream_srv_conf_t *us) +{ + ngx_stream_upstream_hash_srv_conf_t *hcf; + ngx_stream_upstream_hash_peer_data_t *hp; + + hp = ngx_palloc(s->connection->pool, + sizeof(ngx_stream_upstream_hash_peer_data_t)); + if (hp == NULL) { + return NGX_ERROR; + } + + s->upstream->peer.data = &hp->rrp; + + if (ngx_stream_upstream_init_round_robin_peer(s, us) != NGX_OK) { + return NGX_ERROR; + } + + s->upstream->peer.get = ngx_stream_upstream_get_hash_peer; + + hcf = ngx_stream_conf_upstream_srv_conf(us, + ngx_stream_upstream_hash_module); + + if (ngx_stream_complex_value(s, &hcf->key, &hp->key) != NGX_OK) { + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "upstream hash key:\"%V\"", &hp->key); + + hp->conf = hcf; + hp->tries = 0; + hp->rehash = 0; + hp->hash = 0; + hp->get_rr_peer = ngx_stream_upstream_get_round_robin_peer; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data) +{ + ngx_stream_upstream_hash_peer_data_t *hp = data; + + time_t now; + u_char buf[NGX_INT_T_LEN]; + size_t size; + uint32_t hash; + ngx_int_t w; + uintptr_t m; + ngx_uint_t n, p; + ngx_stream_upstream_rr_peer_t *peer; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "get hash peer, try: %ui", pc->tries); + + ngx_stream_upstream_rr_peers_rlock(hp->rrp.peers); + + if (hp->tries > 20 || hp->rrp.peers->number < 2 || hp->key.len == 0) { + ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); + return hp->get_rr_peer(pc, &hp->rrp); + } + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (hp->rrp.peers->config && hp->rrp.config != *hp->rrp.peers->config) { + ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); + return hp->get_rr_peer(pc, &hp->rrp); + } +#endif + + now = ngx_time(); + + pc->connection = NULL; + + for ( ;; ) { + + /* + * Hash expression is compatible with Cache::Memcached: + * ((crc32([REHASH] KEY) >> 16) & 0x7fff) + PREV_HASH + * with REHASH omitted at the first iteration. + */ + + ngx_crc32_init(hash); + + if (hp->rehash > 0) { + size = ngx_sprintf(buf, "%ui", hp->rehash) - buf; + ngx_crc32_update(&hash, buf, size); + } + + ngx_crc32_update(&hash, hp->key.data, hp->key.len); + ngx_crc32_final(hash); + + hash = (hash >> 16) & 0x7fff; + + hp->hash += hash; + hp->rehash++; + + w = hp->hash % hp->rrp.peers->total_weight; + peer = hp->rrp.peers->peer; + p = 0; + + while (w >= peer->weight) { + w -= peer->weight; + peer = peer->next; + p++; + } + + n = p / (8 * sizeof(uintptr_t)); + m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t)); + + if (hp->rrp.tried[n] & m) { + goto next; + } + + ngx_stream_upstream_rr_peer_lock(hp->rrp.peers, peer); + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "get hash peer, value:%uD, peer:%ui", hp->hash, p); + + if (peer->down) { + ngx_stream_upstream_rr_peer_unlock(hp->rrp.peers, peer); + goto next; + } + + if (peer->max_fails + && peer->fails >= peer->max_fails + && now - peer->checked <= peer->fail_timeout) + { + ngx_stream_upstream_rr_peer_unlock(hp->rrp.peers, peer); + goto next; + } + + if (peer->max_conns && peer->conns >= peer->max_conns) { + ngx_stream_upstream_rr_peer_unlock(hp->rrp.peers, peer); + goto next; + } + + break; + + next: + + if (++hp->tries > 20) { + ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); + return hp->get_rr_peer(pc, &hp->rrp); + } + } + + hp->rrp.current = peer; + ngx_stream_upstream_rr_peer_ref(hp->rrp.peers, peer); + + pc->sockaddr = peer->sockaddr; + pc->socklen = peer->socklen; + pc->name = &peer->name; + + peer->conns++; + + if (now - peer->checked > peer->fail_timeout) { + peer->checked = now; + } + + ngx_stream_upstream_rr_peer_unlock(hp->rrp.peers, peer); + ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); + + hp->rrp.tried[n] |= m; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_upstream_init_chash(ngx_conf_t *cf, + ngx_stream_upstream_srv_conf_t *us) +{ + if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) { + return NGX_ERROR; + } + + us->peer.init = ngx_stream_upstream_init_chash_peer; + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (us->shm_zone) { + return NGX_OK; + } +#endif + + return ngx_stream_upstream_update_chash(cf->pool, us); +} + + +static ngx_int_t +ngx_stream_upstream_update_chash(ngx_pool_t *pool, + ngx_stream_upstream_srv_conf_t *us) +{ + u_char *host, *port, c; + size_t host_len, port_len, size; + uint32_t hash, base_hash; + ngx_str_t *server; + ngx_uint_t npoints, i, j; + ngx_stream_upstream_rr_peer_t *peer; + ngx_stream_upstream_rr_peers_t *peers; + ngx_stream_upstream_chash_points_t *points; + ngx_stream_upstream_hash_srv_conf_t *hcf; + union { + uint32_t value; + u_char byte[4]; + } prev_hash; + + hcf = ngx_stream_conf_upstream_srv_conf(us, + ngx_stream_upstream_hash_module); + + if (hcf->points) { + ngx_free(hcf->points); + hcf->points = NULL; + } + + peers = us->peer.data; + npoints = peers->total_weight * 160; + + size = sizeof(ngx_stream_upstream_chash_points_t) + - sizeof(ngx_stream_upstream_chash_point_t) + + sizeof(ngx_stream_upstream_chash_point_t) * npoints; + + points = pool ? ngx_palloc(pool, size) : ngx_alloc(size, ngx_cycle->log); + if (points == NULL) { + return NGX_ERROR; + } + + points->number = 0; + + if (npoints == 0) { + hcf->points = points; + return NGX_OK; + } + + for (peer = peers->peer; peer; peer = peer->next) { + server = &peer->server; + + /* + * Hash expression is compatible with Cache::Memcached::Fast: + * crc32(HOST \0 PORT PREV_HASH). + */ + + if (server->len >= 5 + && ngx_strncasecmp(server->data, (u_char *) "unix:", 5) == 0) + { + host = server->data + 5; + host_len = server->len - 5; + port = NULL; + port_len = 0; + goto done; + } + + for (j = 0; j < server->len; j++) { + c = server->data[server->len - j - 1]; + + if (c == ':') { + host = server->data; + host_len = server->len - j - 1; + port = server->data + server->len - j; + port_len = j; + goto done; + } + + if (c < '0' || c > '9') { + break; + } + } + + host = server->data; + host_len = server->len; + port = NULL; + port_len = 0; + + done: + + ngx_crc32_init(base_hash); + ngx_crc32_update(&base_hash, host, host_len); + ngx_crc32_update(&base_hash, (u_char *) "", 1); + ngx_crc32_update(&base_hash, port, port_len); + + prev_hash.value = 0; + npoints = peer->weight * 160; + + for (j = 0; j < npoints; j++) { + hash = base_hash; + + ngx_crc32_update(&hash, prev_hash.byte, 4); + ngx_crc32_final(hash); + + points->point[points->number].hash = hash; + points->point[points->number].server = server; + points->number++; + +#if (NGX_HAVE_LITTLE_ENDIAN) + prev_hash.value = hash; +#else + prev_hash.byte[0] = (u_char) (hash & 0xff); + prev_hash.byte[1] = (u_char) ((hash >> 8) & 0xff); + prev_hash.byte[2] = (u_char) ((hash >> 16) & 0xff); + prev_hash.byte[3] = (u_char) ((hash >> 24) & 0xff); +#endif + } + } + + ngx_qsort(points->point, + points->number, + sizeof(ngx_stream_upstream_chash_point_t), + ngx_stream_upstream_chash_cmp_points); + + for (i = 0, j = 1; j < points->number; j++) { + if (points->point[i].hash != points->point[j].hash) { + points->point[++i] = points->point[j]; + } + } + + points->number = i + 1; + + hcf->points = points; + + return NGX_OK; +} + + +static int ngx_libc_cdecl +ngx_stream_upstream_chash_cmp_points(const void *one, const void *two) +{ + ngx_stream_upstream_chash_point_t *first = + (ngx_stream_upstream_chash_point_t *) one; + ngx_stream_upstream_chash_point_t *second = + (ngx_stream_upstream_chash_point_t *) two; + + if (first->hash < second->hash) { + return -1; + + } else if (first->hash > second->hash) { + return 1; + + } else { + return 0; + } +} + + +static ngx_uint_t +ngx_stream_upstream_find_chash_point(ngx_stream_upstream_chash_points_t *points, + uint32_t hash) +{ + ngx_uint_t i, j, k; + ngx_stream_upstream_chash_point_t *point; + + /* find first point >= hash */ + + point = &points->point[0]; + + i = 0; + j = points->number; + + while (i < j) { + k = (i + j) / 2; + + if (hash > point[k].hash) { + i = k + 1; + + } else if (hash < point[k].hash) { + j = k; + + } else { + return k; + } + } + + return i; +} + + +static ngx_int_t +ngx_stream_upstream_init_chash_peer(ngx_stream_session_t *s, + ngx_stream_upstream_srv_conf_t *us) +{ + uint32_t hash; + ngx_stream_upstream_hash_srv_conf_t *hcf; + ngx_stream_upstream_hash_peer_data_t *hp; + + if (ngx_stream_upstream_init_hash_peer(s, us) != NGX_OK) { + return NGX_ERROR; + } + + s->upstream->peer.get = ngx_stream_upstream_get_chash_peer; + + hp = s->upstream->peer.data; + hcf = ngx_stream_conf_upstream_srv_conf(us, + ngx_stream_upstream_hash_module); + + hash = ngx_crc32_long(hp->key.data, hp->key.len); + + ngx_stream_upstream_rr_peers_rlock(hp->rrp.peers); + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (hp->rrp.peers->config + && (hcf->points == NULL || hcf->config != *hp->rrp.peers->config)) + { + if (ngx_stream_upstream_update_chash(NULL, us) != NGX_OK) { + ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); + return NGX_ERROR; + } + + hcf->config = *hp->rrp.peers->config; + } +#endif + + if (hcf->points->number) { + hp->hash = ngx_stream_upstream_find_chash_point(hcf->points, hash); + } + + ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data) +{ + ngx_stream_upstream_hash_peer_data_t *hp = data; + + time_t now; + intptr_t m; + ngx_str_t *server; + ngx_int_t total; + ngx_uint_t i, n, best_i; + ngx_stream_upstream_rr_peer_t *peer, *best; + ngx_stream_upstream_chash_point_t *point; + ngx_stream_upstream_chash_points_t *points; + ngx_stream_upstream_hash_srv_conf_t *hcf; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "get consistent hash peer, try: %ui", pc->tries); + + ngx_stream_upstream_rr_peers_wlock(hp->rrp.peers); + + if (hp->tries > 20 || hp->rrp.peers->single || hp->key.len == 0) { + ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); + return hp->get_rr_peer(pc, &hp->rrp); + } + + pc->connection = NULL; + + if (hp->rrp.peers->number == 0) { + pc->name = hp->rrp.peers->name; + ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); + return NGX_BUSY; + } + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (hp->rrp.peers->config && hp->rrp.config != *hp->rrp.peers->config) { + pc->name = hp->rrp.peers->name; + ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); + return NGX_BUSY; + } +#endif + + now = ngx_time(); + hcf = hp->conf; + + points = hcf->points; + point = &points->point[0]; + + for ( ;; ) { + server = point[hp->hash % points->number].server; + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "consistent hash peer:%uD, server:\"%V\"", + hp->hash, server); + + best = NULL; + best_i = 0; + total = 0; + + for (peer = hp->rrp.peers->peer, i = 0; + peer; + peer = peer->next, i++) + { + n = i / (8 * sizeof(uintptr_t)); + m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); + + if (hp->rrp.tried[n] & m) { + continue; + } + + if (peer->down) { + continue; + } + + if (peer->max_fails + && peer->fails >= peer->max_fails + && now - peer->checked <= peer->fail_timeout) + { + continue; + } + + if (peer->max_conns && peer->conns >= peer->max_conns) { + continue; + } + + if (peer->server.len != server->len + || ngx_strncmp(peer->server.data, server->data, server->len) + != 0) + { + continue; + } + + peer->current_weight += peer->effective_weight; + total += peer->effective_weight; + + if (peer->effective_weight < peer->weight) { + peer->effective_weight++; + } + + if (best == NULL || peer->current_weight > best->current_weight) { + best = peer; + best_i = i; + } + } + + if (best) { + best->current_weight -= total; + break; + } + + hp->hash++; + hp->tries++; + + if (hp->tries > 20) { + ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); + return hp->get_rr_peer(pc, &hp->rrp); + } + } + + hp->rrp.current = best; + ngx_stream_upstream_rr_peer_ref(hp->rrp.peers, best); + + pc->sockaddr = best->sockaddr; + pc->socklen = best->socklen; + pc->name = &best->name; + + best->conns++; + + if (now - best->checked > best->fail_timeout) { + best->checked = now; + } + + ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers); + + n = best_i / (8 * sizeof(uintptr_t)); + m = (uintptr_t) 1 << best_i % (8 * sizeof(uintptr_t)); + + hp->rrp.tried[n] |= m; + + return NGX_OK; +} + + +static void * +ngx_stream_upstream_hash_create_conf(ngx_conf_t *cf) +{ + ngx_stream_upstream_hash_srv_conf_t *conf; + + conf = ngx_palloc(cf->pool, sizeof(ngx_stream_upstream_hash_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + conf->points = NULL; + + return conf; +} + + +static char * +ngx_stream_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_upstream_hash_srv_conf_t *hcf = conf; + + ngx_str_t *value; + ngx_stream_upstream_srv_conf_t *uscf; + ngx_stream_compile_complex_value_t ccv; + + value = cf->args->elts; + + ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); + + ccv.cf = cf; + ccv.value = &value[1]; + ccv.complex_value = &hcf->key; + + if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { + return NGX_CONF_ERROR; + } + + uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module); + + if (uscf->peer.init_upstream) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "load balancing method redefined"); + } + + uscf->flags = NGX_STREAM_UPSTREAM_CREATE + |NGX_STREAM_UPSTREAM_MODIFY + |NGX_STREAM_UPSTREAM_WEIGHT + |NGX_STREAM_UPSTREAM_MAX_CONNS + |NGX_STREAM_UPSTREAM_MAX_FAILS + |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT + |NGX_STREAM_UPSTREAM_DOWN; + + if (cf->args->nelts == 2) { + uscf->peer.init_upstream = ngx_stream_upstream_init_hash; + + } else if (ngx_strcmp(value[2].data, "consistent") == 0) { + uscf->peer.init_upstream = ngx_stream_upstream_init_chash; + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream_least_conn_module.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream_least_conn_module.c new file mode 100644 index 000000000..6e8360440 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream_least_conn_module.c @@ -0,0 +1,322 @@ + +/* + * Copyright (C) Maxim Dounin + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +static ngx_int_t ngx_stream_upstream_init_least_conn_peer( + ngx_stream_session_t *s, ngx_stream_upstream_srv_conf_t *us); +static ngx_int_t ngx_stream_upstream_get_least_conn_peer( + ngx_peer_connection_t *pc, void *data); +static char *ngx_stream_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + + +static ngx_command_t ngx_stream_upstream_least_conn_commands[] = { + + { ngx_string("least_conn"), + NGX_STREAM_UPS_CONF|NGX_CONF_NOARGS, + ngx_stream_upstream_least_conn, + 0, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_upstream_least_conn_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_upstream_least_conn_module = { + NGX_MODULE_V1, + &ngx_stream_upstream_least_conn_module_ctx, /* module context */ + ngx_stream_upstream_least_conn_commands, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_int_t +ngx_stream_upstream_init_least_conn(ngx_conf_t *cf, + ngx_stream_upstream_srv_conf_t *us) +{ + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, cf->log, 0, + "init least conn"); + + if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) { + return NGX_ERROR; + } + + us->peer.init = ngx_stream_upstream_init_least_conn_peer; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_upstream_init_least_conn_peer(ngx_stream_session_t *s, + ngx_stream_upstream_srv_conf_t *us) +{ + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "init least conn peer"); + + if (ngx_stream_upstream_init_round_robin_peer(s, us) != NGX_OK) { + return NGX_ERROR; + } + + s->upstream->peer.get = ngx_stream_upstream_get_least_conn_peer; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data) +{ + ngx_stream_upstream_rr_peer_data_t *rrp = data; + + time_t now; + uintptr_t m; + ngx_int_t rc, total; + ngx_uint_t i, n, p, many; + ngx_stream_upstream_rr_peer_t *peer, *best; + ngx_stream_upstream_rr_peers_t *peers; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "get least conn peer, try: %ui", pc->tries); + + if (rrp->peers->single) { + return ngx_stream_upstream_get_round_robin_peer(pc, rrp); + } + + pc->connection = NULL; + + now = ngx_time(); + + peers = rrp->peers; + + ngx_stream_upstream_rr_peers_wlock(peers); + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (peers->config && rrp->config != *peers->config) { + goto busy; + } +#endif + + best = NULL; + total = 0; + +#if (NGX_SUPPRESS_WARN) + many = 0; + p = 0; +#endif + + for (peer = peers->peer, i = 0; + peer; + peer = peer->next, i++) + { + n = i / (8 * sizeof(uintptr_t)); + m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); + + if (rrp->tried[n] & m) { + continue; + } + + if (peer->down) { + continue; + } + + if (peer->max_fails + && peer->fails >= peer->max_fails + && now - peer->checked <= peer->fail_timeout) + { + continue; + } + + if (peer->max_conns && peer->conns >= peer->max_conns) { + continue; + } + + /* + * select peer with least number of connections; if there are + * multiple peers with the same number of connections, select + * based on round-robin + */ + + if (best == NULL + || peer->conns * best->weight < best->conns * peer->weight) + { + best = peer; + many = 0; + p = i; + + } else if (peer->conns * best->weight == best->conns * peer->weight) { + many = 1; + } + } + + if (best == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "get least conn peer, no peer found"); + + goto failed; + } + + if (many) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "get least conn peer, many"); + + for (peer = best, i = p; + peer; + peer = peer->next, i++) + { + n = i / (8 * sizeof(uintptr_t)); + m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); + + if (rrp->tried[n] & m) { + continue; + } + + if (peer->down) { + continue; + } + + if (peer->conns * best->weight != best->conns * peer->weight) { + continue; + } + + if (peer->max_fails + && peer->fails >= peer->max_fails + && now - peer->checked <= peer->fail_timeout) + { + continue; + } + + if (peer->max_conns && peer->conns >= peer->max_conns) { + continue; + } + + peer->current_weight += peer->effective_weight; + total += peer->effective_weight; + + if (peer->effective_weight < peer->weight) { + peer->effective_weight++; + } + + if (peer->current_weight > best->current_weight) { + best = peer; + p = i; + } + } + } + + best->current_weight -= total; + + if (now - best->checked > best->fail_timeout) { + best->checked = now; + } + + pc->sockaddr = best->sockaddr; + pc->socklen = best->socklen; + pc->name = &best->name; + + best->conns++; + + rrp->current = best; + ngx_stream_upstream_rr_peer_ref(peers, best); + + n = p / (8 * sizeof(uintptr_t)); + m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t)); + + rrp->tried[n] |= m; + + ngx_stream_upstream_rr_peers_unlock(peers); + + return NGX_OK; + +failed: + + if (peers->next) { + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "get least conn peer, backup servers"); + + rrp->peers = peers->next; + + n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1)) + / (8 * sizeof(uintptr_t)); + + for (i = 0; i < n; i++) { + rrp->tried[i] = 0; + } + + ngx_stream_upstream_rr_peers_unlock(peers); + + rc = ngx_stream_upstream_get_least_conn_peer(pc, rrp); + + if (rc != NGX_BUSY) { + return rc; + } + + ngx_stream_upstream_rr_peers_wlock(peers); + } + +#if (NGX_STREAM_UPSTREAM_ZONE) +busy: +#endif + + ngx_stream_upstream_rr_peers_unlock(peers); + + pc->name = peers->name; + + return NGX_BUSY; +} + + +static char * +ngx_stream_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_upstream_srv_conf_t *uscf; + + uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module); + + if (uscf->peer.init_upstream) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "load balancing method redefined"); + } + + uscf->peer.init_upstream = ngx_stream_upstream_init_least_conn; + + uscf->flags = NGX_STREAM_UPSTREAM_CREATE + |NGX_STREAM_UPSTREAM_MODIFY + |NGX_STREAM_UPSTREAM_WEIGHT + |NGX_STREAM_UPSTREAM_MAX_CONNS + |NGX_STREAM_UPSTREAM_MAX_FAILS + |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT + |NGX_STREAM_UPSTREAM_DOWN + |NGX_STREAM_UPSTREAM_BACKUP; + + return NGX_CONF_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream_random_module.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream_random_module.c new file mode 100644 index 000000000..71f76f8d8 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream_random_module.c @@ -0,0 +1,531 @@ + +/* + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_stream_upstream_rr_peer_t *peer; + ngx_uint_t range; +} ngx_stream_upstream_random_range_t; + + +typedef struct { + ngx_uint_t two; +#if (NGX_STREAM_UPSTREAM_ZONE) + ngx_uint_t config; +#endif + ngx_stream_upstream_random_range_t *ranges; +} ngx_stream_upstream_random_srv_conf_t; + + +typedef struct { + /* the round robin data must be first */ + ngx_stream_upstream_rr_peer_data_t rrp; + + ngx_stream_upstream_random_srv_conf_t *conf; + u_char tries; +} ngx_stream_upstream_random_peer_data_t; + + +static ngx_int_t ngx_stream_upstream_init_random(ngx_conf_t *cf, + ngx_stream_upstream_srv_conf_t *us); +static ngx_int_t ngx_stream_upstream_update_random(ngx_pool_t *pool, + ngx_stream_upstream_srv_conf_t *us); + +static ngx_int_t ngx_stream_upstream_init_random_peer(ngx_stream_session_t *s, + ngx_stream_upstream_srv_conf_t *us); +static ngx_int_t ngx_stream_upstream_get_random_peer(ngx_peer_connection_t *pc, + void *data); +static ngx_int_t ngx_stream_upstream_get_random2_peer(ngx_peer_connection_t *pc, + void *data); +static ngx_uint_t ngx_stream_upstream_peek_random_peer( + ngx_stream_upstream_rr_peers_t *peers, + ngx_stream_upstream_random_peer_data_t *rp); +static void *ngx_stream_upstream_random_create_conf(ngx_conf_t *cf); +static char *ngx_stream_upstream_random(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + + +static ngx_command_t ngx_stream_upstream_random_commands[] = { + + { ngx_string("random"), + NGX_STREAM_UPS_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE12, + ngx_stream_upstream_random, + NGX_STREAM_SRV_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_upstream_random_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_stream_upstream_random_create_conf, /* create server configuration */ + NULL /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_upstream_random_module = { + NGX_MODULE_V1, + &ngx_stream_upstream_random_module_ctx, /* module context */ + ngx_stream_upstream_random_commands, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_int_t +ngx_stream_upstream_init_random(ngx_conf_t *cf, + ngx_stream_upstream_srv_conf_t *us) +{ + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, cf->log, 0, "init random"); + + if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) { + return NGX_ERROR; + } + + us->peer.init = ngx_stream_upstream_init_random_peer; + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (us->shm_zone) { + return NGX_OK; + } +#endif + + return ngx_stream_upstream_update_random(cf->pool, us); +} + + +static ngx_int_t +ngx_stream_upstream_update_random(ngx_pool_t *pool, + ngx_stream_upstream_srv_conf_t *us) +{ + size_t size; + ngx_uint_t i, total_weight; + ngx_stream_upstream_rr_peer_t *peer; + ngx_stream_upstream_rr_peers_t *peers; + ngx_stream_upstream_random_range_t *ranges; + ngx_stream_upstream_random_srv_conf_t *rcf; + + rcf = ngx_stream_conf_upstream_srv_conf(us, + ngx_stream_upstream_random_module); + if (rcf->ranges) { + ngx_free(rcf->ranges); + rcf->ranges = NULL; + } + + peers = us->peer.data; + + size = peers->number * sizeof(ngx_stream_upstream_random_range_t); + + ranges = pool ? ngx_palloc(pool, size) : ngx_alloc(size, ngx_cycle->log); + if (ranges == NULL) { + return NGX_ERROR; + } + + total_weight = 0; + + for (peer = peers->peer, i = 0; peer; peer = peer->next, i++) { + ranges[i].peer = peer; + ranges[i].range = total_weight; + total_weight += peer->weight; + } + + rcf->ranges = ranges; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_upstream_init_random_peer(ngx_stream_session_t *s, + ngx_stream_upstream_srv_conf_t *us) +{ + ngx_stream_upstream_random_srv_conf_t *rcf; + ngx_stream_upstream_random_peer_data_t *rp; + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "init random peer"); + + rcf = ngx_stream_conf_upstream_srv_conf(us, + ngx_stream_upstream_random_module); + + rp = ngx_palloc(s->connection->pool, + sizeof(ngx_stream_upstream_random_peer_data_t)); + if (rp == NULL) { + return NGX_ERROR; + } + + s->upstream->peer.data = &rp->rrp; + + if (ngx_stream_upstream_init_round_robin_peer(s, us) != NGX_OK) { + return NGX_ERROR; + } + + if (rcf->two) { + s->upstream->peer.get = ngx_stream_upstream_get_random2_peer; + + } else { + s->upstream->peer.get = ngx_stream_upstream_get_random_peer; + } + + rp->conf = rcf; + rp->tries = 0; + + ngx_stream_upstream_rr_peers_rlock(rp->rrp.peers); + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (rp->rrp.peers->config + && (rcf->ranges == NULL || rcf->config != *rp->rrp.peers->config)) + { + if (ngx_stream_upstream_update_random(NULL, us) != NGX_OK) { + ngx_stream_upstream_rr_peers_unlock(rp->rrp.peers); + return NGX_ERROR; + } + + rcf->config = *rp->rrp.peers->config; + } +#endif + + ngx_stream_upstream_rr_peers_unlock(rp->rrp.peers); + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_upstream_get_random_peer(ngx_peer_connection_t *pc, void *data) +{ + ngx_stream_upstream_random_peer_data_t *rp = data; + + time_t now; + uintptr_t m; + ngx_uint_t i, n; + ngx_stream_upstream_rr_peer_t *peer; + ngx_stream_upstream_rr_peers_t *peers; + ngx_stream_upstream_rr_peer_data_t *rrp; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "get random peer, try: %ui", pc->tries); + + rrp = &rp->rrp; + peers = rrp->peers; + + ngx_stream_upstream_rr_peers_rlock(peers); + + if (rp->tries > 20 || peers->number < 2) { + ngx_stream_upstream_rr_peers_unlock(peers); + return ngx_stream_upstream_get_round_robin_peer(pc, rrp); + } + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (peers->config && rrp->config != *peers->config) { + ngx_stream_upstream_rr_peers_unlock(peers); + return ngx_stream_upstream_get_round_robin_peer(pc, rrp); + } +#endif + + pc->cached = 0; + pc->connection = NULL; + + now = ngx_time(); + + for ( ;; ) { + + i = ngx_stream_upstream_peek_random_peer(peers, rp); + + peer = rp->conf->ranges[i].peer; + + n = i / (8 * sizeof(uintptr_t)); + m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); + + if (rrp->tried[n] & m) { + goto next; + } + + ngx_stream_upstream_rr_peer_lock(peers, peer); + + if (peer->down) { + ngx_stream_upstream_rr_peer_unlock(peers, peer); + goto next; + } + + if (peer->max_fails + && peer->fails >= peer->max_fails + && now - peer->checked <= peer->fail_timeout) + { + ngx_stream_upstream_rr_peer_unlock(peers, peer); + goto next; + } + + if (peer->max_conns && peer->conns >= peer->max_conns) { + ngx_stream_upstream_rr_peer_unlock(peers, peer); + goto next; + } + + break; + + next: + + if (++rp->tries > 20) { + ngx_stream_upstream_rr_peers_unlock(peers); + return ngx_stream_upstream_get_round_robin_peer(pc, rrp); + } + } + + rrp->current = peer; + ngx_stream_upstream_rr_peer_ref(peers, peer); + + if (now - peer->checked > peer->fail_timeout) { + peer->checked = now; + } + + pc->sockaddr = peer->sockaddr; + pc->socklen = peer->socklen; + pc->name = &peer->name; + + peer->conns++; + + ngx_stream_upstream_rr_peer_unlock(peers, peer); + ngx_stream_upstream_rr_peers_unlock(peers); + + rrp->tried[n] |= m; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_upstream_get_random2_peer(ngx_peer_connection_t *pc, void *data) +{ + ngx_stream_upstream_random_peer_data_t *rp = data; + + time_t now; + uintptr_t m; + ngx_uint_t i, n, p; + ngx_stream_upstream_rr_peer_t *peer, *prev; + ngx_stream_upstream_rr_peers_t *peers; + ngx_stream_upstream_rr_peer_data_t *rrp; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "get random2 peer, try: %ui", pc->tries); + + rrp = &rp->rrp; + peers = rrp->peers; + + ngx_stream_upstream_rr_peers_wlock(peers); + + if (rp->tries > 20 || peers->number < 2) { + ngx_stream_upstream_rr_peers_unlock(peers); + return ngx_stream_upstream_get_round_robin_peer(pc, rrp); + } + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (peers->config && rrp->config != *peers->config) { + ngx_stream_upstream_rr_peers_unlock(peers); + return ngx_stream_upstream_get_round_robin_peer(pc, rrp); + } +#endif + + pc->cached = 0; + pc->connection = NULL; + + now = ngx_time(); + + prev = NULL; + +#if (NGX_SUPPRESS_WARN) + p = 0; +#endif + + for ( ;; ) { + + i = ngx_stream_upstream_peek_random_peer(peers, rp); + + peer = rp->conf->ranges[i].peer; + + if (peer == prev) { + goto next; + } + + n = i / (8 * sizeof(uintptr_t)); + m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); + + if (rrp->tried[n] & m) { + goto next; + } + + if (peer->down) { + goto next; + } + + if (peer->max_fails + && peer->fails >= peer->max_fails + && now - peer->checked <= peer->fail_timeout) + { + goto next; + } + + if (peer->max_conns && peer->conns >= peer->max_conns) { + goto next; + } + + if (prev) { + if (peer->conns * prev->weight > prev->conns * peer->weight) { + peer = prev; + n = p / (8 * sizeof(uintptr_t)); + m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t)); + } + + break; + } + + prev = peer; + p = i; + + next: + + if (++rp->tries > 20) { + ngx_stream_upstream_rr_peers_unlock(peers); + return ngx_stream_upstream_get_round_robin_peer(pc, rrp); + } + } + + rrp->current = peer; + ngx_stream_upstream_rr_peer_ref(peers, peer); + + if (now - peer->checked > peer->fail_timeout) { + peer->checked = now; + } + + pc->sockaddr = peer->sockaddr; + pc->socklen = peer->socklen; + pc->name = &peer->name; + + peer->conns++; + + ngx_stream_upstream_rr_peers_unlock(peers); + + rrp->tried[n] |= m; + + return NGX_OK; +} + + +static ngx_uint_t +ngx_stream_upstream_peek_random_peer(ngx_stream_upstream_rr_peers_t *peers, + ngx_stream_upstream_random_peer_data_t *rp) +{ + ngx_uint_t i, j, k, x; + + x = ngx_random() % peers->total_weight; + + i = 0; + j = peers->number; + + while (j - i > 1) { + k = (i + j) / 2; + + if (x < rp->conf->ranges[k].range) { + j = k; + + } else { + i = k; + } + } + + return i; +} + + +static void * +ngx_stream_upstream_random_create_conf(ngx_conf_t *cf) +{ + ngx_stream_upstream_random_srv_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_random_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * conf->two = 0; + */ + + return conf; +} + + +static char * +ngx_stream_upstream_random(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_stream_upstream_random_srv_conf_t *rcf = conf; + + ngx_str_t *value; + ngx_stream_upstream_srv_conf_t *uscf; + + uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module); + + if (uscf->peer.init_upstream) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "load balancing method redefined"); + } + + uscf->peer.init_upstream = ngx_stream_upstream_init_random; + + uscf->flags = NGX_STREAM_UPSTREAM_CREATE + |NGX_STREAM_UPSTREAM_MODIFY + |NGX_STREAM_UPSTREAM_WEIGHT + |NGX_STREAM_UPSTREAM_MAX_CONNS + |NGX_STREAM_UPSTREAM_MAX_FAILS + |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT + |NGX_STREAM_UPSTREAM_DOWN; + + if (cf->args->nelts == 1) { + return NGX_CONF_OK; + } + + value = cf->args->elts; + + if (ngx_strcmp(value[1].data, "two") == 0) { + rcf->two = 1; + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[1]); + return NGX_CONF_ERROR; + } + + if (cf->args->nelts == 2) { + return NGX_CONF_OK; + } + + if (ngx_strcmp(value[2].data, "least_conn") != 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream_round_robin.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream_round_robin.c new file mode 100644 index 000000000..dd80322c8 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream_round_robin.c @@ -0,0 +1,1075 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +#define ngx_stream_upstream_tries(p) ((p)->tries \ + + ((p)->next ? (p)->next->tries : 0)) + + +static ngx_stream_upstream_rr_peer_t *ngx_stream_upstream_get_peer( + ngx_stream_upstream_rr_peer_data_t *rrp); +static void ngx_stream_upstream_notify_round_robin_peer( + ngx_peer_connection_t *pc, void *data, ngx_uint_t state); + +#if (NGX_STREAM_SSL) + +static ngx_int_t ngx_stream_upstream_set_round_robin_peer_session( + ngx_peer_connection_t *pc, void *data); +static void ngx_stream_upstream_save_round_robin_peer_session( + ngx_peer_connection_t *pc, void *data); +static ngx_int_t ngx_stream_upstream_empty_set_session( + ngx_peer_connection_t *pc, void *data); +static void ngx_stream_upstream_empty_save_session(ngx_peer_connection_t *pc, + void *data); + +#endif + + +ngx_int_t +ngx_stream_upstream_init_round_robin(ngx_conf_t *cf, + ngx_stream_upstream_srv_conf_t *us) +{ + ngx_url_t u; + ngx_uint_t i, j, n, r, w, t; + ngx_stream_upstream_server_t *server; + ngx_stream_upstream_rr_peer_t *peer, **peerp; + ngx_stream_upstream_rr_peers_t *peers, *backup; +#if (NGX_STREAM_UPSTREAM_ZONE) + ngx_uint_t resolve; + ngx_stream_core_srv_conf_t *cscf; + ngx_stream_upstream_rr_peer_t **rpeerp; +#endif + + us->peer.init = ngx_stream_upstream_init_round_robin_peer; + + if (us->servers) { + server = us->servers->elts; + + n = 0; + r = 0; + w = 0; + t = 0; + +#if (NGX_STREAM_UPSTREAM_ZONE) + resolve = 0; +#endif + + for (i = 0; i < us->servers->nelts; i++) { + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (server[i].host.len) { + resolve = 1; + } +#endif + + if (server[i].backup) { + continue; + } + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (server[i].host.len) { + r++; + continue; + } +#endif + + n += server[i].naddrs; + w += server[i].naddrs * server[i].weight; + + if (!server[i].down) { + t += server[i].naddrs; + } + } + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (us->shm_zone) { + + if (resolve && !(us->flags & NGX_STREAM_UPSTREAM_MODIFY)) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "load balancing method does not support" + " resolving names at run time in" + " upstream \"%V\" in %s:%ui", + &us->host, us->file_name, us->line); + return NGX_ERROR; + } + + cscf = ngx_stream_conf_get_module_srv_conf(cf, + ngx_stream_core_module); + + if (us->resolver == NULL) { + us->resolver = cscf->resolver; + } + + /* + * Without "resolver_timeout" in stream{} the merged value is unset. + */ + ngx_conf_merge_msec_value(us->resolver_timeout, + cscf->resolver_timeout, 30000); + + if (resolve + && (us->resolver == NULL + || us->resolver->connections.nelts == 0)) + { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no resolver defined to resolve names" + " at run time in upstream \"%V\" in %s:%ui", + &us->host, us->file_name, us->line); + return NGX_ERROR; + } + + } else if (resolve) { + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "resolving names at run time requires" + " upstream \"%V\" in %s:%ui" + " to be in shared memory", + &us->host, us->file_name, us->line); + return NGX_ERROR; + } +#endif + + if (n + r == 0) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no servers in upstream \"%V\" in %s:%ui", + &us->host, us->file_name, us->line); + return NGX_ERROR; + } + + peers = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peers_t)); + if (peers == NULL) { + return NGX_ERROR; + } + + peer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peer_t) + * (n + r)); + if (peer == NULL) { + return NGX_ERROR; + } + + peers->single = (n == 1); + peers->number = n; + peers->weighted = (w != n); + peers->total_weight = w; + peers->tries = t; + peers->name = &us->host; + + n = 0; + peerp = &peers->peer; + +#if (NGX_STREAM_UPSTREAM_ZONE) + rpeerp = &peers->resolve; +#endif + + for (i = 0; i < us->servers->nelts; i++) { + if (server[i].backup) { + continue; + } + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (server[i].host.len) { + + peer[n].host = ngx_pcalloc(cf->pool, + sizeof(ngx_stream_upstream_host_t)); + if (peer[n].host == NULL) { + return NGX_ERROR; + } + + peer[n].host->name = server[i].host; + peer[n].host->service = server[i].service; + + peer[n].sockaddr = server[i].addrs[0].sockaddr; + peer[n].socklen = server[i].addrs[0].socklen; + peer[n].name = server[i].addrs[0].name; + peer[n].weight = server[i].weight; + peer[n].effective_weight = server[i].weight; + peer[n].current_weight = 0; + peer[n].max_conns = server[i].max_conns; + peer[n].max_fails = server[i].max_fails; + peer[n].fail_timeout = server[i].fail_timeout; + peer[n].down = server[i].down; + peer[n].server = server[i].name; + *rpeerp = &peer[n]; + rpeerp = &peer[n].next; + n++; + + continue; + } +#endif + + for (j = 0; j < server[i].naddrs; j++) { + peer[n].sockaddr = server[i].addrs[j].sockaddr; + peer[n].socklen = server[i].addrs[j].socklen; + peer[n].name = server[i].addrs[j].name; + peer[n].weight = server[i].weight; + peer[n].effective_weight = server[i].weight; + peer[n].current_weight = 0; + peer[n].max_conns = server[i].max_conns; + peer[n].max_fails = server[i].max_fails; + peer[n].fail_timeout = server[i].fail_timeout; + peer[n].down = server[i].down; + peer[n].server = server[i].name; + + *peerp = &peer[n]; + peerp = &peer[n].next; + n++; + } + } + + us->peer.data = peers; + + /* backup servers */ + + n = 0; + r = 0; + w = 0; + t = 0; + + for (i = 0; i < us->servers->nelts; i++) { + if (!server[i].backup) { + continue; + } + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (server[i].host.len) { + r++; + continue; + } +#endif + + n += server[i].naddrs; + w += server[i].naddrs * server[i].weight; + + if (!server[i].down) { + t += server[i].naddrs; + } + } + + if (n == 0 +#if (NGX_STREAM_UPSTREAM_ZONE) + && !resolve +#endif + ) { + return NGX_OK; + } + + if (n + r == 0 && !(us->flags & NGX_STREAM_UPSTREAM_BACKUP)) { + return NGX_OK; + } + + backup = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peers_t)); + if (backup == NULL) { + return NGX_ERROR; + } + + peer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peer_t) + * (n + r)); + if (peer == NULL) { + return NGX_ERROR; + } + + if (n > 0) { + peers->single = 0; + } + + backup->single = 0; + backup->number = n; + backup->weighted = (w != n); + backup->total_weight = w; + backup->tries = t; + backup->name = &us->host; + + n = 0; + peerp = &backup->peer; + +#if (NGX_STREAM_UPSTREAM_ZONE) + rpeerp = &backup->resolve; +#endif + + for (i = 0; i < us->servers->nelts; i++) { + if (!server[i].backup) { + continue; + } + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (server[i].host.len) { + + peer[n].host = ngx_pcalloc(cf->pool, + sizeof(ngx_stream_upstream_host_t)); + if (peer[n].host == NULL) { + return NGX_ERROR; + } + + peer[n].host->name = server[i].host; + peer[n].host->service = server[i].service; + + peer[n].sockaddr = server[i].addrs[0].sockaddr; + peer[n].socklen = server[i].addrs[0].socklen; + peer[n].name = server[i].addrs[0].name; + peer[n].weight = server[i].weight; + peer[n].effective_weight = server[i].weight; + peer[n].current_weight = 0; + peer[n].max_conns = server[i].max_conns; + peer[n].max_fails = server[i].max_fails; + peer[n].fail_timeout = server[i].fail_timeout; + peer[n].down = server[i].down; + peer[n].server = server[i].name; + *rpeerp = &peer[n]; + rpeerp = &peer[n].next; + n++; + + continue; + } +#endif + + for (j = 0; j < server[i].naddrs; j++) { + peer[n].sockaddr = server[i].addrs[j].sockaddr; + peer[n].socklen = server[i].addrs[j].socklen; + peer[n].name = server[i].addrs[j].name; + peer[n].weight = server[i].weight; + peer[n].effective_weight = server[i].weight; + peer[n].current_weight = 0; + peer[n].max_conns = server[i].max_conns; + peer[n].max_fails = server[i].max_fails; + peer[n].fail_timeout = server[i].fail_timeout; + peer[n].down = server[i].down; + peer[n].server = server[i].name; + + *peerp = &peer[n]; + peerp = &peer[n].next; + n++; + } + } + + peers->next = backup; + + return NGX_OK; + } + + + /* an upstream implicitly defined by proxy_pass, etc. */ + + if (us->port == 0) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no port in upstream \"%V\" in %s:%ui", + &us->host, us->file_name, us->line); + return NGX_ERROR; + } + + ngx_memzero(&u, sizeof(ngx_url_t)); + + u.host = us->host; + u.port = us->port; + + if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) { + if (u.err) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "%s in upstream \"%V\" in %s:%ui", + u.err, &us->host, us->file_name, us->line); + } + + return NGX_ERROR; + } + + n = u.naddrs; + + peers = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peers_t)); + if (peers == NULL) { + return NGX_ERROR; + } + + peer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peer_t) * n); + if (peer == NULL) { + return NGX_ERROR; + } + + peers->single = (n == 1); + peers->number = n; + peers->weighted = 0; + peers->total_weight = n; + peers->tries = n; + peers->name = &us->host; + + peerp = &peers->peer; + + for (i = 0; i < u.naddrs; i++) { + peer[i].sockaddr = u.addrs[i].sockaddr; + peer[i].socklen = u.addrs[i].socklen; + peer[i].name = u.addrs[i].name; + peer[i].weight = 1; + peer[i].effective_weight = 1; + peer[i].current_weight = 0; + peer[i].max_conns = 0; + peer[i].max_fails = 1; + peer[i].fail_timeout = 10; + *peerp = &peer[i]; + peerp = &peer[i].next; + } + + us->peer.data = peers; + + /* implicitly defined upstream has no backup servers */ + + return NGX_OK; +} + + +ngx_int_t +ngx_stream_upstream_init_round_robin_peer(ngx_stream_session_t *s, + ngx_stream_upstream_srv_conf_t *us) +{ + ngx_uint_t n; + ngx_stream_upstream_rr_peer_data_t *rrp; + + rrp = s->upstream->peer.data; + + if (rrp == NULL) { + rrp = ngx_palloc(s->connection->pool, + sizeof(ngx_stream_upstream_rr_peer_data_t)); + if (rrp == NULL) { + return NGX_ERROR; + } + + s->upstream->peer.data = rrp; + } + + rrp->peers = us->peer.data; + rrp->current = NULL; + + ngx_stream_upstream_rr_peers_rlock(rrp->peers); + +#if (NGX_STREAM_UPSTREAM_ZONE) + rrp->config = rrp->peers->config ? *rrp->peers->config : 0; +#endif + + n = rrp->peers->number; + + if (rrp->peers->next && rrp->peers->next->number > n) { + n = rrp->peers->next->number; + } + + s->upstream->peer.tries = ngx_stream_upstream_tries(rrp->peers); + + ngx_stream_upstream_rr_peers_unlock(rrp->peers); + + if (n <= 8 * sizeof(uintptr_t)) { + rrp->tried = &rrp->data; + rrp->data = 0; + + } else { + n = (n + (8 * sizeof(uintptr_t) - 1)) / (8 * sizeof(uintptr_t)); + + rrp->tried = ngx_pcalloc(s->connection->pool, n * sizeof(uintptr_t)); + if (rrp->tried == NULL) { + return NGX_ERROR; + } + } + + s->upstream->peer.get = ngx_stream_upstream_get_round_robin_peer; + s->upstream->peer.free = ngx_stream_upstream_free_round_robin_peer; + s->upstream->peer.notify = ngx_stream_upstream_notify_round_robin_peer; + +#if (NGX_STREAM_SSL) + s->upstream->peer.set_session = + ngx_stream_upstream_set_round_robin_peer_session; + s->upstream->peer.save_session = + ngx_stream_upstream_save_round_robin_peer_session; +#endif + + return NGX_OK; +} + + +ngx_int_t +ngx_stream_upstream_create_round_robin_peer(ngx_stream_session_t *s, + ngx_stream_upstream_resolved_t *ur) +{ + u_char *p; + size_t len; + socklen_t socklen; + ngx_uint_t i, n; + struct sockaddr *sockaddr; + ngx_stream_upstream_rr_peer_t *peer, **peerp; + ngx_stream_upstream_rr_peers_t *peers; + ngx_stream_upstream_rr_peer_data_t *rrp; + + rrp = s->upstream->peer.data; + + if (rrp == NULL) { + rrp = ngx_palloc(s->connection->pool, + sizeof(ngx_stream_upstream_rr_peer_data_t)); + if (rrp == NULL) { + return NGX_ERROR; + } + + s->upstream->peer.data = rrp; + } + + peers = ngx_pcalloc(s->connection->pool, + sizeof(ngx_stream_upstream_rr_peers_t)); + if (peers == NULL) { + return NGX_ERROR; + } + + peer = ngx_pcalloc(s->connection->pool, + sizeof(ngx_stream_upstream_rr_peer_t) * ur->naddrs); + if (peer == NULL) { + return NGX_ERROR; + } + + peers->single = (ur->naddrs == 1); + peers->number = ur->naddrs; + peers->tries = ur->naddrs; + peers->name = &ur->host; + + if (ur->sockaddr) { + peer[0].sockaddr = ur->sockaddr; + peer[0].socklen = ur->socklen; + peer[0].name = ur->name; + peer[0].weight = 1; + peer[0].effective_weight = 1; + peer[0].current_weight = 0; + peer[0].max_conns = 0; + peer[0].max_fails = 1; + peer[0].fail_timeout = 10; + peers->peer = peer; + + } else { + peerp = &peers->peer; + + for (i = 0; i < ur->naddrs; i++) { + + socklen = ur->addrs[i].socklen; + + sockaddr = ngx_palloc(s->connection->pool, socklen); + if (sockaddr == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen); + ngx_inet_set_port(sockaddr, ur->port); + + p = ngx_pnalloc(s->connection->pool, NGX_SOCKADDR_STRLEN); + if (p == NULL) { + return NGX_ERROR; + } + + len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1); + + peer[i].sockaddr = sockaddr; + peer[i].socklen = socklen; + peer[i].name.len = len; + peer[i].name.data = p; + peer[i].weight = 1; + peer[i].effective_weight = 1; + peer[i].current_weight = 0; + peer[i].max_conns = 0; + peer[i].max_fails = 1; + peer[i].fail_timeout = 10; + *peerp = &peer[i]; + peerp = &peer[i].next; + } + } + + rrp->peers = peers; + rrp->current = NULL; + rrp->config = 0; + + if (rrp->peers->number <= 8 * sizeof(uintptr_t)) { + rrp->tried = &rrp->data; + rrp->data = 0; + + } else { + n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1)) + / (8 * sizeof(uintptr_t)); + + rrp->tried = ngx_pcalloc(s->connection->pool, n * sizeof(uintptr_t)); + if (rrp->tried == NULL) { + return NGX_ERROR; + } + } + + s->upstream->peer.get = ngx_stream_upstream_get_round_robin_peer; + s->upstream->peer.free = ngx_stream_upstream_free_round_robin_peer; + s->upstream->peer.tries = ngx_stream_upstream_tries(rrp->peers); +#if (NGX_STREAM_SSL) + s->upstream->peer.set_session = ngx_stream_upstream_empty_set_session; + s->upstream->peer.save_session = ngx_stream_upstream_empty_save_session; +#endif + + return NGX_OK; +} + + +ngx_int_t +ngx_stream_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data) +{ + ngx_stream_upstream_rr_peer_data_t *rrp = data; + + ngx_int_t rc; + ngx_uint_t i, n; + ngx_stream_upstream_rr_peer_t *peer; + ngx_stream_upstream_rr_peers_t *peers; + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "get rr peer, try: %ui", pc->tries); + + pc->connection = NULL; + + peers = rrp->peers; + ngx_stream_upstream_rr_peers_wlock(peers); + +#if (NGX_STREAM_UPSTREAM_ZONE) + if (peers->config && rrp->config != *peers->config) { + goto busy; + } +#endif + + if (peers->single) { + peer = peers->peer; + + if (peer->down) { + goto failed; + } + + if (peer->max_conns && peer->conns >= peer->max_conns) { + goto failed; + } + + rrp->current = peer; + ngx_stream_upstream_rr_peer_ref(peers, peer); + + } else { + + /* there are several peers */ + + peer = ngx_stream_upstream_get_peer(rrp); + + if (peer == NULL) { + goto failed; + } + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "get rr peer, current: %p %i", + peer, peer->current_weight); + } + + pc->sockaddr = peer->sockaddr; + pc->socklen = peer->socklen; + pc->name = &peer->name; + + peer->conns++; + + ngx_stream_upstream_rr_peers_unlock(peers); + + return NGX_OK; + +failed: + + if (peers->next) { + + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0, "backup servers"); + + rrp->peers = peers->next; + + n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1)) + / (8 * sizeof(uintptr_t)); + + for (i = 0; i < n; i++) { + rrp->tried[i] = 0; + } + + ngx_stream_upstream_rr_peers_unlock(peers); + + rc = ngx_stream_upstream_get_round_robin_peer(pc, rrp); + + if (rc != NGX_BUSY) { + return rc; + } + + ngx_stream_upstream_rr_peers_wlock(peers); + } + +#if (NGX_STREAM_UPSTREAM_ZONE) +busy: +#endif + + ngx_stream_upstream_rr_peers_unlock(peers); + + pc->name = peers->name; + + return NGX_BUSY; +} + + +static ngx_stream_upstream_rr_peer_t * +ngx_stream_upstream_get_peer(ngx_stream_upstream_rr_peer_data_t *rrp) +{ + time_t now; + uintptr_t m; + ngx_int_t total; + ngx_uint_t i, n, p; + ngx_stream_upstream_rr_peer_t *peer, *best; + + now = ngx_time(); + + best = NULL; + total = 0; + +#if (NGX_SUPPRESS_WARN) + p = 0; +#endif + + for (peer = rrp->peers->peer, i = 0; + peer; + peer = peer->next, i++) + { + n = i / (8 * sizeof(uintptr_t)); + m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t)); + + if (rrp->tried[n] & m) { + continue; + } + + if (peer->down) { + continue; + } + + if (peer->max_fails + && peer->fails >= peer->max_fails + && now - peer->checked <= peer->fail_timeout) + { + continue; + } + + if (peer->max_conns && peer->conns >= peer->max_conns) { + continue; + } + + peer->current_weight += peer->effective_weight; + total += peer->effective_weight; + + if (peer->effective_weight < peer->weight) { + peer->effective_weight++; + } + + if (best == NULL || peer->current_weight > best->current_weight) { + best = peer; + p = i; + } + } + + if (best == NULL) { + return NULL; + } + + rrp->current = best; + ngx_stream_upstream_rr_peer_ref(rrp->peers, best); + + n = p / (8 * sizeof(uintptr_t)); + m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t)); + + rrp->tried[n] |= m; + + best->current_weight -= total; + + if (now - best->checked > best->fail_timeout) { + best->checked = now; + } + + return best; +} + + +void +ngx_stream_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data, + ngx_uint_t state) +{ + ngx_stream_upstream_rr_peer_data_t *rrp = data; + + time_t now; + ngx_stream_upstream_rr_peer_t *peer; + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "free rr peer %ui %ui", pc->tries, state); + + peer = rrp->current; + + ngx_stream_upstream_rr_peers_rlock(rrp->peers); + ngx_stream_upstream_rr_peer_lock(rrp->peers, peer); + + if (rrp->peers->single) { + + if (peer->fails) { + peer->fails = 0; + } + + peer->conns--; + + if (ngx_stream_upstream_rr_peer_unref(rrp->peers, peer) == NGX_OK) { + ngx_stream_upstream_rr_peer_unlock(rrp->peers, peer); + } + + ngx_stream_upstream_rr_peers_unlock(rrp->peers); + + pc->tries = 0; + return; + } + + if (state & NGX_PEER_FAILED) { + now = ngx_time(); + + peer->fails++; + peer->accessed = now; + peer->checked = now; + + if (peer->max_fails) { + peer->effective_weight -= peer->weight / peer->max_fails; + + if (peer->fails >= peer->max_fails) { + ngx_log_error(NGX_LOG_WARN, pc->log, 0, + "upstream server temporarily disabled"); + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "free rr peer failed: %p %i", + peer, peer->effective_weight); + + if (peer->effective_weight < 0) { + peer->effective_weight = 0; + } + + } else { + + /* mark peer live if check passed */ + + if (peer->accessed < peer->checked) { + peer->fails = 0; + } + } + + peer->conns--; + + if (ngx_stream_upstream_rr_peer_unref(rrp->peers, peer) == NGX_OK) { + ngx_stream_upstream_rr_peer_unlock(rrp->peers, peer); + } + + ngx_stream_upstream_rr_peers_unlock(rrp->peers); + + if (pc->tries) { + pc->tries--; + } +} + + +static void +ngx_stream_upstream_notify_round_robin_peer(ngx_peer_connection_t *pc, + void *data, ngx_uint_t type) +{ + ngx_stream_upstream_rr_peer_data_t *rrp = data; + + ngx_stream_upstream_rr_peer_t *peer; + + peer = rrp->current; + + if (type == NGX_STREAM_UPSTREAM_NOTIFY_CONNECT + && pc->connection->type == SOCK_STREAM) + { + ngx_stream_upstream_rr_peers_rlock(rrp->peers); + ngx_stream_upstream_rr_peer_lock(rrp->peers, peer); + + if (peer->accessed < peer->checked) { + peer->fails = 0; + } + + ngx_stream_upstream_rr_peer_unlock(rrp->peers, peer); + ngx_stream_upstream_rr_peers_unlock(rrp->peers); + } +} + + +#if (NGX_STREAM_SSL) + +static ngx_int_t +ngx_stream_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc, + void *data) +{ + ngx_stream_upstream_rr_peer_data_t *rrp = data; + + ngx_int_t rc; + ngx_ssl_session_t *ssl_session; + ngx_stream_upstream_rr_peer_t *peer; +#if (NGX_STREAM_UPSTREAM_ZONE) + int len; + const u_char *p; + ngx_stream_upstream_rr_peers_t *peers; +#endif + + peer = rrp->current; + +#if (NGX_STREAM_UPSTREAM_ZONE) + peers = rrp->peers; + + if (peers->shpool) { + ngx_stream_upstream_rr_peers_rlock(peers); + ngx_stream_upstream_rr_peer_lock(peers, peer); + + if (peer->ssl_session == NULL) { + ngx_stream_upstream_rr_peer_unlock(peers, peer); + ngx_stream_upstream_rr_peers_unlock(peers); + return NGX_OK; + } + + len = peer->ssl_session_len; + + ngx_memcpy(ngx_ssl_session_buffer, peer->ssl_session, len); + + ngx_stream_upstream_rr_peer_unlock(peers, peer); + ngx_stream_upstream_rr_peers_unlock(peers); + + p = ngx_ssl_session_buffer; + ssl_session = d2i_SSL_SESSION(NULL, &p, len); + + rc = ngx_ssl_set_session(pc->connection, ssl_session); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "set session: %p", ssl_session); + + ngx_ssl_free_session(ssl_session); + + return rc; + } +#endif + + ssl_session = peer->ssl_session; + + rc = ngx_ssl_set_session(pc->connection, ssl_session); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "set session: %p", ssl_session); + + return rc; +} + + +static void +ngx_stream_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc, + void *data) +{ + ngx_stream_upstream_rr_peer_data_t *rrp = data; + + ngx_ssl_session_t *old_ssl_session, *ssl_session; + ngx_stream_upstream_rr_peer_t *peer; +#if (NGX_STREAM_UPSTREAM_ZONE) + int len; + u_char *p; + ngx_stream_upstream_rr_peers_t *peers; +#endif + +#if (NGX_STREAM_UPSTREAM_ZONE) + peers = rrp->peers; + + if (peers->shpool) { + + ssl_session = ngx_ssl_get0_session(pc->connection); + + if (ssl_session == NULL) { + return; + } + + len = i2d_SSL_SESSION(ssl_session, NULL); + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "save session: %p:%d", ssl_session, len); + + /* do not cache too big session */ + + if (len > NGX_SSL_MAX_SESSION_SIZE) { + return; + } + + p = ngx_ssl_session_buffer; + (void) i2d_SSL_SESSION(ssl_session, &p); + + peer = rrp->current; + + ngx_stream_upstream_rr_peers_rlock(peers); + ngx_stream_upstream_rr_peer_lock(peers, peer); + + if (len > peer->ssl_session_len) { + ngx_shmtx_lock(&peers->shpool->mutex); + + if (peer->ssl_session) { + ngx_slab_free_locked(peers->shpool, peer->ssl_session); + } + + peer->ssl_session = ngx_slab_alloc_locked(peers->shpool, len); + + ngx_shmtx_unlock(&peers->shpool->mutex); + + if (peer->ssl_session == NULL) { + peer->ssl_session_len = 0; + + ngx_stream_upstream_rr_peer_unlock(peers, peer); + ngx_stream_upstream_rr_peers_unlock(peers); + return; + } + + peer->ssl_session_len = len; + } + + ngx_memcpy(peer->ssl_session, ngx_ssl_session_buffer, len); + + ngx_stream_upstream_rr_peer_unlock(peers, peer); + ngx_stream_upstream_rr_peers_unlock(peers); + + return; + } +#endif + + ssl_session = ngx_ssl_get_session(pc->connection); + + if (ssl_session == NULL) { + return; + } + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "save session: %p", ssl_session); + + peer = rrp->current; + + old_ssl_session = peer->ssl_session; + peer->ssl_session = ssl_session; + + if (old_ssl_session) { + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0, + "old session: %p", old_ssl_session); + + ngx_ssl_free_session(old_ssl_session); + } +} + + +static ngx_int_t +ngx_stream_upstream_empty_set_session(ngx_peer_connection_t *pc, void *data) +{ + return NGX_OK; +} + + +static void +ngx_stream_upstream_empty_save_session(ngx_peer_connection_t *pc, void *data) +{ + return; +} + +#endif diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream_round_robin.h b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream_round_robin.h new file mode 100644 index 000000000..d36c2ad06 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream_round_robin.h @@ -0,0 +1,232 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_STREAM_UPSTREAM_ROUND_ROBIN_H_INCLUDED_ +#define _NGX_STREAM_UPSTREAM_ROUND_ROBIN_H_INCLUDED_ + + +#include +#include +#include + + +#define NGX_STREAM_UPSTREAM_FAILED 1 + + +typedef struct ngx_stream_upstream_rr_peers_s ngx_stream_upstream_rr_peers_t; +typedef struct ngx_stream_upstream_rr_peer_s ngx_stream_upstream_rr_peer_t; + + +#if (NGX_STREAM_UPSTREAM_ZONE) + +typedef struct { + ngx_event_t event; /* must be first */ + ngx_uint_t worker; + ngx_str_t name; + ngx_str_t service; + time_t valid; + ngx_stream_upstream_rr_peers_t *peers; + ngx_stream_upstream_rr_peer_t *peer; +} ngx_stream_upstream_host_t; + +#endif + + +struct ngx_stream_upstream_rr_peer_s { + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_str_t name; + ngx_str_t server; + + ngx_int_t current_weight; + ngx_int_t effective_weight; + ngx_int_t weight; + + ngx_uint_t conns; + ngx_uint_t max_conns; + + ngx_uint_t fails; + time_t accessed; + time_t checked; + + ngx_uint_t max_fails; + time_t fail_timeout; + ngx_msec_t slow_start; + ngx_msec_t start_time; + + ngx_uint_t down; + + void *ssl_session; + int ssl_session_len; + +#if (NGX_STREAM_UPSTREAM_ZONE) + unsigned zombie:1; + + ngx_atomic_t lock; + ngx_uint_t refs; + ngx_stream_upstream_host_t *host; +#endif + + ngx_stream_upstream_rr_peer_t *next; + + NGX_COMPAT_BEGIN(14) + NGX_COMPAT_END +}; + + +struct ngx_stream_upstream_rr_peers_s { + ngx_uint_t number; + +#if (NGX_STREAM_UPSTREAM_ZONE) + ngx_slab_pool_t *shpool; + ngx_atomic_t rwlock; + ngx_uint_t *config; + ngx_stream_upstream_rr_peer_t *resolve; + ngx_stream_upstream_rr_peers_t *zone_next; +#endif + + ngx_uint_t total_weight; + ngx_uint_t tries; + + unsigned single:1; + unsigned weighted:1; + + ngx_str_t *name; + + ngx_stream_upstream_rr_peers_t *next; + + ngx_stream_upstream_rr_peer_t *peer; +}; + + +#if (NGX_STREAM_UPSTREAM_ZONE) + +#define ngx_stream_upstream_rr_peers_rlock(peers) \ + \ + if (peers->shpool) { \ + ngx_rwlock_rlock(&peers->rwlock); \ + } + +#define ngx_stream_upstream_rr_peers_wlock(peers) \ + \ + if (peers->shpool) { \ + ngx_rwlock_wlock(&peers->rwlock); \ + } + +#define ngx_stream_upstream_rr_peers_unlock(peers) \ + \ + if (peers->shpool) { \ + ngx_rwlock_unlock(&peers->rwlock); \ + } + + +#define ngx_stream_upstream_rr_peer_lock(peers, peer) \ + \ + if (peers->shpool) { \ + ngx_rwlock_wlock(&peer->lock); \ + } + +#define ngx_stream_upstream_rr_peer_unlock(peers, peer) \ + \ + if (peers->shpool) { \ + ngx_rwlock_unlock(&peer->lock); \ + } + + +#define ngx_stream_upstream_rr_peer_ref(peers, peer) \ + (peer)->refs++; + + +static ngx_inline void +ngx_stream_upstream_rr_peer_free_locked(ngx_stream_upstream_rr_peers_t *peers, + ngx_stream_upstream_rr_peer_t *peer) +{ + if (peer->refs) { + peer->zombie = 1; + return; + } + + ngx_slab_free_locked(peers->shpool, peer->sockaddr); + ngx_slab_free_locked(peers->shpool, peer->name.data); + + if (peer->server.data) { + ngx_slab_free_locked(peers->shpool, peer->server.data); + } + +#if (NGX_STREAM_SSL) + if (peer->ssl_session) { + ngx_slab_free_locked(peers->shpool, peer->ssl_session); + } +#endif + + ngx_slab_free_locked(peers->shpool, peer); +} + + +static ngx_inline void +ngx_stream_upstream_rr_peer_free(ngx_stream_upstream_rr_peers_t *peers, + ngx_stream_upstream_rr_peer_t *peer) +{ + ngx_shmtx_lock(&peers->shpool->mutex); + ngx_stream_upstream_rr_peer_free_locked(peers, peer); + ngx_shmtx_unlock(&peers->shpool->mutex); +} + + +static ngx_inline ngx_int_t +ngx_stream_upstream_rr_peer_unref(ngx_stream_upstream_rr_peers_t *peers, + ngx_stream_upstream_rr_peer_t *peer) +{ + peer->refs--; + + if (peers->shpool == NULL) { + return NGX_OK; + } + + if (peer->refs == 0 && peer->zombie) { + ngx_stream_upstream_rr_peer_free(peers, peer); + return NGX_DONE; + } + + return NGX_OK; +} + +#else + +#define ngx_stream_upstream_rr_peers_rlock(peers) +#define ngx_stream_upstream_rr_peers_wlock(peers) +#define ngx_stream_upstream_rr_peers_unlock(peers) +#define ngx_stream_upstream_rr_peer_lock(peers, peer) +#define ngx_stream_upstream_rr_peer_unlock(peers, peer) +#define ngx_stream_upstream_rr_peer_ref(peers, peer) +#define ngx_stream_upstream_rr_peer_unref(peers, peer) NGX_OK + +#endif + + +typedef struct { + ngx_uint_t config; + ngx_stream_upstream_rr_peers_t *peers; + ngx_stream_upstream_rr_peer_t *current; + uintptr_t *tried; + uintptr_t data; +} ngx_stream_upstream_rr_peer_data_t; + + +ngx_int_t ngx_stream_upstream_init_round_robin(ngx_conf_t *cf, + ngx_stream_upstream_srv_conf_t *us); +ngx_int_t ngx_stream_upstream_init_round_robin_peer(ngx_stream_session_t *s, + ngx_stream_upstream_srv_conf_t *us); +ngx_int_t ngx_stream_upstream_create_round_robin_peer(ngx_stream_session_t *s, + ngx_stream_upstream_resolved_t *ur); +ngx_int_t ngx_stream_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, + void *data); +void ngx_stream_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, + void *data, ngx_uint_t state); + + +#endif /* _NGX_STREAM_UPSTREAM_ROUND_ROBIN_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream_zone_module.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream_zone_module.c new file mode 100644 index 000000000..a6874dc33 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_upstream_zone_module.c @@ -0,0 +1,1005 @@ + +/* + * Copyright (C) Ruslan Ermilov + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +static char *ngx_stream_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static ngx_int_t ngx_stream_upstream_init_zone(ngx_shm_zone_t *shm_zone, + void *data); +static ngx_stream_upstream_rr_peers_t *ngx_stream_upstream_zone_copy_peers( + ngx_slab_pool_t *shpool, ngx_stream_upstream_srv_conf_t *uscf, + ngx_stream_upstream_srv_conf_t *ouscf); +static ngx_stream_upstream_rr_peer_t *ngx_stream_upstream_zone_copy_peer( + ngx_stream_upstream_rr_peers_t *peers, ngx_stream_upstream_rr_peer_t *src); +static ngx_int_t ngx_stream_upstream_zone_preresolve( + ngx_stream_upstream_rr_peer_t *resolve, + ngx_stream_upstream_rr_peers_t *peers, + ngx_stream_upstream_rr_peer_t *oresolve, + ngx_stream_upstream_rr_peers_t *opeers); +static void ngx_stream_upstream_zone_set_single( + ngx_stream_upstream_srv_conf_t *uscf); +static void ngx_stream_upstream_zone_remove_peer_locked( + ngx_stream_upstream_rr_peers_t *peers, ngx_stream_upstream_rr_peer_t *peer); +static ngx_int_t ngx_stream_upstream_zone_init_worker(ngx_cycle_t *cycle); +static void ngx_stream_upstream_zone_resolve_timer(ngx_event_t *event); +static void ngx_stream_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx); + + +static ngx_command_t ngx_stream_upstream_zone_commands[] = { + + { ngx_string("zone"), + NGX_STREAM_UPS_CONF|NGX_CONF_TAKE12, + ngx_stream_upstream_zone, + 0, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_stream_module_t ngx_stream_upstream_zone_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_upstream_zone_module = { + NGX_MODULE_V1, + &ngx_stream_upstream_zone_module_ctx, /* module context */ + ngx_stream_upstream_zone_commands, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + ngx_stream_upstream_zone_init_worker, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static char * +ngx_stream_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ssize_t size; + ngx_str_t *value; + ngx_stream_upstream_srv_conf_t *uscf; + ngx_stream_upstream_main_conf_t *umcf; + + uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module); + umcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_upstream_module); + + value = cf->args->elts; + + if (!value[1].len) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid zone name \"%V\"", &value[1]); + return NGX_CONF_ERROR; + } + + if (cf->args->nelts == 3) { + size = ngx_parse_size(&value[2]); + + if (size == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid zone size \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + + if (size < (ssize_t) (8 * ngx_pagesize)) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "zone \"%V\" is too small", &value[1]); + return NGX_CONF_ERROR; + } + + } else { + size = 0; + } + + uscf->shm_zone = ngx_shared_memory_add(cf, &value[1], size, + &ngx_stream_upstream_module); + if (uscf->shm_zone == NULL) { + return NGX_CONF_ERROR; + } + + uscf->shm_zone->init = ngx_stream_upstream_init_zone; + uscf->shm_zone->data = umcf; + + uscf->shm_zone->noreuse = 1; + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_stream_upstream_init_zone(ngx_shm_zone_t *shm_zone, void *data) +{ + size_t len; + ngx_uint_t i, j; + ngx_slab_pool_t *shpool; + ngx_stream_upstream_rr_peers_t *peers, **peersp; + ngx_stream_upstream_srv_conf_t *uscf, *ouscf, **uscfp, **ouscfp; + ngx_stream_upstream_main_conf_t *umcf, *oumcf; + + shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + umcf = shm_zone->data; + uscfp = umcf->upstreams.elts; + + if (shm_zone->shm.exists) { + peers = shpool->data; + + for (i = 0; i < umcf->upstreams.nelts; i++) { + uscf = uscfp[i]; + + if (uscf->shm_zone != shm_zone) { + continue; + } + + uscf->peer.data = peers; + peers = peers->zone_next; + } + + return NGX_OK; + } + + len = sizeof(" in upstream zone \"\"") + shm_zone->shm.name.len; + + shpool->log_ctx = ngx_slab_alloc(shpool, len); + if (shpool->log_ctx == NULL) { + return NGX_ERROR; + } + + ngx_sprintf(shpool->log_ctx, " in upstream zone \"%V\"%Z", + &shm_zone->shm.name); + + + /* copy peers to shared memory */ + + peersp = (ngx_stream_upstream_rr_peers_t **) (void *) &shpool->data; + oumcf = data; + + for (i = 0; i < umcf->upstreams.nelts; i++) { + uscf = uscfp[i]; + + if (uscf->shm_zone != shm_zone) { + continue; + } + + ouscf = NULL; + + if (oumcf) { + ouscfp = oumcf->upstreams.elts; + + for (j = 0; j < oumcf->upstreams.nelts; j++) { + + if (ouscfp[j]->shm_zone == NULL) { + continue; + } + + if (ouscfp[j]->shm_zone->shm.name.len != shm_zone->shm.name.len + || ngx_memcmp(ouscfp[j]->shm_zone->shm.name.data, + shm_zone->shm.name.data, + shm_zone->shm.name.len) + != 0) + { + continue; + } + + if (ouscfp[j]->host.len == uscf->host.len + && ngx_memcmp(ouscfp[j]->host.data, uscf->host.data, + uscf->host.len) + == 0) + { + ouscf = ouscfp[j]; + break; + } + } + } + + peers = ngx_stream_upstream_zone_copy_peers(shpool, uscf, ouscf); + if (peers == NULL) { + return NGX_ERROR; + } + + *peersp = peers; + peersp = &peers->zone_next; + } + + return NGX_OK; +} + + +static ngx_stream_upstream_rr_peers_t * +ngx_stream_upstream_zone_copy_peers(ngx_slab_pool_t *shpool, + ngx_stream_upstream_srv_conf_t *uscf, ngx_stream_upstream_srv_conf_t *ouscf) +{ + ngx_str_t *name; + ngx_uint_t *config; + ngx_stream_upstream_rr_peer_t *peer, **peerp; + ngx_stream_upstream_rr_peers_t *peers, *opeers, *backup; + + opeers = (ouscf ? ouscf->peer.data : NULL); + + config = ngx_slab_calloc(shpool, sizeof(ngx_uint_t)); + if (config == NULL) { + return NULL; + } + + peers = ngx_slab_alloc(shpool, sizeof(ngx_stream_upstream_rr_peers_t)); + if (peers == NULL) { + return NULL; + } + + ngx_memcpy(peers, uscf->peer.data, sizeof(ngx_stream_upstream_rr_peers_t)); + + name = ngx_slab_alloc(shpool, sizeof(ngx_str_t)); + if (name == NULL) { + return NULL; + } + + name->data = ngx_slab_alloc(shpool, peers->name->len); + if (name->data == NULL) { + return NULL; + } + + ngx_memcpy(name->data, peers->name->data, peers->name->len); + name->len = peers->name->len; + + peers->name = name; + + peers->shpool = shpool; + peers->config = config; + + for (peerp = &peers->peer; *peerp; peerp = &peer->next) { + /* pool is unlocked */ + peer = ngx_stream_upstream_zone_copy_peer(peers, *peerp); + if (peer == NULL) { + return NULL; + } + + *peerp = peer; + (*peers->config)++; + } + + for (peerp = &peers->resolve; *peerp; peerp = &peer->next) { + peer = ngx_stream_upstream_zone_copy_peer(peers, *peerp); + if (peer == NULL) { + return NULL; + } + + *peerp = peer; + (*peers->config)++; + } + + if (opeers) { + + if (ngx_stream_upstream_zone_preresolve(peers->resolve, peers, + opeers->resolve, opeers) + != NGX_OK) + { + return NULL; + } + } + + if (peers->next == NULL) { + goto done; + } + + backup = ngx_slab_alloc(shpool, sizeof(ngx_stream_upstream_rr_peers_t)); + if (backup == NULL) { + return NULL; + } + + ngx_memcpy(backup, peers->next, sizeof(ngx_stream_upstream_rr_peers_t)); + + backup->name = name; + + backup->shpool = shpool; + backup->config = config; + + for (peerp = &backup->peer; *peerp; peerp = &peer->next) { + /* pool is unlocked */ + peer = ngx_stream_upstream_zone_copy_peer(backup, *peerp); + if (peer == NULL) { + return NULL; + } + + *peerp = peer; + (*backup->config)++; + } + + for (peerp = &backup->resolve; *peerp; peerp = &peer->next) { + peer = ngx_stream_upstream_zone_copy_peer(backup, *peerp); + if (peer == NULL) { + return NULL; + } + + *peerp = peer; + (*backup->config)++; + } + + peers->next = backup; + + if (opeers && opeers->next) { + + if (ngx_stream_upstream_zone_preresolve(peers->resolve, backup, + opeers->resolve, opeers->next) + != NGX_OK) + { + return NULL; + } + + if (ngx_stream_upstream_zone_preresolve(backup->resolve, backup, + opeers->next->resolve, + opeers->next) + != NGX_OK) + { + return NULL; + } + } + +done: + + uscf->peer.data = peers; + + ngx_stream_upstream_zone_set_single(uscf); + + return peers; +} + + +static ngx_stream_upstream_rr_peer_t * +ngx_stream_upstream_zone_copy_peer(ngx_stream_upstream_rr_peers_t *peers, + ngx_stream_upstream_rr_peer_t *src) +{ + ngx_slab_pool_t *pool; + ngx_stream_upstream_rr_peer_t *dst; + + pool = peers->shpool; + + dst = ngx_slab_calloc_locked(pool, sizeof(ngx_stream_upstream_rr_peer_t)); + if (dst == NULL) { + return NULL; + } + + if (src) { + ngx_memcpy(dst, src, sizeof(ngx_stream_upstream_rr_peer_t)); + dst->sockaddr = NULL; + dst->name.data = NULL; + dst->server.data = NULL; + dst->host = NULL; + } + + dst->sockaddr = ngx_slab_calloc_locked(pool, sizeof(ngx_sockaddr_t)); + if (dst->sockaddr == NULL) { + goto failed; + } + + dst->name.data = ngx_slab_calloc_locked(pool, NGX_SOCKADDR_STRLEN); + if (dst->name.data == NULL) { + goto failed; + } + + if (src) { + ngx_memcpy(dst->sockaddr, src->sockaddr, src->socklen); + ngx_memcpy(dst->name.data, src->name.data, src->name.len); + + dst->server.data = ngx_slab_alloc_locked(pool, src->server.len); + if (dst->server.data == NULL) { + goto failed; + } + + ngx_memcpy(dst->server.data, src->server.data, src->server.len); + + if (src->host) { + dst->host = ngx_slab_calloc_locked(pool, + sizeof(ngx_stream_upstream_host_t)); + if (dst->host == NULL) { + goto failed; + } + + dst->host->name.data = ngx_slab_alloc_locked(pool, + src->host->name.len); + if (dst->host->name.data == NULL) { + goto failed; + } + + dst->host->peers = peers; + dst->host->peer = dst; + + dst->host->name.len = src->host->name.len; + ngx_memcpy(dst->host->name.data, src->host->name.data, + src->host->name.len); + + if (src->host->service.len) { + dst->host->service.data = ngx_slab_alloc_locked(pool, + src->host->service.len); + if (dst->host->service.data == NULL) { + goto failed; + } + + dst->host->service.len = src->host->service.len; + ngx_memcpy(dst->host->service.data, src->host->service.data, + src->host->service.len); + } + } + } + + return dst; + +failed: + + if (dst->host) { + if (dst->host->name.data) { + ngx_slab_free_locked(pool, dst->host->name.data); + } + + ngx_slab_free_locked(pool, dst->host); + } + + if (dst->server.data) { + ngx_slab_free_locked(pool, dst->server.data); + } + + if (dst->name.data) { + ngx_slab_free_locked(pool, dst->name.data); + } + + if (dst->sockaddr) { + ngx_slab_free_locked(pool, dst->sockaddr); + } + + ngx_slab_free_locked(pool, dst); + + return NULL; +} + + +static ngx_int_t +ngx_stream_upstream_zone_preresolve(ngx_stream_upstream_rr_peer_t *resolve, + ngx_stream_upstream_rr_peers_t *peers, + ngx_stream_upstream_rr_peer_t *oresolve, + ngx_stream_upstream_rr_peers_t *opeers) +{ + in_port_t port; + ngx_str_t *server; + ngx_stream_upstream_host_t *host; + ngx_stream_upstream_rr_peer_t *peer, *template, *opeer, **peerp; + + if (resolve == NULL || oresolve == NULL) { + return NGX_OK; + } + + for (peerp = &peers->peer; *peerp; peerp = &(*peerp)->next) { + /* void */ + } + + ngx_stream_upstream_rr_peers_rlock(opeers); + + for (template = resolve; template; template = template->next) { + for (opeer = oresolve; opeer; opeer = opeer->next) { + + if (opeer->host->name.len != template->host->name.len + || ngx_memcmp(opeer->host->name.data, + template->host->name.data, + template->host->name.len) + != 0) + { + continue; + } + + if (opeer->host->service.len != template->host->service.len + || ngx_memcmp(opeer->host->service.data, + template->host->service.data, + template->host->service.len) + != 0) + { + continue; + } + + host = opeer->host; + + for (opeer = opeers->peer; opeer; opeer = opeer->next) { + + if (opeer->host != host) { + continue; + } + + peer = ngx_stream_upstream_zone_copy_peer(peers, NULL); + if (peer == NULL) { + ngx_stream_upstream_rr_peers_unlock(opeers); + return NGX_ERROR; + } + + ngx_memcpy(peer->sockaddr, opeer->sockaddr, opeer->socklen); + + if (template->host->service.len == 0) { + port = ngx_inet_get_port(template->sockaddr); + ngx_inet_set_port(peer->sockaddr, port); + } + + peer->socklen = opeer->socklen; + + peer->name.len = ngx_sock_ntop(peer->sockaddr, peer->socklen, + peer->name.data, + NGX_SOCKADDR_STRLEN, 1); + + peer->host = template->host; + + template->host->valid = host->valid; + + server = template->host->service.len ? &opeer->server + : &template->server; + + peer->server.data = ngx_slab_alloc(peers->shpool, server->len); + if (peer->server.data == NULL) { + ngx_stream_upstream_rr_peers_unlock(opeers); + return NGX_ERROR; + } + + ngx_memcpy(peer->server.data, server->data, server->len); + peer->server.len = server->len; + + if (host->service.len == 0) { + peer->weight = template->weight; + + } else { + peer->weight = (template->weight != 1 ? template->weight + : opeer->weight); + } + + peer->effective_weight = peer->weight; + peer->max_conns = template->max_conns; + peer->max_fails = template->max_fails; + peer->fail_timeout = template->fail_timeout; + peer->down = template->down; + + (*peers->config)++; + + *peerp = peer; + peerp = &peer->next; + + peers->number++; + peers->tries += (peer->down == 0); + peers->total_weight += peer->weight; + peers->weighted = (peers->total_weight != peers->number); + } + + break; + } + } + + ngx_stream_upstream_rr_peers_unlock(opeers); + return NGX_OK; +} + + +static void +ngx_stream_upstream_zone_set_single(ngx_stream_upstream_srv_conf_t *uscf) +{ + ngx_stream_upstream_rr_peers_t *peers; + + peers = uscf->peer.data; + + if (peers->number == 1 + && (peers->next == NULL || peers->next->number == 0)) + { + peers->single = 1; + + } else { + peers->single = 0; + } +} + + +static void +ngx_stream_upstream_zone_remove_peer_locked( + ngx_stream_upstream_rr_peers_t *peers, ngx_stream_upstream_rr_peer_t *peer) +{ + peers->total_weight -= peer->weight; + peers->number--; + peers->tries -= (peer->down == 0); + (*peers->config)++; + peers->weighted = (peers->total_weight != peers->number); + + ngx_stream_upstream_rr_peer_free(peers, peer); +} + + +static ngx_int_t +ngx_stream_upstream_zone_init_worker(ngx_cycle_t *cycle) +{ + time_t now; + ngx_msec_t timer; + ngx_uint_t i; + ngx_event_t *event; + ngx_stream_upstream_rr_peer_t *peer; + ngx_stream_upstream_rr_peers_t *peers; + ngx_stream_upstream_srv_conf_t *uscf, **uscfp; + ngx_stream_upstream_main_conf_t *umcf; + + if (ngx_process != NGX_PROCESS_WORKER + && ngx_process != NGX_PROCESS_SINGLE) + { + return NGX_OK; + } + + now = ngx_time(); + umcf = ngx_stream_cycle_get_module_main_conf(cycle, + ngx_stream_upstream_module); + + if (umcf == NULL) { + return NGX_OK; + } + + uscfp = umcf->upstreams.elts; + + for (i = 0; i < umcf->upstreams.nelts; i++) { + + uscf = uscfp[i]; + + if (uscf->shm_zone == NULL) { + continue; + } + + peers = uscf->peer.data; + + do { + ngx_stream_upstream_rr_peers_wlock(peers); + + for (peer = peers->resolve; peer; peer = peer->next) { + + if (peer->host->worker != ngx_worker) { + continue; + } + + event = &peer->host->event; + ngx_memzero(event, sizeof(ngx_event_t)); + + event->data = uscf; + event->handler = ngx_stream_upstream_zone_resolve_timer; + event->log = cycle->log; + event->cancelable = 1; + + timer = (peer->host->valid > now) + ? (ngx_msec_t) 1000 * (peer->host->valid - now) : 1; + + ngx_add_timer(event, timer); + } + + ngx_stream_upstream_rr_peers_unlock(peers); + + peers = peers->next; + + } while (peers); + } + + return NGX_OK; +} + + +static void +ngx_stream_upstream_zone_resolve_timer(ngx_event_t *event) +{ + ngx_resolver_ctx_t *ctx; + ngx_stream_upstream_host_t *host; + ngx_stream_upstream_srv_conf_t *uscf; + + host = (ngx_stream_upstream_host_t *) event; + uscf = event->data; + + ctx = ngx_resolve_start(uscf->resolver, NULL); + if (ctx == NULL) { + goto retry; + } + + if (ctx == NGX_NO_RESOLVER) { + ngx_log_error(NGX_LOG_ERR, event->log, 0, + "no resolver defined to resolve %V", &host->name); + return; + } + + ctx->name = host->name; + ctx->handler = ngx_stream_upstream_zone_resolve_handler; + ctx->data = host; + ctx->timeout = uscf->resolver_timeout; + ctx->service = host->service; + ctx->cancelable = 1; + + if (ngx_resolve_name(ctx) == NGX_OK) { + return; + } + +retry: + + ngx_add_timer(event, ngx_max(uscf->resolver_timeout, 1000)); +} + + +#define ngx_stream_upstream_zone_addr_marked(addr) \ + ((uintptr_t) (addr)->sockaddr & 1) + +#define ngx_stream_upstream_zone_mark_addr(addr) \ + (addr)->sockaddr = (struct sockaddr *) ((uintptr_t) (addr)->sockaddr | 1) + +#define ngx_stream_upstream_zone_unmark_addr(addr) \ + (addr)->sockaddr = \ + (struct sockaddr *) ((uintptr_t) (addr)->sockaddr & ~((uintptr_t) 1)) + +static void +ngx_stream_upstream_zone_resolve_handler(ngx_resolver_ctx_t *ctx) +{ + time_t now; + u_short min_priority; + in_port_t port; + ngx_str_t *server; + ngx_msec_t timer; + ngx_uint_t i, j, backup, addr_backup; + ngx_event_t *event; + ngx_resolver_addr_t *addr; + ngx_resolver_srv_name_t *srv; + ngx_stream_upstream_host_t *host; + ngx_stream_upstream_rr_peer_t *peer, *template, **peerp; + ngx_stream_upstream_rr_peers_t *peers; + ngx_stream_upstream_srv_conf_t *uscf; + + host = ctx->data; + event = &host->event; + uscf = event->data; + peers = host->peers; + template = host->peer; + + ngx_stream_upstream_rr_peers_wlock(peers); + + now = ngx_time(); + + for (i = 0; i < ctx->nsrvs; i++) { + srv = &ctx->srvs[i]; + + if (srv->state) { + ngx_log_error(NGX_LOG_ERR, event->log, 0, + "%V could not be resolved (%i: %s) " + "while resolving service %V of %V", + &srv->name, srv->state, + ngx_resolver_strerror(srv->state), &ctx->service, + &ctx->name); + } + } + + if (ctx->state) { + if (ctx->service.len) { + ngx_log_error(NGX_LOG_ERR, event->log, 0, + "service %V of %V could not be resolved (%i: %s)", + &ctx->service, &ctx->name, ctx->state, + ngx_resolver_strerror(ctx->state)); + + } else { + ngx_log_error(NGX_LOG_ERR, event->log, 0, + "%V could not be resolved (%i: %s)", + &ctx->name, ctx->state, + ngx_resolver_strerror(ctx->state)); + } + + if (ctx->state != NGX_RESOLVE_NXDOMAIN) { + ngx_stream_upstream_rr_peers_unlock(peers); + + ngx_resolve_name_done(ctx); + + ngx_add_timer(event, ngx_max(uscf->resolver_timeout, 1000)); + return; + } + + /* NGX_RESOLVE_NXDOMAIN */ + + ctx->naddrs = 0; + } + + backup = 0; + min_priority = 65535; + + for (i = 0; i < ctx->naddrs; i++) { + min_priority = ngx_min(ctx->addrs[i].priority, min_priority); + } + +#if (NGX_DEBUG) + { + u_char text[NGX_SOCKADDR_STRLEN]; + size_t len; + + for (i = 0; i < ctx->naddrs; i++) { + len = ngx_sock_ntop(ctx->addrs[i].sockaddr, ctx->addrs[i].socklen, + text, NGX_SOCKADDR_STRLEN, 1); + + ngx_log_debug7(NGX_LOG_DEBUG_STREAM, event->log, 0, + "name %V was resolved to %*s " + "s:\"%V\" n:\"%V\" w:%d %s", + &host->name, len, text, &host->service, + &ctx->addrs[i].name, ctx->addrs[i].weight, + ctx->addrs[i].priority != min_priority ? "backup" : ""); + } + } +#endif + +again: + + for (peerp = &peers->peer; *peerp; /* void */ ) { + peer = *peerp; + + if (peer->host != host) { + goto next; + } + + for (j = 0; j < ctx->naddrs; j++) { + + addr = &ctx->addrs[j]; + + addr_backup = (addr->priority != min_priority); + if (addr_backup != backup) { + continue; + } + + if (ngx_stream_upstream_zone_addr_marked(addr)) { + continue; + } + + if (ngx_cmp_sockaddr(peer->sockaddr, peer->socklen, + addr->sockaddr, addr->socklen, + host->service.len != 0) + != NGX_OK) + { + continue; + } + + if (host->service.len) { + if (addr->name.len != peer->server.len + || ngx_strncmp(addr->name.data, peer->server.data, + addr->name.len)) + { + continue; + } + + if (template->weight == 1 && addr->weight != peer->weight) { + continue; + } + } + + ngx_stream_upstream_zone_mark_addr(addr); + + goto next; + } + + *peerp = peer->next; + ngx_stream_upstream_zone_remove_peer_locked(peers, peer); + + ngx_stream_upstream_zone_set_single(uscf); + + continue; + + next: + + peerp = &peer->next; + } + + for (i = 0; i < ctx->naddrs; i++) { + + addr = &ctx->addrs[i]; + + addr_backup = (addr->priority != min_priority); + if (addr_backup != backup) { + continue; + } + + if (ngx_stream_upstream_zone_addr_marked(addr)) { + ngx_stream_upstream_zone_unmark_addr(addr); + continue; + } + + ngx_shmtx_lock(&peers->shpool->mutex); + peer = ngx_stream_upstream_zone_copy_peer(peers, NULL); + ngx_shmtx_unlock(&peers->shpool->mutex); + + if (peer == NULL) { + ngx_log_error(NGX_LOG_ERR, event->log, 0, + "cannot add new server to upstream \"%V\", " + "memory exhausted", peers->name); + goto done; + } + + ngx_memcpy(peer->sockaddr, addr->sockaddr, addr->socklen); + + if (host->service.len == 0) { + port = ngx_inet_get_port(template->sockaddr); + ngx_inet_set_port(peer->sockaddr, port); + } + + peer->socklen = addr->socklen; + + peer->name.len = ngx_sock_ntop(peer->sockaddr, peer->socklen, + peer->name.data, NGX_SOCKADDR_STRLEN, 1); + + peer->host = template->host; + + server = host->service.len ? &addr->name : &template->server; + + peer->server.data = ngx_slab_alloc(peers->shpool, server->len); + if (peer->server.data == NULL) { + ngx_stream_upstream_rr_peer_free(peers, peer); + + ngx_log_error(NGX_LOG_ERR, event->log, 0, + "cannot add new server to upstream \"%V\", " + "memory exhausted", peers->name); + goto done; + } + + peer->server.len = server->len; + ngx_memcpy(peer->server.data, server->data, server->len); + + if (host->service.len == 0) { + peer->weight = template->weight; + + } else { + peer->weight = (template->weight != 1 ? template->weight + : addr->weight); + } + + peer->effective_weight = peer->weight; + peer->max_conns = template->max_conns; + peer->max_fails = template->max_fails; + peer->fail_timeout = template->fail_timeout; + peer->down = template->down; + + *peerp = peer; + peerp = &peer->next; + + peers->number++; + peers->tries += (peer->down == 0); + peers->total_weight += peer->weight; + peers->weighted = (peers->total_weight != peers->number); + (*peers->config)++; + + ngx_stream_upstream_zone_set_single(uscf); + } + + if (host->service.len && peers->next) { + ngx_stream_upstream_rr_peers_unlock(peers); + + peers = peers->next; + backup = 1; + + ngx_stream_upstream_rr_peers_wlock(peers); + + goto again; + } + +done: + + host->valid = ctx->valid; + + ngx_stream_upstream_rr_peers_unlock(peers); + + while (++i < ctx->naddrs) { + ngx_stream_upstream_zone_unmark_addr(&ctx->addrs[i]); + } + + timer = (ngx_msec_t) 1000 * (ctx->valid > now ? ctx->valid - now + 1 : 1); + + ngx_resolve_name_done(ctx); + + ngx_add_timer(event, timer); +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_variables.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_variables.c new file mode 100644 index 000000000..4658104e1 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_variables.c @@ -0,0 +1,1340 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include + +static ngx_stream_variable_t *ngx_stream_add_prefix_variable(ngx_conf_t *cf, + ngx_str_t *name, ngx_uint_t flags); + +static ngx_int_t ngx_stream_variable_binary_remote_addr( + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_remote_addr(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_remote_port(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_proxy_protocol_addr( + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_proxy_protocol_port( + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_proxy_protocol_tlv( + ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_server_addr(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_server_port(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_server_name(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_bytes(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_session_time(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_status(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_connection(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); + +static ngx_int_t ngx_stream_variable_nginx_version(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_hostname(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_pid(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_msec(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_time_iso8601(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_time_local(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_stream_variable_protocol(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); + + +static ngx_stream_variable_t ngx_stream_core_variables[] = { + + { ngx_string("binary_remote_addr"), NULL, + ngx_stream_variable_binary_remote_addr, 0, 0, 0 }, + + { ngx_string("remote_addr"), NULL, + ngx_stream_variable_remote_addr, 0, 0, 0 }, + + { ngx_string("remote_port"), NULL, + ngx_stream_variable_remote_port, 0, 0, 0 }, + + { ngx_string("proxy_protocol_addr"), NULL, + ngx_stream_variable_proxy_protocol_addr, + offsetof(ngx_proxy_protocol_t, src_addr), 0, 0 }, + + { ngx_string("proxy_protocol_port"), NULL, + ngx_stream_variable_proxy_protocol_port, + offsetof(ngx_proxy_protocol_t, src_port), 0, 0 }, + + { ngx_string("proxy_protocol_server_addr"), NULL, + ngx_stream_variable_proxy_protocol_addr, + offsetof(ngx_proxy_protocol_t, dst_addr), 0, 0 }, + + { ngx_string("proxy_protocol_server_port"), NULL, + ngx_stream_variable_proxy_protocol_port, + offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 }, + + { ngx_string("proxy_protocol_tlv_"), NULL, + ngx_stream_variable_proxy_protocol_tlv, + 0, NGX_STREAM_VAR_PREFIX, 0 }, + + { ngx_string("server_addr"), NULL, + ngx_stream_variable_server_addr, 0, 0, 0 }, + + { ngx_string("server_port"), NULL, + ngx_stream_variable_server_port, 0, 0, 0 }, + + { ngx_string("server_name"), NULL, ngx_stream_variable_server_name, + 0, 0, 0 }, + + { ngx_string("bytes_sent"), NULL, ngx_stream_variable_bytes, + 0, 0, 0 }, + + { ngx_string("bytes_received"), NULL, ngx_stream_variable_bytes, + 1, 0, 0 }, + + { ngx_string("session_time"), NULL, ngx_stream_variable_session_time, + 0, NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + { ngx_string("status"), NULL, ngx_stream_variable_status, + 0, NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + { ngx_string("connection"), NULL, + ngx_stream_variable_connection, 0, 0, 0 }, + + { ngx_string("nginx_version"), NULL, ngx_stream_variable_nginx_version, + 0, 0, 0 }, + + { ngx_string("hostname"), NULL, ngx_stream_variable_hostname, + 0, 0, 0 }, + + { ngx_string("pid"), NULL, ngx_stream_variable_pid, + 0, 0, 0 }, + + { ngx_string("msec"), NULL, ngx_stream_variable_msec, + 0, NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + { ngx_string("time_iso8601"), NULL, ngx_stream_variable_time_iso8601, + 0, NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + { ngx_string("time_local"), NULL, ngx_stream_variable_time_local, + 0, NGX_STREAM_VAR_NOCACHEABLE, 0 }, + + { ngx_string("protocol"), NULL, + ngx_stream_variable_protocol, 0, 0, 0 }, + + ngx_stream_null_variable +}; + + +ngx_stream_variable_value_t ngx_stream_variable_null_value = + ngx_stream_variable(""); +ngx_stream_variable_value_t ngx_stream_variable_true_value = + ngx_stream_variable("1"); + + +static ngx_uint_t ngx_stream_variable_depth = 100; + + +ngx_stream_variable_t * +ngx_stream_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags) +{ + ngx_int_t rc; + ngx_uint_t i; + ngx_hash_key_t *key; + ngx_stream_variable_t *v; + ngx_stream_core_main_conf_t *cmcf; + + if (name->len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"$\""); + return NULL; + } + + if (flags & NGX_STREAM_VAR_PREFIX) { + return ngx_stream_add_prefix_variable(cf, name, flags); + } + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + key = cmcf->variables_keys->keys.elts; + for (i = 0; i < cmcf->variables_keys->keys.nelts; i++) { + if (name->len != key[i].key.len + || ngx_strncasecmp(name->data, key[i].key.data, name->len) != 0) + { + continue; + } + + v = key[i].value; + + if (!(v->flags & NGX_STREAM_VAR_CHANGEABLE)) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the duplicate \"%V\" variable", name); + return NULL; + } + + if (!(flags & NGX_STREAM_VAR_WEAK)) { + v->flags &= ~NGX_STREAM_VAR_WEAK; + } + + return v; + } + + v = ngx_palloc(cf->pool, sizeof(ngx_stream_variable_t)); + if (v == NULL) { + return NULL; + } + + v->name.len = name->len; + v->name.data = ngx_pnalloc(cf->pool, name->len); + if (v->name.data == NULL) { + return NULL; + } + + ngx_strlow(v->name.data, name->data, name->len); + + v->set_handler = NULL; + v->get_handler = NULL; + v->data = 0; + v->flags = flags; + v->index = 0; + + rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v, 0); + + if (rc == NGX_ERROR) { + return NULL; + } + + if (rc == NGX_BUSY) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "conflicting variable name \"%V\"", name); + return NULL; + } + + return v; +} + + +static ngx_stream_variable_t * +ngx_stream_add_prefix_variable(ngx_conf_t *cf, ngx_str_t *name, + ngx_uint_t flags) +{ + ngx_uint_t i; + ngx_stream_variable_t *v; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + v = cmcf->prefix_variables.elts; + for (i = 0; i < cmcf->prefix_variables.nelts; i++) { + if (name->len != v[i].name.len + || ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0) + { + continue; + } + + v = &v[i]; + + if (!(v->flags & NGX_STREAM_VAR_CHANGEABLE)) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the duplicate \"%V\" variable", name); + return NULL; + } + + if (!(flags & NGX_STREAM_VAR_WEAK)) { + v->flags &= ~NGX_STREAM_VAR_WEAK; + } + + return v; + } + + v = ngx_array_push(&cmcf->prefix_variables); + if (v == NULL) { + return NULL; + } + + v->name.len = name->len; + v->name.data = ngx_pnalloc(cf->pool, name->len); + if (v->name.data == NULL) { + return NULL; + } + + ngx_strlow(v->name.data, name->data, name->len); + + v->set_handler = NULL; + v->get_handler = NULL; + v->data = 0; + v->flags = flags; + v->index = 0; + + return v; +} + + +ngx_int_t +ngx_stream_get_variable_index(ngx_conf_t *cf, ngx_str_t *name) +{ + ngx_uint_t i; + ngx_stream_variable_t *v; + ngx_stream_core_main_conf_t *cmcf; + + if (name->len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"$\""); + return NGX_ERROR; + } + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + v = cmcf->variables.elts; + + if (v == NULL) { + if (ngx_array_init(&cmcf->variables, cf->pool, 4, + sizeof(ngx_stream_variable_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + } else { + for (i = 0; i < cmcf->variables.nelts; i++) { + if (name->len != v[i].name.len + || ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0) + { + continue; + } + + return i; + } + } + + v = ngx_array_push(&cmcf->variables); + if (v == NULL) { + return NGX_ERROR; + } + + v->name.len = name->len; + v->name.data = ngx_pnalloc(cf->pool, name->len); + if (v->name.data == NULL) { + return NGX_ERROR; + } + + ngx_strlow(v->name.data, name->data, name->len); + + v->set_handler = NULL; + v->get_handler = NULL; + v->data = 0; + v->flags = 0; + v->index = cmcf->variables.nelts - 1; + + return v->index; +} + + +ngx_stream_variable_value_t * +ngx_stream_get_indexed_variable(ngx_stream_session_t *s, ngx_uint_t index) +{ + ngx_stream_variable_t *v; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); + + if (cmcf->variables.nelts <= index) { + ngx_log_error(NGX_LOG_ALERT, s->connection->log, 0, + "unknown variable index: %ui", index); + return NULL; + } + + if (s->variables[index].not_found || s->variables[index].valid) { + return &s->variables[index]; + } + + v = cmcf->variables.elts; + + if (ngx_stream_variable_depth == 0) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "cycle while evaluating variable \"%V\"", + &v[index].name); + return NULL; + } + + ngx_stream_variable_depth--; + + if (v[index].get_handler(s, &s->variables[index], v[index].data) + == NGX_OK) + { + ngx_stream_variable_depth++; + + if (v[index].flags & NGX_STREAM_VAR_NOCACHEABLE) { + s->variables[index].no_cacheable = 1; + } + + return &s->variables[index]; + } + + ngx_stream_variable_depth++; + + s->variables[index].valid = 0; + s->variables[index].not_found = 1; + + return NULL; +} + + +ngx_stream_variable_value_t * +ngx_stream_get_flushed_variable(ngx_stream_session_t *s, ngx_uint_t index) +{ + ngx_stream_variable_value_t *v; + + v = &s->variables[index]; + + if (v->valid || v->not_found) { + if (!v->no_cacheable) { + return v; + } + + v->valid = 0; + v->not_found = 0; + } + + return ngx_stream_get_indexed_variable(s, index); +} + + +ngx_stream_variable_value_t * +ngx_stream_get_variable(ngx_stream_session_t *s, ngx_str_t *name, + ngx_uint_t key) +{ + size_t len; + ngx_uint_t i, n; + ngx_stream_variable_t *v; + ngx_stream_variable_value_t *vv; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); + + v = ngx_hash_find(&cmcf->variables_hash, key, name->data, name->len); + + if (v) { + if (v->flags & NGX_STREAM_VAR_INDEXED) { + return ngx_stream_get_flushed_variable(s, v->index); + } + + if (ngx_stream_variable_depth == 0) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "cycle while evaluating variable \"%V\"", name); + return NULL; + } + + ngx_stream_variable_depth--; + + vv = ngx_palloc(s->connection->pool, + sizeof(ngx_stream_variable_value_t)); + + if (vv && v->get_handler(s, vv, v->data) == NGX_OK) { + ngx_stream_variable_depth++; + return vv; + } + + ngx_stream_variable_depth++; + return NULL; + } + + vv = ngx_palloc(s->connection->pool, sizeof(ngx_stream_variable_value_t)); + if (vv == NULL) { + return NULL; + } + + len = 0; + + v = cmcf->prefix_variables.elts; + n = cmcf->prefix_variables.nelts; + + for (i = 0; i < cmcf->prefix_variables.nelts; i++) { + if (name->len >= v[i].name.len && name->len > len + && ngx_strncmp(name->data, v[i].name.data, v[i].name.len) == 0) + { + len = v[i].name.len; + n = i; + } + } + + if (n != cmcf->prefix_variables.nelts) { + if (v[n].get_handler(s, vv, (uintptr_t) name) == NGX_OK) { + return vv; + } + + return NULL; + } + + vv->not_found = 1; + + return vv; +} + + +static ngx_int_t +ngx_stream_variable_binary_remote_addr(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + switch (s->connection->sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) s->connection->sockaddr; + + v->len = sizeof(struct in6_addr); + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = sin6->sin6_addr.s6_addr; + + break; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + + v->len = s->connection->addr_text.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = s->connection->addr_text.data; + + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) s->connection->sockaddr; + + v->len = sizeof(in_addr_t); + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) &sin->sin_addr; + + break; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_remote_addr(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + v->len = s->connection->addr_text.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = s->connection->addr_text.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_remote_port(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_uint_t port; + + v->len = 0; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + v->data = ngx_pnalloc(s->connection->pool, sizeof("65535") - 1); + if (v->data == NULL) { + return NGX_ERROR; + } + + port = ngx_inet_get_port(s->connection->sockaddr); + + if (port > 0 && port < 65536) { + v->len = ngx_sprintf(v->data, "%ui", port) - v->data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_proxy_protocol_addr(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_str_t *addr; + ngx_proxy_protocol_t *pp; + + pp = s->connection->proxy_protocol; + if (pp == NULL) { + v->not_found = 1; + return NGX_OK; + } + + addr = (ngx_str_t *) ((char *) pp + data); + + v->len = addr->len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = addr->data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_proxy_protocol_port(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_uint_t port; + ngx_proxy_protocol_t *pp; + + pp = s->connection->proxy_protocol; + if (pp == NULL) { + v->not_found = 1; + return NGX_OK; + } + + v->len = 0; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + v->data = ngx_pnalloc(s->connection->pool, sizeof("65535") - 1); + if (v->data == NULL) { + return NGX_ERROR; + } + + port = *(in_port_t *) ((char *) pp + data); + + if (port > 0 && port < 65536) { + v->len = ngx_sprintf(v->data, "%ui", port) - v->data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_proxy_protocol_tlv(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_str_t *name = (ngx_str_t *) data; + + ngx_int_t rc; + ngx_str_t tlv, value; + + tlv.len = name->len - (sizeof("proxy_protocol_tlv_") - 1); + tlv.data = name->data + sizeof("proxy_protocol_tlv_") - 1; + + rc = ngx_proxy_protocol_get_tlv(s->connection, &tlv, &value); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc == NGX_DECLINED) { + v->not_found = 1; + return NGX_OK; + } + + v->len = value.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = value.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_server_addr(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_str_t str; + u_char addr[NGX_SOCKADDR_STRLEN]; + + str.len = NGX_SOCKADDR_STRLEN; + str.data = addr; + + if (ngx_connection_local_sockaddr(s->connection, &str, 0) != NGX_OK) { + return NGX_ERROR; + } + + str.data = ngx_pnalloc(s->connection->pool, str.len); + if (str.data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(str.data, addr, str.len); + + v->len = str.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = str.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_server_port(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_uint_t port; + + v->len = 0; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + if (ngx_connection_local_sockaddr(s->connection, NULL, 0) != NGX_OK) { + return NGX_ERROR; + } + + v->data = ngx_pnalloc(s->connection->pool, sizeof("65535") - 1); + if (v->data == NULL) { + return NGX_ERROR; + } + + port = ngx_inet_get_port(s->connection->local_sockaddr); + + if (port > 0 && port < 65536) { + v->len = ngx_sprintf(v->data, "%ui", port) - v->data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_server_name(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + ngx_stream_core_srv_conf_t *cscf; + + cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); + + v->len = cscf->server_name.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = cscf->server_name.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_bytes(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + p = ngx_pnalloc(s->connection->pool, NGX_OFF_T_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + if (data == 1) { + v->len = ngx_sprintf(p, "%O", s->received) - p; + + } else { + v->len = ngx_sprintf(p, "%O", s->connection->sent) - p; + } + + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_session_time(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + u_char *p; + ngx_time_t *tp; + ngx_msec_int_t ms; + + p = ngx_pnalloc(s->connection->pool, NGX_TIME_T_LEN + 4); + if (p == NULL) { + return NGX_ERROR; + } + + tp = ngx_timeofday(); + + ms = (ngx_msec_int_t) + ((tp->sec - s->start_sec) * 1000 + (tp->msec - s->start_msec)); + ms = ngx_max(ms, 0); + + v->len = ngx_sprintf(p, "%T.%03M", (time_t) ms / 1000, ms % 1000) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_status(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + v->data = ngx_pnalloc(s->connection->pool, NGX_INT_T_LEN); + if (v->data == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(v->data, "%03ui", s->status) - v->data; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_connection(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + p = ngx_pnalloc(s->connection->pool, NGX_ATOMIC_T_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(p, "%uA", s->connection->number) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_nginx_version(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + v->len = sizeof(NGINX_VERSION) - 1; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) NGINX_VERSION; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_hostname(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + v->len = ngx_cycle->hostname.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = ngx_cycle->hostname.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_pid(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + p = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(p, "%P", ngx_pid) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_msec(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + u_char *p; + ngx_time_t *tp; + + p = ngx_pnalloc(s->connection->pool, NGX_TIME_T_LEN + 4); + if (p == NULL) { + return NGX_ERROR; + } + + tp = ngx_timeofday(); + + v->len = ngx_sprintf(p, "%T.%03M", tp->sec, tp->msec) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_time_iso8601(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + p = ngx_pnalloc(s->connection->pool, ngx_cached_http_log_iso8601.len); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, ngx_cached_http_log_iso8601.data, + ngx_cached_http_log_iso8601.len); + + v->len = ngx_cached_http_log_iso8601.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_time_local(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + u_char *p; + + p = ngx_pnalloc(s->connection->pool, ngx_cached_http_log_time.len); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, ngx_cached_http_log_time.data, ngx_cached_http_log_time.len); + + v->len = ngx_cached_http_log_time.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_variable_protocol(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + v->len = 3; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = (u_char *) (s->connection->type == SOCK_DGRAM ? "UDP" : "TCP"); + + return NGX_OK; +} + + +void * +ngx_stream_map_find(ngx_stream_session_t *s, ngx_stream_map_t *map, + ngx_str_t *match) +{ + void *value; + u_char *low; + size_t len; + ngx_uint_t key; + + len = match->len; + + if (len) { + low = ngx_pnalloc(s->connection->pool, len); + if (low == NULL) { + return NULL; + } + + } else { + low = NULL; + } + + key = ngx_hash_strlow(low, match->data, len); + + value = ngx_hash_find_combined(&map->hash, key, low, len); + if (value) { + return value; + } + +#if (NGX_PCRE) + + if (len && map->nregex) { + ngx_int_t n; + ngx_uint_t i; + ngx_stream_map_regex_t *reg; + + reg = map->regex; + + for (i = 0; i < map->nregex; i++) { + + n = ngx_stream_regex_exec(s, reg[i].regex, match); + + if (n == NGX_OK) { + return reg[i].value; + } + + if (n == NGX_DECLINED) { + continue; + } + + /* NGX_ERROR */ + + return NULL; + } + } + +#endif + + return NULL; +} + + +#if (NGX_PCRE) + +static ngx_int_t +ngx_stream_variable_not_found(ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data) +{ + v->not_found = 1; + return NGX_OK; +} + + +ngx_stream_regex_t * +ngx_stream_regex_compile(ngx_conf_t *cf, ngx_regex_compile_t *rc) +{ + u_char *p; + size_t size; + ngx_str_t name; + ngx_uint_t i, n; + ngx_stream_variable_t *v; + ngx_stream_regex_t *re; + ngx_stream_regex_variable_t *rv; + ngx_stream_core_main_conf_t *cmcf; + + rc->pool = cf->pool; + + if (ngx_regex_compile(rc) != NGX_OK) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc->err); + return NULL; + } + + re = ngx_pcalloc(cf->pool, sizeof(ngx_stream_regex_t)); + if (re == NULL) { + return NULL; + } + + re->regex = rc->regex; + re->ncaptures = rc->captures; + re->name = rc->pattern; + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + cmcf->ncaptures = ngx_max(cmcf->ncaptures, re->ncaptures); + + n = (ngx_uint_t) rc->named_captures; + + if (n == 0) { + return re; + } + + rv = ngx_palloc(rc->pool, n * sizeof(ngx_stream_regex_variable_t)); + if (rv == NULL) { + return NULL; + } + + re->variables = rv; + re->nvariables = n; + + size = rc->name_size; + p = rc->names; + + for (i = 0; i < n; i++) { + rv[i].capture = 2 * ((p[0] << 8) + p[1]); + + name.data = &p[2]; + name.len = ngx_strlen(name.data); + + v = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE); + if (v == NULL) { + return NULL; + } + + rv[i].index = ngx_stream_get_variable_index(cf, &name); + if (rv[i].index == NGX_ERROR) { + return NULL; + } + + v->get_handler = ngx_stream_variable_not_found; + + p += size; + } + + return re; +} + + +ngx_int_t +ngx_stream_regex_exec(ngx_stream_session_t *s, ngx_stream_regex_t *re, + ngx_str_t *str) +{ + ngx_int_t rc, index; + ngx_uint_t i, n, len; + ngx_stream_variable_value_t *vv; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); + + if (re->ncaptures) { + len = cmcf->ncaptures; + + if (s->captures == NULL) { + s->captures = ngx_palloc(s->connection->pool, len * sizeof(int)); + if (s->captures == NULL) { + return NGX_ERROR; + } + } + + } else { + len = 0; + } + + rc = ngx_regex_exec(re->regex, str, s->captures, len); + + if (rc == NGX_REGEX_NO_MATCHED) { + return NGX_DECLINED; + } + + if (rc < 0) { + ngx_log_error(NGX_LOG_ALERT, s->connection->log, 0, + ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"", + rc, str, &re->name); + return NGX_ERROR; + } + + for (i = 0; i < re->nvariables; i++) { + + n = re->variables[i].capture; + index = re->variables[i].index; + vv = &s->variables[index]; + + vv->len = s->captures[n + 1] - s->captures[n]; + vv->valid = 1; + vv->no_cacheable = 0; + vv->not_found = 0; + vv->data = &str->data[s->captures[n]]; + +#if (NGX_DEBUG) + { + ngx_stream_variable_t *v; + + v = cmcf->variables.elts; + + ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream regex set $%V to \"%v\"", &v[index].name, vv); + } +#endif + } + + s->ncaptures = rc * 2; + s->captures_data = str->data; + + return NGX_OK; +} + +#endif + + +ngx_int_t +ngx_stream_variables_add_core_vars(ngx_conf_t *cf) +{ + ngx_stream_variable_t *cv, *v; + ngx_stream_core_main_conf_t *cmcf; + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + cmcf->variables_keys = ngx_pcalloc(cf->temp_pool, + sizeof(ngx_hash_keys_arrays_t)); + if (cmcf->variables_keys == NULL) { + return NGX_ERROR; + } + + cmcf->variables_keys->pool = cf->pool; + cmcf->variables_keys->temp_pool = cf->pool; + + if (ngx_hash_keys_array_init(cmcf->variables_keys, NGX_HASH_SMALL) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_array_init(&cmcf->prefix_variables, cf->pool, 8, + sizeof(ngx_stream_variable_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + for (cv = ngx_stream_core_variables; cv->name.len; cv++) { + v = ngx_stream_add_variable(cf, &cv->name, cv->flags); + if (v == NULL) { + return NGX_ERROR; + } + + *v = *cv; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_stream_variables_init_vars(ngx_conf_t *cf) +{ + size_t len; + ngx_uint_t i, n; + ngx_hash_key_t *key; + ngx_hash_init_t hash; + ngx_stream_variable_t *v, *av, *pv; + ngx_stream_core_main_conf_t *cmcf; + + /* set the handlers for the indexed stream variables */ + + cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); + + v = cmcf->variables.elts; + pv = cmcf->prefix_variables.elts; + key = cmcf->variables_keys->keys.elts; + + for (i = 0; i < cmcf->variables.nelts; i++) { + + for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) { + + av = key[n].value; + + if (v[i].name.len == key[n].key.len + && ngx_strncmp(v[i].name.data, key[n].key.data, v[i].name.len) + == 0) + { + v[i].get_handler = av->get_handler; + v[i].data = av->data; + + av->flags |= NGX_STREAM_VAR_INDEXED; + v[i].flags = av->flags; + + av->index = i; + + if (av->get_handler == NULL + || (av->flags & NGX_STREAM_VAR_WEAK)) + { + break; + } + + goto next; + } + } + + len = 0; + av = NULL; + + for (n = 0; n < cmcf->prefix_variables.nelts; n++) { + if (v[i].name.len >= pv[n].name.len && v[i].name.len > len + && ngx_strncmp(v[i].name.data, pv[n].name.data, pv[n].name.len) + == 0) + { + av = &pv[n]; + len = pv[n].name.len; + } + } + + if (av) { + v[i].get_handler = av->get_handler; + v[i].data = (uintptr_t) &v[i].name; + v[i].flags = av->flags; + + goto next; + } + + if (v[i].get_handler == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "unknown \"%V\" variable", &v[i].name); + return NGX_ERROR; + } + + next: + continue; + } + + + for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) { + av = key[n].value; + + if (av->flags & NGX_STREAM_VAR_NOHASH) { + key[n].key.data = NULL; + } + } + + + hash.hash = &cmcf->variables_hash; + hash.key = ngx_hash_key; + hash.max_size = cmcf->variables_hash_max_size; + hash.bucket_size = cmcf->variables_hash_bucket_size; + hash.name = "variables_hash"; + hash.pool = cf->pool; + hash.temp_pool = NULL; + + if (ngx_hash_init(&hash, cmcf->variables_keys->keys.elts, + cmcf->variables_keys->keys.nelts) + != NGX_OK) + { + return NGX_ERROR; + } + + cmcf->variables_keys = NULL; + + return NGX_OK; +} diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_variables.h b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_variables.h new file mode 100644 index 000000000..4ead2a624 --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_variables.h @@ -0,0 +1,113 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_STREAM_VARIABLES_H_INCLUDED_ +#define _NGX_STREAM_VARIABLES_H_INCLUDED_ + + +#include +#include +#include + + +typedef ngx_variable_value_t ngx_stream_variable_value_t; + +#define ngx_stream_variable(v) { sizeof(v) - 1, 1, 0, 0, 0, (u_char *) v } + +typedef struct ngx_stream_variable_s ngx_stream_variable_t; + +typedef void (*ngx_stream_set_variable_pt) (ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); +typedef ngx_int_t (*ngx_stream_get_variable_pt) (ngx_stream_session_t *s, + ngx_stream_variable_value_t *v, uintptr_t data); + + +#define NGX_STREAM_VAR_CHANGEABLE 1 +#define NGX_STREAM_VAR_NOCACHEABLE 2 +#define NGX_STREAM_VAR_INDEXED 4 +#define NGX_STREAM_VAR_NOHASH 8 +#define NGX_STREAM_VAR_WEAK 16 +#define NGX_STREAM_VAR_PREFIX 32 + + +struct ngx_stream_variable_s { + ngx_str_t name; /* must be first to build the hash */ + ngx_stream_set_variable_pt set_handler; + ngx_stream_get_variable_pt get_handler; + uintptr_t data; + ngx_uint_t flags; + ngx_uint_t index; +}; + +#define ngx_stream_null_variable { ngx_null_string, NULL, NULL, 0, 0, 0 } + + +ngx_stream_variable_t *ngx_stream_add_variable(ngx_conf_t *cf, ngx_str_t *name, + ngx_uint_t flags); +ngx_int_t ngx_stream_get_variable_index(ngx_conf_t *cf, ngx_str_t *name); +ngx_stream_variable_value_t *ngx_stream_get_indexed_variable( + ngx_stream_session_t *s, ngx_uint_t index); +ngx_stream_variable_value_t *ngx_stream_get_flushed_variable( + ngx_stream_session_t *s, ngx_uint_t index); + +ngx_stream_variable_value_t *ngx_stream_get_variable(ngx_stream_session_t *s, + ngx_str_t *name, ngx_uint_t key); + + +#if (NGX_PCRE) + +typedef struct { + ngx_uint_t capture; + ngx_int_t index; +} ngx_stream_regex_variable_t; + + +typedef struct { + ngx_regex_t *regex; + ngx_uint_t ncaptures; + ngx_stream_regex_variable_t *variables; + ngx_uint_t nvariables; + ngx_str_t name; +} ngx_stream_regex_t; + + +typedef struct { + ngx_stream_regex_t *regex; + void *value; +} ngx_stream_map_regex_t; + + +ngx_stream_regex_t *ngx_stream_regex_compile(ngx_conf_t *cf, + ngx_regex_compile_t *rc); +ngx_int_t ngx_stream_regex_exec(ngx_stream_session_t *s, ngx_stream_regex_t *re, + ngx_str_t *str); + +#endif + + +typedef struct { + ngx_hash_combined_t hash; +#if (NGX_PCRE) + ngx_stream_map_regex_t *regex; + ngx_uint_t nregex; +#endif +} ngx_stream_map_t; + + +void *ngx_stream_map_find(ngx_stream_session_t *s, ngx_stream_map_t *map, + ngx_str_t *match); + + +ngx_int_t ngx_stream_variables_add_core_vars(ngx_conf_t *cf); +ngx_int_t ngx_stream_variables_init_vars(ngx_conf_t *cf); + + +extern ngx_stream_variable_value_t ngx_stream_variable_null_value; +extern ngx_stream_variable_value_t ngx_stream_variable_true_value; + + +#endif /* _NGX_STREAM_VARIABLES_H_INCLUDED_ */ diff --git a/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_write_filter_module.c b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_write_filter_module.c new file mode 100644 index 000000000..07dc7b52e --- /dev/null +++ b/src/deps/src/nginx-1.30.1/src/stream/ngx_stream_write_filter_module.c @@ -0,0 +1,306 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +typedef struct { + ngx_chain_t *from_upstream; + ngx_chain_t *from_downstream; +} ngx_stream_write_filter_ctx_t; + + +static ngx_int_t ngx_stream_write_filter(ngx_stream_session_t *s, + ngx_chain_t *in, ngx_uint_t from_upstream); +static ngx_int_t ngx_stream_write_filter_init(ngx_conf_t *cf); + + +static ngx_stream_module_t ngx_stream_write_filter_module_ctx = { + NULL, /* preconfiguration */ + ngx_stream_write_filter_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL /* merge server configuration */ +}; + + +ngx_module_t ngx_stream_write_filter_module = { + NGX_MODULE_V1, + &ngx_stream_write_filter_module_ctx, /* module context */ + NULL, /* module directives */ + NGX_STREAM_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_int_t +ngx_stream_write_filter(ngx_stream_session_t *s, ngx_chain_t *in, + ngx_uint_t from_upstream) +{ + off_t size; + ngx_uint_t last, flush, sync; + ngx_chain_t *cl, *ln, **ll, **out, *chain; + ngx_connection_t *c; + ngx_stream_write_filter_ctx_t *ctx; + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_write_filter_module); + + if (ctx == NULL) { + ctx = ngx_pcalloc(s->connection->pool, + sizeof(ngx_stream_write_filter_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + ngx_stream_set_ctx(s, ctx, ngx_stream_write_filter_module); + } + + if (from_upstream) { + c = s->connection; + out = &ctx->from_upstream; + + } else { + c = s->upstream->peer.connection; + out = &ctx->from_downstream; + } + + if (c->error) { + return NGX_ERROR; + } + + size = 0; + flush = 0; + sync = 0; + last = 0; + ll = out; + + /* find the size, the flush point and the last link of the saved chain */ + + for (cl = *out; cl; cl = cl->next) { + ll = &cl->next; + + ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, + "write old buf t:%d f:%d %p, pos %p, size: %z " + "file: %O, size: %O", + cl->buf->temporary, cl->buf->in_file, + cl->buf->start, cl->buf->pos, + cl->buf->last - cl->buf->pos, + cl->buf->file_pos, + cl->buf->file_last - cl->buf->file_pos); + + if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "zero size buf in writer " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + cl->buf->temporary, + cl->buf->recycled, + cl->buf->in_file, + cl->buf->start, + cl->buf->pos, + cl->buf->last, + cl->buf->file, + cl->buf->file_pos, + cl->buf->file_last); + + ngx_debug_point(); + return NGX_ERROR; + } + + if (ngx_buf_size(cl->buf) < 0) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "negative size buf in writer " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + cl->buf->temporary, + cl->buf->recycled, + cl->buf->in_file, + cl->buf->start, + cl->buf->pos, + cl->buf->last, + cl->buf->file, + cl->buf->file_pos, + cl->buf->file_last); + + ngx_debug_point(); + return NGX_ERROR; + } + + size += ngx_buf_size(cl->buf); + + if (cl->buf->flush || cl->buf->recycled) { + flush = 1; + } + + if (cl->buf->sync) { + sync = 1; + } + + if (cl->buf->last_buf) { + last = 1; + } + } + + /* add the new chain to the existent one */ + + for (ln = in; ln; ln = ln->next) { + cl = ngx_alloc_chain_link(c->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = ln->buf; + *ll = cl; + ll = &cl->next; + + ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, + "write new buf t:%d f:%d %p, pos %p, size: %z " + "file: %O, size: %O", + cl->buf->temporary, cl->buf->in_file, + cl->buf->start, cl->buf->pos, + cl->buf->last - cl->buf->pos, + cl->buf->file_pos, + cl->buf->file_last - cl->buf->file_pos); + + if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "zero size buf in writer " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + cl->buf->temporary, + cl->buf->recycled, + cl->buf->in_file, + cl->buf->start, + cl->buf->pos, + cl->buf->last, + cl->buf->file, + cl->buf->file_pos, + cl->buf->file_last); + + ngx_debug_point(); + return NGX_ERROR; + } + + if (ngx_buf_size(cl->buf) < 0) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "negative size buf in writer " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + cl->buf->temporary, + cl->buf->recycled, + cl->buf->in_file, + cl->buf->start, + cl->buf->pos, + cl->buf->last, + cl->buf->file, + cl->buf->file_pos, + cl->buf->file_last); + + ngx_debug_point(); + return NGX_ERROR; + } + + size += ngx_buf_size(cl->buf); + + if (cl->buf->flush || cl->buf->recycled) { + flush = 1; + } + + if (cl->buf->sync) { + sync = 1; + } + + if (cl->buf->last_buf) { + last = 1; + } + } + + *ll = NULL; + + ngx_log_debug3(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream write filter: l:%ui f:%ui s:%O", last, flush, size); + + if (size == 0 + && !(c->buffered & NGX_LOWLEVEL_BUFFERED) + && !(last && c->need_last_buf) + && !(flush && c->need_flush_buf)) + { + if (last || flush || sync) { + for (cl = *out; cl; /* void */) { + ln = cl; + cl = cl->next; + ngx_free_chain(c->pool, ln); + } + + *out = NULL; + c->buffered &= ~NGX_STREAM_WRITE_BUFFERED; + + return NGX_OK; + } + + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "the stream output chain is empty"); + + ngx_debug_point(); + + return NGX_ERROR; + } + + chain = c->send_chain(c, *out, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, + "stream write filter %p", chain); + + if (chain == NGX_CHAIN_ERROR) { + c->error = 1; + return NGX_ERROR; + } + + for (cl = *out; cl && cl != chain; /* void */) { + ln = cl; + cl = cl->next; + ngx_free_chain(c->pool, ln); + } + + *out = chain; + + if (chain) { + if (c->shared) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "shared connection is busy"); + return NGX_ERROR; + } + + c->buffered |= NGX_STREAM_WRITE_BUFFERED; + return NGX_AGAIN; + } + + c->buffered &= ~NGX_STREAM_WRITE_BUFFERED; + + if (c->buffered & NGX_LOWLEVEL_BUFFERED) { + return NGX_AGAIN; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_stream_write_filter_init(ngx_conf_t *cf) +{ + ngx_stream_top_filter = ngx_stream_write_filter; + + return NGX_OK; +} diff --git a/src/deps/src/nginx/src/core/nginx.h b/src/deps/src/nginx/src/core/nginx.h index ff8c33e57..b05343f74 100644 --- a/src/deps/src/nginx/src/core/nginx.h +++ b/src/deps/src/nginx/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1030000 -#define NGINX_VERSION "1.30.0" +#define nginx_version 1030002 +#define NGINX_VERSION "1.30.2" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD diff --git a/src/deps/src/nginx/src/event/ngx_event_openssl_stapling.c b/src/deps/src/nginx/src/event/ngx_event_openssl_stapling.c index 0f560f17d..86d8b2c55 100644 --- a/src/deps/src/nginx/src/event/ngx_event_openssl_stapling.c +++ b/src/deps/src/nginx/src/event/ngx_event_openssl_stapling.c @@ -113,6 +113,7 @@ struct ngx_ssl_ocsp_ctx_s { ngx_resolver_t *resolver; ngx_msec_t resolver_timeout; + ngx_resolver_ctx_t *resolve; ngx_msec_t timeout; @@ -1341,6 +1342,10 @@ ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx) ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, "ssl ocsp done"); + if (ctx->resolve) { + ngx_resolve_name_done(ctx->resolve); + } + if (ctx->peer.connection) { ngx_close_connection(ctx->peer.connection); } @@ -1433,7 +1438,10 @@ ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx) resolve->data = ctx; resolve->timeout = ctx->resolver_timeout; + ctx->resolve = resolve; + if (ngx_resolve_name(resolve) != NGX_OK) { + ctx->resolve = NULL; ngx_ssl_ocsp_error(ctx); return; } @@ -1522,6 +1530,7 @@ ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve) } ngx_resolve_name_done(resolve); + ctx->resolve = NULL; ngx_ssl_ocsp_connect(ctx); return; @@ -1529,6 +1538,8 @@ ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve) failed: ngx_resolve_name_done(resolve); + ctx->resolve = NULL; + ngx_ssl_ocsp_error(ctx); } diff --git a/src/deps/src/nginx/src/event/quic/ngx_event_quic_migration.c b/src/deps/src/nginx/src/event/quic/ngx_event_quic_migration.c index 42354ca66..bbb105142 100644 --- a/src/deps/src/nginx/src/event/quic/ngx_event_quic_migration.c +++ b/src/deps/src/nginx/src/event/quic/ngx_event_quic_migration.c @@ -194,6 +194,8 @@ valid: path->validated = 1; + ngx_quic_set_connection_path(c, path); + if (path->mtu_unvalidated) { path->mtu_unvalidated = 0; return ngx_quic_validate_path(c, path); @@ -511,9 +513,10 @@ ngx_quic_handle_migration(ngx_connection_t *c, ngx_quic_header_t *pkt) qc->path = next; qc->path->tag = NGX_QUIC_PATH_ACTIVE; - ngx_quic_set_connection_path(c, next); + if (next->validated) { + ngx_quic_set_connection_path(c, next); - if (!next->validated && next->state != NGX_QUIC_PATH_VALIDATING) { + } else if (next->state != NGX_QUIC_PATH_VALIDATING) { if (ngx_quic_validate_path(c, next) != NGX_OK) { return NGX_ERROR; } @@ -807,8 +810,6 @@ ngx_quic_expire_path_validation(ngx_connection_t *c, ngx_quic_path_t *path) qc->path = bkp; qc->path->tag = NGX_QUIC_PATH_ACTIVE; - ngx_quic_set_connection_path(c, qc->path); - ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic path seq:%uL addr:%V is restored from backup", qc->path->seqnum, &qc->path->addr_text); diff --git a/src/deps/src/nginx/src/http/modules/ngx_http_charset_filter_module.c b/src/deps/src/nginx/src/http/modules/ngx_http_charset_filter_module.c index 482960040..edb2db568 100644 --- a/src/deps/src/nginx/src/http/modules/ngx_http_charset_filter_module.c +++ b/src/deps/src/nginx/src/http/modules/ngx_http_charset_filter_module.c @@ -689,7 +689,6 @@ ngx_http_charset_recode_from_utf8(ngx_pool_t *pool, ngx_buf_t *buf, u_char c, *p, *src, *dst, *saved, **table; uint32_t n; ngx_buf_t *b; - ngx_uint_t i; ngx_chain_t *out, *cl, **ll; src = buf->pos; @@ -783,18 +782,12 @@ ngx_http_charset_recode_from_utf8(ngx_pool_t *pool, ngx_buf_t *buf, ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pool->log, 0, "http charset utf saved: %z", ctx->saved_len); - p = src; - - for (i = ctx->saved_len; i < NGX_UTF_LEN; i++) { - ctx->saved[i] = *p++; - - if (p == buf->last) { - break; - } - } + len = ngx_min(NGX_UTF_LEN - ctx->saved_len, (size_t) (buf->last - src)); + ngx_memcpy(&ctx->saved[ctx->saved_len], src, len); + len += ctx->saved_len; saved = ctx->saved; - n = ngx_utf8_decode(&saved, i); + n = ngx_utf8_decode(&saved, len); c = '\0'; @@ -810,7 +803,7 @@ ngx_http_charset_recode_from_utf8(ngx_pool_t *pool, ngx_buf_t *buf, /* incomplete UTF-8 symbol */ - if (i < NGX_UTF_LEN) { + if (len < NGX_UTF_LEN) { out = ngx_http_charset_get_buf(pool, ctx); if (out == NULL) { return NULL; @@ -823,8 +816,7 @@ ngx_http_charset_recode_from_utf8(ngx_pool_t *pool, ngx_buf_t *buf, b->sync = 1; b->shadow = buf; - ngx_memcpy(&ctx->saved[ctx->saved_len], src, i); - ctx->saved_len += i; + ctx->saved_len = len; return out; } diff --git a/src/deps/src/nginx/src/http/modules/ngx_http_proxy_module.c b/src/deps/src/nginx/src/http/modules/ngx_http_proxy_module.c index 0b388b30f..276cc0750 100644 --- a/src/deps/src/nginx/src/http/modules/ngx_http_proxy_module.c +++ b/src/deps/src/nginx/src/http/modules/ngx_http_proxy_module.c @@ -1752,6 +1752,10 @@ ngx_http_proxy_process_status_line(ngx_http_request_t *r) u = r->upstream; + if (r->state == 0) { + r->header_name_start = u->buffer.pos; + } + rc = ngx_http_parse_status_line(r, &u->buffer, &ctx->status); if (rc == NGX_AGAIN) { @@ -1759,6 +1763,7 @@ ngx_http_proxy_process_status_line(ngx_http_request_t *r) } if (rc == NGX_ERROR) { + u->buffer.pos = r->header_name_start; #if (NGX_HTTP_CACHE) diff --git a/src/deps/src/nginx/src/http/modules/ngx_http_proxy_v2_module.c b/src/deps/src/nginx/src/http/modules/ngx_http_proxy_v2_module.c index 4c282a864..0be5691aa 100644 --- a/src/deps/src/nginx/src/http/modules/ngx_http_proxy_v2_module.c +++ b/src/deps/src/nginx/src/http/modules/ngx_http_proxy_v2_module.c @@ -458,9 +458,6 @@ ngx_http_proxy_v2_create_request(ngx_http_request_t *r) ctx->ctx.internal_body_length = body_len; - len += sizeof(ngx_http_proxy_v2_frame_t); - len += body_len; - } else if (r->headers_in.chunked && r->reading_body) { ctx->ctx.internal_body_length = -1; @@ -834,34 +831,6 @@ ngx_http_proxy_v2_create_request(ngx_http_request_t *r) f->flags |= NGX_HTTP_V2_END_HEADERS_FLAG; - if (plcf->body_values) { - f = (ngx_http_proxy_v2_frame_t *) b->last; - b->last += sizeof(ngx_http_proxy_v2_frame_t); - - f->length_0 = (u_char) ((body_len >> 16) & 0xff); - f->length_1 = (u_char) ((body_len >> 8) & 0xff); - f->length_2 = (u_char) (body_len & 0xff); - f->type = NGX_HTTP_V2_DATA_FRAME; - f->flags = NGX_HTTP_V2_END_STREAM_FLAG; - f->stream_id_0 = 0; - f->stream_id_1 = 0; - f->stream_id_2 = 0; - f->stream_id_3 = 1; - - e.ip = plcf->body_values->elts; - e.pos = b->last; - e.request = r; - e.flushed = 1; - e.skip = 0; - - while (*(uintptr_t *) e.ip) { - code = *(ngx_http_script_code_pt *) e.ip; - code((ngx_http_script_engine_t *) &e); - } - - b->last = e.pos; - } - ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http proxy header: %*xs%s, len: %uz", (size_t) ngx_min(b->last - b->pos, 256), b->pos, @@ -903,13 +872,44 @@ ngx_http_proxy_v2_create_request(ngx_http_request_t *r) b->last_buf = 1; + } else if (body_len) { + + u->request_bufs = cl; + + b = ngx_create_temp_buf(r->pool, body_len); + if (b == NULL) { + return NGX_ERROR; + } + + cl->next = ngx_alloc_chain_link(r->pool); + if (cl->next == NULL) { + return NGX_ERROR; + } + + cl = cl->next; + cl->buf = b; + + e.ip = plcf->body_values->elts; + e.pos = b->last; + e.request = r; + e.flushed = 1; + e.skip = 0; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + } + + b->last = e.pos; + b->last_buf = 1; + } else { u->request_bufs = cl; - if (plcf->body_values == NULL) { - f = (ngx_http_proxy_v2_frame_t *) headers_frame; - f->flags |= NGX_HTTP_V2_END_STREAM_FLAG; - } + f = (ngx_http_proxy_v2_frame_t *) headers_frame; + f->flags |= NGX_HTTP_V2_END_STREAM_FLAG; + + b->last_buf = 1; } u->output.output_filter = ngx_http_proxy_v2_body_output_filter; diff --git a/src/deps/src/nginx/src/http/modules/ngx_http_scgi_module.c b/src/deps/src/nginx/src/http/modules/ngx_http_scgi_module.c index 8937464b6..b4a73a5a6 100644 --- a/src/deps/src/nginx/src/http/modules/ngx_http_scgi_module.c +++ b/src/deps/src/nginx/src/http/modules/ngx_http_scgi_module.c @@ -1028,6 +1028,10 @@ ngx_http_scgi_process_status_line(ngx_http_request_t *r) u = r->upstream; + if (r->state == 0) { + r->header_name_start = u->buffer.pos; + } + rc = ngx_http_parse_status_line(r, &u->buffer, status); if (rc == NGX_AGAIN) { @@ -1036,6 +1040,8 @@ ngx_http_scgi_process_status_line(ngx_http_request_t *r) if (rc == NGX_ERROR) { u->process_header = ngx_http_scgi_process_header; + u->buffer.pos = r->header_name_start; + r->state = 0; return ngx_http_scgi_process_header(r); } diff --git a/src/deps/src/nginx/src/http/modules/ngx_http_uwsgi_module.c b/src/deps/src/nginx/src/http/modules/ngx_http_uwsgi_module.c index 2f64d745d..ecd6dd0d7 100644 --- a/src/deps/src/nginx/src/http/modules/ngx_http_uwsgi_module.c +++ b/src/deps/src/nginx/src/http/modules/ngx_http_uwsgi_module.c @@ -1267,6 +1267,10 @@ ngx_http_uwsgi_process_status_line(ngx_http_request_t *r) u = r->upstream; + if (r->state == 0) { + r->header_name_start = u->buffer.pos; + } + rc = ngx_http_parse_status_line(r, &u->buffer, status); if (rc == NGX_AGAIN) { @@ -1275,6 +1279,8 @@ ngx_http_uwsgi_process_status_line(ngx_http_request_t *r) if (rc == NGX_ERROR) { u->process_header = ngx_http_uwsgi_process_header; + u->buffer.pos = r->header_name_start; + r->state = 0; return ngx_http_uwsgi_process_header(r); } diff --git a/src/deps/src/nginx/src/http/ngx_http_script.c b/src/deps/src/nginx/src/http/ngx_http_script.c index a2b9f1b7b..8a28e23a6 100644 --- a/src/deps/src/nginx/src/http/ngx_http_script.c +++ b/src/deps/src/nginx/src/http/ngx_http_script.c @@ -1037,6 +1037,8 @@ ngx_http_script_start_args_code(ngx_http_script_engine_t *e) void ngx_http_script_regex_start_code(ngx_http_script_engine_t *e) { + int *cap; + u_char *p; size_t len; ngx_int_t rc; ngx_uint_t n; @@ -1143,15 +1145,19 @@ ngx_http_script_regex_start_code(ngx_http_script_engine_t *e) if (code->lengths == NULL) { e->buf.len = code->size; - if (code->uri) { - if (r->ncaptures && (r->quoted_uri || r->plus_in_uri)) { - e->buf.len += 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len, - NGX_ESCAPE_ARGS); - } - } + cap = r->captures; + p = r->captures_data; for (n = 2; n < r->ncaptures; n += 2) { - e->buf.len += r->captures[n + 1] - r->captures[n]; + e->buf.len += cap[n + 1] - cap[n]; + + if (code->uri) { + if (r->quoted_uri || r->plus_in_uri) { + e->buf.len += 2 * ngx_escape_uri(NULL, &p[cap[n]], + cap[n + 1] - cap[n], + NGX_ESCAPE_ARGS); + } + } } } else { @@ -1202,6 +1208,7 @@ ngx_http_script_regex_end_code(ngx_http_script_engine_t *e) r = e->request; + e->is_args = 0; e->quote = 0; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, diff --git a/src/linux/Dockerfile-debian-bookworm b/src/linux/Dockerfile-debian-bookworm index 54b2ad8f6..909535b04 100644 --- a/src/linux/Dockerfile-debian-bookworm +++ b/src/linux/Dockerfile-debian-bookworm @@ -1,4 +1,4 @@ -FROM debian:bookworm-slim@sha256:f9c6a2fd2ddbc23e336b6257a5245e31f996953ef06cd13a59fa0a1df2d5c252 AS builder +FROM debian:bookworm-slim@sha256:0104b334637a5f19aa9c983a91b54c89887c0984081f2068983107a6f6c21eeb AS builder # Install Nginx and dependencies RUN apt update && \ @@ -6,7 +6,7 @@ RUN apt update && \ bash curl libssl-dev git libpcre2-dev zlib1g-dev libyajl2 libyajl-dev yajl-tools pkgconf libcurl4-openssl-dev libgeoip-dev apt-utils build-essential autoconf libtool automake g++ gcc libxml2-dev make musl-dev gnupg patch libreadline-dev libbrotli-dev libgd-dev liblmdb-dev libfuzzy-dev cargo coreutils python3.11 python3.11-dev python3-pip -y ENV OS=debian -ENV NGINX_VERSION=1.30.0 +ENV NGINX_VERSION=1.30.2 ENV EXPECTED_FPR=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 # Install Nginx @@ -126,7 +126,7 @@ RUN if [ "$SKIP_MINIFY" != "yes" ] && [ "$SKIP_MINIFY_HTML" != "yes" ]; then \ echo "Skipping HTML minification"; \ fi -FROM debian:bookworm-slim@sha256:f9c6a2fd2ddbc23e336b6257a5245e31f996953ef06cd13a59fa0a1df2d5c252 +FROM debian:bookworm-slim@sha256:0104b334637a5f19aa9c983a91b54c89887c0984081f2068983107a6f6c21eeb # Set default umask to prevent huge recursive chmod increasing the final image size RUN umask 027 diff --git a/src/linux/Dockerfile-debian-trixie b/src/linux/Dockerfile-debian-trixie index b941b1543..43dd13a65 100644 --- a/src/linux/Dockerfile-debian-trixie +++ b/src/linux/Dockerfile-debian-trixie @@ -1,4 +1,4 @@ -FROM debian:trixie-slim@sha256:cedb1ef40439206b673ee8b33a46a03a0c9fa90bf3732f54704f99cb061d2c5a AS builder +FROM debian:trixie-slim@sha256:b6e2a152f22a40ff69d92cb397223c906017e1391a73c952b588e51af8883bf8 AS builder # Install Nginx and dependencies RUN apt update && \ @@ -7,7 +7,7 @@ RUN apt update && \ ENV OS=debian ENV OS_VERSION=13 -ENV NGINX_VERSION=1.30.0 +ENV NGINX_VERSION=1.30.2 ENV EXPECTED_FPR=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 # Install Nginx @@ -127,7 +127,7 @@ RUN if [ "$SKIP_MINIFY" != "yes" ] && [ "$SKIP_MINIFY_HTML" != "yes" ]; then \ echo "Skipping HTML minification"; \ fi -FROM debian:trixie-slim@sha256:cedb1ef40439206b673ee8b33a46a03a0c9fa90bf3732f54704f99cb061d2c5a +FROM debian:trixie-slim@sha256:b6e2a152f22a40ff69d92cb397223c906017e1391a73c952b588e51af8883bf8 # Set default umask to prevent huge recursive chmod increasing the final image size RUN umask 027 diff --git a/src/linux/Dockerfile-fedora-42 b/src/linux/Dockerfile-fedora-42 deleted file mode 100644 index 69ed440d2..000000000 --- a/src/linux/Dockerfile-fedora-42 +++ /dev/null @@ -1,162 +0,0 @@ -FROM fedora:42@sha256:99e203b80b1c3d8f7e161ec10a68fd02b081ef83a3963553e513c82846b97814 AS builder - -ENV OS=fedora -ENV OS_VERSION=42 -ENV NGINX_VERSION=1.30.0 - -# Install Nginx, fpm and dependencies -RUN dnf update -y && \ - dnf install -y curl gnupg2 ca-certificates make gcc && \ - dnf install -y --setopt=install_weak_deps=False python3.12 python3.12-devel python3-pip brotli brotli-devel gperftools-devel perl libxslt-devel libxml2 yajl yajl-devel libxslt bash gd gd-devel gcc-c++ kernel-devel znc-modtcl libmpc-devel gmp-devel gawk mpfr-devel libtool pcre2-devel automake autoconf readline-devel gcc make openssl-devel git zlib-devel libxml2-devel pkgconf libcurl-devel geoip-devel lmdb-devel ssdeep-devel cargo coreutils patch && \ - dnf install --enablerepo=updates-testing nginx-${NGINX_VERSION} -y - -WORKDIR /tmp/bunkerweb/deps - -# Copy dependencies sources folder -COPY src/deps/misc misc -COPY src/deps/src src -COPY src/deps/deps.json deps.json -COPY --chmod=644 src/deps/install.sh install.sh - -# Compile and install dependencies -RUN bash install.sh - -# Copy dependencies sources folder -COPY src/deps/requirements.txt /tmp/requirements-deps.txt -COPY src/scheduler/requirements.txt /tmp/req/requirements-scheduler.txt -COPY src/ui/requirements.txt /tmp/req/requirements-ui.txt -COPY src/api/requirements.txt /tmp/req/requirements-api.txt -COPY src/common/gen/requirements.txt /tmp/req/requirements-gen.txt -COPY src/common/db/requirements.txt /tmp/req/requirements-db.txt - -WORKDIR /usr/share/bunkerweb - -# Compile and install dependencies -RUN export MAKEFLAGS="-j$(nproc)" && \ - mkdir -p deps/python && \ - python3.12 -m ensurepip && \ - python3.12 -m pip install --no-cache-dir --require-hashes --ignore-installed -r /tmp/requirements-deps.txt && \ - python3.12 -m pip install --no-cache-dir --require-hashes --force-reinstall --target deps/python $(for file in $(ls /tmp/req/requirements*.txt) ; do echo "-r ${file}" ; done | xargs) - -# Apply BunkerWeb patches to vendored Python deps before they are tarballed (DNS alias / CNAME delegation for certbot). -COPY src/deps/misc/certbot-dns-alias.patch /tmp/certbot-dns-alias.patch -RUN patch -p1 -F0 --dry-run -d /usr/share/bunkerweb/deps/python < /tmp/certbot-dns-alias.patch && \ - patch -p1 -F0 --no-backup-if-mismatch -d /usr/share/bunkerweb/deps/python < /tmp/certbot-dns-alias.patch && \ - grep -q '_load_domain_aliases' /usr/share/bunkerweb/deps/python/certbot/plugins/dns_common.py && \ - rm -f /tmp/certbot-dns-alias.patch - -RUN dnf install -y pigz && \ - tar --use-compress-program="pigz -1 -p$(nproc)" -cf deps.tar.gz deps && \ - rm -rf deps - -# Copy files -# can't exclude deps from . so we are copying everything by hand -COPY src/bw/loading loading -COPY src/bw/lua lua -COPY src/bw/misc misc -COPY src/common/api api -COPY src/common/cli cli -COPY src/common/confs confs -COPY src/common/core core -COPY src/common/db db -COPY src/common/gen gen -COPY src/common/helpers helpers -COPY src/common/settings.json settings.json -COPY src/common/utils utils -COPY src/scheduler scheduler -COPY src/VERSION VERSION -COPY src/ui ui -COPY src/api api - -WORKDIR /tmp - -COPY src/deps/package.json package.json -COPY src/deps/package-lock.json package-lock.json - -ARG SKIP_MINIFY=no -ARG SKIP_MINIFY_JS=no -ARG SKIP_MINIFY_CSS=no -ARG SKIP_MINIFY_HTML=no -RUN if [ "$SKIP_MINIFY" != "yes" ] && { [ "$SKIP_MINIFY_JS" != "yes" ] || [ "$SKIP_MINIFY_CSS" != "yes" ] || [ "$SKIP_MINIFY_HTML" != "yes" ]; }; then \ - dnf install -y nodejs npm parallel && \ - cd /tmp && \ - npm ci --ignore-scripts; \ - fi - -# Minify JS files -ARG SKIP_MINIFY=no -ARG SKIP_MINIFY_JS=no -RUN if [ "$SKIP_MINIFY" != "yes" ] && [ "$SKIP_MINIFY_JS" != "yes" ]; then \ - find /usr/share/bunkerweb/ui/app/static -type f -name "*.js" ! -name "*.min.js" -print0 \ - | parallel -0 --no-run-if-empty 'npx uglify-js {} --compress --mangle --output {}'; \ - else \ - echo "Skipping JS minification"; \ - fi - -# Minify CSS files -ARG SKIP_MINIFY_CSS=no -RUN if [ "$SKIP_MINIFY" != "yes" ] && [ "$SKIP_MINIFY_CSS" != "yes" ]; then \ - find /usr/share/bunkerweb/ui/app/static -type f -name "*.css" ! -name "*.min.css" -print0 \ - | parallel -0 --no-run-if-empty 'npx postcss {} --use cssnano --no-map -o {}'; \ - else \ - echo "Skipping CSS minification"; \ - fi - -# Minify HTML files -ARG SKIP_MINIFY_HTML=no -RUN if [ "$SKIP_MINIFY" != "yes" ] && [ "$SKIP_MINIFY_HTML" != "yes" ]; then \ - find /usr/share/bunkerweb/loading /usr/share/bunkerweb/core/antibot/files /usr/share/bunkerweb/core/errors/files /usr/share/bunkerweb/core/misc/files -type f -name "*.html" | \ - parallel 'npx html-minifier-terser \ - --collapse-whitespace \ - --remove-comments \ - --remove-optional-tags \ - --remove-redundant-attributes \ - --remove-script-type-attributes \ - --remove-style-link-type-attributes \ - --minify-css true \ - --minify-js true \ - --output {} {}'; \ - else \ - echo "Skipping HTML minification"; \ - fi - -FROM fedora:42@sha256:99e203b80b1c3d8f7e161ec10a68fd02b081ef83a3963553e513c82846b97814 - -# Set default umask to prevent huge recursive chmod increasing the final image size -RUN umask 027 - -# Install fpm -RUN dnf update -y && \ - dnf install -y ruby ruby-devel redhat-rpm-config rpm-build gcc make && \ - gem install -N fpm && \ - dnf -y --setopt=install_weak_deps=False autoremove && \ - dnf clean all && \ - rm -rf /var/cache/dnf - -# Copy dependencies -COPY --from=builder --chown=0:101 /etc/nginx /etc/nginx -COPY --from=builder --chown=0:101 /usr/share/bunkerweb /usr/share/bunkerweb - -WORKDIR /usr/share/bunkerweb - -# Setup BunkerWeb -RUN install -m 755 helpers/bwcli /usr/bin/bwcli && \ - echo "Linux" > INTEGRATION && \ - mkdir -p /etc/bunkerweb/configs /etc/bunkerweb/plugins /var/cache/bunkerweb /var/tmp/bunkerweb /var/run/bunkerweb /var/log/bunkerweb /var/lib/bunkerweb /var/www/html && \ - for dir in $(echo "plugins pro/plugins configs/http configs/stream configs/server-http configs/server-stream configs/default-server-http configs/modsec configs/modsec-crs configs/crs-plugins-before configs/crs-plugins-after") ; do mkdir -p "/etc/bunkerweb/${dir}" ; done && \ - touch /var/log/bunkerweb/error.log /var/log/bunkerweb/access.log /var/log/bunkerweb/modsec_audit.log - -COPY --chmod=660 src/bw/misc/asn.mmdb /var/tmp/bunkerweb/asn.mmdb -COPY --chmod=660 src/bw/misc/country.mmdb /var/tmp/bunkerweb/country.mmdb - -# Copy Linux files -COPY --chmod=740 src/linux/scripts scripts -COPY --chmod=740 src/linux/fpm.sh /usr/share/fpm.sh -COPY src/linux/fpm-fedora-42 /usr/share/.fpm -COPY --chmod=644 src/linux/*.service /lib/systemd/system/ -COPY --chmod=644 src/linux/bunkerweb.logrotate /etc/logrotate.d/bunkerweb - -# Generate RPM at startup -VOLUME /data -WORKDIR /usr/share/ -ENTRYPOINT [ "./fpm.sh", "rpm" ] diff --git a/src/linux/Dockerfile-fedora-43 b/src/linux/Dockerfile-fedora-43 index bf11a916a..129e5c0ad 100644 --- a/src/linux/Dockerfile-fedora-43 +++ b/src/linux/Dockerfile-fedora-43 @@ -2,7 +2,7 @@ FROM fedora:43@sha256:49b5f0ab05516b1a8168c79f295da65e8d8db1cae275a4bec007c0b88c ENV OS=fedora ENV OS_VERSION=43 -ENV NGINX_VERSION=1.30.0 +ENV NGINX_VERSION=1.30.1 # Install Nginx, fpm and dependencies RUN dnf update -y && \ diff --git a/src/linux/Dockerfile-fedora-44 b/src/linux/Dockerfile-fedora-44 index b28a61944..2fc1180ad 100644 --- a/src/linux/Dockerfile-fedora-44 +++ b/src/linux/Dockerfile-fedora-44 @@ -2,7 +2,7 @@ FROM fedora:44@sha256:498c452f32a739b61f0ef215bce9924ebc4866cbe44710f58157d77723 ENV OS=fedora ENV OS_VERSION=44 -ENV NGINX_VERSION=1.30.0 +ENV NGINX_VERSION=1.30.1 # Install Nginx, fpm and dependencies RUN dnf update -y && \ diff --git a/src/linux/Dockerfile-rhel-10 b/src/linux/Dockerfile-rhel-10 index bc91d75e3..16af629b9 100644 --- a/src/linux/Dockerfile-rhel-10 +++ b/src/linux/Dockerfile-rhel-10 @@ -1,8 +1,8 @@ -FROM redhat/ubi10:10.1@sha256:9d3b5102e7ae4f82914a1791610b75acef134b93158be6005b6ae9218c163550 AS builder +FROM redhat/ubi10:10.2@sha256:eb702361cac064da1e7fb9b09eb030562a832209ed1fb1d6765d617ed6294e61 AS builder ENV OS=rhel ENV OS_VERSION=10 -ENV NGINX_VERSION=1.30.0 +ENV NGINX_VERSION=1.30.2 # Copy rocky repo COPY src/linux/rocky-10.repo /etc/yum.repos.d/rocky.repo @@ -145,7 +145,7 @@ RUN if [ "$SKIP_MINIFY" != "yes" ] && [ "$SKIP_MINIFY_HTML" != "yes" ]; then \ echo "Skipping HTML minification"; \ fi -FROM redhat/ubi10:10.1@sha256:9d3b5102e7ae4f82914a1791610b75acef134b93158be6005b6ae9218c163550 +FROM redhat/ubi10:10.2@sha256:eb702361cac064da1e7fb9b09eb030562a832209ed1fb1d6765d617ed6294e61 # Set default umask to prevent huge recursive chmod increasing the final image size RUN umask 027 diff --git a/src/linux/Dockerfile-rhel-8 b/src/linux/Dockerfile-rhel-8 index 4ddac6814..bb136b79e 100644 --- a/src/linux/Dockerfile-rhel-8 +++ b/src/linux/Dockerfile-rhel-8 @@ -1,7 +1,7 @@ -FROM redhat/ubi8:8.10@sha256:cd1299741e13d89764de25b266c2080b355ba33294944fb54663d9bffa3a47d4 AS builder +FROM redhat/ubi8:8.10@sha256:1551c9a922dbb31c5688380fd955ef57f0f00b395bacb36856bc386eba82897b AS builder ENV OS=rhel -ENV NGINX_VERSION=1.30.0 +ENV NGINX_VERSION=1.30.2 # Copy rocky repo COPY src/linux/rocky-8.repo /etc/yum.repos.d/rocky.repo @@ -142,7 +142,7 @@ RUN if [ "$SKIP_MINIFY" != "yes" ] && [ "$SKIP_MINIFY_HTML" != "yes" ]; then \ echo "Skipping HTML minification"; \ fi -FROM redhat/ubi8:8.10@sha256:cd1299741e13d89764de25b266c2080b355ba33294944fb54663d9bffa3a47d4 +FROM redhat/ubi8:8.10@sha256:1551c9a922dbb31c5688380fd955ef57f0f00b395bacb36856bc386eba82897b # Set default umask to prevent huge recursive chmod increasing the final image size RUN umask 027 diff --git a/src/linux/Dockerfile-rhel-9 b/src/linux/Dockerfile-rhel-9 index 1da45603c..c53964b5a 100644 --- a/src/linux/Dockerfile-rhel-9 +++ b/src/linux/Dockerfile-rhel-9 @@ -1,7 +1,7 @@ -FROM redhat/ubi9:9.7@sha256:edabf9436bb372addcca3537aa76111c7a1aba970f5e3b02e2e03bc72943612d AS builder +FROM redhat/ubi9:9.8@sha256:151fc1b2c976f198779d740851436544fdf4e9449ccb4a47e2d0faad34f1733b AS builder ENV OS=rhel -ENV NGINX_VERSION=1.30.0 +ENV NGINX_VERSION=1.30.2 # Copy rocky repo COPY src/linux/rocky-9.repo /etc/yum.repos.d/rocky.repo @@ -143,7 +143,7 @@ RUN if [ "$SKIP_MINIFY" != "yes" ] && [ "$SKIP_MINIFY_HTML" != "yes" ]; then \ echo "Skipping HTML minification"; \ fi -FROM redhat/ubi9:9.7@sha256:edabf9436bb372addcca3537aa76111c7a1aba970f5e3b02e2e03bc72943612d +FROM redhat/ubi9:9.8@sha256:151fc1b2c976f198779d740851436544fdf4e9449ccb4a47e2d0faad34f1733b # Set default umask to prevent huge recursive chmod increasing the final image size RUN umask 027 diff --git a/src/linux/Dockerfile-ubuntu b/src/linux/Dockerfile-ubuntu index 2d4fd030e..7cd69a1a7 100644 --- a/src/linux/Dockerfile-ubuntu +++ b/src/linux/Dockerfile-ubuntu @@ -6,7 +6,7 @@ RUN apt update && \ bash libssl-dev git zlib1g-dev libyajl2 libyajl-dev yajl-tools pkgconf libcurl4-openssl-dev libgeoip-dev apt-utils build-essential autoconf libtool automake g++ gcc libxml2-dev make musl-dev gnupg patch libreadline-dev libpcre2-dev libbrotli-dev libgd-dev liblmdb-dev libfuzzy-dev cargo coreutils python3.12 python3.12-dev python3-pip -y ENV OS=ubuntu -ENV NGINX_VERSION=1.30.0 +ENV NGINX_VERSION=1.30.2 ENV EXPECTED_FPR=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 # Install Nginx diff --git a/src/linux/Dockerfile-ubuntu-jammy b/src/linux/Dockerfile-ubuntu-jammy index 6cd86fccb..2b320edc1 100644 --- a/src/linux/Dockerfile-ubuntu-jammy +++ b/src/linux/Dockerfile-ubuntu-jammy @@ -1,4 +1,4 @@ -FROM ubuntu:jammy@sha256:962f6cadeae0ea6284001009daa4cc9a8c37e75d1f5191cf0eb83fe565b63dd7 AS builder +FROM ubuntu:jammy@sha256:4f838adc7181d9039ac795a7d0aba05a9bd9ecd480d294483169c5def983b64d AS builder # Install Nginx and dependencies RUN apt update && \ @@ -6,7 +6,7 @@ RUN apt update && \ bash libssl-dev git zlib1g-dev libyajl2 libyajl-dev yajl-tools pkgconf libcurl4-openssl-dev libgeoip-dev apt-utils build-essential autoconf libtool automake g++ gcc libxml2-dev make musl-dev gnupg patch libreadline-dev libpcre2-dev libbrotli-dev libgd-dev liblmdb-dev libfuzzy-dev cargo coreutils python3.11 python3.11-dev python3.11-venv python3-pip -y ENV OS=ubuntu -ENV NGINX_VERSION=1.30.0 +ENV NGINX_VERSION=1.30.2 ENV EXPECTED_FPR=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 # Install Nginx @@ -135,7 +135,7 @@ RUN if [ "$SKIP_MINIFY" != "yes" ] && [ "$SKIP_MINIFY_HTML" != "yes" ]; then \ echo "Skipping HTML minification"; \ fi -FROM ubuntu:jammy@sha256:962f6cadeae0ea6284001009daa4cc9a8c37e75d1f5191cf0eb83fe565b63dd7 +FROM ubuntu:jammy@sha256:4f838adc7181d9039ac795a7d0aba05a9bd9ecd480d294483169c5def983b64d # Set default umask to prevent huge recursive chmod increasing the final image size RUN umask 027 diff --git a/src/linux/fpm-debian-bookworm b/src/linux/fpm-debian-bookworm index e66ceaf5a..8927e6e78 100644 --- a/src/linux/fpm-debian-bookworm +++ b/src/linux/fpm-debian-bookworm @@ -3,7 +3,7 @@ --license agpl3 --version %VERSION% --architecture %ARCH% ---depends bash --depends 'nginx = 1.30.0-1~bookworm' --depends python3.11 --depends procps --depends libcurl4 --depends libgeoip-dev --depends libxml2 --depends libyajl2 --depends libmagic1 --depends net-tools --depends sudo --depends lsof --depends libpq5 --depends libpcre2-8-0 --depends liblmdb0 --depends libfuzzy2 --depends libcap2-bin --depends logrotate --depends mariadb-client --depends postgresql-client --depends sqlite3 --depends unzip --depends tar --depends libgd3 --depends curl +--depends bash --depends 'nginx = 1.30.2-1~bookworm' --depends python3.11 --depends procps --depends libcurl4 --depends libgeoip-dev --depends libxml2 --depends libyajl2 --depends libmagic1 --depends net-tools --depends sudo --depends lsof --depends libpq5 --depends libpcre2-8-0 --depends liblmdb0 --depends libfuzzy2 --depends libcap2-bin --depends logrotate --depends mariadb-client --depends postgresql-client --depends sqlite3 --depends unzip --depends tar --depends libgd3 --depends curl --description "BunkerWeb %VERSION% for Debian 12" --url "https://www.bunkerweb.io" --maintainer "Bunkerity " diff --git a/src/linux/fpm-debian-trixie b/src/linux/fpm-debian-trixie index 7b452d357..9d1474c5f 100644 --- a/src/linux/fpm-debian-trixie +++ b/src/linux/fpm-debian-trixie @@ -3,7 +3,7 @@ --license agpl3 --version %VERSION% --architecture %ARCH% ---depends bash --depends 'nginx = 1.30.0-1~trixie' --depends python3 --depends procps --depends libcurl4 --depends libgeoip-dev --depends libxml2 --depends libyajl2 --depends libmagic1 --depends net-tools --depends sudo --depends lsof --depends libpq5 --depends libpcre2-8-0 --depends liblmdb0 --depends libfuzzy2 --depends libcap2-bin --depends logrotate --depends mariadb-client --depends postgresql-client --depends sqlite3 --depends unzip --depends tar --depends libgd3 --depends curl +--depends bash --depends 'nginx = 1.30.2-1~trixie' --depends python3 --depends procps --depends libcurl4 --depends libgeoip-dev --depends libxml2 --depends libyajl2 --depends libmagic1 --depends net-tools --depends sudo --depends lsof --depends libpq5 --depends libpcre2-8-0 --depends liblmdb0 --depends libfuzzy2 --depends libcap2-bin --depends logrotate --depends mariadb-client --depends postgresql-client --depends sqlite3 --depends unzip --depends tar --depends libgd3 --depends curl --description "BunkerWeb %VERSION% for Debian 13" --url "https://www.bunkerweb.io" --maintainer "Bunkerity " diff --git a/src/linux/fpm-fedora-42 b/src/linux/fpm-fedora-42 deleted file mode 100644 index 2be122d9f..000000000 --- a/src/linux/fpm-fedora-42 +++ /dev/null @@ -1,13 +0,0 @@ --s dir ---name bunkerweb ---license agpl3 ---version %VERSION% ---architecture %ARCH% ---depends bash --depends 'nginx = 2:1.30.0' --depends 'nginx-mod-stream = 2:1.30.0' --depends python3.12 --depends libcurl-devel --depends libxml2 --depends yajl --depends lmdb-libs --depends geoip-devel --depends gd --depends sudo --depends procps --depends lsof --depends pcre2 --depends libpq --depends libcap --depends openssl --depends logrotate --depends mysql --depends postgresql --depends sqlite3 --depends unzip --depends tar --depends ssdeep-libs --depends curl ---description "BunkerWeb %VERSION% for Fedora 42" ---url "https://www.bunkerweb.io" ---maintainer "Bunkerity " ---before-install /usr/share/bunkerweb/scripts/beforeInstall.sh ---after-install /usr/share/bunkerweb/scripts/postinstall.sh ---after-remove /usr/share/bunkerweb/scripts/afterRemoveRPM.sh -/usr/share/bunkerweb/=/usr/share/bunkerweb/ /usr/bin/bwcli=/usr/bin/bwcli /etc/bunkerweb/=/etc/bunkerweb /var/tmp/bunkerweb/=/var/tmp/bunkerweb /var/run/bunkerweb/=/var/run/bunkerweb /var/log/bunkerweb/=/var/log/bunkerweb /var/cache/bunkerweb/=/var/cache/bunkerweb /lib/systemd/system/bunkerweb.service=/lib/systemd/system/bunkerweb.service /lib/systemd/system/bunkerweb-ui.service=/lib/systemd/system/bunkerweb-ui.service /lib/systemd/system/bunkerweb-scheduler.service=/lib/systemd/system/bunkerweb-scheduler.service /lib/systemd/system/bunkerweb-api.service=/lib/systemd/system/bunkerweb-api.service /var/lib/bunkerweb/=/var/lib/bunkerweb /etc/logrotate.d/bunkerweb=/etc/logrotate.d/bunkerweb diff --git a/src/linux/fpm-fedora-43 b/src/linux/fpm-fedora-43 index 215254a7f..c58e1d851 100644 --- a/src/linux/fpm-fedora-43 +++ b/src/linux/fpm-fedora-43 @@ -3,7 +3,7 @@ --license agpl3 --version %VERSION% --architecture %ARCH% ---depends bash --depends 'nginx = 2:1.30.0' --depends 'nginx-mod-stream = 2:1.30.0' --depends python3.12 --depends libcurl-devel --depends libxml2 --depends yajl --depends lmdb-libs --depends geoip-devel --depends gd --depends sudo --depends procps --depends lsof --depends pcre2 --depends libpq --depends libcap --depends openssl --depends logrotate --depends mysql --depends postgresql --depends sqlite3 --depends unzip --depends tar --depends ssdeep-libs --depends curl +--depends bash --depends 'nginx = 2:1.30.1' --depends 'nginx-mod-stream = 2:1.30.1' --depends python3.12 --depends libcurl-devel --depends libxml2 --depends yajl --depends lmdb-libs --depends geoip-devel --depends gd --depends sudo --depends procps --depends lsof --depends pcre2 --depends libpq --depends libcap --depends openssl --depends logrotate --depends mysql --depends postgresql --depends sqlite3 --depends unzip --depends tar --depends ssdeep-libs --depends curl --description "BunkerWeb %VERSION% for Fedora 43" --url "https://www.bunkerweb.io" --maintainer "Bunkerity " diff --git a/src/linux/fpm-fedora-44 b/src/linux/fpm-fedora-44 index f351fe475..7d2589d28 100644 --- a/src/linux/fpm-fedora-44 +++ b/src/linux/fpm-fedora-44 @@ -3,7 +3,7 @@ --license agpl3 --version %VERSION% --architecture %ARCH% ---depends bash --depends 'nginx = 2:1.30.0' --depends 'nginx-mod-stream = 2:1.30.0' --depends python3.12 --depends libcurl-devel --depends libxml2 --depends yajl --depends lmdb-libs --depends geoip-devel --depends gd --depends sudo --depends procps --depends lsof --depends pcre2 --depends libpq --depends libcap --depends openssl --depends logrotate --depends mysql --depends postgresql --depends sqlite3 --depends unzip --depends tar --depends ssdeep-libs --depends curl +--depends bash --depends 'nginx = 2:1.30.1' --depends 'nginx-mod-stream = 2:1.30.1' --depends python3.12 --depends libcurl-devel --depends libxml2 --depends yajl --depends lmdb-libs --depends geoip-devel --depends gd --depends sudo --depends procps --depends lsof --depends pcre2 --depends libpq --depends libcap --depends openssl --depends logrotate --depends mysql --depends postgresql --depends sqlite3 --depends unzip --depends tar --depends ssdeep-libs --depends curl --description "BunkerWeb %VERSION% for Fedora 44" --url "https://www.bunkerweb.io" --maintainer "Bunkerity " diff --git a/src/linux/fpm-rhel-10 b/src/linux/fpm-rhel-10 index 27eb77f95..0b3a11da1 100644 --- a/src/linux/fpm-rhel-10 +++ b/src/linux/fpm-rhel-10 @@ -3,7 +3,7 @@ --license agpl3 --version %VERSION% --architecture %ARCH% ---depends bash --depends 'nginx = 2:1.30.0' --depends python3.12 --depends libcurl-devel --depends libxml2 --depends file-libs --depends net-tools --depends gd --depends sudo --depends procps --depends lsof --depends libmaxminddb --depends libpq --depends libcap --depends openssl --depends sqlite --depends unzip --depends tar --depends pcre2 --depends lmdb-libs --depends ssdeep-libs --depends curl +--depends bash --depends 'nginx = 2:1.30.2' --depends python3.12 --depends libcurl-devel --depends libxml2 --depends file-libs --depends net-tools --depends gd --depends sudo --depends procps --depends lsof --depends libmaxminddb --depends libpq --depends libcap --depends openssl --depends sqlite --depends unzip --depends tar --depends pcre2 --depends lmdb-libs --depends ssdeep-libs --depends curl --description "BunkerWeb %VERSION% for RHEL 10" --url "https://www.bunkerweb.io" --maintainer "Bunkerity " diff --git a/src/linux/fpm-rhel-8 b/src/linux/fpm-rhel-8 index 51e0c9f24..833172563 100644 --- a/src/linux/fpm-rhel-8 +++ b/src/linux/fpm-rhel-8 @@ -3,7 +3,7 @@ --license agpl3 --version %VERSION% --architecture %ARCH% ---depends bash --depends 'nginx = 1:1.30.0' --depends python3.11 --depends libcurl-devel --depends libxml2 --depends yajl --depends file-libs --depends net-tools --depends gd --depends sudo --depends procps --depends lsof --depends geoip --depends libpq --depends libcap --depends openssl --depends sqlite --depends unzip --depends tar --depends pcre2 --depends lmdb-libs --depends ssdeep-libs --depends curl +--depends bash --depends 'nginx = 1:1.30.2' --depends python3.11 --depends libcurl-devel --depends libxml2 --depends yajl --depends file-libs --depends net-tools --depends gd --depends sudo --depends procps --depends lsof --depends geoip --depends libpq --depends libcap --depends openssl --depends sqlite --depends unzip --depends tar --depends pcre2 --depends lmdb-libs --depends ssdeep-libs --depends curl --description "BunkerWeb %VERSION% for RHEL 8" --url "https://www.bunkerweb.io" --maintainer "Bunkerity " diff --git a/src/linux/fpm-rhel-9 b/src/linux/fpm-rhel-9 index 388ec9475..95fb3709e 100644 --- a/src/linux/fpm-rhel-9 +++ b/src/linux/fpm-rhel-9 @@ -3,7 +3,7 @@ --license agpl3 --version %VERSION% --architecture %ARCH% ---depends bash --depends 'nginx = 2:1.30.0' --depends python3.11 --depends libcurl-devel --depends libxml2 --depends yajl --depends file-libs --depends net-tools --depends gd --depends sudo --depends procps --depends lsof --depends libmaxminddb --depends libpq --depends libcap --depends openssl --depends sqlite --depends unzip --depends tar --depends pcre2 --depends lmdb-libs --depends ssdeep-libs --depends curl +--depends bash --depends 'nginx = 2:1.30.2' --depends python3.11 --depends libcurl-devel --depends libxml2 --depends yajl --depends file-libs --depends net-tools --depends gd --depends sudo --depends procps --depends lsof --depends libmaxminddb --depends libpq --depends libcap --depends openssl --depends sqlite --depends unzip --depends tar --depends pcre2 --depends lmdb-libs --depends ssdeep-libs --depends curl --description "BunkerWeb %VERSION% for RHEL 9" --url "https://www.bunkerweb.io" --maintainer "Bunkerity " diff --git a/src/linux/fpm-ubuntu b/src/linux/fpm-ubuntu index 7d1a0467b..3cca9ac2f 100644 --- a/src/linux/fpm-ubuntu +++ b/src/linux/fpm-ubuntu @@ -3,7 +3,7 @@ --license agpl3 --version %VERSION% --architecture %ARCH% ---depends bash --depends 'nginx = 1.30.0-1~noble' --depends python3.12 --depends libcurl4 --depends libgeoip-dev --depends libxml2 --depends libyajl2 --depends libmagic1 --depends net-tools --depends sudo --depends procps --depends lsof --depends libpq5 --depends libcap2-bin --depends logrotate --depends mariadb-client --depends postgresql-client --depends sqlite3 --depends unzip --depends libpcre2-8-0 --depends liblmdb0 --depends libfuzzy2 --depends tar --depends libgd3 --depends curl +--depends bash --depends 'nginx = 1.30.2-1~noble' --depends python3.12 --depends libcurl4 --depends libgeoip-dev --depends libxml2 --depends libyajl2 --depends libmagic1 --depends net-tools --depends sudo --depends procps --depends lsof --depends libpq5 --depends libcap2-bin --depends logrotate --depends mariadb-client --depends postgresql-client --depends sqlite3 --depends unzip --depends libpcre2-8-0 --depends liblmdb0 --depends libfuzzy2 --depends tar --depends libgd3 --depends curl --description "BunkerWeb %VERSION% for Ubuntu 24.04" --url "https://www.bunkerweb.io" --maintainer "Bunkerity " diff --git a/src/linux/fpm-ubuntu-jammy b/src/linux/fpm-ubuntu-jammy index 89eab86fc..757d90bec 100644 --- a/src/linux/fpm-ubuntu-jammy +++ b/src/linux/fpm-ubuntu-jammy @@ -3,7 +3,7 @@ --license agpl3 --version %VERSION% --architecture %ARCH% ---depends bash --depends 'nginx = 1.30.0-1~jammy' --depends python3.11 --depends libcurl4 --depends libgeoip-dev --depends libxml2 --depends libyajl2 --depends libmagic1 --depends net-tools --depends sudo --depends procps --depends lsof --depends libpq5 --depends libcap2-bin --depends logrotate --depends mariadb-client --depends postgresql-client --depends sqlite3 --depends unzip --depends libpcre2-8-0 --depends liblmdb0 --depends libfuzzy2 --depends tar --depends libgd3 --depends curl +--depends bash --depends 'nginx = 1.30.2-1~jammy' --depends python3.11 --depends libcurl4 --depends libgeoip-dev --depends libxml2 --depends libyajl2 --depends libmagic1 --depends net-tools --depends sudo --depends procps --depends lsof --depends libpq5 --depends libcap2-bin --depends logrotate --depends mariadb-client --depends postgresql-client --depends sqlite3 --depends unzip --depends libpcre2-8-0 --depends liblmdb0 --depends libfuzzy2 --depends tar --depends libgd3 --depends curl --description "BunkerWeb %VERSION% for Ubuntu 22.04" --url "https://www.bunkerweb.io" --maintainer "Bunkerity " diff --git a/src/linux/rocky-8.repo b/src/linux/rocky-8.repo index f7113497c..1ba0a4c08 100644 --- a/src/linux/rocky-8.repo +++ b/src/linux/rocky-8.repo @@ -20,3 +20,4 @@ gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Rocky-8 # perl and git so this Devel repo only contributes packages that aren't already # available upstream (geoip-devel, lmdb-devel, ssdeep-devel, etc.). excludepkgs = perl perl-* git git-* +module_hotfixes = 1 \ No newline at end of file diff --git a/src/linux/scripts/afterRemoveDEB.sh b/src/linux/scripts/afterRemoveDEB.sh index a0a4031d2..72d5aaab8 100644 --- a/src/linux/scripts/afterRemoveDEB.sh +++ b/src/linux/scripts/afterRemoveDEB.sh @@ -77,10 +77,22 @@ function remove_path() { fi } +function print_leftover_hint() { + echo "ℹ️ Logs and upgrade backups are NEVER auto-removed." + echo "ℹ️ Logs: rm -rf /var/log/bunkerweb" + echo "ℹ️ Backups: rm -f /var/tmp/variables.env /var/tmp/ui.env /var/tmp/scheduler.env \\" + echo "ℹ️ /var/tmp/api.env /var/tmp/api.yml /var/tmp/db.sqlite3 \\" + echo "ℹ️ /var/tmp/bunkerweb_upgrade" +} + # Perform actions for package removal function remove() { echo "ℹ️ Package is being uninstalled" + # Retro-tighten 0644 backups left by pre-fix versions. + chmod 0600 /var/tmp/variables.env /var/tmp/ui.env /var/tmp/scheduler.env \ + /var/tmp/api.env /var/tmp/api.yml /var/tmp/db.sqlite3 2>/dev/null || true + # Stop nginx if it is active if command -v systemctl >/dev/null 2>&1 && systemctl is-active --quiet nginx; then echo "ℹ️ Stopping nginx service" @@ -90,15 +102,16 @@ function remove() { # Dynamically remove all related systemd services remove_systemd_services "bunkerweb" - # Remove associated paths + # /etc, /var/lib, /var/log, /var/tmp backups preserved (Debian Policy §10.8). remove_path "/usr/share/bunkerweb" "application files" remove_path "/var/tmp/bunkerweb" "temporary files" remove_path "/var/run/bunkerweb" "runtime files" - remove_path "/var/log/bunkerweb" "log files" remove_path "/var/cache/bunkerweb" "cache files" remove_path "/usr/bin/bwcli" "CLI binary" echo "ℹ️ BunkerWeb successfully uninstalled" + echo "ℹ️ Preserved (use 'apt-get purge bunkerweb' to delete configs + data): /etc/bunkerweb, /var/lib/bunkerweb" + print_leftover_hint } # Perform actions for package purge @@ -106,11 +119,12 @@ function purge() { echo "ℹ️ Package is being purged" remove - # Remove additional paths during purge + # Logs and /var/tmp backups deliberately kept on purge — admin disposes. remove_path "/var/lib/bunkerweb" "data files" remove_path "/etc/bunkerweb" "configuration files" echo "ℹ️ BunkerWeb successfully purged" + print_leftover_hint } # Check for root privileges @@ -167,23 +181,24 @@ case "$1" in remove_path "/var/tmp/scheduler.env" "Scheduler environment variables" remove_path "/var/tmp/api.env" "API environment variables" remove_path "/var/tmp/db.sqlite3" "database" + # install -m 0600 (atomic) — /var/tmp is mode 1777, must not leak creds. if [ -f /etc/bunkerweb/variables.env ]; then - do_and_check_cmd cp -f /etc/bunkerweb/variables.env /var/tmp/variables.env + do_and_check_cmd install -m 0600 -o root -g root /etc/bunkerweb/variables.env /var/tmp/variables.env fi if [ -f /etc/bunkerweb/ui.env ]; then - do_and_check_cmd cp -f /etc/bunkerweb/ui.env /var/tmp/ui.env + do_and_check_cmd install -m 0600 -o root -g root /etc/bunkerweb/ui.env /var/tmp/ui.env fi if [ -f /etc/bunkerweb/scheduler.env ]; then - do_and_check_cmd cp -f /etc/bunkerweb/scheduler.env /var/tmp/scheduler.env + do_and_check_cmd install -m 0600 -o root -g root /etc/bunkerweb/scheduler.env /var/tmp/scheduler.env fi if [ -f /etc/bunkerweb/api.env ]; then - do_and_check_cmd cp -f /etc/bunkerweb/api.env /var/tmp/api.env + do_and_check_cmd install -m 0600 -o root -g root /etc/bunkerweb/api.env /var/tmp/api.env fi if [ -f /etc/bunkerweb/api.yml ]; then - do_and_check_cmd cp -f /etc/bunkerweb/api.yml /var/tmp/api.yml + do_and_check_cmd install -m 0600 -o root -g root /etc/bunkerweb/api.yml /var/tmp/api.yml fi if [ -f /var/lib/bunkerweb/db.sqlite3 ]; then - do_and_check_cmd cp -f /var/lib/bunkerweb/db.sqlite3 /var/tmp/db.sqlite3 + do_and_check_cmd install -m 0600 -o root -g root /var/lib/bunkerweb/db.sqlite3 /var/tmp/db.sqlite3 fi do_and_check_cmd touch /var/tmp/bunkerweb_upgrade ;; diff --git a/src/linux/scripts/afterRemoveFreeBSD.sh b/src/linux/scripts/afterRemoveFreeBSD.sh index dafd07189..9df538c28 100644 --- a/src/linux/scripts/afterRemoveFreeBSD.sh +++ b/src/linux/scripts/afterRemoveFreeBSD.sh @@ -51,10 +51,22 @@ remove_path() { fi } +print_leftover_hint() { + echo "[INFO] Logs and upgrade backups are NEVER auto-removed." + echo "[INFO] Logs: rm -rf /var/log/bunkerweb" + echo "[INFO] Backups: rm -f /var/tmp/variables.env /var/tmp/ui.env /var/tmp/scheduler.env \\" + echo "[INFO] /var/tmp/api.env /var/tmp/api.yml /var/tmp/db.sqlite3 \\" + echo "[INFO] /var/tmp/bunkerweb_upgrade" +} + # Perform actions for package removal remove() { echo "[INFO] Package is being uninstalled" + # Retro-tighten 0644 backups left by pre-fix versions. + chmod 0600 /var/tmp/variables.env /var/tmp/ui.env /var/tmp/scheduler.env \ + /var/tmp/api.env /var/tmp/api.yml /var/tmp/db.sqlite3 2>/dev/null || true + # Stop nginx if it is active if service nginx status >/dev/null 2>&1; then echo "[INFO] Stopping nginx service" @@ -64,16 +76,18 @@ remove() { # Dynamically remove all related rc.d services remove_rcd_services "bunkerweb" - # Remove associated paths + # /etc, /var/lib, /var/log, /var/tmp backups preserved — admin disposes. remove_path "/usr/share/bunkerweb" "application files" remove_path "/usr/local/etc/sudoers.d/bunkerweb-nginx-reload" "BunkerWeb sudoers reload rule" remove_path "/var/tmp/bunkerweb" "temporary files" remove_path "/var/run/bunkerweb" "runtime files" - remove_path "/var/log/bunkerweb" "log files" remove_path "/var/cache/bunkerweb" "cache files" remove_path "/usr/bin/bwcli" "CLI binary" echo "[INFO] BunkerWeb successfully uninstalled" + echo "[INFO] Preserved: /etc/bunkerweb, /var/lib/bunkerweb" + echo "[INFO] To purge configs + data run: rm -rf /etc/bunkerweb /var/lib/bunkerweb" + print_leftover_hint } # Perform actions for package purge @@ -81,11 +95,12 @@ purge() { echo "[INFO] Package is being purged" remove - # Remove additional paths during purge + # Logs and /var/tmp backups deliberately kept on purge — admin disposes. remove_path "/var/lib/bunkerweb" "data files" remove_path "/etc/bunkerweb" "configuration files" echo "[INFO] BunkerWeb successfully purged" + print_leftover_hint } # Check for root privileges @@ -136,23 +151,24 @@ case "$1" in remove_path "/var/tmp/scheduler.env" "Scheduler environment variables" remove_path "/var/tmp/api.env" "API environment variables" remove_path "/var/tmp/db.sqlite3" "database" + # install -m 0600 (atomic) — /var/tmp is mode 1777, must not leak creds. if [ -f /etc/bunkerweb/variables.env ]; then - do_and_check_cmd cp -f /etc/bunkerweb/variables.env /var/tmp/variables.env + do_and_check_cmd install -m 0600 -o root -g wheel /etc/bunkerweb/variables.env /var/tmp/variables.env fi if [ -f /etc/bunkerweb/ui.env ]; then - do_and_check_cmd cp -f /etc/bunkerweb/ui.env /var/tmp/ui.env + do_and_check_cmd install -m 0600 -o root -g wheel /etc/bunkerweb/ui.env /var/tmp/ui.env fi if [ -f /etc/bunkerweb/scheduler.env ]; then - do_and_check_cmd cp -f /etc/bunkerweb/scheduler.env /var/tmp/scheduler.env + do_and_check_cmd install -m 0600 -o root -g wheel /etc/bunkerweb/scheduler.env /var/tmp/scheduler.env fi if [ -f /etc/bunkerweb/api.env ]; then - do_and_check_cmd cp -f /etc/bunkerweb/api.env /var/tmp/api.env + do_and_check_cmd install -m 0600 -o root -g wheel /etc/bunkerweb/api.env /var/tmp/api.env fi if [ -f /etc/bunkerweb/api.yml ]; then - do_and_check_cmd cp -f /etc/bunkerweb/api.yml /var/tmp/api.yml + do_and_check_cmd install -m 0600 -o root -g wheel /etc/bunkerweb/api.yml /var/tmp/api.yml fi if [ -f /var/lib/bunkerweb/db.sqlite3 ]; then - do_and_check_cmd cp -f /var/lib/bunkerweb/db.sqlite3 /var/tmp/db.sqlite3 + do_and_check_cmd install -m 0600 -o root -g wheel /var/lib/bunkerweb/db.sqlite3 /var/tmp/db.sqlite3 fi do_and_check_cmd touch /var/tmp/bunkerweb_upgrade ;; diff --git a/src/linux/scripts/afterRemoveRPM.sh b/src/linux/scripts/afterRemoveRPM.sh index 4d5eb4c9e..5b629bf10 100644 --- a/src/linux/scripts/afterRemoveRPM.sh +++ b/src/linux/scripts/afterRemoveRPM.sh @@ -77,10 +77,22 @@ function remove_path() { fi } +function print_leftover_hint() { + echo "ℹ️ Logs and upgrade backups are NEVER auto-removed." + echo "ℹ️ Logs: rm -rf /var/log/bunkerweb" + echo "ℹ️ Backups: rm -f /var/tmp/variables.env /var/tmp/ui.env /var/tmp/scheduler.env \\" + echo "ℹ️ /var/tmp/api.env /var/tmp/api.yml /var/tmp/db.sqlite3 \\" + echo "ℹ️ /var/tmp/bunkerweb_upgrade" +} + # Perform actions for package removal function remove() { echo "ℹ️ Package is being uninstalled" + # Retro-tighten 0644 backups left by pre-fix versions. + chmod 0600 /var/tmp/variables.env /var/tmp/ui.env /var/tmp/scheduler.env \ + /var/tmp/api.env /var/tmp/api.yml /var/tmp/db.sqlite3 2>/dev/null || true + # Stop nginx if it is active if command -v systemctl >/dev/null 2>&1 && systemctl is-active --quiet nginx; then echo "ℹ️ Stopping nginx service" @@ -90,15 +102,17 @@ function remove() { # Dynamically remove all related systemd services remove_systemd_services "bunkerweb" - # Remove associated paths + # /etc, /var/lib, /var/log, /var/tmp backups preserved (Fedora Packaging Guidelines). remove_path "/usr/share/bunkerweb" "application files" remove_path "/var/tmp/bunkerweb" "temporary files" remove_path "/var/run/bunkerweb" "runtime files" - remove_path "/var/log/bunkerweb" "log files" remove_path "/var/cache/bunkerweb" "cache files" remove_path "/usr/bin/bwcli" "CLI binary" echo "ℹ️ BunkerWeb successfully uninstalled" + echo "ℹ️ Preserved: /etc/bunkerweb, /var/lib/bunkerweb" + echo "ℹ️ To purge configs + data run: rm -rf /etc/bunkerweb /var/lib/bunkerweb" + print_leftover_hint } # Perform actions for package purge @@ -106,11 +120,12 @@ function purge() { echo "ℹ️ Package is being purged" remove - # Remove additional paths during purge + # Logs and /var/tmp backups deliberately kept on purge — admin disposes. remove_path "/var/lib/bunkerweb" "data files" remove_path "/etc/bunkerweb" "configuration files" echo "ℹ️ BunkerWeb successfully purged" + print_leftover_hint } # Check for root privileges @@ -155,7 +170,10 @@ fi # Handle script arguments case "$1" in 0) + # Uninstall — mirror DEB `remove`. Purge runs only via explicit arg below. remove + ;; + purge) purge ;; 1) @@ -166,23 +184,24 @@ case "$1" in remove_path "/var/tmp/scheduler.env" "Scheduler environment variables" remove_path "/var/tmp/api.env" "API environment variables" remove_path "/var/tmp/db.sqlite3" "database" + # install -m 0600 (atomic) — /var/tmp is mode 1777, must not leak creds. if [ -f /etc/bunkerweb/variables.env ]; then - do_and_check_cmd cp -f /etc/bunkerweb/variables.env /var/tmp/variables.env + do_and_check_cmd install -m 0600 -o root -g root /etc/bunkerweb/variables.env /var/tmp/variables.env fi if [ -f /etc/bunkerweb/ui.env ]; then - do_and_check_cmd cp -f /etc/bunkerweb/ui.env /var/tmp/ui.env + do_and_check_cmd install -m 0600 -o root -g root /etc/bunkerweb/ui.env /var/tmp/ui.env fi if [ -f /etc/bunkerweb/scheduler.env ]; then - do_and_check_cmd cp -f /etc/bunkerweb/scheduler.env /var/tmp/scheduler.env + do_and_check_cmd install -m 0600 -o root -g root /etc/bunkerweb/scheduler.env /var/tmp/scheduler.env fi if [ -f /etc/bunkerweb/api.env ]; then - do_and_check_cmd cp -f /etc/bunkerweb/api.env /var/tmp/api.env + do_and_check_cmd install -m 0600 -o root -g root /etc/bunkerweb/api.env /var/tmp/api.env fi if [ -f /etc/bunkerweb/api.yml ]; then - do_and_check_cmd cp -f /etc/bunkerweb/api.yml /var/tmp/api.yml + do_and_check_cmd install -m 0600 -o root -g root /etc/bunkerweb/api.yml /var/tmp/api.yml fi if [ -f /var/lib/bunkerweb/db.sqlite3 ]; then - do_and_check_cmd cp -f /var/lib/bunkerweb/db.sqlite3 /var/tmp/db.sqlite3 + do_and_check_cmd install -m 0600 -o root -g root /var/lib/bunkerweb/db.sqlite3 /var/tmp/db.sqlite3 fi do_and_check_cmd touch /var/tmp/bunkerweb_upgrade ;; diff --git a/src/linux/scripts/postinstall-freebsd.sh b/src/linux/scripts/postinstall-freebsd.sh index b88d20ad1..b85bc972a 100644 --- a/src/linux/scripts/postinstall-freebsd.sh +++ b/src/linux/scripts/postinstall-freebsd.sh @@ -70,6 +70,10 @@ if [ -d db/alembic/ ]; then chmod -R 770 db/alembic/ fi +# Retro-tighten 0644 backups left by pre-fix versions. +chmod 0600 /var/tmp/variables.env /var/tmp/ui.env /var/tmp/scheduler.env \ + /var/tmp/api.env /var/tmp/api.yml /var/tmp/db.sqlite3 2>/dev/null || true + # Function to migrate files from old locations to new ones migrate_file() { old_path="$1" diff --git a/src/linux/scripts/postinstall.sh b/src/linux/scripts/postinstall.sh index 7f64ec9e7..2724ad4b7 100644 --- a/src/linux/scripts/postinstall.sh +++ b/src/linux/scripts/postinstall.sh @@ -56,6 +56,10 @@ find . \( -path './api' -o -path './scheduler' -o -path './ui' -o -path './cli' find api/ scheduler/ ui/ cli/ lua/ core/ db/ gen/ utils/ helpers/ scripts/ deps/ -type f ! -path 'deps/python/bin/*' ! -name '*.lua' ! -name '*.py' ! -name '*.pyc' ! -name '*.sh' ! -name '*.so' -print0 | xargs -0 -P "$(nproc)" -n 1024 chmod 440 chmod 770 -R db/alembic/ +# Retro-tighten 0644 backups left by pre-fix versions. +chmod 0600 /var/tmp/variables.env /var/tmp/ui.env /var/tmp/scheduler.env \ + /var/tmp/api.env /var/tmp/api.yml /var/tmp/db.sqlite3 2>/dev/null || true + # Function to migrate files from old locations to new ones function migrate_file() { local old_path="$1" diff --git a/src/scheduler/Dockerfile b/src/scheduler/Dockerfile index c0a830966..fc00f1088 100644 --- a/src/scheduler/Dockerfile +++ b/src/scheduler/Dockerfile @@ -52,12 +52,11 @@ FROM python:3.13-alpine@sha256:420cd0bf0f3998275875e02ecd5808168cf0843cbb4d3c536 RUN umask 027 # Install runtime dependencies and add scheduler user -RUN apk add --no-cache bash unzip curl libgcc libstdc++ libpq openssl libmagic mariadb-connector-c mariadb-client sqlite postgresql-client tzdata sed grep && \ +RUN apk add --no-cache bash unzip curl libgcc libstdc++ libpq openssl libmagic mariadb-connector-c mariadb-client postgresql-client sqlite tzdata sed grep && \ addgroup -g 101 scheduler && \ adduser -h /usr/share/bunkerweb/scheduler -g scheduler -s /sbin/nologin -G scheduler -D -H -u 101 --disabled-password scheduler # Fix CVEs -RUN apk add --no-cache "nghttp2-libs>=1.68.1" # CVE-2026-27135 RUN apk add --no-cache "xz-libs>=5.8.3-r0" # CVE-2026-34743 # Copy dependencies @@ -87,7 +86,7 @@ COPY --chown=root:scheduler --chmod=770 src/bw/misc/asn.mmdb /var/tmp/bunkerweb/ COPY --chown=root:scheduler --chmod=770 src/bw/misc/country.mmdb /var/tmp/bunkerweb/country.mmdb LABEL maintainer="Bunkerity " -LABEL version="1.6.10~rc7" +LABEL version="1.6.11~rc1" LABEL url="https://www.bunkerweb.io" LABEL bunkerweb.type="scheduler" diff --git a/src/scheduler/JobScheduler.py b/src/scheduler/JobScheduler.py index 5a9033f55..b271fd83c 100644 --- a/src/scheduler/JobScheduler.py +++ b/src/scheduler/JobScheduler.py @@ -25,12 +25,28 @@ for deps_path in [os.path.join(os.sep, "usr", "share", "bunkerweb", *paths) for sys_path.append(deps_path) from common_utils import effective_cpu_count # type: ignore -from Database import Database # type: ignore +from Database import Database, DEFAULT_POOL_MAX_OVERFLOW, DEFAULT_POOL_SIZE # type: ignore from logger import getLogger # type: ignore from ApiCaller import ApiCaller # type: ignore class JobScheduler(ApiCaller): + # Auto-default bounds for the job executor. Scheduler jobs are I/O bound (LE, + # blocklist sync, plugin downloads); ceiling caps DB-pool pressure, floor keeps a + # slow LE renew from serializing the loop. Override via SCHEDULER_MAX_WORKERS. + _SCHEDULER_WORKERS_AUTO_CEILING = 8 + _SCHEDULER_WORKERS_AUTO_FLOOR = 2 + _SCHEDULER_WORKERS_AUTO_MULTIPLIER = 2 + + @classmethod + def _auto_max_workers(cls) -> int: + """Conservative I/O-bound default for the job executor: ``min(ceiling, max(floor, cpu * mult))``.""" + cpu = max(1, effective_cpu_count()) + return min( + cls._SCHEDULER_WORKERS_AUTO_CEILING, + max(cls._SCHEDULER_WORKERS_AUTO_FLOOR, cpu * cls._SCHEDULER_WORKERS_AUTO_MULTIPLIER), + ) + def __init__( self, logger: Optional[Logger] = None, @@ -52,7 +68,7 @@ class JobScheduler(ApiCaller): # instead of only knowing that "at least one" did. self.__failed_jobs: List[str] = [] self.__job_reload = False - self.__executor = ThreadPoolExecutor(max_workers=min(8, effective_cpu_count() * 4)) + self.__executor = ThreadPoolExecutor(max_workers=self.__resolve_max_workers()) self.__compiled_regexes = self.__compile_regexes() self.__module_paths = set() self.__module_paths_lock = Lock() # Dedicated lock for module paths @@ -61,6 +77,49 @@ class JobScheduler(ApiCaller): self.__cache_permissions_updated = False # Track if permissions were updated self.update_jobs() + def __resolve_max_workers(self) -> int: + """Resolve ``SCHEDULER_MAX_WORKERS`` or fall back to :py:meth:`_auto_max_workers`. + + Warns on invalid/non-positive values and on resolved > DB pool capacity + (``DATABASE_POOL_SIZE`` + ``DATABASE_POOL_MAX_OVERFLOW``). ``max_overflow<0`` + is SQLAlchemy's "unlimited" sentinel and skips the cap check. + """ + default = self._auto_max_workers() + raw = os.getenv("SCHEDULER_MAX_WORKERS", "").strip() + if not raw: + resolved = default + else: + try: + value = int(raw) + except ValueError: + self.__logger.warning(f"Invalid SCHEDULER_MAX_WORKERS value: {raw!r}, using default ({default})") + resolved = default + else: + if value <= 0: + self.__logger.warning(f"SCHEDULER_MAX_WORKERS must be > 0 (got {value}), using default ({default})") + resolved = default + else: + resolved = value + + # Advisory cap check vs DB pool. Reuses Database.py defaults so the two stay aligned. + with suppress(Exception): + pool_size_raw = os.getenv("DATABASE_POOL_SIZE", str(DEFAULT_POOL_SIZE)).strip() or str(DEFAULT_POOL_SIZE) + overflow_raw = os.getenv("DATABASE_POOL_MAX_OVERFLOW", str(DEFAULT_POOL_MAX_OVERFLOW)).strip() or str(DEFAULT_POOL_MAX_OVERFLOW) + pool_size = int(pool_size_raw) if pool_size_raw.isdigit() else DEFAULT_POOL_SIZE + try: + overflow = int(overflow_raw) + except ValueError: + overflow = DEFAULT_POOL_MAX_OVERFLOW + # SQLAlchemy QueuePool: any max_overflow < 0 means "unlimited". + if overflow >= 0 and resolved > pool_size + overflow: + self.__logger.warning( + f"SCHEDULER_MAX_WORKERS={resolved} exceeds the DB pool capacity " + f"(DATABASE_POOL_SIZE={pool_size} + DATABASE_POOL_MAX_OVERFLOW={overflow} = {pool_size + overflow}); " + "scheduler threads may stall on pool checkout under burst." + ) + + return resolved + def __compile_regexes(self): """Precompile regular expressions for job validation.""" return { diff --git a/src/scheduler/requirements.in b/src/scheduler/requirements.in index 9e53bbec5..327265354 100644 --- a/src/scheduler/requirements.in +++ b/src/scheduler/requirements.in @@ -1,19 +1,19 @@ -certbot==5.5.0 +certbot==5.6.0 # Official certbot plugins -certbot-dns-cloudflare==5.5.0 -certbot-dns-digitalocean==5.5.0 -certbot-dns-dnsimple==5.5.0 -certbot-dns-dnsmadeeasy==5.5.0 -certbot-dns-gehirn==5.5.0 -certbot-dns-google==5.5.0 -certbot-dns-linode==5.5.0 -certbot-dns-luadns==5.5.0 -certbot-dns-nsone==5.5.0 -certbot-dns-ovh==5.5.0 -certbot-dns-rfc2136==5.5.0 -certbot-dns-route53==5.5.0 -certbot-dns-sakuracloud==5.5.0 +certbot-dns-cloudflare==5.6.0 +certbot-dns-digitalocean==5.6.0 +certbot-dns-dnsimple==5.6.0 +certbot-dns-dnsmadeeasy==5.6.0 +certbot-dns-gehirn==5.6.0 +certbot-dns-google==5.6.0 +certbot-dns-linode==5.6.0 +certbot-dns-luadns==5.6.0 +certbot-dns-nsone==5.6.0 +certbot-dns-ovh==5.6.0 +certbot-dns-rfc2136==5.6.0 +certbot-dns-route53==5.6.0 +certbot-dns-sakuracloud==5.6.0 # Third-party certbot plugins certbot-dns-bunny==3.0.0 @@ -39,7 +39,7 @@ importlib-metadata==9.0.0 maxminddb==3.1.1 pydantic==2.13.4 python-magic==0.4.27 -requests==2.33.1 +requests==2.34.2 schedule==1.2.2 setuptools<81 diff --git a/src/scheduler/requirements.txt b/src/scheduler/requirements.txt index 7d85b79f4..db73c1e24 100644 --- a/src/scheduler/requirements.txt +++ b/src/scheduler/requirements.txt @@ -4,9 +4,9 @@ # # pip-compile --allow-unsafe --generate-hashes --strip-extras requirements.in # -acme==5.5.0 \ - --hash=sha256:0c237fe013a5406e9ea8b8bb04d6b753a0ed30005df1315175cb99d9c8f2248d \ - --hash=sha256:4a7f653952300053765c92359575992bc644f6253f4c95ff197681b2830d971e +acme==5.6.0 \ + --hash=sha256:23f706ef1c437fc743b2f90704b97f263a56d83198b3af1c25c7dd42c4b463e4 \ + --hash=sha256:fbf7ee8880485f2339942a2eff9a87deb79b74a8dcac4a572c08e7e640f046cd # via # certbot # certbot-dns-bunny @@ -34,27 +34,29 @@ annotated-types==0.7.0 \ --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \ --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89 # via pydantic -attrs==26.1.0 \ - --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \ - --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32 - # via jsonlines +anyio==4.13.0 \ + --hash=sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708 \ + --hash=sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc + # via + # cloudflare + # httpx beautifulsoup4==4.14.3 \ --hash=sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb \ --hash=sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86 # via dns-lexicon -boto3==1.43.6 \ - --hash=sha256:179601ec2992726a718053bf41e43c223ceba397d31ceab11f64d9c910d9fc3a \ - --hash=sha256:e6315effaf12b890b99956e6f8e2c3000a3f64e4ee91943cec3895ce9a836afb +boto3==1.43.10 \ + --hash=sha256:27342e5d5f6170fcc8d1e21cdd939af2448d58ac56b08d494250eaad998e30c7 \ + --hash=sha256:83918184d95967e4c6e9ed1e9a2f58250b291e6ea2cb847ab0825d52596b39e5 # via certbot-dns-route53 -botocore==1.43.6 \ - --hash=sha256:b1e395b347356860398da42e61c808cf1e34b6fa7180cf2b9d87d986e1a06ba0 \ - --hash=sha256:b6d1fdbc6f65a5fe0b7e947823aa37535d3f39f3ba4d21110fab1f55bbbcc04b +botocore==1.43.10 \ + --hash=sha256:2f4af585b41dbccdfc9f49677d7bd72d713a12ef89a1dc9c8538a927649498bf \ + --hash=sha256:8a0176d8c2f8bebe95d4f923a824a1ace04b02f360e220681c388e097f32c3b6 # via # boto3 # s3transfer -certbot==5.5.0 \ - --hash=sha256:921acc125fdaf3efa91b057cf5d6fcbb44d9bd511de8f9febd1260847cd40ae0 \ - --hash=sha256:c72c813c4c8646a9658bc7a9530c757c76067503fa2f5d7eb12027e8bbf798d7 +certbot==5.6.0 \ + --hash=sha256:81ea876500ba60022a98a3e92fe4230088c7792b9d375357bb23c58741d2b0da \ + --hash=sha256:89052854e28cd2fcac72a8857aad6dfc6b02ba992f4b8f255062fe29c8aed3ee # via # -r requirements.in # certbot-dns-bunny @@ -90,9 +92,9 @@ certbot-dns-bunny==3.0.0 \ --hash=sha256:507fe45c3aa399c93c30fd68b06ba6e5646bb56e1d454a7f1ffac8c8677e5a21 \ --hash=sha256:e58e450747e79fd9dad6cdf21d3685b23e7e3d2fea65c16aca3a4dfd8cd31397 # via -r requirements.in -certbot-dns-cloudflare==5.5.0 \ - --hash=sha256:71200a7761c0663f5072d6b26e6aa0f336fb9e431ad4bfa7a91cd850c8a7ca7f \ - --hash=sha256:ff3b2d75055b1f31bbedd8801ed654e1da8dc408bdcb158986972eada9312afc +certbot-dns-cloudflare==5.6.0 \ + --hash=sha256:6b8a586286a8c30e29471c4d737987c4fd5b3f41dd416c7954451e5572b2b5ab \ + --hash=sha256:84021fa3f8acddf20f059ff0d9a2134722348401f12a0cbd29055f2c322d4d89 # via -r requirements.in certbot-dns-cloudns==0.7.0 \ --hash=sha256:41f27d345c3e4e7d211f2b376d23e50e8f9f54e592ba3832d769a6f7a00642cc \ @@ -102,17 +104,17 @@ certbot-dns-desec==1.3.2 \ --hash=sha256:3b7aed97b808383874da4f8c3668d4142002c870d8e26cda3f7feb2a319a0964 \ --hash=sha256:7dec89001609abbc9fe9bf67b9ccb893c6de9fe061c3530fa176b4cc00ac52bb # via -r requirements.in -certbot-dns-digitalocean==5.5.0 \ - --hash=sha256:a22ecfea50250934e3131cded83bcb3197ab5296a238cad37c9653ed324c88ca \ - --hash=sha256:e9f97862d245ebedc2cde018020385ac25b34f351e4a54a274441d47dc47e6ab +certbot-dns-digitalocean==5.6.0 \ + --hash=sha256:44733015c0879a3750ae55d337db942f70192a9f19d02cd9412d4e4b87cc7b7e \ + --hash=sha256:7d1cc478e8e8851ff17c13b2697e0eb9420ca9c41929415d2b6e084aed82abca # via -r requirements.in -certbot-dns-dnsimple==5.5.0 \ - --hash=sha256:aef1c5087b3b84db7cd930bd1e3ddea2b0eb12b198c45aab149e3914b9eb602d \ - --hash=sha256:d26a5663921a0c4d31b5ce069d1d6e79765e53feebc6930d66d666c45d23b58d +certbot-dns-dnsimple==5.6.0 \ + --hash=sha256:ab1fe1ca3aa69e58fafd4cb942966a14230b0984d08c4502f3491dd9721cdd41 \ + --hash=sha256:c764ed30179b2d43d9dfe2ff0b09a76335b8dfe3714ff52990871e5e454f913d # via -r requirements.in -certbot-dns-dnsmadeeasy==5.5.0 \ - --hash=sha256:9308674aa7047fffebaba4c021c18084767c2f3c4dd6fc9f86d00c34bd0d5a23 \ - --hash=sha256:c5a61a7764aaa43fc2177f910f596aa077051b51f5267d38185f5e1a11cca613 +certbot-dns-dnsmadeeasy==5.6.0 \ + --hash=sha256:200df318303f4231e6324ec5057c7d30e8e4b1692c619f80f810fffb781ebf26 \ + --hash=sha256:3225bbd7660788e1fdd44197443bff104e9eeec3a68d3cdec4db664b83d0b5ae # via -r requirements.in certbot-dns-domainoffensive==2.0.0 \ --hash=sha256:bfe7cb33eee6bc48e62437c811d0605cf974f723e211ac27fc396615ff5a2a05 \ @@ -132,17 +134,17 @@ certbot-dns-dynu==0.0.6.post2502111739272536 \ certbot-dns-gandi @ https://github.com/TheophileDiot/certbot-plugin-gandi/archive/da4655bc6d39941b44de2a7c049163a7e34a5a39.zip \ --hash=sha256:b875034494ec49f305dba96212c202973def92618936636db3107e498a2abec4 # via -r requirements.in -certbot-dns-gehirn==5.5.0 \ - --hash=sha256:2f93ca18ef926c19d7b86a470885130efa29b79f572af93027d7ec8dc1badff0 \ - --hash=sha256:86f2c70d8e0834130d8151dbb30c24d9148c25c74f4c4ac2b81bb6ef73c99a6e +certbot-dns-gehirn==5.6.0 \ + --hash=sha256:9e3827d844501a43d61eea3f9ff58e389d2ad91fb93f0a2e593bafc5bd988624 \ + --hash=sha256:d438e074189b94f3148f5e1ff77a620c5317b69b591df569e1c7da9c3b52aa90 # via -r requirements.in certbot-dns-godaddy==2.8.0 \ --hash=sha256:9ed10c9542d1c9610d0b47fa95c4e01569fcc87d6d5a277f3e7a42ce59b8c3c4 \ --hash=sha256:cc80af1781de3996aec4b11d2a42b0d7be2d38926a6c815ca4af1082cb4dafcf # via -r requirements.in -certbot-dns-google==5.5.0 \ - --hash=sha256:062db3c135bb956dd5a00705c2144ec0a35bc6a48104a3ae16ba79fabcfdfdcb \ - --hash=sha256:ee6cd586e83a95ed582b3ff815fc9a7122f8de19d7e8db5628a1dcc3a5a51636 +certbot-dns-google==5.6.0 \ + --hash=sha256:0edb1fc6faf575f962f1f9bd2f358b66ef1578c8b7c11ca2ae0e0c7f9f9296dd \ + --hash=sha256:4387cbe0038c05676f89de1b5ec402fcd18f17e35a973c2ccf4531bebfcc848f # via -r requirements.in certbot-dns-hetzner==4.0.0 \ --hash=sha256:1dd5d43467bb624996d36a416ceeffb4068be7b315a774a6e7fc2b562140743f \ @@ -156,41 +158,41 @@ certbot-dns-ionos==2024.11.9 \ --hash=sha256:333be82806ae28a02c979baa8eb0bbc4862ad26549f60685468fadbf1fe3609e \ --hash=sha256:619aa7965272aef93d080094e0b7b79da0598d89567511cb683d44db6fa8c803 # via -r requirements.in -certbot-dns-linode==5.5.0 \ - --hash=sha256:13cddc99f2f43ed8a2a20012b58a26802d9cdd2bad4342d86f599e16c52e8b1e \ - --hash=sha256:71ccbd802a34602dcd937525a412cc2b7deb86dc2450dbb64c9e2b0e2702cee3 +certbot-dns-linode==5.6.0 \ + --hash=sha256:7361ce766647944cbd06475e7bfeb37e246cbff80d62a99d5d0ab60c0f082100 \ + --hash=sha256:84ae0d0489b45b26ee559df4ce36c7dc7d30f0d4ecd06e237186350fec7756ab # via -r requirements.in -certbot-dns-luadns==5.5.0 \ - --hash=sha256:4fa1bfb179ee2077836da5d2c165ad970074383df359ce77535bdeea14a7adf3 \ - --hash=sha256:90975f33c6a9a224d2d60497866c17a18e41aa68dc443f47144c79203f6d9ba6 +certbot-dns-luadns==5.6.0 \ + --hash=sha256:04420a936ce40f1315e198e8433c23dbae4e13653b71f7ced3d0d353d6bcffd4 \ + --hash=sha256:626c985228d3001fa2aed813a279d13c147bcb19c0f465fb75be8f457df29294 # via -r requirements.in certbot-dns-njalla==2.0.2 \ --hash=sha256:668f91f3027edd4c4cbca99095fd5ec66d4ccefcb4b9366367873e25e624e1bd \ --hash=sha256:b771c6e55b6ecdaadd7a5dd88d3798aa39290414a8d2f916c89b8cb52b29eea8 # via -r requirements.in -certbot-dns-nsone==5.5.0 \ - --hash=sha256:eefc1c6893ffbd0a3f15b3f830abd89d25de1c1ae0928d435a684dd955ae75e3 \ - --hash=sha256:ef8eb054a28a572898fd67a4105ff1c2996ff1057aa9fff0528dc6a51341ce01 +certbot-dns-nsone==5.6.0 \ + --hash=sha256:1b94c706a98252fd80add6a7a5766915abb1aec87f7f7fba2026ae4c56cbad1d \ + --hash=sha256:7362fa1ca2c9fb9d02771d5306f524b678861153a9cfb403da403329d4b93631 # via -r requirements.in -certbot-dns-ovh==5.5.0 \ - --hash=sha256:eec4e31d18c0fcb0134973bf3cdc3f294c634a48264efd6e96162e1ddcf86ecf \ - --hash=sha256:f0b5e2ff3070dd9057e72305e991b13185ac6258a52480beba26eb307dbfa847 +certbot-dns-ovh==5.6.0 \ + --hash=sha256:47993513bbd27eab20336565d5437e0a248e07d5107cf010f8e39767e3ec798c \ + --hash=sha256:a42636fa00fcf7f18fbab2bec063b004ee5100d4d813c9c421a419a2d3f8b1a2 # via -r requirements.in certbot-dns-pdns==0.1.1 \ --hash=sha256:8e0bf82245323d33ca1c889c9facc24349c5f3ffc951136fe8bf88e1c82060fe \ --hash=sha256:d059d1c1cc21eab259a24ee69c1d9d8fb077fd90f58cf8de904b0f5bd576986f # via -r requirements.in -certbot-dns-rfc2136==5.5.0 \ - --hash=sha256:7fe27154013e135b6186e2f1dedd48a0d466b0fa3ac62803560a3edfab6b61b5 \ - --hash=sha256:ef26302b9fb0496bbb60ec820b7d1f87e887507842c7ee3632af9af4ad6d22df +certbot-dns-rfc2136==5.6.0 \ + --hash=sha256:9e98048439a1db0f0e7f891d44f546f1bdd3bd0159dc8ba1452e4d1e3d70ae9b \ + --hash=sha256:a879536de89e88fc3ea63b4d8cc8a6b5faa08d39e77e0da0c8936efb9bf43990 # via -r requirements.in -certbot-dns-route53==5.5.0 \ - --hash=sha256:26bd1c82e3317351ccb0faa13f56eba55e851dcc69f25689d92bad54266de773 \ - --hash=sha256:8ab904fc2fb950e03372533ab8384625ac6d2aab7e6958f795811f7029d50caa +certbot-dns-route53==5.6.0 \ + --hash=sha256:54aeeff8fa26878264f1a898332db32b422d694648fa2a25d2b50ecd61038071 \ + --hash=sha256:d1cbf22eda3928986316f9cf72f5940c18c37459e34600bddf0f2cf11848b80f # via -r requirements.in -certbot-dns-sakuracloud==5.5.0 \ - --hash=sha256:3179a1ad13928667527dc65251d37fac4b48bc1e21cf9b9d61c8c8082f70d423 \ - --hash=sha256:62385beb78e3c3d9c3f4cfbc5ac753803fe0a1422c02a01b09b708c133c74a47 +certbot-dns-sakuracloud==5.6.0 \ + --hash=sha256:410321a91b3e73c859dab4f0d9307488c19a578019d297367a3f4bcc70651f34 \ + --hash=sha256:bad77a1606aeba79165a6b3bcbb8b470e032a559ab9e6441bdece411f659657f # via -r requirements.in certbot-dns-scaleway==0.0.7 \ --hash=sha256:999dda5b8689277facb77e1757b8a6b207baeecc0ded0c27aea2c51331affc92 \ @@ -204,6 +206,8 @@ certifi==2026.4.22 \ --hash=sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580 # via # domeneshop + # httpcore + # httpx # requests cffi==2.0.0 \ --hash=sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb \ @@ -424,8 +428,9 @@ charset-normalizer==3.4.7 \ --hash=sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79 \ --hash=sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464 # via requests -cloudflare==2.19.4 \ - --hash=sha256:3b6000a01a237c23bccfdf6d20256ea5111ec74a826ae9e74f9f0e5bb5b2383f +cloudflare==5.1.0 \ + --hash=sha256:c9ad846ef889aa5476ce15cd509eacc294ffac3ac834cdf924d4582d5638c759 \ + --hash=sha256:ed06f7544e3bb2dac43e2029f91170d2e7c427ac33bb3eb3e464965f46a852a6 # via certbot-dns-cloudflare cloudns-api==0.9.3 \ --hash=sha256:0be157365c997d8a4162065867f07702398882cd3481bc7936637d8a807eb58f \ @@ -501,7 +506,9 @@ cryptography==48.0.0 \ distro==1.9.0 \ --hash=sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed \ --hash=sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2 - # via certbot + # via + # certbot + # cloudflare dns-lexicon==3.25.2 \ --hash=sha256:0592a30d8d721226668b34b736d7af1332d5c5fccf138626964d644c2b17b9be \ --hash=sha256:a377bf2b4017ee46de8d5515cf2c204bfda74b49032dc5420bd3100993f28deb @@ -544,9 +551,9 @@ google-api-python-client==2.196.0 \ --hash=sha256:2591e9b47dcb17e4e62a09370aaee3bcf323af8f28ccecdabcd0a42a23ca4db5 \ --hash=sha256:9f335d38f6caaa2747bcf64335ed1a9a19047d53e86538eda6a1b17d37f1743d # via certbot-dns-google -google-auth==2.52.0 \ - --hash=sha256:01f30e1a9e3638698d89464f5e603ce29d18e1c0e63ec31ac570aba4e164aaf5 \ - --hash=sha256:aee92803ba0ff93a70a3b8a35c7b4797837751cd6380b63ff38372b98f3ed627 +google-auth==2.53.0 \ + --hash=sha256:6e7449917c599b35126a99ec268ec6880301f2fea41dce198fe8fd83ff642b68 \ + --hash=sha256:e7e6aa16f6bee7b2b264830fd04f08087a1d5a836df516251a5d15327b246c9c # via # certbot-dns-google # google-api-core @@ -560,21 +567,35 @@ googleapis-common-protos==1.75.0 \ --hash=sha256:53a062ff3c32552fbd62c11fe23768b78e4ddf0494d5e5fd97d3f4689c75fbbd \ --hash=sha256:961ed60399c457ceb0ee8f285a84c870aabc9c6a832b9d37bb281b5bebde43ed # via google-api-core +h11==0.16.0 \ + --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \ + --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86 + # via httpcore hcloud==2.20.0 \ --hash=sha256:27052effab1a25bb3cbfc8e2d2472e0e8503b20aecab5bbad5c4b0e8697696d5 \ --hash=sha256:7373c807c3dbbe5e35e25945e7c1a54d66b6b126d700cc2b8accf0c160f035a6 # via certbot-dns-hetzner +httpcore==1.0.9 \ + --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \ + --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8 + # via httpx httplib2==0.31.2 \ --hash=sha256:385e0869d7397484f4eab426197a4c020b606edd43372492337c0b4010ae5d24 \ --hash=sha256:dbf0c2fa3862acf3c55c078ea9c0bc4481d7dc5117cae71be9514912cf9f8349 # via # google-api-python-client # google-auth-httplib2 -idna==3.14 \ - --hash=sha256:466d810d7a2cc1022bea9b037c39728d51ae7dad40d480fc9b7d7ecf98ba8ee3 \ - --hash=sha256:e677eaf072e290f7b725f9acf0b3a2bd55f9fd6f7c70abe5f0e34823d0accf69 +httpx==0.28.1 \ + --hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \ + --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad + # via cloudflare +idna==3.15 \ + --hash=sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8 \ + --hash=sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc # via + # anyio # certbot-dns-infomaniak + # httpx # requests # tldextract importlib-metadata==9.0.0 \ @@ -593,10 +614,6 @@ josepy==2.2.0 \ # via # acme # certbot -jsonlines==4.0.0 \ - --hash=sha256:0c6d2c09117550c089995247f605ae4cf77dd1533041d366351f6f298822ea74 \ - --hash=sha256:185b334ff2ca5a91362993f42e83588a360cf95ce4b71a73548502bda52a7c55 - # via cloudflare jsonpickle==4.1.1 \ --hash=sha256:bb141da6057898aa2438ff268362b126826c812a1721e31cf08a6e142910dc91 \ --hash=sha256:f86e18f13e2b96c1c1eede0b7b90095bbb61d99fedc14813c44dc2f361dbbae1 @@ -724,7 +741,9 @@ pycparser==3.0 \ pydantic==2.13.4 \ --hash=sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba \ --hash=sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6 - # via -r requirements.in + # via + # -r requirements.in + # cloudflare pydantic-core==2.46.4 \ --hash=sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0 \ --hash=sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262 \ @@ -957,12 +976,10 @@ pyyaml==6.0.3 \ --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \ --hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \ --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 - # via - # cloudflare - # dns-lexicon -requests==2.33.1 \ - --hash=sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517 \ - --hash=sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a + # via dns-lexicon +requests==2.34.2 \ + --hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \ + --hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed # via # -r requirements.in # acme @@ -976,7 +993,6 @@ requests==2.33.1 \ # certbot-dns-infomaniak # certbot-dns-ionos # certbot-dns-scaleway - # cloudflare # cloudns-api # dns-lexicon # google-api-core @@ -1023,6 +1039,10 @@ six==1.17.0 \ --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 # via python-dateutil +sniffio==1.3.1 \ + --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \ + --hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc + # via cloudflare soupsieve==2.8.3 \ --hash=sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349 \ --hash=sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95 @@ -1037,7 +1057,9 @@ typing-extensions==4.15.0 \ --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 # via + # anyio # beautifulsoup4 + # cloudflare # pydantic # pydantic-core # pyopenssl @@ -1057,9 +1079,9 @@ urllib3==2.7.0 \ # botocore # domeneshop # requests -zipp==3.23.1 \ - --hash=sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc \ - --hash=sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110 +zipp==4.1.0 \ + --hash=sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f \ + --hash=sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602 # via importlib-metadata zope-interface==8.4 \ --hash=sha256:049ba3c7b38cc400ae08e011617635706e0f442e1d075db1b015246fcbf6091e \ diff --git a/src/ui/Dockerfile b/src/ui/Dockerfile index f07117f39..518b20284 100644 --- a/src/ui/Dockerfile +++ b/src/ui/Dockerfile @@ -105,7 +105,7 @@ RUN echo "Docker" > INTEGRATION && \ chmod 660 INTEGRATION LABEL maintainer="Bunkerity " -LABEL version="1.6.10~rc7" +LABEL version="1.6.11~rc1" LABEL url="https://www.bunkerweb.io" LABEL bunkerweb.type="ui" diff --git a/src/ui/app/routes/configs.py b/src/ui/app/routes/configs.py index 31dbaf8d3..876a5ae54 100644 --- a/src/ui/app/routes/configs.py +++ b/src/ui/app/routes/configs.py @@ -9,6 +9,8 @@ from flask import Blueprint, redirect, render_template, request, send_file, url_ from flask_login import login_required from werkzeug.utils import secure_filename +from common_utils import bytes_hash # type: ignore + from app.dependencies import BW_CONFIG, CONFIG_TASKS_EXECUTOR, DATA, DB from app.utils import flash, is_editable_method @@ -126,8 +128,9 @@ def apply_imported_configs(parsed_configs: List[Dict], overwrite: bool, parse_er services_value = BW_CONFIG.get_config(global_only=True, methods=False, with_drafts=True, filtered_settings=("SERVER_NAME",)).get("SERVER_NAME", "") existing_services = set(services_value.split()) if services_value else set() + # Key on the normalized (underscore) type: template-provided configs carry the hyphenated form. existing_configs = { - (db_config.get("service_id") or None, db_config["type"], db_config["name"]): db_config + (db_config.get("service_id") or None, db_config["type"].strip().replace("-", "_").lower(), db_config["name"]): db_config for db_config in DB.get_custom_configs(with_drafts=True, with_data=False) } @@ -145,10 +148,18 @@ def apply_imported_configs(parsed_configs: List[Dict], overwrite: bool, parse_er is_new = existing is None if existing is not None: - if existing.get("template") or not is_editable_method(existing.get("method")): + if existing.get("template"): + # The target serves this config from a template. Honor the change-detection rule: + # only materialize a real UI override when the imported data differs from the + # template default; identical data stays template-managed (no shadowing row). + if bytes_hash(entry["data"], algorithm="sha256") == existing.get("checksum"): + results["skipped"].append(f"{label} (unchanged from template)") + continue + is_new = True + elif not is_editable_method(existing.get("method")): results["skipped"].append(f"{label} (non-editable method: {existing.get('method')})") continue - if not overwrite: + elif not overwrite: results["skipped"].append(f"{label} (already exists; enable 'Overwrite existing' to replace)") continue @@ -725,10 +736,9 @@ def configs_export(): selection_set = set(selection) if selection else None for db_config in db_configs: - if db_config.get("template"): - continue service_id = db_config.get("service_id") or None - config_type = db_config["type"] + # Normalize: template-provided configs carry hyphenated types (see get_custom_configs), real rows use the underscore enum form. + config_type = db_config["type"].strip().replace("-", "_").lower() config_name = db_config["name"] if selection_set is not None and (service_id, config_type, config_name) not in selection_set: continue diff --git a/src/ui/app/routes/services.py b/src/ui/app/routes/services.py index 54c357411..ad9ed0f7b 100644 --- a/src/ui/app/routes/services.py +++ b/src/ui/app/routes/services.py @@ -51,13 +51,7 @@ def parse_services_export(content: str) -> Tuple[Dict[str, Dict[str, str]], List @services.route("/services", methods=["GET"]) @login_required def services_page(): - services_with_configs = sorted( - { - config["service_id"] - for config in DB.get_custom_configs(with_drafts=True, with_data=False) - if config.get("service_id") and not config.get("template") and is_editable_method(config.get("method")) - } - ) + services_with_configs = sorted({config["service_id"] for config in DB.get_custom_configs(with_drafts=True, with_data=False) if config.get("service_id")}) return render_template( "services.html", services=DB.get_services(with_drafts=True), @@ -665,8 +659,6 @@ def services_service_export(): db_configs = DB.get_custom_configs(with_drafts=True, with_data=True) configs_payload: List[Dict] = [] for db_config_row in db_configs: - if db_config_row.get("template"): - continue service_id = db_config_row.get("service_id") or None if service_id not in selected_services: continue @@ -681,7 +673,7 @@ def services_service_export(): configs_payload.append( { "service_id": service_id, - "type": db_config_row["type"], + "type": db_config_row["type"].strip().replace("-", "_").lower(), "name": db_config_row["name"], "data": data_str, "is_draft": bool(db_config_row.get("is_draft", False)), diff --git a/src/ui/app/utils.py b/src/ui/app/utils.py index 8a6b69bbc..5b7ebf813 100644 --- a/src/ui/app/utils.py +++ b/src/ui/app/utils.py @@ -26,6 +26,8 @@ LOGGER = getLogger("UI") RESERVED_SERVICE_NAMES = frozenset({"unknown", "Web UI", "bwcli", "default server", ""}) USER_PASSWORD_RX = re_compile(r"^(?=.*\p{Ll})(?=.*\p{Lu})(?=.*\d)(?=.*\P{Alnum}).{8,}$") +BCRYPT_HASH_RX = re_compile(r"^\$2[aby]\$\d{2}\$[./A-Za-z0-9]{53}\Z") +RECOMMENDED_BCRYPT_COST = 12 # below this, a supplied pre-hashed ADMIN_PASSWORD triggers a warning PLUGIN_NAME_RX = re_compile(r"^[\w.-]{4,64}$") BISCUIT_PUBLIC_KEY_FILE = LIB_DIR.joinpath(".biscuit_public_key") @@ -276,6 +278,22 @@ def gen_password_hash(password: str) -> bytes: return hashpw(password.encode("utf-8"), gensalt(rounds=13)) +def is_bcrypt_hash(value: str) -> bool: + """True if value is a well-formed bcrypt hash this build's bcrypt lib can verify.""" + if not BCRYPT_HASH_RX.match(value): + return False + try: + checkpw(b"bunkerweb-bcrypt-probe", value.encode("utf-8")) + except (ValueError, TypeError): + return False # prefix/format the installed bcrypt lib cannot parse -> treat as plaintext + return True + + +def bcrypt_cost(value: str) -> int: + """Cost factor of a bcrypt hash. Caller must ensure value passed is_bcrypt_hash() first.""" + return int(value[4:6]) + + def check_password(password: str, hashed: bytes) -> bool: return checkpw(password.encode("utf-8"), hashed) diff --git a/src/ui/requirements.in b/src/ui/requirements.in index ff3727ac3..04e774f3a 100644 --- a/src/ui/requirements.in +++ b/src/ui/requirements.in @@ -1,6 +1,6 @@ bcrypt<5.0.0 biscuit-python==0.4.0 -certbot==5.5.0 +certbot==5.6.0 defusedcsv==3.0.0 Flask==3.1.3 Flask-Login==0.6.3 diff --git a/src/ui/requirements.txt b/src/ui/requirements.txt index 8c28ce0b8..83946b5b9 100644 --- a/src/ui/requirements.txt +++ b/src/ui/requirements.txt @@ -4,9 +4,9 @@ # # pip-compile --allow-unsafe --generate-hashes --strip-extras requirements.in # -acme==5.5.0 \ - --hash=sha256:0c237fe013a5406e9ea8b8bb04d6b753a0ed30005df1315175cb99d9c8f2248d \ - --hash=sha256:4a7f653952300053765c92359575992bc644f6253f4c95ff197681b2830d971e +acme==5.6.0 \ + --hash=sha256:23f706ef1c437fc743b2f90704b97f263a56d83198b3af1c25c7dd42c4b463e4 \ + --hash=sha256:fbf7ee8880485f2339942a2eff9a87deb79b74a8dcac4a572c08e7e640f046cd # via certbot bcrypt==4.3.0 \ --hash=sha256:0042b2e342e9ae3d2ed22727c1262f76cc4f345683b5c1715f0250cf4277294f \ @@ -186,9 +186,9 @@ cachelib==0.14.0 \ --hash=sha256:4671000b032baa8fac47ad19850f4f522785cee764b4e04c5cfe8955a18d67de \ --hash=sha256:73fedcadd0ba818fb2bb9f3c7cd5fcc2a71e86286f1842f55f28d500faee17f1 # via flask-session -certbot==5.5.0 \ - --hash=sha256:921acc125fdaf3efa91b057cf5d6fcbb44d9bd511de8f9febd1260847cd40ae0 \ - --hash=sha256:c72c813c4c8646a9658bc7a9530c757c76067503fa2f5d7eb12027e8bbf798d7 +certbot==5.6.0 \ + --hash=sha256:81ea876500ba60022a98a3e92fe4230088c7792b9d375357bb23c58741d2b0da \ + --hash=sha256:89052854e28cd2fcac72a8857aad6dfc6b02ba992f4b8f255062fe29c8aed3ee # via -r requirements.in certifi==2026.4.22 \ --hash=sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a \ @@ -411,9 +411,9 @@ charset-normalizer==3.4.7 \ --hash=sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79 \ --hash=sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464 # via requests -click==8.3.3 \ - --hash=sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2 \ - --hash=sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613 +click==8.4.0 \ + --hash=sha256:40c50b7c6c6adac2823d411041ec84f3f103f1b280d5e9ce0d7f998995832f81 \ + --hash=sha256:638f1338fe1235c8f4e008e4a8a254fb5c5fbdcbb40ece3c9142ebb78e792973 # via flask configargparse==1.7.5 \ --hash=sha256:1e63fdffedf94da9cd435fc13a1cd24777e76879dd2343912c1f871d4ac8c592 \ @@ -528,9 +528,9 @@ hyperframe==6.1.0 \ --hash=sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5 \ --hash=sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08 # via h2 -idna==3.14 \ - --hash=sha256:466d810d7a2cc1022bea9b037c39728d51ae7dad40d480fc9b7d7ecf98ba8ee3 \ - --hash=sha256:e677eaf072e290f7b725f9acf0b3a2bd55f9fd6f7c70abe5f0e34823d0accf69 +idna==3.15 \ + --hash=sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8 \ + --hash=sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc # via requests itsdangerous==2.2.0 \ --hash=sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef \ @@ -964,9 +964,9 @@ regex==2026.5.9 \ --hash=sha256:fd190e88a895a8901325fad284a3f74ea52b1da8525b76cc811fa9b1edf0ce2b \ --hash=sha256:ff8d372ac2acdc048d1c19916f27ee61bc5722728458ba6ca5052f2c72d51763 # via -r requirements.in -requests==2.33.1 \ - --hash=sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517 \ - --hash=sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a +requests==2.34.2 \ + --hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \ + --hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed # via acme six==1.17.0 \ --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ diff --git a/src/ui/utils/gunicorn.conf.py b/src/ui/utils/gunicorn.conf.py index d70503a8c..2ee307067 100644 --- a/src/ui/utils/gunicorn.conf.py +++ b/src/ui/utils/gunicorn.conf.py @@ -23,7 +23,17 @@ from logger import getLogger, log_types # type: ignore from app.models.ui_database import UIDatabase from app.dependencies import reload_plugins -from app.utils import BISCUIT_PRIVATE_KEY_FILE, BISCUIT_PUBLIC_KEY_FILE, USER_PASSWORD_RX, check_password, gen_password_hash, get_latest_stable_release +from app.utils import ( + BISCUIT_PRIVATE_KEY_FILE, + BISCUIT_PUBLIC_KEY_FILE, + RECOMMENDED_BCRYPT_COST, + USER_PASSWORD_RX, + bcrypt_cost, + check_password, + gen_password_hash, + get_latest_stable_release, + is_bcrypt_hash, +) TMP_DIR = Path(sep, "var", "tmp", "bunkerweb") TMP_UI_DIR = TMP_DIR.joinpath("ui") @@ -470,8 +480,8 @@ def on_starting(server): LOGGER.debug(f"Couldn't get the admin user: {e}") sleep(1) - env_admin_username = getenv("ADMIN_USERNAME", "") - env_admin_password = getenv("ADMIN_PASSWORD", "") + env_admin_username = getenv("ADMIN_USERNAME", "").strip() + env_admin_password = getenv("ADMIN_PASSWORD", "").strip() if ADMIN_USER: if invalid_totp_encryption_keys: @@ -490,7 +500,18 @@ def on_starting(server): ADMIN_USER["username"] = env_admin_username updated = True - if env_admin_password and not check_password(env_admin_password, ADMIN_USER["password"]): + if env_admin_password and is_bcrypt_hash(env_admin_password): + if env_admin_password.encode("utf-8") != ADMIN_USER["password"]: + cost = bcrypt_cost(env_admin_password) + if cost < RECOMMENDED_BCRYPT_COST: + LOGGER.warning( + f"The provided bcrypt ADMIN_PASSWORD uses cost factor {cost}, below the recommended {RECOMMENDED_BCRYPT_COST}. " + "Consider re-hashing with a higher cost for stronger protection against offline cracking." + ) + ADMIN_USER["password"] = env_admin_password.encode("utf-8") + updated = True + LOGGER.info("ADMIN_PASSWORD provided as a bcrypt hash; storing it as-is.") + elif env_admin_password and not check_password(env_admin_password, ADMIN_USER["password"]): if not USER_PASSWORD_RX.match(env_admin_password): LOGGER.warning( "The admin password is not strong enough. It must contain at least 8 characters, including at least 1 uppercase letter, 1 lowercase letter, 1 number and 1 special character. It will not be updated." @@ -519,19 +540,33 @@ def on_starting(server): elif env_admin_username and env_admin_password: user_name = env_admin_username or "admin" + prehashed = is_bcrypt_hash(env_admin_password) + if not DEBUG: if len(user_name) > 256: message = "The admin username is too long. It must be less than 256 characters." LOGGER.error(message) ERROR_FILE.write_text(message, encoding="utf-8") exit(1) - elif not USER_PASSWORD_RX.match(env_admin_password): + elif not prehashed and not USER_PASSWORD_RX.match(env_admin_password): message = "The admin password is not strong enough. It must contain at least 8 characters, including at least 1 uppercase letter, 1 lowercase letter, 1 number and 1 special character." LOGGER.error(message) ERROR_FILE.write_text(message, encoding="utf-8") exit(1) - ret = DB.create_ui_user(user_name, gen_password_hash(env_admin_password), ["admin"], admin=True) + if prehashed: + cost = bcrypt_cost(env_admin_password) + if cost < RECOMMENDED_BCRYPT_COST: + LOGGER.warning( + f"The provided bcrypt ADMIN_PASSWORD uses cost factor {cost}, below the recommended {RECOMMENDED_BCRYPT_COST}. " + "Consider re-hashing with a higher cost for stronger protection against offline cracking." + ) + LOGGER.info("ADMIN_PASSWORD provided as a bcrypt hash; storing it as-is.") + password_hash = env_admin_password.encode("utf-8") + else: + password_hash = gen_password_hash(env_admin_password) + + ret = DB.create_ui_user(user_name, password_hash, ["admin"], admin=True) if ret and "already exists" not in ret: message = f"Couldn't create the admin user in the database: {ret}" LOGGER.critical(message) diff --git a/tests/LinuxTest.py b/tests/LinuxTest.py index 690f4fbf7..c0fd03687 100644 --- a/tests/LinuxTest.py +++ b/tests/LinuxTest.py @@ -16,7 +16,6 @@ class LinuxTest(Test): "ubuntu", "debian-bookworm", "debian-trixie", - "fedora-42", "fedora-43", "fedora-44", "centos", diff --git a/tests/Upgrade.py b/tests/Upgrade.py index 22eda6eaa..204cd78eb 100644 --- a/tests/Upgrade.py +++ b/tests/Upgrade.py @@ -4,7 +4,7 @@ import tempfile import time import pathlib -NGINX_VERSION = "1.30.0" +NGINX_VERSION = "1.30.2" distro = sys.argv[1] if distro == "ubuntu": @@ -66,7 +66,7 @@ if distro == "ubuntu": http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \ | sudo tee /etc/apt/sources.list.d/nginx.list sudo apt update && \ - sudo apt install -y nginx=1.30.0-1~jammy + sudo apt install -y nginx=1.30.2-1~jammy sudo apt install /data/bunkerweb.deb -y """ @@ -427,7 +427,7 @@ if distro == "ubuntu": echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \ http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \ | sudo tee /etc/apt/sources.list.d/nginx.list - sudo apt update && sudo apt install -y nginx=1.30.0-1~jammy + sudo apt update && sudo apt install -y nginx=1.30.2-1~jammy curl -s https://repo.bunkerweb.io/install/script.deb.sh | sudo bash && \ sudo apt update && \ sudo apt install -y bunkerweb=1.4.5 @@ -595,7 +595,7 @@ elif distro == "debian": echo "deb-src https://nginx.org/packages/debian/ bookworm nginx" >> /etc/apt/sources.list.d/nginx.list && \ apt-key adv --keyserver keyserver.ubuntu.com --recv-keys ABF5BD827BD9BF62 && \ apt-get update && \ - apt-get install -y --no-install-recommends nginx=1.30.0-1~bookworm + apt-get install -y --no-install-recommends nginx=1.30.2-1~bookworm apt install /data/bunkerweb.deb -y """ @@ -947,7 +947,7 @@ elif distro == "debian": echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \ http://nginx.org/packages/debian `lsb_release -cs` nginx" \ | sudo tee /etc/apt/sources.list.d/nginx.list - sudo apt update && sudo apt install -y nginx=1.30.0-1~bookworm + sudo apt update && sudo apt install -y nginx=1.30.2-1~bookworm curl -s https://repo.bunkerweb.io/install/script.deb.sh | sudo bash && \ sudo apt update && \ sudo apt install -y bunkerweb=1.4.5 @@ -1112,7 +1112,7 @@ elif distro == "fedora": bash_script = """ dnf update -y dnf install -y curl gnupg2 ca-certificates redhat-lsb-core - dnf install -y nginx-1.30.0-1.fc37 + dnf install -y nginx-1.30.1-1.fc37 dnf install /data/bunkerweb.rpm -y """ @@ -1552,7 +1552,7 @@ elif distro == "fedora": "systemd-fedora", "bash", "-c", - "sudo dnf install nginx-1.30.0-1.fc37 -y", + "sudo dnf install nginx-1.30.1-1.fc37 -y", ] ) subprocess.run( @@ -1647,15 +1647,15 @@ elif distro.startswith("rhel"): bash_script = ( """ dnf install yum-utils wget sudo -y - wget https://nginx.org/packages/rhel/9/x86_64/RPMS/nginx-1.30.0-1.el9.ngx.x86_64.rpm - dnf install nginx-1.30.0-1.el9.ngx.x86_64.rpm -y + wget https://nginx.org/packages/rhel/9/x86_64/RPMS/nginx-1.30.2-1.el9.ngx.x86_64.rpm + dnf install nginx-1.30.2-1.el9.ngx.x86_64.rpm -y dnf install /data/bunkerweb.rpm -y """ if distro.endswith("9") else """ dnf install yum-utils wget sudo -y - wget https://nginx.org/packages/rhel/8/x86_64/RPMS/nginx-1.30.0-1.el8.ngx.x86_64.rpm - dnf install nginx-1.30.0-1.el8.ngx.x86_64.rpm -y + wget https://nginx.org/packages/rhel/8/x86_64/RPMS/nginx-1.30.2-1.el8.ngx.x86_64.rpm + dnf install nginx-1.30.2-1.el8.ngx.x86_64.rpm -y dnf install /data/bunkerweb.rpm -y """ ) @@ -2166,7 +2166,7 @@ elif distro == "centos": print("Installing bunkerweb...") bash_script = """ dnf install yum-utils epel-release redhat-lsb-core -y - dnf install -y nginx-1.30.0 + dnf install -y nginx-1.30.2 dnf install /data/bunkerweb.rpm -y """ diff --git a/tests/linux.sh b/tests/linux.sh index 7ab6f5f87..d18da08ef 100644 --- a/tests/linux.sh +++ b/tests/linux.sh @@ -36,7 +36,7 @@ function build_image() { } if [ ! -d /opt/packages ] ; then - do_and_check_cmd sudo mkdir -p /opt/packages/{dev,prod}/{ubuntu,debian-bookworm,debian-trixie,fedora-42,fedora-43,fedora-44,rhel-8,rhel-9,rhel-10} + do_and_check_cmd sudo mkdir -p /opt/packages/{dev,prod}/{ubuntu,debian-bookworm,debian-trixie,fedora-43,fedora-44,rhel-8,rhel-9,rhel-10} do_and_check_cmd sudo chmod -R 777 /opt/packages/ fi @@ -55,8 +55,6 @@ find /opt/packages/ -type f -exec rm -f {} \; # gen_package "$1" "debian" # echo "Building centos package ..." # gen_package "$1" "centos" -echo "Building fedora 42 package ..." -gen_package "$1" "fedora-42" echo "Building fedora 43 package ..." gen_package "$1" "fedora-43" echo "Building fedora 44 package ..." @@ -78,8 +76,6 @@ do_and_check_cmd cp -r "/opt/packages/$1" ./packages # build_image "debian" # echo "Building centos test image ..." # build_image "centos" -echo "Building fedora 42 test image ..." -build_image "fedora-42" echo "Building fedora 43 test image ..." build_image "fedora-43" echo "Building fedora 44 test image ..." diff --git a/tests/linux/Dockerfile-debian-bookworm b/tests/linux/Dockerfile-debian-bookworm index 3059ef42f..9476659e8 100644 --- a/tests/linux/Dockerfile-debian-bookworm +++ b/tests/linux/Dockerfile-debian-bookworm @@ -4,7 +4,7 @@ ENV container=docker ENV LC_ALL=C ENV DEBIAN_FRONTEND=noninteractive ENV OS=debian -ENV NGINX_VERSION=1.30.0 +ENV NGINX_VERSION=1.30.2 ENV EXPECTED_FPR=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 RUN apt-get update \ diff --git a/tests/linux/Dockerfile-debian-trixie b/tests/linux/Dockerfile-debian-trixie index 38ce5cd41..d6312a2db 100644 --- a/tests/linux/Dockerfile-debian-trixie +++ b/tests/linux/Dockerfile-debian-trixie @@ -4,7 +4,7 @@ ENV container=docker ENV LC_ALL=C ENV DEBIAN_FRONTEND=noninteractive ENV OS=debian -ENV NGINX_VERSION=1.30.0 +ENV NGINX_VERSION=1.30.2 ENV EXPECTED_FPR=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 RUN apt-get update \ diff --git a/tests/linux/Dockerfile-fedora-42 b/tests/linux/Dockerfile-fedora-42 deleted file mode 100644 index bd2d34bc3..000000000 --- a/tests/linux/Dockerfile-fedora-42 +++ /dev/null @@ -1,31 +0,0 @@ -FROM fedora:42@sha256:b3d16134560afa00d7cc2a9e4967eb5b954512805f3fe27d8e70bbed078e22ea - -ENV container=docker -ENV NGINX_VERSION=1.30.0 - -RUN dnf -y update \ - && dnf -y install systemd \ - && dnf clean all - -RUN cd /lib/systemd/system/sysinit.target.wants/; \ - for i in *; do [ $i = systemd-tmpfiles-setup.service ] || rm -f $i; done - -RUN rm -f /lib/systemd/system/multi-user.target.wants/* \ - /etc/systemd/system/*.wants/* \ - /lib/systemd/system/local-fs.target.wants/* \ - /lib/systemd/system/sockets.target.wants/*udev* \ - /lib/systemd/system/sockets.target.wants/*initctl* \ - /lib/systemd/system/basic.target.wants/* \ - /lib/systemd/system/anaconda.target.wants/* - -# Nginx -RUN dnf update -y && \ - dnf install -y php-fpm curl gnupg2 ca-certificates python3-pip which && \ - dnf config-manager setopt updates-testing.enabled=1 && \ - dnf install nginx-${NGINX_VERSION} -y - -COPY ./package-fedora-42/*.rpm /opt - -VOLUME ["/sys/fs/cgroup"] - -CMD ["/usr/sbin/init"] diff --git a/tests/linux/Dockerfile-fedora-43 b/tests/linux/Dockerfile-fedora-43 index f4610315d..51e23591b 100644 --- a/tests/linux/Dockerfile-fedora-43 +++ b/tests/linux/Dockerfile-fedora-43 @@ -1,7 +1,7 @@ FROM fedora:43@sha256:6cd815d862109208adf6040ea13391fe6aeb87a9dc80735c2ab07083fdf5e03a ENV container=docker -ENV NGINX_VERSION=1.30.0 +ENV NGINX_VERSION=1.30.1 RUN dnf -y update \ && dnf -y install systemd \ diff --git a/tests/linux/Dockerfile-fedora-44 b/tests/linux/Dockerfile-fedora-44 index 70f4ca7ce..5e844cfc8 100644 --- a/tests/linux/Dockerfile-fedora-44 +++ b/tests/linux/Dockerfile-fedora-44 @@ -1,7 +1,7 @@ FROM fedora:44@sha256:498c452f32a739b61f0ef215bce9924ebc4866cbe44710f58157d77723b7a6d2 ENV container=docker -ENV NGINX_VERSION=1.30.0 +ENV NGINX_VERSION=1.30.1 RUN dnf -y update \ && dnf -y install systemd \ diff --git a/tests/linux/Dockerfile-rhel-10 b/tests/linux/Dockerfile-rhel-10 index abfdc2a21..f85d9b000 100644 --- a/tests/linux/Dockerfile-rhel-10 +++ b/tests/linux/Dockerfile-rhel-10 @@ -1,6 +1,6 @@ -FROM redhat/ubi10-init:10.1@sha256:dfa781f8b52ae2144816cad25b721476cfea40491437161a0b00c88f6b57b984 +FROM redhat/ubi10-init:10.2@sha256:1727635d21ec50eeb108b64342ebb62a0abef68509bebaef537c4acae4043933 -ENV NGINX_VERSION=1.30.0 +ENV NGINX_VERSION=1.30.2 # Copy rocky repo COPY src/linux/rocky-10.repo /etc/yum.repos.d/rocky.repo diff --git a/tests/linux/Dockerfile-rhel-8 b/tests/linux/Dockerfile-rhel-8 index 6c068dd94..37a668dc5 100644 --- a/tests/linux/Dockerfile-rhel-8 +++ b/tests/linux/Dockerfile-rhel-8 @@ -1,6 +1,6 @@ FROM redhat/ubi8-init:8.10@sha256:2cf715e32e908d17546ce8eaeb31c68b2b4de1bbff82b2c16485ba6ae80d1482 -ENV NGINX_VERSION=1.30.0 +ENV NGINX_VERSION=1.30.2 # Copy rocky repo COPY src/linux/rocky-8.repo /etc/yum.repos.d/rocky.repo diff --git a/tests/linux/Dockerfile-rhel-9 b/tests/linux/Dockerfile-rhel-9 index bf08194c8..4748cf0db 100644 --- a/tests/linux/Dockerfile-rhel-9 +++ b/tests/linux/Dockerfile-rhel-9 @@ -1,6 +1,6 @@ -FROM redhat/ubi9-init:9.7@sha256:6632d5c118d796e64d8c3080e424d92c1c1302d51ff1057f13c757d9a2d81ea8 +FROM redhat/ubi9-init:9.8@sha256:1d9b8da63de5fc7bb42add5274792e7f1489f71a5d8d0f137d8d13c112d98b1d -ENV NGINX_VERSION=1.30.0 +ENV NGINX_VERSION=1.30.2 # Copy rocky repo COPY src/linux/rocky-9.repo /etc/yum.repos.d/rocky.repo diff --git a/tests/linux/Dockerfile-ubuntu b/tests/linux/Dockerfile-ubuntu index 7137418a3..f4729eae4 100644 --- a/tests/linux/Dockerfile-ubuntu +++ b/tests/linux/Dockerfile-ubuntu @@ -4,7 +4,7 @@ ENV container=docker ENV LC_ALL=C ENV DEBIAN_FRONTEND=noninteractive ENV OS=ubuntu -ENV NGINX_VERSION=1.30.0 +ENV NGINX_VERSION=1.30.2 ENV EXPECTED_FPR=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 RUN apt-get update \ diff --git a/tests/linux/Dockerfile-ubuntu-jammy b/tests/linux/Dockerfile-ubuntu-jammy index da92884c7..f70881ce2 100644 --- a/tests/linux/Dockerfile-ubuntu-jammy +++ b/tests/linux/Dockerfile-ubuntu-jammy @@ -4,7 +4,7 @@ ENV container=docker ENV LC_ALL=C ENV DEBIAN_FRONTEND=noninteractive ENV OS=ubuntu -ENV NGINX_VERSION=1.30.0 +ENV NGINX_VERSION=1.30.2 ENV EXPECTED_FPR=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 RUN apt-get update \ diff --git a/tests/linux/Linux.sh b/tests/linux/Linux.sh index ce9ac220b..950a2fe30 100644 --- a/tests/linux/Linux.sh +++ b/tests/linux/Linux.sh @@ -28,9 +28,6 @@ function buildPackage() { if [ "$DISTRO" = "debian-trixie" ]; then sudo docker build -t linux-debian-trixie -f src/linux/Dockerfile-debian-trixie . fi - if [ "$DISTRO" = "fedora-42" ]; then - sudo docker build -t linux-fedora-42 -f src/linux/Dockerfile-fedora-42 . - fi if [ "$DISTRO" = "fedora-43" ]; then sudo docker build -t linux-fedora-43 -f src/linux/Dockerfile-fedora-43 . fi @@ -65,9 +62,6 @@ function createContainer() { if [ "$DISTRO" = "debian-trixie" ]; then sudo docker run -v /tmp/debian-trixie:/data linux-debian-trixie fi - if [ "$DISTRO" = "fedora-42" ]; then - sudo docker run -v /tmp/fedora-42:/data linux-fedora-42 - fi if [ "$DISTRO" = "fedora-43" ]; then sudo docker run -v /tmp/fedora-43:/data linux-fedora-43 fi @@ -92,7 +86,7 @@ function createContainer() { # Retrieve $DISTRO from the user function retrieveDistro() { - echo "Which distro do you want to use? (ubuntu, debian-bookworm, debian-trixie, fedora-42, fedora-43, fedora-44, rhel-8, rhel-9, rhel-10)" + echo "Which distro do you want to use? (ubuntu, debian-bookworm, debian-trixie, fedora-43, fedora-44, rhel-8, rhel-9, rhel-10)" read -r DISTRO }